mirror of
https://github.com/christianvidalwolf-prog/CrazeAnalytix.git
synced 2026-08-03 13:45:23 +02:00
feat: Add Amazon Ads Weekly integration with Dashboard KPIs, Grid summary, and chart lines
- Update AdsRecord interface to support weekly data (year, week, cpc, ctr, acos, conversions) - Rewrite processAdsExcel to parse multiple sheets (2025, 2026) with 12-column format - Update mergeSalesAndAdsData to match by ASIN+Country+Year+Week - Add Advertising Performance section to Dashboard with Ad Spend, Attributed Sales, ACOS, ROAS - Add Ads toggle button and summary bar to DataGrid - Add Ad Spend lines (fuchsia dashed) to weekly comparison chart - Fix import paths in Dashboard.tsx and AdvertisingDashboard.tsx
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
|
||||
import React, { useState, useMemo, useEffect } from 'react';
|
||||
import { AggregatedData, GrowthMetric } from './types';
|
||||
import { AggregatedData, GrowthMetric, AdsRecord } from '../types';
|
||||
import {
|
||||
BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer,
|
||||
LineChart, Line, Legend
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
interface DashboardProps {
|
||||
data: AggregatedData;
|
||||
contextData?: AggregatedData | null;
|
||||
adsData?: AdsRecord[];
|
||||
}
|
||||
|
||||
const COLORS = ['#6366f1', '#ec4899', '#10b981', '#f59e0b', '#8b5cf6', '#0ea5e9'];
|
||||
@@ -454,10 +455,31 @@ const GrowthTable: React.FC<{
|
||||
);
|
||||
}
|
||||
|
||||
const Dashboard: React.FC<DashboardProps> = ({ data, contextData }) => {
|
||||
const Dashboard: React.FC<DashboardProps> = ({ data, contextData, adsData = [] }) => {
|
||||
const [seasonalityMetric, setSeasonalityMetric] = useState<'sellOut' | 'units'>('sellOut');
|
||||
const [top10Metric, setTop10Metric] = useState<'sellOut' | 'units'>('sellOut');
|
||||
|
||||
// Calculate Ads KPIs
|
||||
const adsKPIs = useMemo(() => {
|
||||
if (!adsData || adsData.length === 0) return null;
|
||||
|
||||
const totals = adsData.reduce((acc, ad) => ({
|
||||
cost: acc.cost + ad.cost,
|
||||
attributedSales: acc.attributedSales + ad.attributedSales30d,
|
||||
clicks: acc.clicks + ad.clicks,
|
||||
impressions: acc.impressions + ad.impressions,
|
||||
}), { cost: 0, attributedSales: 0, clicks: 0, impressions: 0 });
|
||||
|
||||
return {
|
||||
totalSpend: totals.cost,
|
||||
attributedSales: totals.attributedSales,
|
||||
acos: totals.attributedSales > 0 ? (totals.cost / totals.attributedSales) * 100 : 0,
|
||||
roas: totals.cost > 0 ? totals.attributedSales / totals.cost : 0,
|
||||
cpc: totals.clicks > 0 ? totals.cost / totals.clicks : 0,
|
||||
ctr: totals.impressions > 0 ? (totals.clicks / totals.impressions) * 100 : 0,
|
||||
};
|
||||
}, [adsData]);
|
||||
|
||||
// Decide which data source to use for Product Line charts
|
||||
// If contextData is provided (drill down), we use that to show the "Total Line" view.
|
||||
// Otherwise we use the standard filtered data.
|
||||
@@ -470,6 +492,39 @@ const Dashboard: React.FC<DashboardProps> = ({ data, contextData }) => {
|
||||
return (
|
||||
<div className="p-6 space-y-6 max-w-7xl mx-auto animate-fade-in pb-24">
|
||||
|
||||
{/* Ads Performance Section - Only shown when ads data is loaded */}
|
||||
{adsKPIs && (
|
||||
<div className="bg-gradient-to-r from-fuchsia-900/20 to-indigo-900/20 border border-fuchsia-500/30 rounded-xl p-6 animate-fade-in">
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<span className="w-2 h-2 rounded-full bg-fuchsia-400 animate-pulse"></span>
|
||||
<h3 className="text-sm font-bold text-fuchsia-400 uppercase tracking-wider">Advertising Performance</h3>
|
||||
<span className="text-xs text-slate-500 ml-auto">{adsData.length.toLocaleString('de-DE')} ad records loaded</span>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||
<div className="bg-slate-900/50 rounded-lg p-4">
|
||||
<span className="text-xs text-slate-400 block mb-1">Total Ad Spend</span>
|
||||
<span className="text-2xl font-bold text-fuchsia-400">€{adsKPIs.totalSpend.toLocaleString('de-DE', { maximumFractionDigits: 0 })}</span>
|
||||
</div>
|
||||
<div className="bg-slate-900/50 rounded-lg p-4">
|
||||
<span className="text-xs text-slate-400 block mb-1">Attributed Sales</span>
|
||||
<span className="text-2xl font-bold text-emerald-400">€{adsKPIs.attributedSales.toLocaleString('de-DE', { maximumFractionDigits: 0 })}</span>
|
||||
</div>
|
||||
<div className="bg-slate-900/50 rounded-lg p-4">
|
||||
<span className="text-xs text-slate-400 block mb-1">ACOS</span>
|
||||
<span className={`text-2xl font-bold ${adsKPIs.acos <= 30 ? 'text-emerald-400' : adsKPIs.acos <= 50 ? 'text-amber-400' : 'text-red-400'}`}>
|
||||
{adsKPIs.acos.toFixed(1)}%
|
||||
</span>
|
||||
</div>
|
||||
<div className="bg-slate-900/50 rounded-lg p-4">
|
||||
<span className="text-xs text-slate-400 block mb-1">ROAS</span>
|
||||
<span className={`text-2xl font-bold ${adsKPIs.roas >= 3 ? 'text-emerald-400' : adsKPIs.roas >= 2 ? 'text-amber-400' : 'text-red-400'}`}>
|
||||
{adsKPIs.roas.toFixed(2)}x
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* KPI Section - Pass both specific data and context data */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<MultiYearKPICard
|
||||
|
||||
Reference in New Issue
Block a user