feat: enhance Google Ads export with XLSX and high-fidelity HTML reports
This commit is contained in:
@@ -10,7 +10,7 @@ import modelAdapter from "@/lib/services/adapter-instance";
|
||||
import { Megaphone, Copy, Loader2, CheckCircle2, Settings, Plus, X, ChevronDown, ChevronUp, Wand2, Target, TrendingUp, ShieldAlert, BarChart3, Users, Rocket, Download, FileSpreadsheet } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { GoogleAdsResult } from "@/types";
|
||||
import { downloadFile, generateGoogleAdsCSV, generateGoogleAdsHTML } from "@/lib/export-utils";
|
||||
import { downloadFile, generateGoogleAdsCSV, generateGoogleAdsHTML, generateGoogleAdsExcel } from "@/lib/export-utils";
|
||||
import { translations } from "@/lib/i18n/translations";
|
||||
|
||||
export default function GoogleAdsGenerator() {
|
||||
@@ -364,149 +364,16 @@ export default function GoogleAdsGenerator() {
|
||||
}
|
||||
};
|
||||
|
||||
const exportCSV = () => {
|
||||
const exportExcel = () => {
|
||||
if (!googleAdsResult && !magicWandResult) return;
|
||||
const csvContent = generateGoogleAdsCSV(googleAdsResult || undefined, magicWandResult || undefined);
|
||||
downloadFile(`google-ads-report-${new Date().toISOString().split('T')[0]}.csv`, csvContent, 'text/csv;charset=utf-8;');
|
||||
const excelBlob = generateGoogleAdsExcel(googleAdsResult || undefined, magicWandResult || undefined);
|
||||
downloadFile(`google-ads-full-intel-${new Date().toISOString().split('T')[0]}.xlsx`, excelBlob, 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
||||
};
|
||||
|
||||
const exportHTML = () => {
|
||||
if (!googleAdsResult && !magicWandResult) return;
|
||||
|
||||
const htmlContent = generateGoogleAdsHTML(googleAdsResult || undefined, magicWandResult || undefined);
|
||||
downloadFile(`google-ads-report-${new Date().toISOString().split('T')[0]}.html`, htmlContent, 'text/html');
|
||||
return; /*
|
||||
parts.push(`<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Google Ads Strategy Report</title>`);
|
||||
parts.push(`<style>
|
||||
:root { --bg: #0f172a; --card: #1e293b; --text: #e2e8f0; --accent: #6366f1; }
|
||||
body { font-family: system-ui, -apple-system, sans-serif; background: var(--bg); color: var(--text); line-height: 1.6; padding: 40px; }
|
||||
.container { max-width: 1000px; margin: 0 auto; }
|
||||
h1 { font-size: 2.5rem; background: linear-gradient(to right, #818cf8, #c084fc); -webkit-background-clip: text; -webkit-text-fill-color: transparent; margin-bottom: 0.5rem; }
|
||||
.section { background: var(--card); border-radius: 12px; padding: 24px; margin-bottom: 24px; border: 1px solid rgba(255,255,255,0.1); }
|
||||
h2 { font-size: 1.25rem; color: #818cf8; margin-bottom: 1rem; border-bottom: 1px solid rgba(255,255,255,0.1); padding-bottom: 0.5rem; }
|
||||
h3 { font-size: 1rem; color: #94a3b8; margin: 1rem 0 0.5rem; text-transform: uppercase; letter-spacing: 0.05em; }
|
||||
.tag { display: inline-block; background: rgba(99,102,241,0.2); border: 1px solid rgba(99,102,241,0.3); color: #c3dafe; padding: 4px 12px; border-radius: 99px; font-size: 0.85rem; margin: 0 6px 6px 0; }
|
||||
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 16px; }
|
||||
.card { background: rgba(0,0,0,0.2); padding: 16px; border-radius: 8px; border: 1px solid rgba(255,255,255,0.05); }
|
||||
.stat { font-size: 1.5rem; font-weight: 700; color: #4ade80; }
|
||||
ul { padding-left: 20px; color: #cbd5e1; }
|
||||
li { margin-bottom: 4px; }
|
||||
table { width: 100%; border-collapse: collapse; margin-top: 1rem; }
|
||||
.footer { text-align: center; color: #64748b; margin-top: 4rem; font-size: 0.85rem; }
|
||||
</style></head><body><div class="container">`);
|
||||
|
||||
parts.push(`<h1>Google Ads Strategy Report</h1><p style="color:#94a3b8; margin-bottom: 2rem">Generated by PromptArch on ${new Date().toLocaleDateString()}</p>`);
|
||||
|
||||
if (googleAdsResult) {
|
||||
// Keywords
|
||||
parts.push(`<div class="section"><h2>🎯 Keyword Research</h2>`);
|
||||
if (googleAdsResult.keywords) {
|
||||
if (Array.isArray(googleAdsResult.keywords.primary)) {
|
||||
parts.push(`<h3>Primary Keywords</h3><div style="margin-bottom:1rem">`);
|
||||
for (const k of googleAdsResult.keywords.primary) {
|
||||
parts.push(`<span class="tag">${k.keyword} <span style="opacity:0.6; font-size:0.8em">(${k.cpc || 'N/A'})</span></span>`);
|
||||
}
|
||||
parts.push(`</div>`);
|
||||
}
|
||||
if (Array.isArray(googleAdsResult.keywords.longTail)) {
|
||||
parts.push(`<h3>Long-tail Opportunities</h3><div style="margin-bottom:1rem">`);
|
||||
for (const k of googleAdsResult.keywords.longTail) {
|
||||
parts.push(`<span class="tag">${k.keyword} <span style="opacity:0.6; font-size:0.8em">(${k.cpc || 'N/A'})</span></span>`);
|
||||
}
|
||||
parts.push(`</div>`);
|
||||
}
|
||||
if (Array.isArray(googleAdsResult.keywords.negative)) {
|
||||
parts.push(`<h3>Negative Keywords</h3><div style="margin-bottom:1rem">`);
|
||||
for (const k of googleAdsResult.keywords.negative) {
|
||||
parts.push(`<span class="tag">${k.keyword} <span style="opacity:0.6; font-size:0.8em">(${k.cpc || 'N/A'})</span></span>`);
|
||||
}
|
||||
parts.push(`</div>`);
|
||||
}
|
||||
}
|
||||
parts.push(`</div>`);
|
||||
|
||||
// Ad Copies
|
||||
if (Array.isArray(googleAdsResult.adCopies)) {
|
||||
parts.push(`<div class="section"><h2>✍️ Ad Copy Variations</h2><div class="grid">`);
|
||||
let i = 0;
|
||||
for (const ad of googleAdsResult.adCopies) {
|
||||
i++;
|
||||
parts.push(`<div class="card"><div style="color:#818cf8; font-size:0.8rem; margin-bottom:0.5rem">Variation ${i}</div>`);
|
||||
if (Array.isArray(ad.headlines)) {
|
||||
for (const h of ad.headlines) {
|
||||
parts.push(`<div style="font-weight:600; color:#f1f5f9">${h}</div>`);
|
||||
}
|
||||
}
|
||||
if (Array.isArray(ad.descriptions)) {
|
||||
parts.push(`<div style="margin: 12px 0; color:#cbd5e1; font-size:0.9rem">${(ad.descriptions || []).join('<br>')}</div>`);
|
||||
}
|
||||
if (ad.callToAction) {
|
||||
parts.push(`<div style="display:inline-block; background:#4f46e5; color:white; font-size:0.8rem; padding:4px 12px; border-radius:4px">${ad.callToAction}</div>`);
|
||||
}
|
||||
parts.push(`</div>`);
|
||||
}
|
||||
parts.push(`</div></div>`);
|
||||
}
|
||||
|
||||
// Campaigns
|
||||
if (Array.isArray(googleAdsResult.campaigns)) {
|
||||
parts.push(`<div class="section"><h2>🏗️ Campaign Structure</h2><div class="grid">`);
|
||||
for (const c of googleAdsResult.campaigns) {
|
||||
parts.push(`<div class="card"><h3 style="color:#f8fafc; margin-top:0">${c.name}</h3>`);
|
||||
parts.push(`<p style="font-size:0.9rem; color:#94a3b8">${c.type.toUpperCase()} • ${c.budget?.daily} ${c.budget?.currency}/day</p>`);
|
||||
const locs = c.targeting?.locations ? c.targeting.locations.join(', ') : 'Global';
|
||||
parts.push(`<div style="margin-top:1rem; font-size:0.9rem"><strong>Locations:</strong> ${locs}<br><strong>Ad Groups:</strong> ${c.adGroups ? c.adGroups.length : 0}</div></div>`);
|
||||
}
|
||||
parts.push(`</div></div>`);
|
||||
}
|
||||
|
||||
// Implementation & Predictions
|
||||
parts.push(`<div class="section"><h2>🚀 Implementation & Forecast</h2><div class="grid">`);
|
||||
if (googleAdsResult.implementation && Array.isArray(googleAdsResult.implementation.setupSteps)) {
|
||||
parts.push(`<div><h3>Setup Steps</h3><ul>`);
|
||||
for (const s of googleAdsResult.implementation.setupSteps) {
|
||||
parts.push(`<li>${s}</li>`);
|
||||
}
|
||||
parts.push(`</ul></div>`);
|
||||
}
|
||||
|
||||
if (googleAdsResult.predictions) {
|
||||
const p = googleAdsResult.predictions;
|
||||
parts.push(`<div class="card" style="background:rgba(16,185,129,0.1)"><h3 style="color:#34d399; margin-top:0">Monthly Estimations</h3><div style="display:grid; grid-template-columns:1fr 1fr; gap:12px">`);
|
||||
parts.push(`<div><div class="stat">${p.estimatedClicks || '-'}</div><div style="font-size:0.8rem; opacity:0.7">Clicks</div></div>`);
|
||||
parts.push(`<div><div class="stat">${p.estimatedCtr || '-'}</div><div style="font-size:0.8rem; opacity:0.7">CTR</div></div>`);
|
||||
parts.push(`<div><div class="stat">${p.estimatedConversions || '-'}</div><div style="font-size:0.8rem; opacity:0.7">Convs</div></div>`);
|
||||
parts.push(`</div></div>`);
|
||||
}
|
||||
parts.push(`</div></div>`);
|
||||
}
|
||||
|
||||
if (magicWandResult) {
|
||||
parts.push(`<div class="section"><h2>🧠 Market Intelligence</h2><div class="grid">`);
|
||||
parts.push(`<div class="card"><h3>Strategy Rationale</h3><p style="font-size:0.9rem; color:#cbd5e1">${magicWandResult.rationale}</p></div>`);
|
||||
const ma = magicWandResult.marketAnalysis;
|
||||
parts.push(`<div><h3>Market Data</h3><p><strong>Growth Rate:</strong> ${ma?.growthRate || 'N/A'}</p><h3>Top Competitors</h3><ul>`);
|
||||
if (ma && Array.isArray(ma.topCompetitors)) {
|
||||
for (const c of ma.topCompetitors) {
|
||||
parts.push(`<li>${c}</li>`);
|
||||
}
|
||||
} else {
|
||||
parts.push(`<li>None identified</li>`);
|
||||
}
|
||||
parts.push(`</ul></div></div></div>`);
|
||||
}
|
||||
|
||||
parts.push(`<div class="footer">PromptArch • AI-Powered Marketing Tools</div></div></body></html>`);
|
||||
|
||||
const blob = new Blob([parts.join('')], { type: 'text/html' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const link = document.createElement("a");
|
||||
link.setAttribute("href", url);
|
||||
link.setAttribute("download", `google-ads-report-${new Date().toISOString().split('T')[0]}.html`);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
*/
|
||||
downloadFile(`google-ads-intelligence-report-${new Date().toISOString().split('T')[0]}.html`, htmlContent, 'text/html');
|
||||
};
|
||||
|
||||
const sections = [
|
||||
@@ -1235,10 +1102,10 @@ export default function GoogleAdsGenerator() {
|
||||
<Copy className="h-3.5 w-3.5 lg:h-4 lg:w-4" />
|
||||
)}
|
||||
</Button>
|
||||
<Button variant="ghost" size="icon" onClick={exportCSV} className="h-8 w-8 lg:h-9 lg:w-9" title="Export as CSV">
|
||||
<Button variant="ghost" size="icon" onClick={exportExcel} className="h-8 w-8 lg:h-9 lg:w-9 text-emerald-500 hover:text-emerald-600 hover:bg-emerald-50" title="Download Full Intel Report (XLSX)">
|
||||
<FileSpreadsheet className="h-3.5 w-3.5 lg:h-4 lg:w-4" />
|
||||
</Button>
|
||||
<Button variant="ghost" size="icon" onClick={exportHTML} className="h-8 w-8 lg:h-9 lg:w-9" title="Export as HTML">
|
||||
<Button variant="ghost" size="icon" onClick={exportHTML} className="h-8 w-8 lg:h-9 lg:w-9 text-indigo-500 hover:text-indigo-600 hover:bg-indigo-50" title="Download Intelligence Dashboard (HTML)">
|
||||
<Download className="h-3.5 w-3.5 lg:h-4 lg:w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -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>`;
|
||||
};
|
||||
|
||||
@@ -478,27 +478,42 @@ OUTPUT FORMAT - Return ONLY valid JSON with this structure:
|
||||
"headlines": ["Headline 1 (30 chars)", "Headline 2", "Headline 3"],
|
||||
"descriptions": ["Description 1 (90 chars)", "Description 2"],
|
||||
"callToAction": "Get Started",
|
||||
"mobileOptimized": true
|
||||
"mobileOptimized": true,
|
||||
"positioning": "Value proposition used"
|
||||
}],
|
||||
"campaigns": [{
|
||||
"id": "campaign-1",
|
||||
"name": "Campaign Name",
|
||||
"type": "search",
|
||||
"budget": {"daily": 50, "monthly": 1500, "currency": "USD"},
|
||||
"targeting": {"locations": [], "demographics": [], "devices": []},
|
||||
"adGroups": [{"id": "adgroup-1", "name": "Group", "theme": "Theme", "keywords": [], "biddingStrategy": "Maximize conversions"}]
|
||||
"biddingStrategy": "Maximize conversions",
|
||||
"targeting": {
|
||||
"locations": ["Specific regions/cities"],
|
||||
"demographics": {"age": ["18-24", "25-34"], "gender": ["Male", "Female"], "interests": ["Tech", "Business"]},
|
||||
"devices": {"mobile": "60%", "desktop": "30%", "tablet": "10%"},
|
||||
"schedule": ["Mon-Fri, 9am-5pm"]
|
||||
},
|
||||
"adGroups": [{"id": "adgroup-1", "name": "Group", "theme": "Theme", "keywords": [], "biddingStrategy": "Manual CPC"}]
|
||||
}],
|
||||
"implementation": {
|
||||
"setupSteps": [],
|
||||
"qualityScoreTips": [],
|
||||
"trackingSetup": [],
|
||||
"optimizationTips": []
|
||||
"setupSteps": ["Step 1", "Step 2"],
|
||||
"qualityScoreTips": ["Tip 1", "Tip 2"],
|
||||
"trackingSetup": ["Conversion tag info", "GTM setup"],
|
||||
"optimizationTips": ["Tip 1", "Tip 2"]
|
||||
},
|
||||
"predictions": {
|
||||
"estimatedClicks": "500-800/month",
|
||||
"estimatedImpressions": "15,000-25,000/month",
|
||||
"estimatedCtr": "3.2%-4.5%",
|
||||
"estimatedConversions": "25-50/month"
|
||||
"estimatedClicks": "500-800",
|
||||
"estimatedImpressions": "15,000-25,000",
|
||||
"estimatedCtr": "3.5%",
|
||||
"estimatedConversions": "30-50",
|
||||
"conversionRate": "4.2%",
|
||||
"avgCpc": "$1.85"
|
||||
},
|
||||
"historicalBenchmarks": {
|
||||
"industryAverageCtr": "3.1%",
|
||||
"industryAverageCpc": "$2.10",
|
||||
"seasonalTrends": "Peak in Q4",
|
||||
"geographicInsights": "London/NY show highest ROI"
|
||||
}
|
||||
}
|
||||
\`\`\`
|
||||
@@ -553,30 +568,44 @@ OUTPUT FORMAT - Return ONLY valid JSON with this EXACT structure:
|
||||
"competitorInsights": [
|
||||
{
|
||||
"competitor": "Competitor Name",
|
||||
"website": "URL",
|
||||
"estimatedSpend": "$10k-$50k/mo",
|
||||
"targetAudience": "Who they target",
|
||||
"strengths": ["Strength 1", "Strength 2"],
|
||||
"weaknesses": ["Weakness 1", "Weakness 2"],
|
||||
"adStrategy": "Their current advertising approach"
|
||||
"adStrategy": "Their approach",
|
||||
"topKeywords": ["keyword 1", "keyword 2"],
|
||||
"adCopyExamples": [
|
||||
{"headline": "Example Headline", "description": "Example Description"}
|
||||
]
|
||||
}
|
||||
],
|
||||
"strategies": [
|
||||
{
|
||||
"id": "strategy-1",
|
||||
"direction": "Strategic Direction Name",
|
||||
"rationale": "Why this strategy works for this product/market",
|
||||
"targetAudience": "Specific audience segment",
|
||||
"competitiveAdvantage": "How this beats competitors",
|
||||
"keyMessages": ["Message 1", "Message 2", "Message 3"],
|
||||
"adCopyGuide": {
|
||||
"headlines": ["Headline 1 (max 30 symbols)", "Headline 2", "Headline 3"],
|
||||
"descriptions": ["Description 1 (max 90 symbols)", "Description 2"],
|
||||
"keywords": ["keyword 1", "keyword 2", "keyword 3"],
|
||||
"setupGuide": "Friendly step-by-step for a beginner on where exactly to paste these in Google Ads Manager"
|
||||
"rationale": "Why this strategy works",
|
||||
"targetAudience": "Audience segment with demographics (age 25-45, interests etc)",
|
||||
"targetingDetails": {
|
||||
"geography": "Primary locations",
|
||||
"demographics": "Specific age/gender groups",
|
||||
"behavior": "User behaviors"
|
||||
},
|
||||
"recommendedChannels": ["Google Search", "Display", "YouTube"],
|
||||
"competitiveAdvantage": "How this beats competitors",
|
||||
"messagingPillars": ["Pillar 1", "Pillar 2"],
|
||||
"keyMessages": ["Message 1", "Message 2"],
|
||||
"adCopyGuide": {
|
||||
"headlines": ["Headline 1", "Headline 2"],
|
||||
"descriptions": ["Description 1", "Description 2"],
|
||||
"keywords": ["keyword 1", "keyword 2"],
|
||||
"setupGuide": "Step-by-step for Google Ads Manager"
|
||||
},
|
||||
"recommendedChannels": ["Search", "Display", "YouTube"],
|
||||
"estimatedBudgetAllocation": { "search": 40, "display": 30, "video": 20, "social": 10 },
|
||||
"expectedROI": "150-200%",
|
||||
"riskLevel": "low",
|
||||
"timeToResults": "2-3 months"
|
||||
"timeToResults": "2-3 months",
|
||||
"successMetrics": ["CTR > 3%", "CPA < $20"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -810,27 +810,42 @@ OUTPUT FORMAT - Return ONLY valid JSON with this structure:
|
||||
"headlines": ["Headline 1 (30 chars)", "Headline 2", "Headline 3"],
|
||||
"descriptions": ["Description 1 (90 chars)", "Description 2"],
|
||||
"callToAction": "Get Started",
|
||||
"mobileOptimized": true
|
||||
"mobileOptimized": true,
|
||||
"positioning": "Value proposition used"
|
||||
}],
|
||||
"campaigns": [{
|
||||
"id": "campaign-1",
|
||||
"name": "Campaign Name",
|
||||
"type": "search",
|
||||
"budget": {"daily": 50, "monthly": 1500, "currency": "USD"},
|
||||
"targeting": {"locations": [], "demographics": [], "devices": []},
|
||||
"adGroups": [{"id": "adgroup-1", "name": "Group", "theme": "Theme", "keywords": [], "biddingStrategy": "Maximize conversions"}]
|
||||
"biddingStrategy": "Maximize conversions",
|
||||
"targeting": {
|
||||
"locations": ["Specific regions/cities"],
|
||||
"demographics": {"age": ["18-24", "25-34"], "gender": ["Male", "Female"], "interests": ["Tech", "Business"]},
|
||||
"devices": {"mobile": "60%", "desktop": "30%", "tablet": "10%"},
|
||||
"schedule": ["Mon-Fri, 9am-5pm"]
|
||||
},
|
||||
"adGroups": [{"id": "adgroup-1", "name": "Group", "theme": "Theme", "keywords": [], "biddingStrategy": "Manual CPC"}]
|
||||
}],
|
||||
"implementation": {
|
||||
"setupSteps": [],
|
||||
"qualityScoreTips": [],
|
||||
"trackingSetup": [],
|
||||
"optimizationTips": []
|
||||
"setupSteps": ["Step 1", "Step 2"],
|
||||
"qualityScoreTips": ["Tip 1", "Tip 2"],
|
||||
"trackingSetup": ["Conversion tag info", "GTM setup"],
|
||||
"optimizationTips": ["Tip 1", "Tip 2"]
|
||||
},
|
||||
"predictions": {
|
||||
"estimatedClicks": "500-800/month",
|
||||
"estimatedImpressions": "15,000-25,000/month",
|
||||
"estimatedCtr": "3.2%-4.5%",
|
||||
"estimatedConversions": "25-50/month"
|
||||
"estimatedClicks": "500-800",
|
||||
"estimatedImpressions": "15,000-25,000",
|
||||
"estimatedCtr": "3.5%",
|
||||
"estimatedConversions": "30-50",
|
||||
"conversionRate": "4.2%",
|
||||
"avgCpc": "$1.85"
|
||||
},
|
||||
"historicalBenchmarks": {
|
||||
"industryAverageCtr": "3.1%",
|
||||
"industryAverageCpc": "$2.10",
|
||||
"seasonalTrends": "Peak in Q4",
|
||||
"geographicInsights": "London/NY show highest ROI"
|
||||
}
|
||||
}
|
||||
\`\`\`
|
||||
@@ -888,30 +903,44 @@ OUTPUT FORMAT - Return ONLY valid JSON with this EXACT structure:
|
||||
"competitorInsights": [
|
||||
{
|
||||
"competitor": "Competitor Name",
|
||||
"website": "URL",
|
||||
"estimatedSpend": "$10k-$50k/mo",
|
||||
"targetAudience": "Who they target",
|
||||
"strengths": ["Strength 1", "Strength 2"],
|
||||
"weaknesses": ["Weakness 1", "Weakness 2"],
|
||||
"adStrategy": "Their current advertising approach"
|
||||
"adStrategy": "Their approach",
|
||||
"topKeywords": ["keyword 1", "keyword 2"],
|
||||
"adCopyExamples": [
|
||||
{"headline": "Example Headline", "description": "Example Description"}
|
||||
]
|
||||
}
|
||||
],
|
||||
"strategies": [
|
||||
{
|
||||
"id": "strategy-1",
|
||||
"direction": "Strategic Direction Name",
|
||||
"rationale": "Why this strategy works for this product/market",
|
||||
"targetAudience": "Specific audience segment",
|
||||
"competitiveAdvantage": "How this beats competitors",
|
||||
"keyMessages": ["Message 1", "Message 2", "Message 3"],
|
||||
"adCopyGuide": {
|
||||
"headlines": ["Headline 1 (max 30 symbols)", "Headline 2", "Headline 3"],
|
||||
"descriptions": ["Description 1 (max 90 symbols)", "Description 2"],
|
||||
"keywords": ["keyword 1", "keyword 2", "keyword 3"],
|
||||
"setupGuide": "Friendly step-by-step for a beginner on where exactly to paste these in Google Ads Manager"
|
||||
"rationale": "Why this strategy works",
|
||||
"targetAudience": "Audience segment with demographics (age 25-45, interests etc)",
|
||||
"targetingDetails": {
|
||||
"geography": "Primary locations",
|
||||
"demographics": "Specific age/gender groups",
|
||||
"behavior": "User behaviors"
|
||||
},
|
||||
"recommendedChannels": ["Google Search", "Display", "YouTube"],
|
||||
"competitiveAdvantage": "How this beats competitors",
|
||||
"messagingPillars": ["Pillar 1", "Pillar 2"],
|
||||
"keyMessages": ["Message 1", "Message 2"],
|
||||
"adCopyGuide": {
|
||||
"headlines": ["Headline 1", "Headline 2"],
|
||||
"descriptions": ["Description 1", "Description 2"],
|
||||
"keywords": ["keyword 1", "keyword 2"],
|
||||
"setupGuide": "Step-by-step for Google Ads Manager"
|
||||
},
|
||||
"recommendedChannels": ["Search", "Display", "YouTube"],
|
||||
"estimatedBudgetAllocation": { "search": 40, "display": 30, "video": 20, "social": 10 },
|
||||
"expectedROI": "150-200%",
|
||||
"riskLevel": "low",
|
||||
"timeToResults": "2-3 months"
|
||||
"timeToResults": "2-3 months",
|
||||
"successMetrics": ["CTR > 3%", "CPA < $20"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -448,106 +448,54 @@ OUTPUT FORMAT - Return ONLY valid JSON:
|
||||
\`\`\`json
|
||||
{
|
||||
"keywords": {
|
||||
"primary": [
|
||||
{
|
||||
"keyword": "exact keyword phrase",
|
||||
"type": "primary",
|
||||
"searchVolume": 12000,
|
||||
"competition": "medium",
|
||||
"difficultyScore": 65,
|
||||
"relevanceScore": 95,
|
||||
"cpc": "$2.50"
|
||||
}
|
||||
],
|
||||
"longTail": [
|
||||
{
|
||||
"keyword": "longer specific keyword phrase",
|
||||
"type": "long-tail",
|
||||
"searchVolume": 1200,
|
||||
"competition": "low",
|
||||
"difficultyScore": 35,
|
||||
"relevanceScore": 90,
|
||||
"cpc": "$1.25"
|
||||
}
|
||||
],
|
||||
"negative": [
|
||||
{
|
||||
"keyword": "irrelevant term to exclude",
|
||||
"type": "negative",
|
||||
"competition": "low"
|
||||
}
|
||||
]
|
||||
"primary": [{"keyword": "term", "type": "primary", "searchVolume": 12000, "competition": "medium", "cpc": "$2.50"}],
|
||||
"longTail": [{"keyword": "specific term", "type": "long-tail", "searchVolume": 1200, "competition": "low", "cpc": "$1.25"}],
|
||||
"negative": [{"keyword": "exclude term", "type": "negative", "competition": "low"}]
|
||||
},
|
||||
"adCopies": [
|
||||
{
|
||||
"id": "ad-1",
|
||||
"campaignType": "search",
|
||||
"headlines": [
|
||||
"Headline 1 (max 30 chars)",
|
||||
"Headline 2 (max 30 chars)",
|
||||
"Headline 3 (max 30 chars)"
|
||||
],
|
||||
"descriptions": [
|
||||
"Description line 1 - compelling copy under 90 chars",
|
||||
"Description line 2 - call to action under 90 chars"
|
||||
],
|
||||
"callToAction": "Get Started Today",
|
||||
"displayUrl": "example.com/offers",
|
||||
"mobileOptimized": true
|
||||
}
|
||||
],
|
||||
"campaigns": [
|
||||
{
|
||||
"id": "campaign-1",
|
||||
"name": "Campaign Name",
|
||||
"type": "search",
|
||||
"budget": {
|
||||
"daily": 50,
|
||||
"monthly": 1500,
|
||||
"currency": "USD"
|
||||
},
|
||||
"targeting": {
|
||||
"locations": ["United States", "Canada"],
|
||||
"demographics": ["25-54", "All genders"],
|
||||
"devices": ["Desktop", "Mobile", "Tablet"],
|
||||
"schedule": ["Mon-Fri 8am-8pm"]
|
||||
},
|
||||
"adGroups": [
|
||||
{
|
||||
"id": "adgroup-1",
|
||||
"name": "Product Category Group",
|
||||
"theme": "Main product focus",
|
||||
"keywords": ["keyword1", "keyword2"],
|
||||
"biddingStrategy": "Maximize conversions"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"adCopies": [{
|
||||
"id": "ad-1",
|
||||
"campaignType": "search",
|
||||
"headlines": ["Headline 1 (30 chars)", "Headline 2", "Headline 3"],
|
||||
"descriptions": ["Description 1 (90 chars)", "Description 2"],
|
||||
"callToAction": "Get Started",
|
||||
"mobileOptimized": true,
|
||||
"positioning": "Value proposition used"
|
||||
}],
|
||||
"campaigns": [{
|
||||
"id": "campaign-1",
|
||||
"name": "Campaign Name",
|
||||
"type": "search",
|
||||
"budget": {"daily": 50, "monthly": 1500, "currency": "USD"},
|
||||
"biddingStrategy": "Maximize conversions",
|
||||
"targeting": {
|
||||
"locations": ["Specific regions/cities"],
|
||||
"demographics": {"age": ["18-24", "25-34"], "gender": ["Male", "Female"], "interests": ["Tech", "Business"]},
|
||||
"devices": {"mobile": "60%", "desktop": "30%", "tablet": "10%"},
|
||||
"schedule": ["Mon-Fri, 9am-5pm"]
|
||||
},
|
||||
"adGroups": [{"id": "adgroup-1", "name": "Group", "theme": "Theme", "keywords": [], "biddingStrategy": "Manual CPC"}]
|
||||
}],
|
||||
"implementation": {
|
||||
"setupSteps": [
|
||||
"Step 1: Create Google Ads account...",
|
||||
"Step 2: Set up conversion tracking..."
|
||||
],
|
||||
"qualityScoreTips": [
|
||||
"Tip 1: Match keywords to ad copy...",
|
||||
"Tip 2: Optimize landing pages..."
|
||||
],
|
||||
"trackingSetup": [
|
||||
"Install Google Tag Manager...",
|
||||
"Set up conversion goals..."
|
||||
],
|
||||
"optimizationTips": [
|
||||
"Monitor search terms weekly...",
|
||||
"A/B test ad variations..."
|
||||
]
|
||||
"setupSteps": ["Step 1", "Step 2"],
|
||||
"qualityScoreTips": ["Tip 1", "Tip 2"],
|
||||
"trackingSetup": ["Conversion tag info", "GTM setup"],
|
||||
"optimizationTips": ["Tip 1", "Tip 2"]
|
||||
},
|
||||
"predictions": {
|
||||
"estimatedClicks": "500-800 per month",
|
||||
"estimatedImpressions": "15,000-25,000 per month",
|
||||
"estimatedCtr": "3.2%-4.5%",
|
||||
"estimatedConversions": "25-50 per month"
|
||||
"estimatedClicks": "500-800",
|
||||
"estimatedImpressions": "15,000-25,000",
|
||||
"estimatedCtr": "3.5%",
|
||||
"estimatedConversions": "30-50",
|
||||
"conversionRate": "4.2%",
|
||||
"avgCpc": "$1.85"
|
||||
},
|
||||
"historicalBenchmarks": {
|
||||
"industryAverageCtr": "3.1%",
|
||||
"industryAverageCpc": "$2.10",
|
||||
"seasonalTrends": "Peak in Q4",
|
||||
"geographicInsights": "London/NY show highest ROI"
|
||||
}
|
||||
}
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
KEYWORD RESEARCH REQUIREMENTS:
|
||||
@@ -632,30 +580,44 @@ OUTPUT FORMAT - Return ONLY valid JSON with this EXACT structure:
|
||||
"competitorInsights": [
|
||||
{
|
||||
"competitor": "Competitor Name",
|
||||
"website": "URL",
|
||||
"estimatedSpend": "$10k-$50k/mo",
|
||||
"targetAudience": "Who they target",
|
||||
"strengths": ["Strength 1", "Strength 2"],
|
||||
"weaknesses": ["Weakness 1", "Weakness 2"],
|
||||
"adStrategy": "Their current advertising approach"
|
||||
"adStrategy": "Their approach",
|
||||
"topKeywords": ["keyword 1", "keyword 2"],
|
||||
"adCopyExamples": [
|
||||
{"headline": "Example Headline", "description": "Example Description"}
|
||||
]
|
||||
}
|
||||
],
|
||||
"strategies": [
|
||||
{
|
||||
"id": "strategy-1",
|
||||
"direction": "Strategic Direction Name",
|
||||
"rationale": "Why this strategy works for this product/market",
|
||||
"targetAudience": "Specific audience segment",
|
||||
"competitiveAdvantage": "How this beats competitors",
|
||||
"keyMessages": ["Message 1", "Message 2", "Message 3"],
|
||||
"adCopyGuide": {
|
||||
"headlines": ["Headline 1 (max 30 symbols)", "Headline 2", "Headline 3"],
|
||||
"descriptions": ["Description 1 (max 90 symbols)", "Description 2"],
|
||||
"keywords": ["keyword 1", "keyword 2", "keyword 3"],
|
||||
"setupGuide": "Friendly step-by-step for a beginner on where exactly to paste these in Google Ads Manager"
|
||||
"rationale": "Why this strategy works",
|
||||
"targetAudience": "Audience segment with demographics (age 25-45, interests etc)",
|
||||
"targetingDetails": {
|
||||
"geography": "Primary locations",
|
||||
"demographics": "Specific age/gender groups",
|
||||
"behavior": "User behaviors"
|
||||
},
|
||||
"recommendedChannels": ["Google Search", "Display", "YouTube"],
|
||||
"competitiveAdvantage": "How this beats competitors",
|
||||
"messagingPillars": ["Pillar 1", "Pillar 2"],
|
||||
"keyMessages": ["Message 1", "Message 2"],
|
||||
"adCopyGuide": {
|
||||
"headlines": ["Headline 1", "Headline 2"],
|
||||
"descriptions": ["Description 1", "Description 2"],
|
||||
"keywords": ["keyword 1", "keyword 2"],
|
||||
"setupGuide": "Step-by-step for Google Ads Manager"
|
||||
},
|
||||
"recommendedChannels": ["Search", "Display", "YouTube"],
|
||||
"estimatedBudgetAllocation": { "search": 40, "display": 30, "video": 20, "social": 10 },
|
||||
"expectedROI": "150-200%",
|
||||
"riskLevel": "low",
|
||||
"timeToResults": "2-3 months"
|
||||
"timeToResults": "2-3 months",
|
||||
"successMetrics": ["CTR > 3%", "CPA < $20"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
104
package-lock.json
generated
104
package-lock.json
generated
@@ -31,6 +31,7 @@
|
||||
"tailwind-merge": "^3.4.0",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"typescript": "^5.7.2",
|
||||
"xlsx": "^0.18.5",
|
||||
"zod": "^4.2.1",
|
||||
"zustand": "^5.0.9"
|
||||
},
|
||||
@@ -1748,6 +1749,15 @@
|
||||
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/adler-32": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz",
|
||||
"integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
@@ -2242,6 +2252,19 @@
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/cfb": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz",
|
||||
"integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"adler-32": "~1.3.0",
|
||||
"crc-32": "~1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/chalk": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
@@ -2361,6 +2384,15 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/codepage": {
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz",
|
||||
"integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
@@ -2404,6 +2436,18 @@
|
||||
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/crc-32": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
|
||||
"integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"crc32": "bin/crc32.njs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||
@@ -3508,6 +3552,15 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/frac": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz",
|
||||
"integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/fraction.js": {
|
||||
"version": "5.3.4",
|
||||
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz",
|
||||
@@ -6816,6 +6869,18 @@
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/ssf": {
|
||||
"version": "0.11.2",
|
||||
"resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz",
|
||||
"integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"frac": "~1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/stable-hash": {
|
||||
"version": "0.0.5",
|
||||
"resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz",
|
||||
@@ -7754,6 +7819,24 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/wmf": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz",
|
||||
"integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/word": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz",
|
||||
"integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/word-wrap": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
|
||||
@@ -7763,6 +7846,27 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/xlsx": {
|
||||
"version": "0.18.5",
|
||||
"resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz",
|
||||
"integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"adler-32": "~1.3.0",
|
||||
"cfb": "~1.2.1",
|
||||
"codepage": "~1.15.0",
|
||||
"crc-32": "~1.2.1",
|
||||
"ssf": "~0.11.2",
|
||||
"wmf": "~1.0.1",
|
||||
"word": "~0.3.0"
|
||||
},
|
||||
"bin": {
|
||||
"xlsx": "bin/xlsx.njs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/yocto-queue": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
"tailwind-merge": "^3.4.0",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"typescript": "^5.7.2",
|
||||
"xlsx": "^0.18.5",
|
||||
"zod": "^4.2.1",
|
||||
"zustand": "^5.0.9"
|
||||
},
|
||||
|
||||
@@ -157,8 +157,16 @@ export interface GoogleAdsCampaign {
|
||||
};
|
||||
targeting: {
|
||||
locations?: string[];
|
||||
demographics?: string[];
|
||||
devices?: string[];
|
||||
demographics?: {
|
||||
age?: string[];
|
||||
gender?: string[];
|
||||
interests?: string[];
|
||||
};
|
||||
devices?: {
|
||||
mobile?: string;
|
||||
desktop?: string;
|
||||
tablet?: string;
|
||||
};
|
||||
schedule?: string[];
|
||||
};
|
||||
adGroups: GoogleAdGroup[];
|
||||
@@ -197,6 +205,14 @@ export interface GoogleAdsResult {
|
||||
estimatedImpressions?: string;
|
||||
estimatedCtr?: string;
|
||||
estimatedConversions?: string;
|
||||
conversionRate?: string;
|
||||
avgCpc?: string;
|
||||
};
|
||||
historicalBenchmarks?: {
|
||||
industryAverageCtr?: string;
|
||||
industryAverageCpc?: string;
|
||||
seasonalTrends?: string;
|
||||
geographicInsights?: string;
|
||||
};
|
||||
|
||||
rawContent: string;
|
||||
@@ -207,7 +223,13 @@ export interface MagicWandStrategy {
|
||||
direction: string;
|
||||
rationale: string;
|
||||
targetAudience: string;
|
||||
targetingDetails?: {
|
||||
geography?: string;
|
||||
demographics?: string;
|
||||
behavior?: string;
|
||||
};
|
||||
competitiveAdvantage: string;
|
||||
messagingPillars?: string[];
|
||||
keyMessages: string[];
|
||||
adCopyGuide: {
|
||||
headlines: string[];
|
||||
@@ -225,6 +247,7 @@ export interface MagicWandStrategy {
|
||||
expectedROI: string;
|
||||
riskLevel: "low" | "medium" | "high";
|
||||
timeToResults: string;
|
||||
successMetrics?: string[];
|
||||
}
|
||||
|
||||
export interface MagicWandResult {
|
||||
@@ -245,9 +268,14 @@ export interface MagicWandResult {
|
||||
// Competitive Intelligence
|
||||
competitorInsights: {
|
||||
competitor: string;
|
||||
website?: string;
|
||||
estimatedSpend?: string;
|
||||
targetAudience?: string;
|
||||
strengths: string[];
|
||||
weaknesses: string[];
|
||||
adStrategy: string;
|
||||
topKeywords?: string[];
|
||||
adCopyExamples?: { headline: string; description: string }[];
|
||||
}[];
|
||||
|
||||
// Strategic Directions
|
||||
|
||||
Reference in New Issue
Block a user