import React, { useState, useMemo } from 'react'; import { ItemGrowthMetric } from '../types'; interface ItemGrowthTableProps { title: string; data: ItemGrowthMetric[]; type: 'growth' | 'decline'; periods: { current: string; previous: string }; } type SortConfig = { key: keyof ItemGrowthMetric | null; direction: 'asc' | 'desc' }; const ItemGrowthTable: React.FC = ({ title, data, type, periods }) => { const [sortConfig, setSortConfig] = useState({ key: null, direction: 'desc' }); const sortedData = useMemo(() => { if (!sortConfig.key) { // Default sort by units growth value for initial view return [...data].sort((a, b) => type === 'growth' ? b.unitsGrowthValue - a.unitsGrowthValue : a.unitsGrowthValue - b.unitsGrowthValue ); } return [...data].sort((a, b) => { const aVal = a[sortConfig.key!] as number | string; const bVal = b[sortConfig.key!] as number | string; if (typeof aVal === 'string' && typeof bVal === 'string') { return sortConfig.direction === 'asc' ? (aVal as string).localeCompare(bVal as string) : (bVal as string).localeCompare(aVal as string); } if (aVal < bVal) return sortConfig.direction === 'asc' ? -1 : 1; if (aVal > bVal) return sortConfig.direction === 'asc' ? 1 : -1; return 0; }); }, [data, sortConfig, type]); const requestSort = (key: keyof ItemGrowthMetric) => { let direction: 'asc' | 'desc' = 'desc'; // If already sorting by this key, toggle direction if (sortConfig.key === key && sortConfig.direction === 'desc') { direction = 'asc'; } setSortConfig({ key, direction }); }; const getSortIndicator = (key: keyof ItemGrowthMetric) => { if (sortConfig.key !== key) { return ( ); } return ( {sortConfig.direction === 'asc' ? : } ); }; return (
{/* Sell Out Columns */} {/* Units Columns */} {sortedData.length > 0 ? ( sortedData.map((item, idx) => ( {/* Sell Out Columns */} {/* Units Columns */} )) ) : ( )}
requestSort('sku')} >
SKU {getSortIndicator('sku')}
requestSort('asin')} >
ASIN {getSortIndicator('asin')}
requestSort('title')} >
Product Title {getSortIndicator('title')}
requestSort('line')} >
Product Line {getSortIndicator('line')}
requestSort('previousYearSellOut')} >
Sell Out {periods.previous} {getSortIndicator('previousYearSellOut')}
requestSort('currentYearSellOut')} >
Sell Out {periods.current} {getSortIndicator('currentYearSellOut')}
requestSort('sellOutGrowthValue')} >
SO Diff {getSortIndicator('sellOutGrowthValue')}
requestSort('sellOutGrowthPercentage')} >
SO Growth % {getSortIndicator('sellOutGrowthPercentage')}
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')}
{item.sku || '-'} {item.asin || '-'} {item.title || '-'} {item.line || '-'}€{item.previousYearSellOut.toLocaleString(undefined, {maximumFractionDigits: 0})} €{item.currentYearSellOut.toLocaleString(undefined, {maximumFractionDigits: 0})} = 0 ? 'text-emerald-400' : 'text-red-400'}`}> {item.sellOutGrowthValue > 0 ? '+' : ''}€{item.sellOutGrowthValue.toLocaleString(undefined, {maximumFractionDigits: 0})} = 0 ? 'bg-emerald-500/10 text-emerald-400' : 'bg-red-500/10 text-red-400'}`}> {item.sellOutGrowthPercentage.toFixed(1)}% {item.previousYearUnits.toLocaleString()} {item.currentYearUnits.toLocaleString()} = 0 ? 'text-violet-400' : 'text-orange-400'}`}> {item.unitsGrowthValue > 0 ? '+' : ''}{item.unitsGrowthValue.toLocaleString()} = 0 ? 'bg-violet-500/10 text-violet-400' : 'bg-orange-500/10 text-orange-400'}`}> {item.unitsGrowthPercentage.toFixed(1)}%
Insufficient data to calculate {type}. (Select a customer and at least 2 distinct years/periods)
); } export default ItemGrowthTable;