diff --git a/components/DataGrid.tsx b/components/DataGrid.tsx index b7baa27..1a37e00 100644 --- a/components/DataGrid.tsx +++ b/components/DataGrid.tsx @@ -1199,45 +1199,48 @@ const DataGrid: React.FC = ({ data, filters, hasCustomerFilter, a SKU Title - {years.map((year, idx) => ( - - - {year} SO {idx === 0 && '↓'} - - - {year} U - - - ))} - S.O. Δ% - S.O. Δ€ + {years.map((year, idx) => { + const prevYear = years[idx + 1]; + return ( + + + {year} SO {idx === 0 && '↓'} + + + {year} U + + {prevYear && ( + <> + + Δ% {year}/{prevYear} + + + Δ€ {year}/{prevYear} + + + U Δ% {year}/{prevYear} + + + U Δ {year}/{prevYear} + + + )} + + ); + })} {paginatedRows.map((row) => { - const latestYear = years[0] || '2026'; - const prevYear = years[1] || '2025'; + // Helper to get color code based on year comparison + const getYearCellColor = (year: string) => { + const sorted = [...years].sort((a, b) => parseInt(b) - parseInt(a)); + const index = sorted.indexOf(year); + if (index === 0) return 'text-cyan-400'; // Current Year (Cyan) + if (index === 1) return 'text-indigo-400'; // Previous Year (Indigo) + return 'text-slate-400'; // Reference years + }; - const currentData = row.totalsByYear[latestYear]; - const prevData = row.totalsByYear[prevYear]; - - const currentSOVal = currentData?.sellOut || 0; - const currentUnitsVal = currentData?.units || 0; - - const prevSOVal = prevData?.sellOut || 0; - const prevUnitsVal = prevData?.units || 0; - - // Calculate Growth Values - let sellOutGrowthPct = 0; - if (prevSOVal > 0) { - sellOutGrowthPct = ((currentSOVal - prevSOVal) / prevSOVal) * 100; - } else if (currentSOVal > 0) { - sellOutGrowthPct = 100; - } - - const sellOutDeltaVal = currentSOVal - prevSOVal; - - // Calculate Weeks of Coverage (WOC) const skuClean = row.sku?.replace(/(DE|EN)$/i, ''); const internalStockVal = stockMap?.get(skuClean) || 0; const vendorStockVal = (top50Mode === 'uk' ? vendorStockMap?.get(row.asin?.trim().toUpperCase())?.uk : vendorStockMap?.get(row.asin?.trim().toUpperCase())?.eu) || 0; @@ -1319,42 +1322,92 @@ const DataGrid: React.FC = ({ data, filters, hasCustomerFilter, a - {/* Metrics Data */} - {years.map((year) => { + {/* Metrics Data and Growth Columns */} + {years.map((year, idx) => { const yData = row.totalsByYear[year]; + const prevYear = years[idx + 1]; + const prevData = prevYear ? row.totalsByYear[prevYear] : null; + + const currentSOVal = yData?.sellOut || 0; + const currentUnitsVal = yData?.units || 0; + const prevSOVal = prevData?.sellOut || 0; + const prevUnitsVal = prevData?.units || 0; + + // Sell Out Growth + let sellOutGrowthPct = 0; + if (prevSOVal > 0) { + sellOutGrowthPct = ((currentSOVal - prevSOVal) / prevSOVal) * 100; + } else if (currentSOVal > 0) { + sellOutGrowthPct = 100; + } + const sellOutDeltaVal = currentSOVal - prevSOVal; + + // Units Growth + let unitsGrowthPct = 0; + if (prevUnitsVal > 0) { + unitsGrowthPct = ((currentUnitsVal - prevUnitsVal) / prevUnitsVal) * 100; + } else if (currentUnitsVal > 0) { + unitsGrowthPct = 100; + } + const unitsDeltaVal = currentUnitsVal - prevUnitsVal; + + const cellColor = getYearCellColor(year); + return ( - + {yData ? `€${yData.sellOut.toLocaleString('de-DE', { maximumFractionDigits: 0 })}` : '-'} - + {yData ? yData.units.toLocaleString('de-DE') : '-'} + {prevYear && ( + <> + {/* Sell Out Growth Pct */} + + {sellOutGrowthPct !== 0 ? ( + 0 ? 'bg-emerald-500/10 text-emerald-400 border border-emerald-500/20' : 'bg-rose-500/10 text-rose-400 border border-rose-500/20'}`}> + {sellOutGrowthPct > 0 ? '▲' : '▼'} {Math.round(Math.abs(sellOutGrowthPct))}% + + ) : ( + - + )} + + {/* Sell Out Delta Val */} + = 0 ? 'text-emerald-400' : 'text-rose-500'}`}> + {sellOutDeltaVal !== 0 ? ( + + {sellOutDeltaVal > 0 ? '+' : ''}€{sellOutDeltaVal.toLocaleString('de-DE', { maximumFractionDigits: 0 })} + + ) : ( + - + )} + + {/* Units Growth Pct */} + + {unitsGrowthPct !== 0 ? ( + 0 ? 'bg-emerald-500/10 text-emerald-400 border border-emerald-500/20' : 'bg-rose-500/10 text-rose-400 border border-rose-500/20'}`}> + {unitsGrowthPct > 0 ? '▲' : '▼'} {Math.round(Math.abs(unitsGrowthPct))}% + + ) : ( + - + )} + + {/* Units Delta Val */} + = 0 ? 'text-emerald-400' : 'text-rose-500'}`}> + {unitsDeltaVal !== 0 ? ( + + {unitsDeltaVal > 0 ? '+' : ''}{unitsDeltaVal.toLocaleString('de-DE')} + + ) : ( + - + )} + + + )} ); })} - - {/* Delta growth % */} - - {sellOutGrowthPct !== 0 ? ( - - ▲ {Math.round(sellOutGrowthPct)}% - - ) : ( - - - )} - - - {/* Delta growth € */} - - {sellOutDeltaVal !== 0 ? ( - - {sellOutDeltaVal > 0 ? '+' : ''}€{sellOutDeltaVal.toLocaleString('de-DE', { maximumFractionDigits: 0 })} - - ) : ( - - - )} - ); })}