import React, { useMemo } from 'react'; import { CombinedKPIs } from '../types'; import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, LineChart, Line, Legend, ComposedChart, Area } from 'recharts'; interface AdvertisingDashboardProps { data: CombinedKPIs[]; } // Helper to aggregate data for charts const aggregateByMonth = (data: CombinedKPIs[]) => { const map = new Map(); // Sort chronologically if needed, but assuming input might be mixed // We'll create keys like "Jan 23", "Feb 23" and sort them later data.forEach(item => { const key = item.month; // e.g. "May-24" if (!map.has(key)) { map.set(key, { name: key, salesTotal: 0, salesAds: 0, cost: 0, salesOrganic: 0, impressions: 0, clicks: 0, unitsTotal: 0, unitsAds: 0 }); } const entry = map.get(key); entry.salesTotal += item.salesTotal; entry.salesAds += item.salesAds; entry.cost += item.cost; entry.salesOrganic += item.salesOrganic; entry.impressions += item.impressions; entry.clicks += item.clicks; entry.unitsTotal += item.unitsTotal; entry.unitsAds += item.unitsAds; }); const result = Array.from(map.values()).map(r => ({ ...r, acos: r.salesAds > 0 ? (r.cost / r.salesAds) * 100 : 0, tacos: r.salesTotal > 0 ? (r.cost / r.salesTotal) * 100 : 0, ctr: r.impressions > 0 ? (r.clicks / r.impressions) * 100 : 0, cpc: r.clicks > 0 ? r.cost / r.clicks : 0 })); // Sort by Date return result.sort((a, b) => { const [mA, yA] = a.name.split('-'); const [mB, yB] = b.name.split('-'); // Year comparison if (yA !== yB) return parseInt(yA) - parseInt(yB); // Month comparison const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; return months.indexOf(mA) - months.indexOf(mB); }); }; const KPICard = ({ title, value, subValue, type = 'currency' }: { title: string, value: number, subValue?: string, type?: 'currency' | 'percent' | 'number' }) => { const formatted = type === 'currency' ? `€${value.toLocaleString('de-DE', { maximumFractionDigits: 0 })}` : type === 'percent' ? `${value.toFixed(2)}%` : value.toLocaleString('de-DE'); return (
{title} {formatted} {subValue && {subValue}}
); }; const AdvertisingDashboard: React.FC = ({ data }) => { const aggregated = useMemo(() => aggregateByMonth(data), [data]); const totals = useMemo(() => { return data.reduce((acc, curr) => ({ salesTotal: acc.salesTotal + curr.salesTotal, salesAds: acc.salesAds + curr.salesAds, cost: acc.cost + curr.cost, clicks: acc.clicks + curr.clicks, impressions: acc.impressions + curr.impressions, salesOrganic: acc.salesOrganic + curr.salesOrganic }), { salesTotal: 0, salesAds: 0, cost: 0, clicks: 0, impressions: 0, salesOrganic: 0 }); }, [data]); const kpiAcos = totals.salesAds > 0 ? (totals.cost / totals.salesAds) * 100 : 0; const kpiTacos = totals.salesTotal > 0 ? (totals.cost / totals.salesTotal) * 100 : 0; const kpiRoas = totals.cost > 0 ? totals.salesAds / totals.cost : 0; if (data.length === 0) { return (

No Advertising Data Available

Please upload an Ads CSV file via "Connect Data".

) } return (
{/* KPI Grid */}
{/* Charts Row 1 */}
{/* Spend vs Sales Trend */}

Ad Spend vs Ad Sales Trend

`€${v / 1000}k`} /> `€${val.toLocaleString('de-DE')}`} />
{/* ACOS vs TACOS Trend */}

Efficiency: ACOS vs TACOS

`${val.toFixed(2)}%`} />
{/* Charts Row 2 */}
{/* Organic vs Paid Sales */}

Total Sales Composition (Organic vs Paid)

`€${v / 1000}k`} /> `€${val.toLocaleString('de-DE')}`} />
{/* Funnel: Impressions -> Clicks */}

Marketing Funnel (Impressions & Clicks)

`${(v / 1000).toFixed(0)}k`} label={{ value: 'Impressions', angle: -90, position: 'insideLeft', fill: '#8b5cf6' }} />
{/* Detailed Table */}

Monthly Advertising Breakdown

{[...aggregated].reverse().map((row, idx) => ( ))}
Period Spend Ad Sales Total Sales ACOS TACOS Clicks CPC
{row.name} €{row.cost.toLocaleString('de-DE', { maximumFractionDigits: 0 })} €{row.salesAds.toLocaleString('de-DE', { maximumFractionDigits: 0 })} €{row.salesTotal.toLocaleString('de-DE', { maximumFractionDigits: 0 })} {row.acos.toFixed(2)}% {row.tacos.toFixed(2)}% {row.clicks.toLocaleString('de-DE')} €{row.cpc.toFixed(2)}
); }; // Fix for AreaChart reference error in some bundlers, just export import { AreaChart } from 'recharts'; export default AdvertisingDashboard;