diff --git a/components/GoogleAdsGenerator.tsx b/components/GoogleAdsGenerator.tsx index 20106ea..5576abd 100644 --- a/components/GoogleAdsGenerator.tsx +++ b/components/GoogleAdsGenerator.tsx @@ -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(`Google Ads Strategy Report`); - parts.push(`
`); - - parts.push(`

Google Ads Strategy Report

Generated by PromptArch on ${new Date().toLocaleDateString()}

`); - - if (googleAdsResult) { - // Keywords - parts.push(`

🎯 Keyword Research

`); - if (googleAdsResult.keywords) { - if (Array.isArray(googleAdsResult.keywords.primary)) { - parts.push(`

Primary Keywords

`); - for (const k of googleAdsResult.keywords.primary) { - parts.push(`${k.keyword} (${k.cpc || 'N/A'})`); - } - parts.push(`
`); - } - if (Array.isArray(googleAdsResult.keywords.longTail)) { - parts.push(`

Long-tail Opportunities

`); - for (const k of googleAdsResult.keywords.longTail) { - parts.push(`${k.keyword} (${k.cpc || 'N/A'})`); - } - parts.push(`
`); - } - if (Array.isArray(googleAdsResult.keywords.negative)) { - parts.push(`

Negative Keywords

`); - for (const k of googleAdsResult.keywords.negative) { - parts.push(`${k.keyword} (${k.cpc || 'N/A'})`); - } - parts.push(`
`); - } - } - parts.push(`
`); - - // Ad Copies - if (Array.isArray(googleAdsResult.adCopies)) { - parts.push(`

✍️ Ad Copy Variations

`); - let i = 0; - for (const ad of googleAdsResult.adCopies) { - i++; - parts.push(`
Variation ${i}
`); - if (Array.isArray(ad.headlines)) { - for (const h of ad.headlines) { - parts.push(`
${h}
`); - } - } - if (Array.isArray(ad.descriptions)) { - parts.push(`
${(ad.descriptions || []).join('
')}
`); - } - if (ad.callToAction) { - parts.push(`
${ad.callToAction}
`); - } - parts.push(`
`); - } - parts.push(`
`); - } - - // Campaigns - if (Array.isArray(googleAdsResult.campaigns)) { - parts.push(`

🏗️ Campaign Structure

`); - for (const c of googleAdsResult.campaigns) { - parts.push(`

${c.name}

`); - parts.push(`

${c.type.toUpperCase()} • ${c.budget?.daily} ${c.budget?.currency}/day

`); - const locs = c.targeting?.locations ? c.targeting.locations.join(', ') : 'Global'; - parts.push(`
Locations: ${locs}
Ad Groups: ${c.adGroups ? c.adGroups.length : 0}
`); - } - parts.push(`
`); - } - - // Implementation & Predictions - parts.push(`

🚀 Implementation & Forecast

`); - if (googleAdsResult.implementation && Array.isArray(googleAdsResult.implementation.setupSteps)) { - parts.push(`

Setup Steps

    `); - for (const s of googleAdsResult.implementation.setupSteps) { - parts.push(`
  • ${s}
  • `); - } - parts.push(`
`); - } - - if (googleAdsResult.predictions) { - const p = googleAdsResult.predictions; - parts.push(`

Monthly Estimations

`); - parts.push(`
${p.estimatedClicks || '-'}
Clicks
`); - parts.push(`
${p.estimatedCtr || '-'}
CTR
`); - parts.push(`
${p.estimatedConversions || '-'}
Convs
`); - parts.push(`
`); - } - parts.push(`
`); - } - - if (magicWandResult) { - parts.push(`

🧠 Market Intelligence

`); - parts.push(`

Strategy Rationale

${magicWandResult.rationale}

`); - const ma = magicWandResult.marketAnalysis; - parts.push(`

Market Data

Growth Rate: ${ma?.growthRate || 'N/A'}

Top Competitors

    `); - if (ma && Array.isArray(ma.topCompetitors)) { - for (const c of ma.topCompetitors) { - parts.push(`
  • ${c}
  • `); - } - } else { - parts.push(`
  • None identified
  • `); - } - parts.push(`
`); - } - - parts.push(`
`); - - 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() { )} - - diff --git a/lib/export-utils.ts b/lib/export-utils.ts index c9500b3..0dbdb31 100644 --- a/lib/export-utils.ts +++ b/lib/export-utils.ts @@ -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(`Report`); +export const generateGoogleAdsExcel = (googleAds?: any, magic?: any): Blob => { + const wb = XLSX.utils.book_new(); - parts.push(`

Marketing Strategy

`); + // 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(`

Keywords

`); + // 2. Keywords Sheet + if (googleAds?.keywords) { const kw = googleAds.keywords; - if (kw && Array.isArray(kw.primary)) { - parts.push(`

Primary

`); - kw.primary.forEach((k: any) => parts.push(`${k.keyword} `)); - } - parts.push(`
`); + 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(`

Ads

`); - ads.forEach((ad: any) => { - parts.push(`
${(ad.headlines || [])[0] || ''}

${(ad.descriptions || [])[0] || ''}

`); - }); - parts.push(`
`); - } + const kwSheet = XLSX.utils.aoa_to_sheet(kwData); + XLSX.utils.book_append_sheet(wb, kwSheet, "Keywords"); } - if (magic) { - parts.push(`

Market

`); - const ma = magic.marketAnalysis; - if (ma) { - parts.push(`

Size: ${ma.industrySize || 'N/A'}

`); - parts.push(`

Growth: ${ma.growthRate || 'N/A'}

`); - } - const strats = magic.strategies; - if (Array.isArray(strats)) { - parts.push(`

Strategies

`); - strats.forEach((s: any) => { - parts.push(`

${s.direction}

${s.rationale}

`); - }); - } - parts.push(`
`); + // 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(``); - 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 ` + + + + + 360° Google Ads Strategy Intelligence Report + + + + + + +
+ +
+
+

Strategy Intelligence

+

360° Campaign Architecture for ${googleAds?.websiteUrl || 'New Project'}

+
+
+
+

Generated On

+

${new Date().toLocaleDateString()}

+
+
PA
+
+
+ + +
+
+

Est. Monthly Impressions

+

${googleAds?.predictions?.estimatedImpressions || '15k-25k'}

+
+
+
+

Target CTR

+

${googleAds?.predictions?.estimatedCtr || '3.5%'}

+
+
+
+

Est. Conversions

+

${googleAds?.predictions?.estimatedConversions || '30-50'}

+
+
+
+

Industry Avg CTR

+

${googleAds?.historicalBenchmarks?.industryAverageCtr || '3.1%'}

+
+
+
+ + +
+
+

Performance Forecast

+
+ +
+
+
+

Device Distribution

+
+ +
+
+
+
Mobile
+ ${googleAds?.campaigns?.[0]?.targeting?.devices?.mobile || '60%'} +
+
+
Desktop
+ ${googleAds?.campaigns?.[0]?.targeting?.devices?.desktop || '30%'} +
+
+
Tablet
+ ${googleAds?.campaigns?.[0]?.targeting?.devices?.tablet || '10%'} +
+
+
+
+ + +
+
+

Keyword Intelligence

+ Semantic Mapping Enabled +
+
+ +
+

+
Primary Keywords +

+ ${(googleAds?.keywords?.primary || []).slice(0, 10).map((k: any) => ` +
+ ${k.keyword} + ${k.cpc || '$1.50'} +
+ `).join('')} +
+ +
+

+
Long-Tail Intent +

+ ${(googleAds?.keywords?.longTail || []).slice(0, 10).map((k: any) => ` +
+ ${k.keyword} + ${k.competition || 'low'} +
+ `).join('')} +
+ +
+

+
Exclusion List +

+ ${(googleAds?.keywords?.negative || []).slice(0, 8).map((k: any) => ` +
+ ${k.keyword} + EXCLUDE +
+ `).join('')} +
+
+
+ + +
+

High-Performance Ad Copy Suite

+
+ ${(googleAds?.adCopies || []).map((ad: any, i: number) => ` +
+
+ Variation ${i + 1} + ${(ad.campaignType || 'Search').toUpperCase()} +
+
+ ${(ad.headlines || []).map((h: any) => ` +
${h}
+ `).join('')} +
+ ${(ad.descriptions || []).map((d: any) => ` +
${d}
+ `).join('')} +
+
+
${ad.callToAction}
+
+
+
+
+
+
+ `).join('')} +
+
+ + + ${magic?.competitorInsights ? ` +
+
+

Competitive Intelligence Matrix

+
+

Market Sentiment

+
+
+
+
+
+
+
+
+
+ ${(magic.competitorInsights || []).map((c: any) => ` +
+
+
+

${c.competitor}

+

${c.website || 'Direct Competitor'}

+
+
+

Est. Spend

+

${c.estimatedSpend || 'Undisclosed'}

+
+
+ +
+
+

Core Strengths

+
    + ${(c.strengths || []).map((s: any) => `
  • ${s}
  • `).join('')} +
+
+
+

Weaknesses

+
    + ${(c.weaknesses || []).map((w: any) => `
  • ${w}
  • `).join('')} +
+
+
+ +
+

Counter-Strategy Rationale

+

"${c.adStrategy}"

+
+
+ `).join('')} +
+
+ ` : ''} + + + ${magic?.strategies ? ` +
+

Strategic Directions

+
+ ${(magic.strategies || []).map((s: any, idx: number) => ` +
+
+
+ +
+
${idx + 1}
+
+

${s.direction}

+ ${s.riskLevel || 'low'} risk profile +
+
+
+

Expected ROI

+

${s.expectedROI}

+
+
+

Time to Impact

+

${s.timeToResults}

+
+
+
+ + +
+
+

Strategic Rationale

+

${s.rationale}

+
+
+
+

Target Audience

+

${s.targetAudience}

+
+
+

Competitive Edge

+

${s.competitiveAdvantage}

+
+
+
+ + +
+
+

Channel Matrix

+
+ ${Object.entries(s.estimatedBudgetAllocation || {}).map(([c, v]: [string, any]) => ` +
+
+ ${c} + ${v}% +
+
+
+
+
+ `).join('')} +
+
+
+

Success Thresholds

+
+ ${(s.successMetrics || []).map((m: any) => ` + ${m} + `).join('')} +
+
+
+
+
+ `).join('')} +
+
+ ` : ''} + + +
+

© ${new Date().getFullYear()} PromptArch Intelligence Unit • Confident Strategic Asset

+
+
PA
+
Q
+
+
+
+ + + +`; }; diff --git a/lib/services/ollama-cloud.ts b/lib/services/ollama-cloud.ts index 6e6dca5..11db769 100644 --- a/lib/services/ollama-cloud.ts +++ b/lib/services/ollama-cloud.ts @@ -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"] } ] } diff --git a/lib/services/qwen-oauth.ts b/lib/services/qwen-oauth.ts index f8acea6..6cf9cf3 100644 --- a/lib/services/qwen-oauth.ts +++ b/lib/services/qwen-oauth.ts @@ -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"] } ] } diff --git a/lib/services/zai-plan.ts b/lib/services/zai-plan.ts index 684556e..bdd71b6 100644 --- a/lib/services/zai-plan.ts +++ b/lib/services/zai-plan.ts @@ -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"] } ] } diff --git a/package-lock.json b/package-lock.json index 3dca7bc..a6609be 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index d3933fe..9061d38 100644 --- a/package.json +++ b/package.json @@ -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" }, diff --git a/types/index.ts b/types/index.ts index 127eee0..f85bb3b 100644 --- a/types/index.ts +++ b/types/index.ts @@ -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