feat: add Top 50 Best Sellers badge in Weekly Sales

- Calculate static Top 50 ranking based on 2025 Sell Out (independent of filters)
- Display trophy badge with position number for Top 50 ASINs
- Badge shows 🏆 followed by rank (1-50)
- Ranking stays static regardless of date/country filters applied
- Pre-computed ranking based on cumulative 2025 data
This commit is contained in:
Christian Vidal Wolf
2026-01-22 12:02:21 +01:00
parent 15f5b08c71
commit 1c8ec7b835
2 changed files with 78 additions and 37 deletions
+27 -1
View File
@@ -254,6 +254,32 @@ const App: React.FC = () => {
const filteredAdsData = useMemo(() => filterAdsData(adsData, filters), [adsData, filters]);
const aggregatedData = useMemo(() => aggregateData(filteredData), [filteredData]);
// Calculate static Top 50 Best Sellers for 2025 (independent of current filters)
const top50Ranking2025 = useMemo(() => {
// Filter only 2025 data
const data2025 = rawData.filter(r => r.year === 2025);
// Aggregate Sell Out by ASIN
const asinTotals = new Map<string, number>();
data2025.forEach(r => {
const asin = r.asin.trim().toUpperCase();
asinTotals.set(asin, (asinTotals.get(asin) || 0) + r.sellOut);
});
// Sort by total Sell Out descending and take top 50
const sorted = Array.from(asinTotals.entries())
.sort((a, b) => b[1] - a[1])
.slice(0, 50);
// Create a Map of ASIN -> rank (1-50)
const rankMap = new Map<string, number>();
sorted.forEach(([asin], index) => {
rankMap.set(asin, index + 1);
});
return rankMap;
}, [rawData]);
// Combine Sales & Ads Data dynamically based on current filters
const combinedAdsData = useMemo(() => {
return mergeSalesAndAdsData(filteredData, filteredAdsData);
@@ -427,7 +453,7 @@ const App: React.FC = () => {
)}
<Suspense fallback={<LoadingSpinner />}>
{view === 'table' && <DataGrid data={combinedAdsData} hasCustomerFilter={filters.customer.length > 0} adsData={filteredAdsData} />}
{view === 'weekly' && <WeeklyGrid data={combinedAdsData} />}
{view === 'weekly' && <WeeklyGrid data={combinedAdsData} top50Ranking={top50Ranking2025} />}
{view === 'movers' && <TopMovers data={filteredData} />}
</Suspense>
</div>