feat: add stock, vendor and buybox badges to Top Movers table

- Added StockBadge, VendorStockBadge and BuyBoxWarningBadge to TopMovers component
- Updated Dashboard to pass stockMap, vendorStockMap, buyBoxLostMap and top50Mode props
- Updated App.tsx to pass the required maps to Dashboard
- Table now shows 3 new columns: Stock (GMBH), Vendor (Amazon Warehouse), and Buy Box status

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
Christian Vidal Wolf
2026-03-16 15:05:11 +01:00
co-authored by Qwen-Coder
parent 167273e659
commit 19f1137a30
3 changed files with 64 additions and 6 deletions
+10 -1
View File
@@ -836,7 +836,16 @@ const App: React.FC = () => {
<FilterBar filters={filters} onFilterChange={handleFilterChange} options={filterOptions} />
<div className="mt-6">
<div className={view === 'dashboard' ? '' : 'hidden'}>
<Dashboard data={ytdAggregatedData} filterState={filters} adsData={ytdFilteredAdsData} stockMap={stockMap} rawData={ytdFilteredData} />
<Dashboard
data={ytdAggregatedData}
filterState={filters}
adsData={ytdFilteredAdsData}
stockMap={stockMap}
vendorStockMap={vendorStockMap}
buyBoxLostMap={buyBoxLostMap}
top50Mode={top50Mode}
rawData={ytdFilteredData}
/>
</div>
<Suspense fallback={<LoadingSpinner />}>
+11 -1
View File
@@ -12,6 +12,10 @@ interface DashboardProps {
contextData?: AggregatedData | null;
adsData?: AdsRecord[];
rawData?: SalesRecord[];
stockMap?: Map<string, number>;
vendorStockMap?: Map<string, { eu: number; uk: number }>;
buyBoxLostMap?: Map<string, { countries: string[]; reasons: Record<string, string> }>;
top50Mode?: 'eu' | 'uk';
}
const COLORS = ['#6366f1', '#ec4899', '#10b981', '#f59e0b', '#8b5cf6', '#0ea5e9'];
@@ -814,7 +818,13 @@ const Dashboard: React.FC<DashboardProps> = ({ data, contextData, adsData = [],
{/* Top Movers Table - Full Width */}
{rawData && rawData.length > 0 && (
<div className="mt-6">
<TopMovers data={rawData} />
<TopMovers
data={rawData}
stockMap={stockMap}
vendorStockMap={vendorStockMap}
buyBoxLostMap={buyBoxLostMap}
top50Mode={top50Mode}
/>
</div>
)}
</div>
+43 -4
View File
@@ -2,15 +2,23 @@ import React, { useState, useMemo, useCallback } from 'react';
import * as XLSX from 'xlsx';
import { SalesRecord } from '../types';
import { DownloadIcon } from './Icons';
import { StockBadge } from './StockBadge';
import { VendorStockBadge } from './VendorStockBadge';
import { BuyBoxWarningBadge } from './BuyBoxWarningBadge';
interface TopMoversProps {
data: SalesRecord[];
stockMap?: Map<string, number>;
vendorStockMap?: Map<string, { eu: number; uk: number }>;
buyBoxLostMap?: Map<string, { countries: string[]; reasons: Record<string, string> }>;
top50Mode?: 'eu' | 'uk';
}
type Metric = 'sellOut' | 'units';
interface SkuAggr {
sku: string;
asin: string;
title: string;
line: string;
previousValue: number;
@@ -27,7 +35,11 @@ const MoversTable: React.FC<{
previousYear: number;
currentYear: number;
type: 'growth' | 'decline';
}> = ({ title, data, metric, previousYear, currentYear, type }) => {
stockMap?: Map<string, number>;
vendorStockMap?: Map<string, { eu: number; uk: number }>;
buyBoxLostMap?: Map<string, { countries: string[]; reasons: Record<string, string> }>;
top50Mode?: 'eu' | 'uk';
}> = ({ title, data, metric, previousYear, currentYear, type, stockMap, vendorStockMap, buyBoxLostMap, top50Mode = 'eu' }) => {
const formatValue = (val: number) => {
if (metric === 'sellOut') return `${val.toLocaleString('de-DE', { maximumFractionDigits: 0 })}`;
@@ -84,6 +96,9 @@ const MoversTable: React.FC<{
<th className="px-6 py-3 border-b border-border w-16 text-center">Rank</th>
<th className="px-6 py-3 border-b border-border">SKU Details</th>
<th className="px-6 py-3 border-b border-border">Product Line</th>
<th className="px-6 py-3 border-b border-border text-center">Stock</th>
<th className="px-6 py-3 border-b border-border text-center">Vendor</th>
<th className="px-6 py-3 border-b border-border text-center">Buy Box</th>
<th className="px-6 py-3 border-b border-border text-right">{previousYear}</th>
<th className="px-6 py-3 border-b border-border text-right">{currentYear}</th>
<th className="px-6 py-3 border-b border-border text-right">Diff</th>
@@ -113,6 +128,25 @@ const MoversTable: React.FC<{
</span>
</td>
{/* Stock Badge */}
<td className="px-6 py-3 text-center">
<StockBadge stock={stockMap?.get(item.sku)} />
</td>
{/* Vendor Stock Badge */}
<td className="px-6 py-3 text-center">
<VendorStockBadge
asin={item.asin}
vendorStockMap={vendorStockMap}
mode={top50Mode}
/>
</td>
{/* Buy Box Warning Badge */}
<td className="px-6 py-3 text-center">
<BuyBoxWarningBadge asin={item.asin} buyBoxLostMap={buyBoxLostMap} />
</td>
<td className="px-6 py-3 text-right text-slate-500">
{formatValue(item.previousValue)}
</td>
@@ -148,7 +182,7 @@ const MoversTable: React.FC<{
);
};
const TopMovers: React.FC<TopMoversProps> = ({ data }) => {
const TopMovers: React.FC<TopMoversProps> = ({ data, stockMap, vendorStockMap, buyBoxLostMap, top50Mode = 'eu' }) => {
const [metric, setMetric] = useState<Metric>('sellOut');
const [viewMode, setViewMode] = useState<'growth' | 'decline'>('growth');
@@ -167,14 +201,14 @@ const TopMovers: React.FC<TopMoversProps> = ({ data }) => {
if (!currentYear || !previousYear) return { growers: [], decliners: [] };
// Map: SKU -> { currentVal, previousVal, metadata }
const map = new Map<string, { current: number; previous: number; title: string; line: string }>();
const map = new Map<string, { current: number; previous: number; title: string; line: string; asin: string }>();
data.forEach(row => {
// Only care about the two comparison years
if (row.year !== currentYear && row.year !== previousYear) return;
if (!map.has(row.sku)) {
map.set(row.sku, { current: 0, previous: 0, title: row.title, line: row.line });
map.set(row.sku, { current: 0, previous: 0, title: row.title, line: row.line, asin: row.asin });
}
const entry = map.get(row.sku)!;
@@ -204,6 +238,7 @@ const TopMovers: React.FC<TopMoversProps> = ({ data }) => {
list.push({
sku,
asin: val.asin,
title: val.title,
line: val.line,
previousValue: val.previous,
@@ -297,6 +332,10 @@ const TopMovers: React.FC<TopMoversProps> = ({ data }) => {
previousYear={previousYear}
currentYear={currentYear}
type={viewMode}
stockMap={stockMap}
vendorStockMap={vendorStockMap}
buyBoxLostMap={buyBoxLostMap}
top50Mode={top50Mode}
/>
</div>