import React, { useMemo, useState, useEffect, useCallback, useRef } from 'react'; import * as XLSX from 'xlsx'; import { CombinedKPIs, ColumnFilterCondition, SalesRecord } from '../types'; import { pivotWeeklySalesData, WeeklyPivotRow, PAN_EU_COUNTRIES, checkNumericConditions, filterData } from '../services/dataProcessor'; import { StockBadge } from './StockBadge'; import { InColumnStockFilter } from './InColumnStockFilter'; import { NumericColumnFilter, NumericFilterConfig, passesNumericFilter } from './NumericColumnFilter'; import { Top50Badge } from './Top50Badge'; import { VendorStockBadge } from './VendorStockBadge'; import { BuyBoxWarningBadge } from './BuyBoxWarningBadge'; import { WarehouseIcon, AmazonSmileIcon, CoverageIcon } from './Icons'; import { ExcelFilter } from './ExcelFilter'; interface WeeklyGridProps { data: CombinedKPIs[]; top50Ranking?: { eu: Map; uk: Map; }; onDrillDown?: (sku: string) => void; stockMap?: Map; stockFilter: string[]; onStockFilterChange: (newFilters: string[]) => void; customerFilters: string[]; vendorStockMap?: Map; vendorStockFilter: string[]; onVendorStockFilterChange: (newFilters: string[]) => void; wocFilter: string[]; onWocFilterChange: (newFilters: string[]) => void; velocityMap?: Map; buyBoxLostMap?: Map }>; top50Mode?: 'eu' | 'uk'; } type SortConfig = { key: string; // weekKey or 'rank' direction: 'asc' | 'desc'; metric: 'units' | 'spend' | 'rank' | 'gv'; } | null; const ROWS_PER_PAGE = 50; const MAX_VISIBLE_WEEKS = 20; // Limit visible weeks for performance // Debounce hook for search const useDebounce = (value: string, delay: number) => { const [debouncedValue, setDebouncedValue] = useState(value); useEffect(() => { const handler = setTimeout(() => setDebouncedValue(value), delay); return () => clearTimeout(handler); }, [value, delay]); return debouncedValue; }; // Tooltip component to show comparison details on hover (WoW and YoY) const MetricDetailTooltip: React.FC<{ children: React.ReactNode; currentValue: number; previousValue: number; yoyValue: number; currentWeekLabel: string; previousWeekLabel: string; yoyWeekLabel: string; metricName: string; metricColor: string; formatValue?: (val: number) => string; }> = ({ children, currentValue, previousValue, yoyValue, currentWeekLabel, previousWeekLabel, yoyWeekLabel, metricName, metricColor, formatValue }) => { const [isVisible, setIsVisible] = useState(false); const wowGrowth = previousValue > 0 ? ((currentValue - previousValue) / previousValue) * 100 : (currentValue > 0 ? 100 : 0); const yoyGrowth = yoyValue > 0 ? ((currentValue - yoyValue) / yoyValue) * 100 : null; const format = formatValue || ((v: number) => v.toLocaleString('de-DE')); const hasYoyData = yoyValue > 0; return (
setIsVisible(true)} onMouseLeave={() => setIsVisible(false)} >
{children}
{isVisible && (
📊 {metricName} Comparison
{/* Current Week */}
{currentWeekLabel} {format(currentValue)}
{/* Previous Week */}
{previousWeekLabel} {previousValue > 0 ? format(previousValue) : 'N/A'}
{/* Same Week Last Year - ALWAYS SHOWN */}
{yoyWeekLabel} {hasYoyData ? format(yoyValue) : 'No data'}
{/* WoW Growth */}
vs Previous Week {previousValue > 0 ? ( = 0 ? 'text-emerald-400' : 'text-red-400'}`}> {wowGrowth >= 0 ? '▲' : '▼'} {Math.abs(wowGrowth).toFixed(1)}% ) : ( N/A )}
{/* YoY Growth - ALWAYS SHOWN */}
vs Same Week Last Year {yoyGrowth !== null ? ( = 0 ? 'text-emerald-400' : 'text-red-400'}`}> {yoyGrowth >= 0 ? '▲' : '▼'} {Math.abs(yoyGrowth).toFixed(1)}% ) : ( No data )}
{/* Arrow */}
)}
); }; const WeeklyRow: React.FC<{ row: WeeklyPivotRow; weeks: string[]; onDrillDown?: (sku: string) => void; stockMap?: Map; top50Ranking?: { eu: Map; uk: Map }; top50Mode: 'eu' | 'uk'; sortConfig: SortConfig; renderGrowth: (current: number, previous: number) => React.ReactNode; customerFilters: string[]; vendorStockMap?: Map; velocityMap?: Map; buyBoxLostMap?: Map }>; primaryMetric: 'units' | 'revenue'; }> = React.memo(({ row, weeks, onDrillDown, stockMap, top50Ranking, top50Mode, sortConfig, renderGrowth, customerFilters, vendorStockMap, velocityMap, buyBoxLostMap, primaryMetric }) => { const ranks: { rank: number; label: string; theme: 'amber' | 'blue' | 'indigo' }[] = []; const asin = row.asin.trim().toUpperCase(); if (top50Ranking) { if (top50Mode === 'eu') { const rank = top50Ranking.eu.get(asin); if (rank) ranks.push({ rank, label: 'EU', theme: 'indigo' }); } else { const rank = top50Ranking.uk.get(asin); if (rank) ranks.push({ rank, label: 'UK', theme: 'blue' }); } } return (
{ranks.map((r, i) => ( ))} onDrillDown?.(row.sku)} className={`text-xs font-black uppercase tracking-tighter truncate max-w-[120px] transition-all ${onDrillDown ? 'text-indigo-400 cursor-pointer hover:text-indigo-300 hover:underline' : 'text-indigo-400/70'}`} title={onDrillDown ? `Click to see Ads detail for ${row.sku}` : ''} > {row.sku || '-'} {row.asin}
{row.title} {stockMap && ( )}
{row.line}
{weeks.map((week, idx) => { const val = row.unitsByWeek[week] || 0; const prevVal = row.unitsByWeek[weeks[idx + 1]] || 0; const spend = row.spendByWeek[week] || 0; const prevSpend = row.spendByWeek[weeks[idx + 1]] || 0; const revenue = row.revenueByWeek[week] || 0; const prevRevenue = row.revenueByWeek[weeks[idx + 1]] || 0; const gv = row.gvByWeek?.[week] || 0; const prevGv = row.gvByWeek?.[weeks[idx + 1]] || 0; // Parse week as YYYY-WW const [year, weekNum] = week.split('-'); const prevWeekNum = weeks[idx + 1]?.split('-')[1] || '-'; // Calculate same week last year key (e.g., 2026-05 -> 2025-05) const lastYearWeek = `${parseInt(year) - 1}-${weekNum}`; const yoyUnits = row.unitsByWeek[lastYearWeek] || 0; const yoySpend = row.spendByWeek[lastYearWeek] || 0; const yoyRevenue = row.revenueByWeek[lastYearWeek] || 0; const yoyGv = row.gvByWeek?.[lastYearWeek] || 0; return ( {/* Primary Metric based on toggle */} {primaryMetric === 'units' ? (
0 ? (sortConfig?.key === week && sortConfig.metric === 'units' ? 'text-indigo-400' : 'text-white') : 'text-slate-700'}`}> {val > 0 ? val.toLocaleString('de-DE') : '-'} {val > 0 && renderGrowth(val, prevVal)}
) : ( `€${v.toLocaleString('de-DE', { minimumFractionDigits: 0, maximumFractionDigits: 0 })}`} >
0 ? (sortConfig?.key === week && sortConfig.metric === 'spend' ? 'text-amber-400' : 'text-white') : 'text-slate-700'}`}> {revenue > 0 ? `€${revenue.toLocaleString('de-DE', { minimumFractionDigits: 0, maximumFractionDigits: 0 })}` : '-'} {revenue > 0 && renderGrowth(revenue, prevRevenue)}
)}
{/* Secondary Metrics & Ads Spend */}
{primaryMetric === 'units' ? ( revenue > 0 && ( `€${v.toLocaleString('de-DE', { minimumFractionDigits: 0, maximumFractionDigits: 0 })}`} >
€{revenue.toLocaleString('de-DE', { minimumFractionDigits: 0, maximumFractionDigits: 0 })} {renderGrowth(revenue, prevRevenue)}
) ) : ( val > 0 && (
{val.toLocaleString('de-DE')} {renderGrowth(val, prevVal)}
) )} {/* Always show Ads Spend (if > 0) */} {spend > 0 && ( `€${v.toLocaleString('de-DE', { minimumFractionDigits: 0, maximumFractionDigits: 0 })}`} >
Ads: €{spend.toLocaleString('de-DE', { minimumFractionDigits: 0, maximumFractionDigits: 0 })} {renderGrowth(spend, prevSpend)}
)}
{/* GV with tooltip - Always small/bottom */} {gv > 0 && (
GV: {gv.toLocaleString('de-DE')} {renderGrowth(gv, prevGv)}
)}
); })} ); }); const WeeklyGrid: React.FC = ({ data, top50Ranking, onDrillDown, stockMap, vendorStockMap, stockFilter, onStockFilterChange, vendorStockFilter, onVendorStockFilterChange, wocFilter, onWocFilterChange, customerFilters, top50Mode, velocityMap, buyBoxLostMap }) => { // Pivot data - memoized const { rows, weeks: allWeeks } = useMemo(() => pivotWeeklySalesData(data), [data]); // Limit visible weeks for performance const weeks = useMemo(() => allWeeks.slice(0, MAX_VISIBLE_WEEKS), [allWeeks]); const [searchTerm, setSearchTerm] = useState(''); const debouncedSearch = useDebounce(searchTerm, 300); const [currentPage, setCurrentPage] = useState(1); const [growthFilterMode, setGrowthFilterMode] = useState<'all' | 'up' | 'down' | 'stable'>('all'); const [growthThreshold, setGrowthThreshold] = useState(10); const [showOnlyTop50, setShowOnlyTop50] = useState(false); const [displayCount, setDisplayCount] = useState(50); const [primaryMetric, setPrimaryMetric] = useState<'units' | 'revenue'>('units'); const scrollContainerRef = useRef(null); // State for Column Filters (SKU, ASIN, Title, Line) const [columnFilters, setColumnFilters] = useState>({}); const handleColumnFilterChange = (columnKey: string, condition: ColumnFilterCondition | undefined) => { setColumnFilters(prev => { const next = { ...prev }; if (condition) next[columnKey] = condition; else delete next[columnKey]; return next; }); setCurrentPage(1); }; // Numeric filters for Units, Spend, GV const [numericFilters, setNumericFilters] = useState([]); // Helper to get/set filter for a specific week+metric const getNumericFilter = useCallback((week: string, metric: 'units' | 'spend' | 'gv') => { return numericFilters.find(f => f.week === week && f.metric === metric) || null; }, [numericFilters]); const setNumericFilter = useCallback((filter: NumericFilterConfig | null, week: string, metric: 'units' | 'spend' | 'gv') => { setNumericFilters(prev => { // Remove existing filter for this week+metric const filtered = prev.filter(f => !(f.week === week && f.metric === metric)); // Add new filter if provided if (filter) { return [...filtered, filter]; } return filtered; }); }, []); // Default sort: most recent week, descending, units const [sortConfig, setSortConfig] = useState(() => { if (weeks.length > 0) { return { key: weeks[0], direction: 'desc', metric: 'units' }; } return null; }); // Reset pagination when search changes useEffect(() => { setCurrentPage(1); setDisplayCount(50); }, [debouncedSearch, showOnlyTop50, top50Mode]); const handleSort = useCallback((weekKey: string, metric: 'units' | 'spend' | 'rank' | 'gv') => { setSortConfig(prev => { if (prev?.key === weekKey && prev.metric === metric) { return { key: weekKey, direction: prev.direction === 'asc' ? 'desc' : 'asc', metric }; } return { key: weekKey, direction: metric === 'rank' ? 'asc' : 'desc', metric }; }); }, []); // Calculate totals in a SINGLE PASS const weekTotals = useMemo(() => { const totals: { [weekKey: string]: { units: number, spend: number, revenue: number, gv: number } } = {}; const visibleWeeks = weeks; // Initialize visible weeks for (let i = 0; i < visibleWeeks.length; i++) { totals[visibleWeeks[i]] = { units: 0, spend: 0, revenue: 0, gv: 0 }; } // Single pass through rows for (let i = 0; i < rows.length; i++) { const row = rows[i]; for (let j = 0; j < visibleWeeks.length; j++) { const w = visibleWeeks[j]; totals[w].units += (row.unitsByWeek[w] || 0); totals[w].spend += (row.spendByWeek[w] || 0); totals[w].revenue += (row.revenueByWeek[w] || 0); totals[w].gv += (row.gvByWeek?.[w] || 0); } } return totals; }, [rows, weeks]); // 1. Filter by search term, Top 50, and growth (using debounced value) const filteredRows = useMemo(() => { let result = rows; // Top 50 Filter if (showOnlyTop50 && top50Ranking) { result = result.filter(r => { const asin = r.asin.trim().toUpperCase(); const isUK = r.customer.toLowerCase().includes('uk'); if (top50Mode === 'eu') { return !isUK && top50Ranking.eu.has(asin); } else { return isUK && top50Ranking.uk.has(asin); } }); } // Column Filters (Excel-style) - using logic adapted from filterData if (Object.keys(columnFilters).length > 0) { (Object.entries(columnFilters) as [string, ColumnFilterCondition][]).forEach(([key, condition]) => { if (!condition) return; if (condition.selectedValues && condition.selectedValues.length > 0) { result = result.filter(r => { const val = String((r as any)[key] || ''); return condition.selectedValues?.includes(val); }); } if (condition.textFilter) { const { operator, value } = condition.textFilter; const lowerValue = value.toLowerCase(); result = result.filter(r => { const rowVal = String((r as any)[key] || '').toLowerCase(); switch (operator) { case 'equals': return rowVal === lowerValue; case 'notEquals': return rowVal !== lowerValue; case 'contains': return rowVal.includes(lowerValue); case 'notContains': return !rowVal.includes(lowerValue); case 'startsWith': return rowVal.startsWith(lowerValue); case 'notStartsWith': return !rowVal.startsWith(lowerValue); case 'endsWith': return rowVal.endsWith(lowerValue); case 'notEndsWith': return !rowVal.endsWith(lowerValue); default: return true; } }); } }); } // Search Filter if (debouncedSearch) { const searchTerms = debouncedSearch.toLowerCase().split(/[\s,]+/).filter(t => t.length > 0); if (searchTerms.length > 0) { result = result.filter(r => { const rowValues = [ r.sku.toLowerCase(), r.asin.toLowerCase(), r.title.toLowerCase(), r.line.toLowerCase() ]; return searchTerms.some(term => rowValues.some(val => val.includes(term)) ); }); } } // Growth Filter (if active) if (growthFilterMode !== 'all' && sortConfig) { const currentWeek = sortConfig.key; const currentWeekIdx = weeks.indexOf(currentWeek); const prevWeek = weeks[currentWeekIdx + 1]; if (prevWeek) { result = result.filter(r => { const currentUnits = r.unitsByWeek[currentWeek] || 0; const prevUnits = r.unitsByWeek[prevWeek] || 0; if (prevUnits === 0) { if (currentUnits === 0) return growthFilterMode === 'stable'; return growthFilterMode === 'up'; } const growth = ((currentUnits - prevUnits) / prevUnits) * 100; if (growthFilterMode === 'up') return growth >= growthThreshold; if (growthFilterMode === 'down') return growth <= -Math.abs(growthThreshold); if (growthFilterMode === 'stable') return Math.abs(growth) < growthThreshold; return true; }); } } // WOC Filter if (wocFilter && wocFilter.length > 0 && velocityMap && vendorStockMap) { result = result.filter(r => { const asin = r.asin.trim().toUpperCase(); const stockData = vendorStockMap.get(asin); if (!stockData) return false; const stock = top50Mode === 'uk' ? stockData.uk : stockData.eu; const velocity = velocityMap.get(asin) || 0; let woc: number = 0; if (velocity > 0) { woc = stock / velocity; } else if (stock > 0) { woc = 999; } return checkNumericConditions(woc, wocFilter); }); } // Numeric Filters (Units, Spend, GV) if (numericFilters.length > 0) { result = result.filter(row => { return numericFilters.every(filter => { let value = 0; if (filter.metric === 'units') { value = row.unitsByWeek[filter.week] || 0; } else if (filter.metric === 'spend') { value = row.spendByWeek[filter.week] || 0; } else if (filter.metric === 'gv') { value = row.gvByWeek?.[filter.week] || 0; } return passesNumericFilter(value, filter); }); }); } return result; }, [rows, debouncedSearch, showOnlyTop50, top50Ranking, growthFilterMode, growthThreshold, sortConfig, weeks, top50Mode, wocFilter, velocityMap, vendorStockMap, numericFilters, columnFilters]); // 2. Sort results const sortedRows = useMemo(() => { if (!sortConfig || filteredRows.length === 0) return filteredRows; const result = [...filteredRows]; const { key: weekKey, direction, metric } = sortConfig; if (metric === 'rank') { if (!top50Ranking) return result; const rankMap = top50Mode === 'eu' ? top50Ranking.eu : top50Ranking.uk; result.sort((a, b) => { const asinA = a.asin.trim().toUpperCase(); const asinB = b.asin.trim().toUpperCase(); const rankA = rankMap.get(asinA) || 999; const rankB = rankMap.get(asinB) || 999; return direction === 'asc' ? rankA - rankB : rankB - rankA; }); } else { const metricKey = metric === 'units' ? 'unitsByWeek' : metric === 'spend' ? 'spendByWeek' : 'gvByWeek'; result.sort((a, b) => { const valA = a[metricKey][weekKey] || 0; const valB = b[metricKey][weekKey] || 0; if (valA === valB) return 0; return direction === 'asc' ? valA - valB : valB - valA; }); } return result; }, [filteredRows, sortConfig, top50Ranking, top50Mode]); // 3. Paginate const paginatedRows = useMemo(() => { const start = (currentPage - 1) * ROWS_PER_PAGE; return sortedRows.slice(start, start + ROWS_PER_PAGE); }, [sortedRows, currentPage]); const totalPages = Math.ceil(sortedRows.length / ROWS_PER_PAGE); const renderGrowth = useCallback((current: number, previous: number) => { if (!previous || previous === 0) return null; const pct = ((current - previous) / previous) * 100; const isPositive = pct >= 0; return ( {isPositive ? '▲' : '▼'}{Math.abs(pct).toFixed(0)}% ); }, []); // Export to Excel const handleExportExcel = useCallback(() => { const exportData = sortedRows.map(row => { const rowData: { [key: string]: string | number } = { SKU: row.sku, ASIN: row.asin, Title: row.title, Line: row.line, }; allWeeks.forEach(week => { rowData[`${week} Units`] = row.unitsByWeek[week] || 0; rowData[`${week} Spend`] = row.spendByWeek[week] || 0; rowData[`${week} GV`] = row.gvByWeek[week] || 0; }); return rowData; }); const totalsRow: any = { SKU: 'TOTALS', ASIN: '', Title: 'ALL FILTERED PRODUCTS', Line: '', }; allWeeks.forEach(week => { totalsRow[`${week} Units`] = weekTotals[week]?.units || 0; totalsRow[`${week} Spend`] = weekTotals[week]?.spend || 0; totalsRow[`${week} GV`] = weekTotals[week]?.gv || 0; }); exportData.push(totalsRow); const ws = XLSX.utils.json_to_sheet(exportData); const wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, ws, 'Weekly Sales'); XLSX.writeFile(wb, `Weekly_Sales_Export_${new Date().toISOString().slice(0, 10)}.xlsx`); }, [sortedRows, allWeeks, weekTotals]); return (
{/* Toolbar: Search & Pagination */}
{/* Search Input */}
setSearchTerm(e.target.value)} className="w-full bg-slate-950 border border-white/10 rounded-lg px-4 py-2 text-sm text-white focus:outline-none focus:ring-2 focus:ring-indigo-500 transition-all pl-10" />
{/* Growth Filter */}
Growth Filter {growthFilterMode !== 'all' && (
Thresh: setGrowthThreshold(Number(e.target.value))} className="w-12 bg-slate-900 border border-white/10 rounded px-1.5 py-1 text-xs text-white text-center focus:outline-none focus:ring-1 focus:ring-indigo-500" /> %
)}
{/* Top 50 Filter Toggle */} {top50Ranking && ( (top50Ranking.eu.size || 0) > 0 || (top50Ranking.uk.size || 0) > 0 ) && (
)} {/* Metric Toggle */}
{/* Export Button */}
Showing {Math.min(filteredRows.length, (currentPage - 1) * ROWS_PER_PAGE + 1)}-{Math.min(filteredRows.length, currentPage * ROWS_PER_PAGE)} of {filteredRows.length}
{currentPage} / {Math.max(1, totalPages)}
{weeks.map(week => ( ))} {weeks.map((week, idx) => ( ))} {sortedRows.length > 0 ? ( (() => { const displayRows = sortedRows.slice(0, displayCount); return ( <> {displayRows.map(row => ( ))} {displayCount < sortedRows.length && ( )} ); })() ) : ( )}
handleSort('rank', 'rank')} > Product Details {sortConfig?.metric === 'rank' && ( {sortConfig.direction === 'asc' ? '↑' : '↓'} )}
r.sku))).sort()} currentFilter={columnFilters['sku']} onFilterChange={handleColumnFilterChange} icon={SKU} /> r.asin))).sort()} currentFilter={columnFilters['asin']} onFilterChange={handleColumnFilterChange} icon={ASIN} /> r.title))).sort()} currentFilter={columnFilters['title']} onFilterChange={handleColumnFilterChange} icon={Title} /> r.line))).sort()} currentFilter={columnFilters['line']} onFilterChange={handleColumnFilterChange} icon={Line} />
} /> } options={[ 'Out of Stock (0)', 'In Stock (>0)', 'In Stock (>20)', 'Low Stock (<10)', ]} /> } options={[ '< 4 Weeks', '> 4 Weeks', 'Out of Stock', 'Infinite Cover' ]} />
{week.split('-')[1]}/{week.split('-')[0].slice(-2)}
handleSort(week, 'units')} className="text-[9px] cursor-pointer hover:text-indigo-300" > Units {sortConfig?.key === week && sortConfig.metric === 'units' && ( {sortConfig.direction === 'asc' ? '↑' : '↓'} )} setNumericFilter(f, week, 'units')} accentColor="indigo" />
handleSort(week, 'spend')} className="text-[9px] cursor-pointer hover:text-amber-300" > Spend {sortConfig?.key === week && sortConfig.metric === 'spend' && ( {sortConfig.direction === 'asc' ? '↑' : '↓'} )} setNumericFilter(f, week, 'spend')} accentColor="amber" />
handleSort(week, 'gv')} className="text-[9px] cursor-pointer hover:text-teal-300" > GV {sortConfig?.key === week && sortConfig.metric === 'gv' && ( {sortConfig.direction === 'asc' ? '↑' : '↓'} )} setNumericFilter(f, week, 'gv')} accentColor="teal" />
TOTALS
{primaryMetric === 'units' ? ( <>
{weekTotals[week]?.units.toLocaleString('de-DE') || 0} {renderGrowth(weekTotals[week]?.units || 0, weekTotals[weeks[idx + 1]]?.units || 0)}
€{(weekTotals[week]?.revenue || 0).toLocaleString('de-DE', { minimumFractionDigits: 0, maximumFractionDigits: 0 })} {renderGrowth(weekTotals[week]?.revenue || 0, weekTotals[weeks[idx + 1]]?.revenue || 0)}
Ads: €{(weekTotals[week]?.spend || 0).toLocaleString('de-DE', { minimumFractionDigits: 0, maximumFractionDigits: 0 })} {renderGrowth(weekTotals[week]?.spend || 0, weekTotals[weeks[idx + 1]]?.spend || 0)}
) : ( <>
€{(weekTotals[week]?.revenue || 0).toLocaleString('de-DE', { minimumFractionDigits: 0, maximumFractionDigits: 0 })} {renderGrowth(weekTotals[week]?.revenue || 0, weekTotals[weeks[idx + 1]]?.revenue || 0)}
{(weekTotals[week]?.units || 0).toLocaleString('de-DE')} Units {renderGrowth(weekTotals[week]?.units || 0, weekTotals[weeks[idx + 1]]?.units || 0)}
Ads: €{(weekTotals[week]?.spend || 0).toLocaleString('de-DE', { minimumFractionDigits: 0, maximumFractionDigits: 0 })} {renderGrowth(weekTotals[week]?.spend || 0, weekTotals[weeks[idx + 1]]?.spend || 0)}
)}
GV: {(weekTotals[week]?.gv || 0).toLocaleString('de-DE')} {renderGrowth(weekTotals[week]?.gv || 0, weekTotals[weeks[idx + 1]]?.gv || 0)}
No SKUs found matching "{debouncedSearch}"
); }; export default WeeklyGrid;