From f26ea171cebf648b1260e82c3133667c1d43488c Mon Sep 17 00:00:00 2001 From: Christian Vidal Wolf Date: Fri, 30 Jan 2026 08:49:17 +0100 Subject: [PATCH] feat: add Top 50 filter and sorting to DataGrid + fix BuyBox badge hover --- components/BuyBoxWarningBadge.tsx | 2 +- components/DataGrid.tsx | 139 ++++++++++++++++++++++++------ 2 files changed, 115 insertions(+), 26 deletions(-) diff --git a/components/BuyBoxWarningBadge.tsx b/components/BuyBoxWarningBadge.tsx index 549df81..713c4dc 100644 --- a/components/BuyBoxWarningBadge.tsx +++ b/components/BuyBoxWarningBadge.tsx @@ -35,7 +35,7 @@ export const BuyBoxWarningBadge: React.FC = ({ asin, bu {/* Hover Popup */} {isHovered && ( -
+
⚠️ Buy Box Lost Reason diff --git a/components/DataGrid.tsx b/components/DataGrid.tsx index 1045286..705e838 100644 --- a/components/DataGrid.tsx +++ b/components/DataGrid.tsx @@ -242,11 +242,13 @@ const ExpandableChartCard: React.FC<{ title: string; children: React.ReactNode; const DataGrid: React.FC = ({ data, hasCustomerFilter, adsData, stockMap, vendorStockMap, top50Ranking, top50Mode, velocityMap, buyBoxLostMap, defaultSort }) => { const [currentPage, setCurrentPage] = useState(1); + const [searchTerm, setSearchTerm] = useState(''); const [sortConfig, setSortConfig] = useState(defaultSort || { key: null, direction: 'desc' }); const [showChart, setShowChart] = useState(true); const [visibleMetrics, setVisibleMetrics] = useState<('sellOut' | 'units')[]>(['sellOut', 'units']); const [showAdsMetrics, setShowAdsMetrics] = useState(true); const [showAttributedSales, setShowAttributedSales] = useState(false); + const [showOnlyTop50, setShowOnlyTop50] = useState(false); const [showDimensionMenu, setShowDimensionMenu] = useState(false); const dimensionRef = useRef(null); @@ -377,6 +379,29 @@ const DataGrid: React.FC = ({ data, hasCustomerFilter, adsData, s const processedRows = useMemo(() => { let result = pivotRows; + // NEW: Filter for Top 50 + if (showOnlyTop50 && top50Ranking) { + const rankMap = top50Mode === 'eu' ? top50Ranking.eu : top50Ranking.uk; + result = result.filter(row => { + const asin = (row.asin || '').trim().toUpperCase(); + return asin && rankMap.has(asin); + }); + } + + // 0. Global Search Filter + if (searchTerm) { + const term = searchTerm.toLowerCase().trim(); + result = result.filter(row => { + return ( + (row.sku?.toLowerCase().includes(term)) || + (row.asin?.toLowerCase().includes(term)) || + (row.title?.toLowerCase().includes(term)) || + (row.line?.toLowerCase().includes(term)) || + (row.customer?.toLowerCase().includes(term)) + ); + }); + } + // 1. Filter if (rowFilters.length > 0) { const latestYear = years[0]; @@ -473,10 +498,18 @@ const DataGrid: React.FC = ({ data, hasCustomerFilter, adsData, s if (valA > valB) return sortConfig.direction === 'asc' ? 1 : -1; return 0; }); + } else if (showOnlyTop50 && top50Ranking) { + // Default sort by Rank when Top 50 is active + const rankMap = top50Mode === 'eu' ? top50Ranking.eu : top50Ranking.uk; + result = [...result].sort((a, b) => { + const rankA = a.asin ? rankMap.get(a.asin.toUpperCase()) || 999 : 999; + const rankB = b.asin ? rankMap.get(b.asin.toUpperCase()) || 999 : 999; + return rankA - rankB; + }); } return result; - }, [pivotRows, rowFilters, sortConfig, years]); + }, [pivotRows, rowFilters, sortConfig, years, searchTerm, showOnlyTop50, top50Ranking, top50Mode]); // Calculate aggregated totals for the header row const filteredTotalsByYear = useMemo(() => { @@ -592,7 +625,7 @@ const DataGrid: React.FC = ({ data, hasCustomerFilter, adsData, s // Reset pagination when filters change useEffect(() => { setCurrentPage(1); - }, [rowFilters, data, effectiveDimensions]); + }, [rowFilters, data, effectiveDimensions, searchTerm]); return (
@@ -798,6 +831,20 @@ const DataGrid: React.FC = ({ data, hasCustomerFilter, adsData, s )} + {/* Top 50 Toggle */} + {top50Ranking && (top50Ranking.eu.size > 0 || top50Ranking.uk.size > 0) && ( + + )} + {/* Attributed Sales Toggle - Only show when ads data is loaded and ads metrics are shown */} {adsSummary && (
: dim === 'asin' || dim === 'sku' - ?
+ ?
{(() => { const asin = (row.asin || '').trim().toUpperCase(); if (asin && top50Ranking) { @@ -1122,8 +1169,10 @@ const DataGrid: React.FC = ({ data, hasCustomerFilter, adsData, s } return null; })()} - {(row[dim as keyof PivotRow] as string) || '-'} - + {(row[dim as keyof PivotRow] as string) || '-'} +
+ +
: (row[dim as keyof PivotRow] as string) || '-' } @@ -1206,7 +1255,7 @@ const DataGrid: React.FC = ({ data, hasCustomerFilter, adsData, s {paginatedRows.length === 0 && ( - + No data matches your filters. @@ -1215,26 +1264,66 @@ const DataGrid: React.FC = ({ data, hasCustomerFilter, adsData, s
- {/* Pagination */} -
-
- Page {currentPage} of {totalPages || 1} + {/* Pagination and Search */} +
+
+ {/* Search Bar */} +
+ setSearchTerm(e.target.value)} + className="w-full bg-slate-950 border border-white/10 rounded-xl px-4 py-2 text-sm text-white focus:outline-none focus:ring-2 focus:ring-indigo-500 transition-all pl-10" + /> + + + + {searchTerm && ( + + )} +
+ +
+ +
-
- - + +
+
+ +
+ {currentPage} / {totalPages || 1} +
+ +
+ +
+ {processedRows.length} Records found +