/* JustVitamin × QuikCue — Live AI Demos
Demo A renders as a real conversion-optimised PDP page */
const $ = s => document.querySelector(s);
const esc = s => { if (!s) return ''; const d = document.createElement('div'); d.textContent = s; return d.innerHTML; };
function setStep(id, cls, text) {
const el = $(id);
if (!el) return;
el.parentElement.className = `step ${cls}`;
el.innerHTML = text;
}
function setBtn(id, loading, text) {
const btn = $(id);
btn.disabled = loading;
btn.classList.toggle('loading', loading);
btn.textContent = text;
}
// ═══════════════════════════════════════════════════════════════
// DEMO A — Product URL → Full PDP + Real Product Images
// ═══════════════════════════════════════════════════════════════
let demoA_product = null;
let demoA_pack = null;
let demoA_imgs = null;
async function runDemoA() {
const url = $('#demoA-url').value.trim();
if (!url) return;
setBtn('#demoA-btn', true, 'Working...');
$('#demoA-output').classList.add('hidden');
['#a-s1','#a-s2','#a-s3'].forEach(s => setStep(s, '', 'Waiting'));
// Step 1: Scrape
setStep('#a-s1', 'active', 'Scraping product page...');
try {
const r = await fetch('/api/scrape', {
method: 'POST', headers: {'Content-Type':'application/json'},
body: JSON.stringify({url})
});
const product = await r.json();
if (product.error) throw new Error(product.error);
demoA_product = product;
setStep('#a-s1', 'done', `✓ ${esc(product.title.substring(0,40))}...`);
} catch(e) {
setStep('#a-s1', 'error', `✗ ${esc(e.message)}`);
setBtn('#demoA-btn', false, '🔴 Generate the Whole Pack');
return;
}
// Step 2 + 3: AI copy + images in parallel
setStep('#a-s2', 'active', 'Gemini generating PDP copy...');
setStep('#a-s3', 'active', 'Generating product images from real photo...');
const packP = fetch('/api/generate-pack', {
method: 'POST', headers: {'Content-Type':'application/json'},
body: JSON.stringify(demoA_product)
}).then(r => r.json());
const imgP = fetch('/api/generate-images', {
method: 'POST', headers: {'Content-Type':'application/json'},
body: JSON.stringify(demoA_product)
}).then(r => r.json());
let packOk = false, imgsOk = false;
try {
demoA_pack = await packP;
if (demoA_pack.error) throw new Error(demoA_pack.error);
packOk = true;
setStep('#a-s2', 'done', `✓ PDP + assets generated (${demoA_pack._generation_time})`);
} catch(e) {
setStep('#a-s2', 'error', `✗ ${esc(e.message)}`);
}
try {
demoA_imgs = await imgP;
if (demoA_imgs.error && !demoA_imgs.original) throw new Error(demoA_imgs.error);
imgsOk = true;
setStep('#a-s3', 'done', `✓ Product images generated (${demoA_imgs._generation_time || ''})`);
} catch(e) {
setStep('#a-s3', 'error', `✗ ${esc(e.message)}`);
}
if (packOk) {
renderPDP(demoA_product, demoA_pack, demoA_imgs);
$('#demoA-output').classList.remove('hidden');
}
setBtn('#demoA-btn', false, '✓ Done — Run Again');
}
// ── Render as a real PDP page ───────────────────────────────
function renderPDP(product, pack, imgs) {
const pdp = pack.pdp || {};
const out = $('#demoA-pdp');
if (!out) return;
// Build gallery images
const gallery = buildGallery(product, imgs);
const bullets = (pdp.benefit_bullets || []).map(b =>
`
${esc(b.icon||'✓')}
${esc(b.headline)}
${esc(b.detail)}
${b.proof ? `${esc(b.proof)}` : ''}
`
).join('');
const trustBar = (pdp.trust_signals || []).map(t =>
`${esc(t.icon)}${esc(t.text)}
`
).join('');
const whyParas = (pdp.why_paragraphs || []).map(p => `${esc(p)}
`).join('');
const statsBar = (pdp.stats_bar || []).map(s =>
`${esc(s.number)}${esc(s.label)}
`
).join('');
const faq = (pdp.faq || []).map((f,i) =>
`
${esc(f.q)}
${esc(f.a)}
`
).join('');
const priceInfo = pdp.price_display || {};
const review = pdp.review_quote || {};
// Ad hooks section
const hooks = (pack.ad_hooks || []).map(h =>
`
"${esc(h.hook || h)}"
${h.angle ? `
${esc(h.angle)} · ${esc(h.platform||'')}` : ''}
`
).join('');
// Email subjects
const emails = (pack.email_subjects || []).map(e =>
`
${esc(e.flow||'')}
${esc(e.subject)}
${esc(e.preview)}
`
).join('');
// Meta SEO
const meta = pack.meta_seo || {};
out.innerHTML = `
${gallery}
${esc(product.category)} › ${esc(product.title.split(' ').slice(0,4).join(' '))}...
${esc(pdp.hero_headline || product.title)}
${esc(pdp.hero_subhead || product.subtitle)}
${(pdp.value_props||[]).map(v=>`✓ ${esc(v)}`).join('')}
${esc(priceInfo.main_price || product.price)}
${esc(product.quantity)}
${esc(priceInfo.price_per_day || '')}
${esc(priceInfo.comparison || '')}
${trustBar}
${pdp.urgency_note ? `
⚡ ${esc(pdp.urgency_note)}
` : ''}
${esc(pdp.usage_instructions || '')}
${statsBar}
${esc(pdp.why_section_title || 'Why This Formula')}
${whyParas}
${'★'.repeat(review.stars||5)}
"${esc(review.text || '')}"
— ${esc(review.author || 'Verified Buyer')}
Frequently Asked Questions
${faq}
📦 Additional Generated Assets
${meta.title ? `
🔍 Meta SEO
${esc(meta.title)}
justvitamins.co.uk › ${esc(meta.primary_keyword || '')}
${esc(meta.description)}
` : ''}
${hooks ? `
` : ''}
${emails ? `
📧 Email Sequences
${emails}
` : ''}
`;
}
function buildGallery(product, imgs) {
const items = [];
// Original product images first
(product.images || []).forEach((src, i) => {
items.push({src: src, label: i === 0 ? 'Original — Main' : `Original — ${i+1}`, isOriginal: true});
});
// AI-generated images
if (imgs) {
const aiSlots = [
{key:'hero', label:'AI — Clean Studio'},
{key:'lifestyle', label:'AI — Lifestyle'},
{key:'scale', label:'AI — Scale'},
{key:'ingredients', label:'AI — Detail'},
{key:'banner', label:'AI — Banner', wide: true},
];
aiSlots.forEach(slot => {
const d = imgs[slot.key];
if (d && d.filename) {
items.push({src: `/generated/${d.filename}`, label: slot.label,
caption: d.caption || '', model: d.model || '', wide: slot.wide});
}
});
}
if (!items.length) return 'No images available
';
const mainImg = items[0];
const thumbs = items.map((item, i) =>
`
${esc(item.label)}
`
).join('');
return `
${mainImg.isOriginal ? '📷 Original' : '🤖 AI Generated'}
${thumbs}
`;
}
window.switchGallery = function(idx, el) {
const src = el.dataset.src;
const mainImg = $('#pdpMainImg');
if (mainImg) mainImg.src = src;
document.querySelectorAll('.pdp-thumb').forEach(t => t.classList.remove('active'));
el.classList.add('active');
const badge = $('.pdp-gallery-badge');
if (badge) {
const label = el.querySelector('.pdp-thumb-label');
badge.textContent = label?.classList.contains('orig') ? '📷 Original' : '🤖 AI Generated';
}
};
// ═══════════════════════════════════════════════════════════════
// DEMO B — Competitor X-Ray (unchanged logic, same render)
// ═══════════════════════════════════════════════════════════════
async function runDemoB() {
const url = $('#demoB-url').value.trim();
if (!url) return;
setBtn('#demoB-btn', true, 'Scanning...');
$('#demoB-output').classList.add('hidden');
setStep('#b-s1', 'active', 'Scraping competitor...');
setStep('#b-s2', '', 'Waiting');
try {
const r = await fetch('/api/competitor-xray', {
method: 'POST', headers: {'Content-Type':'application/json'},
body: JSON.stringify({url})
});
const data = await r.json();
if (data.error) throw new Error(data.error);
setStep('#b-s1', 'done', `✓ ${esc((data._scrape_data?.title||'').substring(0,30))}...`);
setStep('#b-s2', 'done', `✓ Analysis (${data._generation_time})`);
renderXray(data);
$('#demoB-output').classList.remove('hidden');
setBtn('#demoB-btn', false, '✓ Done — Try Another');
} catch(e) {
setStep('#b-s1', 'error', `✗ ${esc(e.message)}`);
setBtn('#demoB-btn', false, '🔍 X-Ray This Competitor');
}
}
function renderXray(data) {
const tactics = (data.top_5_tactics||[]).map(t =>
`${esc(t.tactic)} — ${esc(t.explanation)}`).join('');
$('#demoB-left').innerHTML = `
❌ ${esc(data.competitor_name || 'Competitor')}
What they're really selling
${esc(data.what_theyre_selling)}
Top 5 Persuasion Tactics
${tactics}
Weakest Claim / Gap
⚠️ ${esc(data.weakest_claim)}
`;
const hero = data.jv_hero_section || {};
const diffs = (data.differentiators||[]).map(d =>
`🎯${esc(d.point)} — ${esc(d.proof_idea)}`).join('');
const donts = (data.do_not_say||[]).map(d => `${esc(d)}`).join('');
$('#demoB-right').innerHTML = `
✓ Just Vitamins — Upgraded
${esc(hero.headline)}
${esc(hero.body)}
${esc(hero.value_prop)}
`;
}
// ═══════════════════════════════════════════════════════════════
// DEMO C — PDP Surgeon
// ═══════════════════════════════════════════════════════════════
let demoC_product = null, demoC_cache = {};
async function runDemoC() {
const url = $('#demoC-url').value.trim();
if (!url) return;
setBtn('#demoC-btn', true, 'Working...');
$('#demoC-output').classList.add('hidden');
demoC_cache = {};
setStep('#c-s1', 'active', 'Scraping product...');
setStep('#c-s2', '', 'Waiting');
try {
const r = await fetch('/api/scrape', {method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({url})});
const product = await r.json();
if (product.error) throw new Error(product.error);
demoC_product = product;
setStep('#c-s1', 'done', `✓ ${esc(product.title.substring(0,35))}...`);
} catch(e) {
setStep('#c-s1', 'error', `✗ ${esc(e.message)}`);
setBtn('#demoC-btn', false, '🎨 Scrape & Rewrite');
return;
}
const active = document.querySelector('#demoC-toggles .toggle.active');
await rewriteStyle(active?.dataset.style || 'balanced');
setBtn('#demoC-btn', false, '✓ Done — Change URL');
}
async function switchDemoC(style, btn) {
document.querySelectorAll('#demoC-toggles .toggle').forEach(t => t.classList.remove('active'));
btn.classList.add('active');
if (!demoC_product) return;
await rewriteStyle(style);
}
async function rewriteStyle(style) {
if (demoC_cache[style]) { renderSurgeon(demoC_product, demoC_cache[style]); return; }
setStep('#c-s2', 'active', `Gemini rewriting as ${style}...`);
try {
const r = await fetch('/api/pdp-surgeon', {method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({product:demoC_product,style})});
const result = await r.json();
if (result.error) throw new Error(result.error);
demoC_cache[style] = result;
setStep('#c-s2', 'done', `✓ ${style} (${result._generation_time})`);
renderSurgeon(demoC_product, result);
$('#demoC-output').classList.remove('hidden');
} catch(e) { setStep('#c-s2', 'error', `✗ ${esc(e.message)}`); }
}
function renderSurgeon(product, result) {
const bullets = (product.benefits||[]).map(b => `${esc(b)}`).join('');
$('#demoC-left').innerHTML = `
✕ Current PDP
${product.images?.[0] ? `
` : ''}
${esc(product.title)}
${esc(product.subtitle)}
${esc(product.price)} ${esc(product.quantity)}
${bullets.replace(/- /g,'
- ')}
`;
const rBullets = (result.bullets||[]).map(b =>
`${esc(b.text)}
↑ ${esc(b.annotation)}`).join('');
$('#demoC-right').innerHTML = `
✓ ${esc(result.style||'balanced').toUpperCase()}
${esc(result.title)}
↑ SEO title
${esc(result.subtitle)}
${esc(result.hero_copy)}
↑ ${esc(result.hero_annotation)}
${rBullets}
⭐ ${esc(result.social_proof)}
↑ ${esc(result.social_proof_annotation)}
${esc(result.price_reframe)}
↑ ${esc(result.price_annotation)}
${esc(result.usage_instruction)}
↑ ${esc(result.usage_annotation)}
${result.cta_annotation ? `↑ ${esc(result.cta_annotation)}` : ''}`;
}
// Smooth scroll
document.querySelectorAll('a[href^="#"]').forEach(a => {
a.addEventListener('click', e => {
const t = document.querySelector(a.getAttribute('href'));
if (t) { e.preventDefault(); t.scrollIntoView({behavior:'smooth'}); }
});
});