key_word_Fast_API / static /index.html
ihtesham0345's picture
feat: Multi-platform batch generation + simple UI
91bd745
Raw
History Blame Contribute Delete
16.1 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Content Engine - Social Media SEO</title>
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;background:#f0f2f5;color:#1a1a2e;min-height:100vh}
.container{max-width:960px;margin:0 auto;padding:20px}
header{text-align:center;padding:30px 0 8px}
header h1{font-size:26px;font-weight:800;background:linear-gradient(135deg,#4361ee,#7209b7);-webkit-background-clip:text;-webkit-text-fill-color:transparent}
header p{font-size:14px;color:#666;margin-top:4px}
.input-card{background:#fff;border-radius:14px;padding:20px;box-shadow:0 1px 4px rgba(0,0,0,0.07);margin-bottom:20px}
.input-row{display:flex;gap:10px;flex-wrap:wrap}
.input-row input{flex:1;min-width:200px;padding:12px 16px;border:1px solid #ddd;border-radius:8px;font-size:15px;outline:none;transition:border .2s}
.input-row input:focus{border-color:#4361ee}
.btn-row{display:flex;gap:8px;flex-wrap:wrap;margin-top:14px;align-items:center}
.platform-checks{display:flex;gap:8px;flex-wrap:wrap;flex:1}
.platform-check{display:flex;align-items:center;gap:5px;padding:7px 12px;border:1px solid #ddd;border-radius:8px;cursor:pointer;font-size:13px;transition:all .2s;user-select:none;background:#fff}
.platform-check:hover{border-color:#4361ee}
.platform-check.checked{background:#4361ee;color:#fff;border-color:#4361ee}
.platform-check input{display:none}
.action-btns{display:flex;gap:8px;flex-wrap:wrap}
.analyze-btn{padding:10px 20px;background:#4361ee;color:#fff;border:none;border-radius:8px;font-size:14px;font-weight:600;cursor:pointer;transition:background .2s;white-space:nowrap}
.analyze-btn:hover{background:#3651d4}
.analyze-btn:disabled{opacity:0.5;cursor:not-allowed}
.analyze-btn.green{background:#06d6a0}
.analyze-btn.green:hover{background:#05bf8e}
.results{margin-top:10px}
.platform-tabs{display:flex;gap:4px;flex-wrap:wrap;margin-bottom:16px}
.platform-tab{padding:8px 16px;border-radius:8px;font-size:13px;font-weight:500;cursor:pointer;background:#fff;border:1px solid #ddd;transition:all .2s}
.platform-tab:hover{background:#f0f2ff}
.platform-tab.active{background:#4361ee;color:#fff;border-color:#4361ee}
.platform-tab .badge{display:inline-block;margin-left:6px;background:rgba(255,255,255,0.3);padding:0 6px;border-radius:4px;font-size:11px}
.platform-tab.active .badge{background:rgba(255,255,255,0.25)}
.platform-block{display:none}
.platform-block.active{display:block}
.platform-heading{font-size:18px;font-weight:700;margin-bottom:12px;padding-bottom:8px;border-bottom:2px solid #4361ee}
.section{margin-bottom:14px}
.section-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:6px}
.section-title{font-size:14px;font-weight:600;color:#1a1a2e}
.copy-all-btn{padding:4px 12px;border:1px solid #4361ee;border-radius:6px;background:transparent;color:#4361ee;cursor:pointer;font-size:11px;font-weight:500;transition:all .2s}
.copy-all-btn:hover{background:#4361ee;color:#fff}
.section-card{background:#fff;border-radius:10px;padding:14px;box-shadow:0 1px 3px rgba(0,0,0,0.05)}
.item-row{display:flex;justify-content:space-between;align-items:center;padding:7px 0;border-bottom:1px solid #f0f0f0;gap:10px}
.item-row:last-child{border-bottom:none}
.item-text{flex:1;font-size:14px;line-height:1.4;word-break:break-word}
.copy-btn{padding:4px 10px;border:1px solid #ddd;border-radius:5px;background:#fff;cursor:pointer;font-size:11px;color:#666;transition:all .2s;white-space:nowrap;flex-shrink:0}
.copy-btn:hover{border-color:#4361ee;color:#4361ee}
.copy-btn.copied,.copy-all-btn.copied{background:#06d6a0;border-color:#06d6a0;color:#fff}
.single-line{font-size:14px;line-height:1.6;padding:4px 0}
.error-msg{background:#fff0f0;color:#d32f2f;padding:14px 18px;border-radius:8px;font-size:14px;margin-bottom:10px}
.loading{text-align:center;padding:40px 0;color:#888}
.spinner{width:32px;height:32px;border:3px solid #e0e0e0;border-top-color:#4361ee;border-radius:50%;animation:spin .6s linear infinite;margin:0 auto 10px}
@keyframes spin{to{transform:rotate(360deg)}}
.hidden{display:none}
footer{text-align:center;padding:24px;font-size:12px;color:#aaa}
@media(max-width:640px){.btn-row{flex-direction:column}.action-btns{width:100%}.action-btns .analyze-btn{flex:1}}
</style>
</head>
<body>
<div class="container">
<header>
<h1>Content Engine</h1>
<p>One topic. Every platform. Instantly.</p>
</header>
<div class="input-card">
<div class="input-row">
<input type="text" id="queryInput" placeholder="Enter your topic, keyword, or content..." onkeydown="if(event.key==='Enter')generateAll()">
</div>
<div class="btn-row">
<div class="platform-checks" id="platformChecks"></div>
<div class="action-btns">
<button class="analyze-btn" id="analyzeBtn" onclick="analyzeSingle()">Analyze</button>
<button class="analyze-btn green" id="batchBtn" onclick="generateAll()">Generate All Selected</button>
</div>
</div>
</div>
<div id="results" class="results hidden"></div>
<footer>Content Engine &mdash; Powered by AI</footer>
</div>
<script>
const PLATFORMS = [
{id:'youtube',label:'YouTube',icon:'🎬'},
{id:'instagram',label:'Instagram',icon:'📸'},
{id:'linkedin',label:'LinkedIn',icon:'💼'},
{id:'facebook',label:'Facebook',icon:'👍'},
{id:'twitter',label:'X / Twitter',icon:'🐦'},
{id:'tiktok',label:'TikTok',icon:'🎵'},
{id:'pinterest',label:'Pinterest',icon:'📌'},
{id:'grammar',label:'Grammar',icon:'✏️'},
];
let checked = new Set(['youtube']);
let batchData = {};
let currentTab = null;
function init() {
const container = document.getElementById('platformChecks');
PLATFORMS.forEach(p => {
const el = document.createElement('label');
el.className = 'platform-check' + (checked.has(p.id) ? ' checked' : '');
el.innerHTML = `<input type="checkbox" ${checked.has(p.id)?'checked':''}>${p.icon} ${p.label}`;
el.addEventListener('click', (e) => {
if (e.target.tagName !== 'INPUT') {
const cb = el.querySelector('input');
cb.checked = !cb.checked;
}
togglePlatform(p.id, el);
});
el.querySelector('input').addEventListener('change', () => togglePlatform(p.id, el));
container.appendChild(el);
});
document.getElementById('queryInput').focus();
}
function togglePlatform(id, el) {
const cb = el.querySelector('input');
if (cb.checked) checked.add(id); else checked.delete(id);
el.classList.toggle('checked', cb.checked);
}
function getSelected() { return Array.from(checked); }
async function analyzeSingle() {
const platforms = getSelected();
if (platforms.length === 0) return;
await doBatch(platforms);
}
async function generateAll() {
const platforms = getSelected();
if (platforms.length === 0) return;
await doBatch(platforms);
}
async function doBatch(platforms) {
const query = document.getElementById('queryInput').value.trim();
if (!query) return;
const btn = document.getElementById('batchBtn');
const sbtn = document.getElementById('analyzeBtn');
btn.disabled = true; btn.textContent = 'Generating...';
sbtn.disabled = true;
const results = document.getElementById('results');
results.classList.remove('hidden');
results.innerHTML = `<div class="loading"><div class="spinner"></div><div>Generating content for ${platforms.length} platform${platforms.length>1?'s':''}...</div></div>`;
try {
const res = await fetch('/analyze/batch', {
method:'POST', headers:{'Content-Type':'application/json'},
body: JSON.stringify({content: query, platforms})
});
const data = await res.json();
if (!res.ok) throw new Error(data.detail || 'Server error');
batchData = data.results || {};
if (data.error) { results.innerHTML = `<div class="error-msg">${data.error}</div>`; return; }
renderBatch();
} catch (err) {
results.innerHTML = `<div class="error-msg">Error: ${err.message}</div>`;
} finally {
btn.disabled = false; btn.textContent = 'Generate All Selected';
sbtn.disabled = false;
}
}
function renderBatch() {
const ids = Object.keys(batchData).filter(k => batchData[k] && !batchData[k].error);
if (ids.length === 0) {
document.getElementById('results').innerHTML = `<div class="error-msg">No results returned. Try again.</div>`;
return;
}
currentTab = ids[0];
let tabs = '', blocks = '';
ids.forEach(id => {
const p = PLATFORMS.find(x => x.id === id);
const label = p ? `${p.icon} ${p.label}` : id;
const active = id === currentTab ? ' active' : '';
tabs += `<div class="platform-tab${active}" onclick="switchTab('${id}')">${label} <span class="badge">${getSectionCount(batchData[id])}</span></div>`;
blocks += `<div class="platform-block${active}" id="block-${id}">${renderPlatformContent(id, batchData[id])}</div>`;
});
document.getElementById('results').innerHTML = `<div class="platform-tabs">${tabs}</div><div>${blocks}</div>`;
}
function switchTab(id) {
currentTab = id;
document.querySelectorAll('.platform-tab').forEach(t => t.classList.remove('active'));
document.querySelectorAll('.platform-block').forEach(b => b.classList.remove('active'));
document.querySelector(`.platform-tab:nth-child(${Object.keys(batchData).indexOf(id)+1})`).classList.add('active');
document.getElementById('block-'+id).classList.add('active');
}
function getSectionCount(data) {
let c = 0;
for (const k in data) if (Array.isArray(data[k]) && data[k].length > 0) c++;
return c;
}
function renderPlatformContent(platform, data) {
if (!data || data.error) return `<div class="error-msg">${data.error || 'No data'}</div>`;
let h = '';
if (platform === 'youtube') {
h += renderSection('Titles', data.video_titles, (t) => `${t.title} (CTR: ${t.expected_ctr})`);
h += renderListSection('Tags', data.tags);
h += renderKV('Description Template', data.description_template);
h += renderListSection('Thumbnail Ideas', data.thumbnail_ideas);
h += renderKV('Best Posting Time', data.best_posting_time);
h += renderListSection('Engagement Tips', data.engagement_strategies);
h += renderKV('Competition Gap', data.competition_gap_analysis);
} else if (platform === 'instagram') {
h += renderSection('Captions', data.captions, (c) => `${c.caption} (Tone: ${c.tone})`);
if (data.hashtag_sets) {
h += renderListSection('Hashtags (Small)', data.hashtag_sets.small);
h += renderListSection('Hashtags (Medium)', data.hashtag_sets.medium);
h += renderListSection('Hashtags (Large)', data.hashtag_sets.large);
}
h += renderKV('Best Posting Time', data.best_posting_time);
if (data.content_ideas) {
h += renderListSection('Reel Ideas', data.content_ideas.reels);
h += renderListSection('Carousel Ideas', data.content_ideas.carousels);
h += renderListSection('Story Ideas', data.content_ideas.stories);
}
h += renderListSection('Growth Strategies', data.growth_strategies);
} else if (platform === 'linkedin') {
h += renderSection('Post Drafts', data.post_drafts, (d) => `${d.headline} — Hook: ${d.hook} | Body: ${d.body}`);
h += renderListSection('Hashtags', data.hashtags);
h += renderListSection('Article Topics', data.article_topics);
h += renderListSection('Leadership Angles', data.thought_leadership_angles);
h += renderKV('Best Posting Time', data.best_posting_time);
h += renderKV('Industry Insights', data.industry_insights);
} else if (platform === 'facebook') {
h += renderSection('Post Ideas', data.post_ideas, (p) => `${p.type}: ${p.content}`);
h += renderListSection('Engagement Hooks', data.engagement_hooks);
h += renderListSection('Hashtags', data.hashtags);
h += renderListSection('Ad Copy Suggestions', data.ad_copy_suggestions);
h += renderKV('Best Posting Time', data.best_posting_time);
h += renderListSection('Page Growth Tips', data.page_growth_tips);
} else if (platform === 'twitter') {
h += renderSection('Tweet Threads', data.tweet_threads, (t) => `${t.theme}: ${t.tweets.join(' | ')}`);
h += renderListSection('Viral Hooks', data.viral_hooks);
h += renderListSection('Hashtags', data.hashtags);
h += renderKV('Best Posting Time', data.best_posting_time);
h += renderListSection('Engagement Tactics', data.engagement_tactics);
} else if (platform === 'tiktok') {
h += renderSection('Video Concepts', data.video_concepts, (v) => `Hook: ${v.hook} | Script: ${v.script_snippet} | Sound: ${v.sound_suggestion}`);
h += renderListSection('Trending Angles', data.trending_angles);
h += renderListSection('Hashtags', data.hashtags);
h += renderKV('Best Posting Time', data.best_posting_time);
h += renderListSection('Viral Strategies', data.viral_strategies);
} else if (platform === 'pinterest') {
h += renderSection('Pin Ideas', data.pin_ideas, (p) => `${p.title}${p.description} | Keywords: ${p.keyword_focus}`);
h += renderListSection('Board Organization', data.board_organization);
h += renderListSection('SEO Keywords', data.seo_keywords);
h += renderKV('Best Posting Time', data.best_posting_time);
h += renderListSection('Traffic Strategies', data.traffic_strategies);
} else if (platform === 'grammar') {
h += `<div class="section"><div class="section-header"><span class="section-title">Grammar Score: ${data.grammar_score}/100</span></div><div class="section-card">`;
h += `<div class="item-row"><div class="item-text"><strong>Corrected Text</strong></div><button class="copy-btn" onclick="copyText(this,'${esc(data.corrected_text)}')">Copy</button></div></div></div>`;
h += renderSection('Corrections', data.corrections, (c) => `${c.original}${c.corrected} (${c.error_type}: ${c.explanation})`);
h += renderSection('Issues', data.issues, (iss) => `${iss.issue_type} at "${iss.location}": ${iss.suggestion}`);
h += renderKV('Readability', data.readability_score);
h += renderKV('Word Count', data.word_count);
h += renderKV('Sentence Count', data.sentence_count);
h += renderKV('Tone', data.tone);
h += renderListSection('Style Suggestions', data.style_suggestions);
}
return h;
}
function esc(s) { return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;'); }
function copyText(btn, text) {
navigator.clipboard.writeText(text).then(() => {
btn.textContent = 'Copied!';
btn.classList.add('copied');
setTimeout(() => { btn.textContent = 'Copy'; btn.classList.remove('copied'); }, 1500);
});
}
function copyAll(btn, items, sep) {
const text = items.join(sep);
navigator.clipboard.writeText(text).then(() => {
btn.textContent = 'Copied!';
btn.classList.add('copied');
setTimeout(() => { btn.textContent = 'Copy All'; btn.classList.remove('copied'); }, 1500);
});
}
function renderSection(title, items, format) {
const arr = items || [];
const texts = arr.map((item,i) => format(item,i));
let h = `<div class="section"><div class="section-header"><span class="section-title">${title}</span>`;
if (texts.length > 0) h += `<button class="copy-all-btn" onclick="copyAll(this,${JSON.stringify(texts)},'\\n\\n')">Copy All</button>`;
h += `</div><div class="section-card">`;
if (texts.length === 0) { h += `<div class="single-line" style="color:#999">No data</div>`; }
else {
texts.forEach(t => {
h += `<div class="item-row"><span class="item-text">${t}</span><button class="copy-btn" onclick="copyText(this,'${esc(t)}')">Copy</button></div>`;
});
}
h += `</div></div>`;
return h;
}
function renderListSection(title, items) {
return renderSection(title, items, (item) => item);
}
function renderKV(title, value) {
if (value == null || value === '') return '';
return `<div class="section"><div class="section-header"><span class="section-title">${title}</span><button class="copy-all-btn" onclick="copyText(this,'${esc(value)}')">Copy</button></div><div class="section-card"><div class="single-line">${value}</div></div></div>`;
}
init();
</script>
</body>
</html>