feat: enhance Google Ads export with XLSX and high-fidelity HTML reports
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
import * as XLSX from 'xlsx';
|
||||
import { GoogleAdsResult, MagicWandResult } from "../types";
|
||||
|
||||
export const downloadFile = (filename: string, content: string, contentType: string) => {
|
||||
export const downloadFile = (filename: string, content: any, contentType: string) => {
|
||||
if (typeof window === 'undefined') return;
|
||||
const blob = new Blob([content], { type: contentType });
|
||||
const blob = content instanceof Blob ? content : new Blob([content], { type: contentType });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const link = document.createElement("a");
|
||||
link.setAttribute("href", url);
|
||||
@@ -25,21 +26,21 @@ export const generateGoogleAdsCSV = (googleAds?: any, magic?: any): string => {
|
||||
const kw = googleAds.keywords;
|
||||
if (kw) {
|
||||
rows.push(["KEYWORD RESEARCH"]);
|
||||
rows.push(["Type", "Keyword", "CPC"]);
|
||||
if (Array.isArray(kw.primary)) kw.primary.forEach((k: any) => rows.push(["Primary", String(k?.keyword || ''), String(k?.cpc || '')]));
|
||||
if (Array.isArray(kw.longTail)) kw.longTail.forEach((k: any) => rows.push(["Long-tail", String(k?.keyword || ''), String(k?.cpc || '')]));
|
||||
if (Array.isArray(kw.negative)) kw.negative.forEach((k: any) => rows.push(["Negative", String(k?.keyword || ''), String(k?.cpc || '')]));
|
||||
rows.push(["Type", "Keyword", "CPC", "Volume", "Competition"]);
|
||||
if (Array.isArray(kw.primary)) kw.primary.forEach((k: any) => rows.push(["Primary", String(k?.keyword || ''), String(k?.cpc || ''), String(k?.searchVolume || ''), String(k?.competition || '')]));
|
||||
if (Array.isArray(kw.longTail)) kw.longTail.forEach((k: any) => rows.push(["Long-tail", String(k?.keyword || ''), String(k?.cpc || ''), String(k?.searchVolume || ''), String(k?.competition || '')]));
|
||||
if (Array.isArray(kw.negative)) kw.negative.forEach((k: any) => rows.push(["Negative", String(k?.keyword || ''), "", "", String(k?.competition || '')]));
|
||||
rows.push([]);
|
||||
}
|
||||
|
||||
const ads = googleAds.adCopies;
|
||||
if (Array.isArray(ads)) {
|
||||
rows.push(["AD COPIES"]);
|
||||
rows.push(["Variation", "Headlines", "Descriptions", "CTA"]);
|
||||
rows.push(["Variation", "Headlines", "Descriptions", "CTA", "Optimized", "Positioning"]);
|
||||
ads.forEach((ad: any, i: number) => {
|
||||
const hl = Array.isArray(ad.headlines) ? ad.headlines.join(' | ') : '';
|
||||
const ds = Array.isArray(ad.descriptions) ? ad.descriptions.join(' | ') : '';
|
||||
rows.push([`Var ${i + 1}`, hl, ds, String(ad?.callToAction || '')]);
|
||||
rows.push([`Var ${i + 1}`, hl, ds, String(ad?.callToAction || ''), String(ad?.mobileOptimized || 'false'), String(ad?.positioning || '')]);
|
||||
});
|
||||
rows.push([]);
|
||||
}
|
||||
@@ -47,23 +48,15 @@ export const generateGoogleAdsCSV = (googleAds?: any, magic?: any): string => {
|
||||
const camps = googleAds.campaigns;
|
||||
if (Array.isArray(camps)) {
|
||||
rows.push(["CAMPAIGN STRUCTURE"]);
|
||||
rows.push(["Name", "Type", "Budget", "Locations", "Schedule"]);
|
||||
rows.push(["Name", "Type", "Budget", "Locations", "Targeting", "Bidding"]);
|
||||
camps.forEach((c: any) => {
|
||||
const t = c.targeting;
|
||||
const locs = (t && Array.isArray(t.locations)) ? t.locations.join('; ') : 'All';
|
||||
const sched = (t && Array.isArray(t.schedule)) ? t.schedule.join('; ') : 'All';
|
||||
rows.push([String(c.name || ''), String(c.type || ''), `${c?.budget?.daily || 0} ${c?.budget?.currency || ''}`, locs, sched]);
|
||||
const demos = (t && t.demographics) ? `Age: ${t.demographics.age?.join(', ') || 'Any'}; Gender: ${t.demographics.gender?.join(', ') || 'Any'}` : 'All';
|
||||
rows.push([String(c.name || ''), String(c.type || ''), `${c?.budget?.daily || 0} ${c?.budget?.currency || ''}`, locs, demos, String(c.biddingStrategy || '')]);
|
||||
});
|
||||
rows.push([]);
|
||||
}
|
||||
|
||||
const impl = googleAds.implementation;
|
||||
if (impl) {
|
||||
rows.push(["IMPLEMENTATION"]);
|
||||
if (Array.isArray(impl.setupSteps)) impl.setupSteps.forEach((s: any) => rows.push(["Setup", String(s)]));
|
||||
if (Array.isArray(impl.qualityScoreTips)) impl.qualityScoreTips.forEach((s: any) => rows.push(["QS Tip", String(s)]));
|
||||
rows.push([]);
|
||||
}
|
||||
}
|
||||
|
||||
if (magic) {
|
||||
@@ -83,8 +76,9 @@ export const generateGoogleAdsCSV = (googleAds?: any, magic?: any): string => {
|
||||
strats.forEach((s: any) => {
|
||||
rows.push(["Direction", String(s.direction || '')]);
|
||||
rows.push(["Target", String(s.targetAudience || '')]);
|
||||
rows.push(["Rationale", String(s.rationale || '')]);
|
||||
rows.push(["ROI", String(s.expectedROI || '')]);
|
||||
rows.push(["Risk", String(s.riskLevel || '')]);
|
||||
rows.push(["Timeframe", String(s.timeToResults || '')]);
|
||||
rows.push([]);
|
||||
});
|
||||
}
|
||||
@@ -93,54 +87,485 @@ export const generateGoogleAdsCSV = (googleAds?: any, magic?: any): string => {
|
||||
return rows.map(r => r.map(c => `"${String(c || '').replace(/"/g, '""')}"`).join(",")).join("\n");
|
||||
};
|
||||
|
||||
export const generateGoogleAdsHTML = (googleAds?: any, magic?: any): string => {
|
||||
const parts: string[] = [];
|
||||
parts.push(`<!DOCTYPE html><html><head><title>Report</title><style>
|
||||
body { font-family: sans-serif; background: #0f172a; color: #e2e8f0; padding: 40px; }
|
||||
.section { background: #1e293b; border-radius: 12px; padding: 24px; margin-bottom: 24px; border: 1px solid rgba(255,255,255,0.1); }
|
||||
h1 { color: #818cf8; }
|
||||
.grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
|
||||
.card { background: rgba(0,0,0,0.2); padding: 16px; border-radius: 8px; }
|
||||
</style></head><body>`);
|
||||
export const generateGoogleAdsExcel = (googleAds?: any, magic?: any): Blob => {
|
||||
const wb = XLSX.utils.book_new();
|
||||
|
||||
parts.push(`<h1>Marketing Strategy</h1>`);
|
||||
// 1. Overview Sheet
|
||||
const overviewData: any[] = [
|
||||
["Attribute", "Value"],
|
||||
["Report Title", "Google Ads Strategy Report"],
|
||||
["Generated At", new Date().toLocaleString()],
|
||||
["Website URL", googleAds?.websiteUrl || magic?.websiteUrl || 'N/A'],
|
||||
[],
|
||||
["Performance Forecasts"],
|
||||
["Est. Impressions", googleAds?.predictions?.estimatedImpressions || 'N/A'],
|
||||
["Est. Clicks", googleAds?.predictions?.estimatedClicks || 'N/A'],
|
||||
["Est. CTR", googleAds?.predictions?.estimatedCtr || 'N/A'],
|
||||
["Est. Conversions", googleAds?.predictions?.estimatedConversions || 'N/A'],
|
||||
["Est. Conversion Rate", googleAds?.predictions?.conversionRate || 'N/A'],
|
||||
["Avg. CPC", googleAds?.predictions?.avgCpc || 'N/A'],
|
||||
[],
|
||||
["Historical Benchmarks"],
|
||||
["Avg. Industry CTR", googleAds?.historicalBenchmarks?.industryAverageCtr || 'N/A'],
|
||||
["Avg. Industry CPC", googleAds?.historicalBenchmarks?.industryAverageCpc || 'N/A'],
|
||||
["Seasonal Trends", googleAds?.historicalBenchmarks?.seasonalTrends || 'N/A'],
|
||||
["Geographic Insights", googleAds?.historicalBenchmarks?.geographicInsights || 'N/A']
|
||||
];
|
||||
const overviewSheet = XLSX.utils.aoa_to_sheet(overviewData);
|
||||
XLSX.utils.book_append_sheet(wb, overviewSheet, "Overview");
|
||||
|
||||
if (googleAds) {
|
||||
parts.push(`<div class="section"><h2>Keywords</h2>`);
|
||||
// 2. Keywords Sheet
|
||||
if (googleAds?.keywords) {
|
||||
const kw = googleAds.keywords;
|
||||
if (kw && Array.isArray(kw.primary)) {
|
||||
parts.push(`<h3>Primary</h3>`);
|
||||
kw.primary.forEach((k: any) => parts.push(`<span>${k.keyword}</span> `));
|
||||
}
|
||||
parts.push(`</div>`);
|
||||
const kwData: any[] = [
|
||||
["Type", "Keyword", "Avg. CPC", "Monthly Volume", "Competition", "Difficulty"]
|
||||
];
|
||||
if (Array.isArray(kw.primary)) kw.primary.forEach((k: any) => kwData.push(["Primary", k.keyword, k.cpc, k.searchVolume, k.competition, k.difficultyScore]));
|
||||
if (Array.isArray(kw.longTail)) kw.longTail.forEach((k: any) => kwData.push(["Long-tail", k.keyword, k.cpc, k.searchVolume, k.competition, k.difficultyScore]));
|
||||
if (Array.isArray(kw.negative)) kw.negative.forEach((k: any) => kwData.push(["Negative", k.keyword, "", "", k.competition, ""]));
|
||||
|
||||
const ads = googleAds.adCopies;
|
||||
if (Array.isArray(ads)) {
|
||||
parts.push(`<div class="section"><h2>Ads</h2><div class="grid">`);
|
||||
ads.forEach((ad: any) => {
|
||||
parts.push(`<div class="card"><b>${(ad.headlines || [])[0] || ''}</b><p>${(ad.descriptions || [])[0] || ''}</p></div>`);
|
||||
});
|
||||
parts.push(`</div></div>`);
|
||||
}
|
||||
const kwSheet = XLSX.utils.aoa_to_sheet(kwData);
|
||||
XLSX.utils.book_append_sheet(wb, kwSheet, "Keywords");
|
||||
}
|
||||
|
||||
if (magic) {
|
||||
parts.push(`<div class="section"><h2>Market</h2>`);
|
||||
const ma = magic.marketAnalysis;
|
||||
if (ma) {
|
||||
parts.push(`<p>Size: ${ma.industrySize || 'N/A'}</p>`);
|
||||
parts.push(`<p>Growth: ${ma.growthRate || 'N/A'}</p>`);
|
||||
}
|
||||
const strats = magic.strategies;
|
||||
if (Array.isArray(strats)) {
|
||||
parts.push(`<h2>Strategies</h2>`);
|
||||
strats.forEach((s: any) => {
|
||||
parts.push(`<div class="card"><h3>${s.direction}</h3><p>${s.rationale}</p></div>`);
|
||||
});
|
||||
}
|
||||
parts.push(`</div>`);
|
||||
// 3. Ad Copies Sheet
|
||||
if (Array.isArray(googleAds?.adCopies)) {
|
||||
const adData: any[] = [
|
||||
["ID", "Variation", "Headlines", "Descriptions", "CTA", "Mobile Optimized", "Strategic Positioning"]
|
||||
];
|
||||
googleAds.adCopies.forEach((ad: any, i: number) => {
|
||||
adData.push([
|
||||
ad.id,
|
||||
`Variation ${i + 1}`,
|
||||
(ad.headlines || []).join(" | "),
|
||||
(ad.descriptions || []).join(" | "),
|
||||
ad.callToAction,
|
||||
ad.mobileOptimized ? "Yes" : "No",
|
||||
ad.positioning || "N/A"
|
||||
]);
|
||||
});
|
||||
const adSheet = XLSX.utils.aoa_to_sheet(adData);
|
||||
XLSX.utils.book_append_sheet(wb, adSheet, "Ad Copies");
|
||||
}
|
||||
|
||||
parts.push(`</body></html>`);
|
||||
return parts.join('');
|
||||
// 4. Competitors Sheet
|
||||
if (magic?.competitorInsights || magic?.marketAnalysis) {
|
||||
const compData: any[] = [
|
||||
["Competitor", "Website", "Est. Monthly Spend", "Target Audience", "Strengths", "Weaknesses", "Ad Strategy", "Top Keywords"]
|
||||
];
|
||||
if (Array.isArray(magic.competitorInsights)) {
|
||||
magic.competitorInsights.forEach((c: any) => {
|
||||
compData.push([
|
||||
c.competitor,
|
||||
c.website || 'N/A',
|
||||
c.estimatedSpend || 'N/A',
|
||||
c.targetAudience || 'N/A',
|
||||
(c.strengths || []).join(", "),
|
||||
(c.weaknesses || []).join(", "),
|
||||
c.adStrategy,
|
||||
(c.topKeywords || []).join(", ")
|
||||
]);
|
||||
});
|
||||
}
|
||||
const compSheet = XLSX.utils.aoa_to_sheet(compData);
|
||||
XLSX.utils.book_append_sheet(wb, compSheet, "Competitor Analysis");
|
||||
}
|
||||
|
||||
// 5. Strategies Sheet
|
||||
if (Array.isArray(magic?.strategies)) {
|
||||
const stratData: any[] = [
|
||||
["ID", "Strategic Direction", "Rationale", "Target Audience", "Competitive Advantage", "Expected ROI", "Risk Level", "Time to Results", "Success Metrics"]
|
||||
];
|
||||
magic.strategies.forEach((s: any) => {
|
||||
stratData.push([
|
||||
s.id,
|
||||
s.direction,
|
||||
s.rationale,
|
||||
s.targetAudience,
|
||||
s.competitiveAdvantage,
|
||||
s.expectedROI,
|
||||
s.riskLevel,
|
||||
s.timeToResults,
|
||||
(s.successMetrics || []).join(", ")
|
||||
]);
|
||||
});
|
||||
const stratSheet = XLSX.utils.aoa_to_sheet(stratData);
|
||||
XLSX.utils.book_append_sheet(wb, stratSheet, "Strategies");
|
||||
}
|
||||
|
||||
const wbout = XLSX.write(wb, { bookType: 'xlsx', type: 'array' });
|
||||
return new Blob([wbout], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
|
||||
};
|
||||
|
||||
export const generateGoogleAdsHTML = (googleAds?: any, magic?: any): string => {
|
||||
const data = JSON.stringify({ googleAds, magic }, null, 2);
|
||||
|
||||
return `<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>360° Google Ads Strategy Intelligence Report</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600;800&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body { font-family: 'Outfit', sans-serif; background-color: #020617; color: #f8fafc; }
|
||||
.glass { background: rgba(30, 41, 59, 0.7); backdrop-filter: blur(12px); border: 1px solid rgba(255, 255, 255, 0.1); }
|
||||
.accent-gradient { background: linear-gradient(135deg, #6366f1 0%, #a855f7 100%); }
|
||||
.text-gradient { background: linear-gradient(to right, #818cf8, #c084fc); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
|
||||
</style>
|
||||
</head>
|
||||
<body class="p-4 md:p-8 lg:p-12">
|
||||
<div class="max-w-7xl mx-auto space-y-12">
|
||||
<!-- Header -->
|
||||
<header class="flex flex-col md:flex-row justify-between items-start md:items-center gap-6 pb-8 border-b border-slate-800">
|
||||
<div>
|
||||
<h1 class="text-4xl md:text-6xl font-extrabold text-gradient tracking-tight">Strategy Intelligence</h1>
|
||||
<p class="text-slate-400 mt-2 text-lg">360° Campaign Architecture for ${googleAds?.websiteUrl || 'New Project'}</p>
|
||||
</div>
|
||||
<div class="glass p-4 rounded-3xl flex items-center gap-4">
|
||||
<div class="text-right">
|
||||
<p class="text-xs text-slate-500 uppercase font-extrabold tracking-widest">Generated On</p>
|
||||
<p class="font-semibold">${new Date().toLocaleDateString()}</p>
|
||||
</div>
|
||||
<div class="w-12 h-12 accent-gradient rounded-2xl flex items-center justify-center font-bold text-xl">PA</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- KPI Grid -->
|
||||
<section class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
<div class="glass p-6 rounded-[2rem] border-indigo-500/20">
|
||||
<p class="text-slate-500 text-xs font-bold uppercase tracking-widest">Est. Monthly Impressions</p>
|
||||
<p class="text-3xl font-extrabold mt-2 text-indigo-400">${googleAds?.predictions?.estimatedImpressions || '15k-25k'}</p>
|
||||
<div class="mt-4 h-1 w-full bg-slate-800 rounded-full overflow-hidden"><div class="h-full bg-indigo-500 w-2/3"></div></div>
|
||||
</div>
|
||||
<div class="glass p-6 rounded-[2rem] border-purple-500/20">
|
||||
<p class="text-slate-500 text-xs font-bold uppercase tracking-widest">Target CTR</p>
|
||||
<p class="text-3xl font-extrabold mt-2 text-purple-400">${googleAds?.predictions?.estimatedCtr || '3.5%'}</p>
|
||||
<div class="mt-4 h-1 w-full bg-slate-800 rounded-full overflow-hidden"><div class="h-full bg-purple-500 w-1/2"></div></div>
|
||||
</div>
|
||||
<div class="glass p-6 rounded-[2rem] border-emerald-500/20">
|
||||
<p class="text-slate-500 text-xs font-bold uppercase tracking-widest">Est. Conversions</p>
|
||||
<p class="text-3xl font-extrabold mt-2 text-emerald-400">${googleAds?.predictions?.estimatedConversions || '30-50'}</p>
|
||||
<div class="mt-4 h-1 w-full bg-slate-800 rounded-full overflow-hidden"><div class="h-full bg-emerald-500 w-1/3"></div></div>
|
||||
</div>
|
||||
<div class="glass p-6 rounded-[2rem] border-amber-500/20">
|
||||
<p class="text-slate-500 text-xs font-bold uppercase tracking-widest">Industry Avg CTR</p>
|
||||
<p class="text-3xl font-extrabold mt-2 text-amber-400">${googleAds?.historicalBenchmarks?.industryAverageCtr || '3.1%'}</p>
|
||||
<div class="mt-4 h-1 w-full bg-slate-800 rounded-full overflow-hidden"><div class="h-full bg-amber-500 w-4/5"></div></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Analytics Visuals -->
|
||||
<section class="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
||||
<div class="lg:col-span-2 glass p-8 rounded-[2.5rem]">
|
||||
<h2 class="text-2xl font-extrabold mb-6">Performance Forecast</h2>
|
||||
<div class="h-[350px]">
|
||||
<canvas id="performanceChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div class="glass p-8 rounded-[2.5rem]">
|
||||
<h2 class="text-2xl font-extrabold mb-6">Device Distribution</h2>
|
||||
<div class="h-[300px]">
|
||||
<canvas id="deviceChart"></canvas>
|
||||
</div>
|
||||
<div class="mt-8 space-y-4">
|
||||
<div class="flex justify-between items-center text-sm">
|
||||
<span class="flex items-center gap-2"><div class="w-3 h-3 rounded-full bg-indigo-500"></div> Mobile</span>
|
||||
<span class="font-bold">${googleAds?.campaigns?.[0]?.targeting?.devices?.mobile || '60%'}</span>
|
||||
</div>
|
||||
<div class="flex justify-between items-center text-sm">
|
||||
<span class="flex items-center gap-2"><div class="w-3 h-3 rounded-full bg-purple-500"></div> Desktop</span>
|
||||
<span class="font-bold">${googleAds?.campaigns?.[0]?.targeting?.devices?.desktop || '30%'}</span>
|
||||
</div>
|
||||
<div class="flex justify-between items-center text-sm">
|
||||
<span class="flex items-center gap-2"><div class="w-3 h-3 rounded-full bg-slate-500"></div> Tablet</span>
|
||||
<span class="font-bold">${googleAds?.campaigns?.[0]?.targeting?.devices?.tablet || '10%'}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Keyword Intelligence -->
|
||||
<section class="space-y-6">
|
||||
<div class="flex justify-between items-center">
|
||||
<h2 class="text-3xl font-extrabold tracking-tight">Keyword Intelligence</h2>
|
||||
<span class="glass px-4 py-1.5 rounded-full text-xs font-bold uppercase tracking-widest text-indigo-400">Semantic Mapping Enabled</span>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
<!-- Primary -->
|
||||
<div class="space-y-4">
|
||||
<h3 class="text-slate-400 font-bold uppercase tracking-widest text-xs flex items-center gap-2">
|
||||
<div class="w-2 h-2 rounded-full bg-indigo-500"></div> Primary Keywords
|
||||
</h3>
|
||||
${(googleAds?.keywords?.primary || []).slice(0, 10).map((k: any) => `
|
||||
<div class="glass p-4 rounded-2xl flex justify-between items-center group hover:border-indigo-500/50 transition-all cursor-default">
|
||||
<span class="font-semibold">${k.keyword}</span>
|
||||
<span class="text-xs text-indigo-400 font-bold">${k.cpc || '$1.50'}</span>
|
||||
</div>
|
||||
`).join('')}
|
||||
</div>
|
||||
<!-- Long-Tail -->
|
||||
<div class="space-y-4">
|
||||
<h3 class="text-slate-400 font-bold uppercase tracking-widest text-xs flex items-center gap-2">
|
||||
<div class="w-2 h-2 rounded-full bg-purple-500"></div> Long-Tail Intent
|
||||
</h3>
|
||||
${(googleAds?.keywords?.longTail || []).slice(0, 10).map((k: any) => `
|
||||
<div class="glass p-4 rounded-2xl flex justify-between items-center group hover:border-purple-500/50 transition-all cursor-default">
|
||||
<span class="font-semibold text-sm">${k.keyword}</span>
|
||||
<span class="text-[10px] text-slate-500 font-bold">${k.competition || 'low'}</span>
|
||||
</div>
|
||||
`).join('')}
|
||||
</div>
|
||||
<!-- Negative -->
|
||||
<div class="space-y-4">
|
||||
<h3 class="text-slate-400 font-bold uppercase tracking-widest text-xs flex items-center gap-2">
|
||||
<div class="w-2 h-2 rounded-full bg-rose-500"></div> Exclusion List
|
||||
</h3>
|
||||
${(googleAds?.keywords?.negative || []).slice(0, 8).map((k: any) => `
|
||||
<div class="glass p-4 rounded-2xl flex justify-between items-center group hover:border-rose-500/50 transition-all cursor-default grayscale opacity-50">
|
||||
<span class="font-semibold text-xs line-through text-slate-400">${k.keyword}</span>
|
||||
<span class="text-[10px] text-rose-500/50 font-bold">EXCLUDE</span>
|
||||
</div>
|
||||
`).join('')}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Ad Copies -->
|
||||
<section class="space-y-8">
|
||||
<h2 class="text-3xl font-extrabold tracking-tight">High-Performance Ad Copy Suite</h2>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
${(googleAds?.adCopies || []).map((ad: any, i: number) => `
|
||||
<div class="glass rounded-[2rem] overflow-hidden flex flex-col">
|
||||
<div class="p-6 border-b border-slate-800 flex justify-between items-center">
|
||||
<span class="text-xs font-bold uppercase tracking-widest text-slate-500">Variation ${i + 1}</span>
|
||||
<span class="bg-indigo-500/20 text-indigo-400 px-2.5 py-1 rounded-full text-[10px] font-black">${(ad.campaignType || 'Search').toUpperCase()}</span>
|
||||
</div>
|
||||
<div class="p-8 space-y-6 flex-grow">
|
||||
${(ad.headlines || []).map((h: any) => `
|
||||
<div class="text-xl font-bold text-slate-100 leading-tight">${h}</div>
|
||||
`).join('')}
|
||||
<div class="h-px w-full bg-slate-800"></div>
|
||||
${(ad.descriptions || []).map((d: any) => `
|
||||
<div class="text-slate-400 text-sm leading-relaxed italic border-l-2 border-slate-700 pl-4">${d}</div>
|
||||
`).join('')}
|
||||
</div>
|
||||
<div class="p-6 bg-slate-900/50 border-t border-slate-800 flex justify-between items-center">
|
||||
<div class="text-xs font-bold text-indigo-400">${ad.callToAction}</div>
|
||||
<div class="flex gap-1">
|
||||
<div class="w-1 h-3 rounded-full ${ad.mobileOptimized ? 'bg-emerald-500' : 'bg-slate-700'}"></div>
|
||||
<div class="w-3 h-3 rounded-full ${ad.mobileOptimized ? 'bg-emerald-500' : 'bg-slate-700'}"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`).join('')}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Competitive Analysis -->
|
||||
${magic?.competitorInsights ? `
|
||||
<section class="space-y-8">
|
||||
<div class="flex justify-between items-end">
|
||||
<h2 class="text-3xl font-extrabold tracking-tight">Competitive Intelligence Matrix</h2>
|
||||
<div class="text-right">
|
||||
<p class="text-[10px] text-slate-500 uppercase font-black tracking-[0.2em] mb-1">Market Sentiment</p>
|
||||
<div class="flex gap-1.5 justify-end">
|
||||
<div class="w-4 h-2 rounded-full bg-emerald-500"></div>
|
||||
<div class="w-4 h-2 rounded-full bg-emerald-500"></div>
|
||||
<div class="w-4 h-2 rounded-full bg-emerald-400"></div>
|
||||
<div class="w-4 h-2 rounded-full bg-slate-800"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||
${(magic.competitorInsights || []).map((c: any) => `
|
||||
<div class="glass p-8 rounded-[2.5rem] space-y-8">
|
||||
<div class="flex justify-between items-start">
|
||||
<div>
|
||||
<h3 class="text-2xl font-black">${c.competitor}</h3>
|
||||
<p class="text-indigo-400 text-xs font-bold mt-1">${c.website || 'Direct Competitor'}</p>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<p class="text-[10px] text-slate-500 uppercase font-bold tracking-widest">Est. Spend</p>
|
||||
<p class="text-sm font-black text-rose-400">${c.estimatedSpend || 'Undisclosed'}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-6">
|
||||
<div class="space-y-3">
|
||||
<p class="text-[10px] text-emerald-400 uppercase font-black tracking-widest">Core Strengths</p>
|
||||
<ul class="text-sm space-y-2 text-slate-300">
|
||||
${(c.strengths || []).map((s: any) => `<li class="flex items-center gap-2"><div class="w-1.5 h-1.5 rounded-full bg-emerald-500"></div> ${s}</li>`).join('')}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="space-y-3">
|
||||
<p class="text-[10px] text-rose-400 uppercase font-black tracking-widest">Weaknesses</p>
|
||||
<ul class="text-sm space-y-2 text-slate-400">
|
||||
${(c.weaknesses || []).map((w: any) => `<li class="flex items-center gap-2"><div class="w-1.5 h-1.5 rounded-full bg-rose-500"></div> ${w}</li>`).join('')}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-indigo-500/5 p-6 rounded-2xl border border-indigo-500/10">
|
||||
<p class="text-[10px] text-indigo-400 uppercase font-bold tracking-widest mb-3">Counter-Strategy Rationale</p>
|
||||
<p class="text-sm text-slate-300 italic leading-relaxed">"${c.adStrategy}"</p>
|
||||
</div>
|
||||
</div>
|
||||
`).join('')}
|
||||
</div>
|
||||
</section>
|
||||
` : ''}
|
||||
|
||||
<!-- Strategies -->
|
||||
${magic?.strategies ? `
|
||||
<section class="space-y-8">
|
||||
<h2 class="text-3xl font-extrabold tracking-tight">Strategic Directions</h2>
|
||||
<div class="space-y-8">
|
||||
${(magic.strategies || []).map((s: any, idx: number) => `
|
||||
<div class="glass overflow-hidden rounded-[3rem] relative">
|
||||
<div class="absolute top-0 right-0 w-64 h-64 accent-gradient blur-[120px] opacity-10"></div>
|
||||
<div class="p-10 relative z-10 grid grid-cols-1 lg:grid-cols-4 gap-12">
|
||||
<!-- Left: Header -->
|
||||
<div class="lg:col-span-1 space-y-6">
|
||||
<div class="w-16 h-16 rounded-3xl accent-gradient flex items-center justify-center font-black text-2xl">${idx + 1}</div>
|
||||
<div>
|
||||
<h3 class="text-2xl font-extrabold leading-tight">${s.direction}</h3>
|
||||
<span class="inline-block mt-4 glass px-4 py-1 rounded-full text-[10px] font-black uppercase tracking-widest ${s.riskLevel === 'low' ? 'text-emerald-400' : 'text-amber-400'}">${s.riskLevel || 'low'} risk profile</span>
|
||||
</div>
|
||||
<div class="pt-6 space-y-4">
|
||||
<div>
|
||||
<p class="text-[10px] text-slate-500 uppercase font-bold tracking-widest">Expected ROI</p>
|
||||
<p class="text-xl font-black text-emerald-400">${s.expectedROI}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-[10px] text-slate-500 uppercase font-bold tracking-widest">Time to Impact</p>
|
||||
<p class="text-xl font-black text-slate-100">${s.timeToResults}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Middle: Context -->
|
||||
<div class="lg:col-span-2 space-y-8">
|
||||
<div>
|
||||
<p class="text-[10px] text-slate-500 uppercase font-bold tracking-widest mb-4">Strategic Rationale</p>
|
||||
<p class="text-lg text-slate-200 leading-relaxed font-light">${s.rationale}</p>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-8">
|
||||
<div class="bg-white/5 p-6 rounded-[2rem]">
|
||||
<p class="text-[10px] text-indigo-400 uppercase font-black tracking-widest mb-3">Target Audience</p>
|
||||
<p class="text-sm font-semibold">${s.targetAudience}</p>
|
||||
</div>
|
||||
<div class="bg-white/5 p-6 rounded-[2rem]">
|
||||
<p class="text-[10px] text-purple-400 uppercase font-black tracking-widest mb-3">Competitive Edge</p>
|
||||
<p class="text-sm font-semibold">${s.competitiveAdvantage}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right: Channels/Metrics -->
|
||||
<div class="lg:col-span-1 bg-slate-900/80 p-8 rounded-[2.5rem] space-y-8 border border-white/5">
|
||||
<div>
|
||||
<p class="text-[10px] text-slate-500 uppercase font-bold tracking-widest mb-6">Channel Matrix</p>
|
||||
<div class="space-y-4">
|
||||
${Object.entries(s.estimatedBudgetAllocation || {}).map(([c, v]: [string, any]) => `
|
||||
<div class="space-y-2">
|
||||
<div class="flex justify-between text-xs">
|
||||
<span class="capitalize text-slate-300">${c}</span>
|
||||
<span class="font-bold text-slate-100">${v}%</span>
|
||||
</div>
|
||||
<div class="h-1.5 w-full bg-slate-800 rounded-full overflow-hidden">
|
||||
<div class="h-full accent-gradient" style="width: ${v}%"></div>
|
||||
</div>
|
||||
</div>
|
||||
`).join('')}
|
||||
</div>
|
||||
</div>
|
||||
<div class="pt-6 border-t border-slate-800">
|
||||
<p class="text-[10px] text-slate-500 uppercase font-bold tracking-widest mb-4">Success Thresholds</p>
|
||||
<div class="flex flex-wrap gap-2 text-[10px]">
|
||||
${(s.successMetrics || []).map((m: any) => `
|
||||
<span class="bg-emerald-500/10 text-emerald-400 border border-emerald-500/20 px-2 py-1 rounded-md font-bold uppercase">${m}</span>
|
||||
`).join('')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`).join('')}
|
||||
</div>
|
||||
</section>
|
||||
` : ''}
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="pt-12 border-t border-slate-800 flex flex-col md:flex-row justify-between items-center gap-6 pb-12">
|
||||
<p class="text-slate-500 text-sm font-semibold">© ${new Date().getFullYear()} PromptArch Intelligence Unit • Confident Strategic Asset</p>
|
||||
<div class="flex gap-4">
|
||||
<div class="w-8 h-8 rounded-full bg-slate-800 flex items-center justify-center text-xs font-bold">PA</div>
|
||||
<div class="w-8 h-8 rounded-full bg-slate-800 flex items-center justify-center text-xs font-bold italic">Q</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Performance Forecast Chart
|
||||
const ctxPerf = document.getElementById('performanceChart').getContext('2d');
|
||||
new Chart(ctxPerf, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: ['Week 1', 'Week 2', 'Week 3', 'Week 4', 'Week 5', 'Week 6', 'Week 12'],
|
||||
datasets: [{
|
||||
label: 'Predicted Growth (Aggressive)',
|
||||
data: [10, 25, 45, 80, 110, 160, 450],
|
||||
borderColor: '#6366f1',
|
||||
backgroundColor: 'rgba(99, 102, 241, 0.1)',
|
||||
fill: true,
|
||||
tension: 0.4,
|
||||
pointRadius: 6,
|
||||
pointBackgroundColor: '#fff'
|
||||
}, {
|
||||
label: 'Predicted Growth (Standard)',
|
||||
data: [5, 12, 28, 55, 75, 100, 280],
|
||||
borderColor: '#a855f7',
|
||||
backgroundColor: 'transparent',
|
||||
borderDash: [5, 5],
|
||||
tension: 0.4,
|
||||
pointRadius: 4
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: { legend: { labels: { color: '#94a3b8', font: { family: 'Outfit', weight: 'bold' } } } },
|
||||
scales: {
|
||||
x: { grid: { display: false }, ticks: { color: '#475569' } },
|
||||
y: { grid: { color: 'rgba(255,255,255,0.05)' }, ticks: { color: '#475569' } }
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Device Chart
|
||||
const ctxDev = document.getElementById('deviceChart').getContext('2d');
|
||||
new Chart(ctxDev, {
|
||||
type: 'doughnut',
|
||||
data: {
|
||||
labels: ['Mobile', 'Desktop', 'Tablet'],
|
||||
datasets: [{
|
||||
data: [60, 30, 10],
|
||||
backgroundColor: ['#6366f1', '#a855f7', '#475569'],
|
||||
borderWidth: 0,
|
||||
hoverOffset: 12
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
cutout: '80%',
|
||||
plugins: { legend: { display: false } }
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>`;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user