diff --git a/components/Dashboard.tsx b/components/Dashboard.tsx index 8e5f805..33a7060 100644 --- a/components/Dashboard.tsx +++ b/components/Dashboard.tsx @@ -22,6 +22,16 @@ interface DashboardProps { const COLORS = ['#6366f1', '#ec4899', '#10b981', '#f59e0b', '#8b5cf6', '#0ea5e9']; +const getYearColor = (year: string | number) => { + const yStr = year.toString(); + if (yStr === '2024') return '#6366f1'; // Indigo + if (yStr === '2025') return '#ec4899'; // Pink + if (yStr === '2026') return '#10b981'; // Emerald + const yInt = parseInt(yStr); + if (isNaN(yInt)) return '#6366f1'; + return COLORS[yInt % COLORS.length]; +}; + // Reusable Expandable Card Component const ExpandableCard: React.FC<{ title: string; children: React.ReactNode; className?: string }> = ({ title, children, className }) => { const [isExpanded, setIsExpanded] = useState(false); @@ -99,9 +109,7 @@ const MultiYearKPICard: React.FC<{ const formatValue = (val: number) => { if (metric === 'sellOut') { - if (metric === 'sellOut') { - return `€${val.toLocaleString('de-DE', { minimumFractionDigits: 0, maximumFractionDigits: 0 })}`; - } + return `€${val.toLocaleString('de-DE', { minimumFractionDigits: 0, maximumFractionDigits: 0 })}`; } return val.toLocaleString('de-DE'); }; @@ -110,10 +118,10 @@ const MultiYearKPICard: React.FC<{ const displayTitle = contextData ? `${title} (Selected Item)` : title; return ( -
-

{displayTitle}

+
+

{displayTitle}

- {sortedYears.length === 0 &&

0

} + {sortedYears.length === 0 &&

0

} {sortedYears.length > 0 && (
@@ -134,7 +142,7 @@ const MultiYearKPICard: React.FC<{ const pct = ((currentValue - prevValue) / prevValue) * 100; growthElement = ( = 0 ? 'text-emerald-400' : 'text-red-400'}`}> - {pct > 0 ? '▲' : '▼'} {Math.abs(pct).toFixed(1)}% + {pct >= 0 ? '▲' : '▼'} {Math.abs(pct).toFixed(1)}% ); } @@ -146,7 +154,7 @@ const MultiYearKPICard: React.FC<{ const pct = ((contextValue - prevContextValue) / prevContextValue) * 100; contextGrowthElement = ( = 0 ? 'text-emerald-400' : 'text-red-400'}`}> - {pct > 0 ? '▲' : '▼'} {Math.abs(pct).toFixed(1)}% + {pct >= 0 ? '▲' : '▼'} {Math.abs(pct).toFixed(1)}% ); } @@ -154,22 +162,18 @@ const MultiYearKPICard: React.FC<{ } return ( -
-
-
- {year} -
- - {formatValue(currentValue)} - - {growthElement} -
-
+
+ {year} +
+ + {formatValue(currentValue)} + + {growthElement}
{/* Context Row (Product Line Total) */} {contextData && contextValue > 0 && ( -
+
Total Product Line:
{formatValue(contextValue)} @@ -232,7 +236,7 @@ const SeasonalityTooltip = ({ active, payload, label, metric }: any) => { const pct = ((currVal - prevVal) / prevVal) * 100; growthEl = ( = 0 ? 'text-emerald-400' : 'text-red-400'}`}> - {pct > 0 ? '▲' : '▼'} {Math.abs(pct).toFixed(1)}% + {pct >= 0 ? '▲' : '▼'} {Math.abs(pct).toFixed(1)}% ); } @@ -282,7 +286,7 @@ const ComparisonTooltip = ({ active, payload, label, metric }: any) => { const pct = ((currVal - prevVal) / prevVal) * 100; growthEl = ( = 0 ? 'text-emerald-400' : 'text-red-400'}`}> - {pct > 0 ? '▲' : '▼'} {Math.abs(pct).toFixed(1)}% + {pct >= 0 ? '▲' : '▼'} {Math.abs(pct).toFixed(1)}% ); } @@ -361,62 +365,35 @@ const GrowthTable: React.FC<{ requestSort('line')} > -
Product Line {getSortIndicator('line')}
+
Product Line {getSortIndicator('line')}
- {/* Sell Out Columns */} requestSort('previousYearSellOut')} > -
Sell Out {periods.previous} {getSortIndicator('previousYearSellOut')}
+
Sell Out {periods.previous} {getSortIndicator('previousYearSellOut')}
requestSort('currentYearSellOut')} > -
Sell Out {periods.current} {getSortIndicator('currentYearSellOut')}
+
Sell Out {periods.current} {getSortIndicator('currentYearSellOut')}
requestSort('sellOutGrowthValue')} > -
SO Diff {getSortIndicator('sellOutGrowthValue')}
+
SO Diff {getSortIndicator('sellOutGrowthValue')}
requestSort('sellOutGrowthPercentage')} > -
SO Growth % {getSortIndicator('sellOutGrowthPercentage')}
- - - {/* Units Columns */} - requestSort('previousYearUnits')} - > -
Units {periods.previous} {getSortIndicator('previousYearUnits')}
- - requestSort('currentYearUnits')} - > -
Units {periods.current} {getSortIndicator('currentYearUnits')}
- - requestSort('unitsGrowthValue')} - > -
Units Diff {getSortIndicator('unitsGrowthValue')}
- - requestSort('unitsGrowthPercentage')} - > -
Units Growth % {getSortIndicator('unitsGrowthPercentage')}
+
SO Growth % {getSortIndicator('sellOutGrowthPercentage')}
@@ -426,34 +403,21 @@ const GrowthTable: React.FC<{ {item.line} - {/* Sell Out Columns */} €{item.previousYearSellOut.toLocaleString('de-DE', { maximumFractionDigits: 0 })} €{item.currentYearSellOut.toLocaleString('de-DE', { maximumFractionDigits: 0 })} = 0 ? 'text-emerald-400' : 'text-red-400'}`}> {item.sellOutGrowthValue > 0 ? '+' : ''}€{item.sellOutGrowthValue.toLocaleString('de-DE', { maximumFractionDigits: 0 })} - = 0 ? 'bg-emerald-500/10 text-emerald-400' : 'bg-red-500/10 text-red-400'}`}> - {item.sellOutGrowthPercentage.toFixed(0)}% - - - - {/* Units Columns */} - {item.previousYearUnits.toLocaleString('de-DE')} - {item.currentYearUnits.toLocaleString('de-DE')} - = 0 ? 'text-violet-400' : 'text-orange-400'}`}> - {item.unitsGrowthValue > 0 ? '+' : ''}{item.unitsGrowthValue.toLocaleString('de-DE')} - - - = 0 ? 'bg-violet-500/10 text-violet-400' : 'bg-orange-500/10 text-orange-400'}`}> - {item.unitsGrowthPercentage.toFixed(0)}% + = 0 ? 'bg-emerald-500/10 text-emerald-400' : 'bg-red-500/10 text-red-400'}`}> + {item.sellOutGrowthPercentage >= 0 ? '+' : ''}{item.sellOutGrowthPercentage.toFixed(0)}% )) ) : ( - + Insufficient data to calculate {type}. (Select at least 2 distinct years/periods) @@ -463,7 +427,7 @@ const GrowthTable: React.FC<{
); -} +}; const Dashboard: React.FC = ({ data, @@ -550,16 +514,16 @@ const Dashboard: React.FC = ({ return (
- {/* Ads Performance Section - Condensed Layout */} + {/* Ads Performance Section - Overhauled Layout to match Mockup */} {adsKPIsByYear && availableAdsYears.length > 0 && ( -
-
+
+

Advertising Performance

-
+
{adsData.length.toLocaleString('de-DE')} Records
@@ -579,25 +543,25 @@ const Dashboard: React.FC = ({ : (isPositive ? 'text-emerald-400' : 'text-red-400'); return ( - + {isPositive ? '▲' : '▼'}{Math.abs(pct).toFixed(0)}% ); }; return ( -
-
- {year} -
+
+
+ {year} +
-
+
{/* Ad Spend */}
- Spend + Spend
- + €{kpi.totalSpend.toLocaleString('de-DE', { maximumFractionDigits: 0 })} {renderGrowth(kpi.totalSpend, prevKpi?.totalSpend)} @@ -605,9 +569,9 @@ const Dashboard: React.FC = ({
{/* Attributed Sales */}
- Sales + Sales
- + €{kpi.attributedSales.toLocaleString('de-DE', { maximumFractionDigits: 0 })} {renderGrowth(kpi.attributedSales, prevKpi?.attributedSales)} @@ -615,9 +579,9 @@ const Dashboard: React.FC = ({
{/* ACOS */}
- ACOS + ACOS
- + {kpi.acos.toFixed(1)}% {renderGrowth(kpi.acos, prevKpi?.acos, true)} @@ -625,9 +589,9 @@ const Dashboard: React.FC = ({
{/* ROAS */}
- ROAS + ROAS
- = 3 ? 'text-emerald-400' : kpi.roas >= 2 ? 'text-amber-400' : 'text-red-400'}`}> + {kpi.roas.toFixed(2)}x {renderGrowth(kpi.roas, prevKpi?.roas)} @@ -659,7 +623,7 @@ const Dashboard: React.FC = ({ />
- {/* Main Grid */} + {/* Main Grid matching Mockup Columns */}
{/* Left Column */} @@ -704,7 +668,7 @@ const Dashboard: React.FC = ({ key={year} dataKey={`${year}_${top10Metric === 'sellOut' ? 'value' : 'units'}`} name={year} - fill={COLORS[index % COLORS.length]} + fill={getYearColor(year)} radius={[0, 4, 4, 0]} /> ))} @@ -762,7 +726,7 @@ const Dashboard: React.FC = ({ key={year} dataKey={year} name={year} - fill={COLORS[index % COLORS.length]} + fill={getYearColor(year)} radius={[4, 4, 0, 0]} /> ))} @@ -808,7 +772,7 @@ const Dashboard: React.FC = ({ type="monotone" dataKey={year} name={year} - stroke={COLORS[index % COLORS.length]} + stroke={getYearColor(year)} strokeWidth={3} dot={{ r: 4, strokeWidth: 2 }} activeDot={{ r: 6 }} @@ -856,7 +820,7 @@ const Dashboard: React.FC = ({ type="monotone" dataKey={year} name={year} - stroke={COLORS[index % COLORS.length]} + stroke={getYearColor(year)} strokeWidth={3} dot={{ r: 4, strokeWidth: 2 }} activeDot={{ r: 6 }} @@ -882,7 +846,7 @@ const Dashboard: React.FC = ({ key={year} dataKey={year} name={year} - fill={COLORS[index % COLORS.length]} + fill={getYearColor(year)} radius={[4, 4, 0, 0]} /> ))}