import React, { useState, useMemo } from 'react'; import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts'; import MultiSelectDropdown from './MultiSelectDropdown'; import { BSRRecord } from '../types'; const MARKET_COLORS: Record = { DE: '#3b82f6', UK: '#ef4444', IT: '#22c55e', FR: '#a855f7', ES: '#f59e0b', }; interface VendorDataViewProps { bsrData: BSRRecord[]; } interface WeeklyChartPoint { weekLabel: string; [key: string]: number | string | null; } const VendorDataView: React.FC = ({ bsrData }) => { // Filters const [selectedMarkets, setSelectedMarkets] = useState([]); const [selectedTopCats, setSelectedTopCats] = useState([]); const [selectedDetailCats, setSelectedDetailCats] = useState([]); const [asinSearch, setAsinSearch] = useState(''); // Extract available filter options from the dataset const { availableMarkets, availableTopCats, availableDetailCats } = useMemo(() => { const markets = new Set(); const topCats = new Set(); const detailCats = new Set(); bsrData.forEach(r => { if (r.market) markets.add(r.market); if (r.topLevelName) topCats.add(r.topLevelName); if (r.detailLevelName) detailCats.add(r.detailLevelName); }); return { availableMarkets: Array.from(markets).sort(), availableTopCats: Array.from(topCats).sort(), availableDetailCats: Array.from(detailCats).sort() }; }, [bsrData]); // Apply filters const filteredData = useMemo(() => { let result = bsrData; if (selectedMarkets.length > 0) { result = result.filter(r => selectedMarkets.includes(r.market)); } if (selectedTopCats.length > 0) { result = result.filter(r => r.topLevelName && selectedTopCats.includes(r.topLevelName)); } if (selectedDetailCats.length > 0) { result = result.filter(r => r.detailLevelName && selectedDetailCats.includes(r.detailLevelName)); } if (asinSearch.trim()) { const terms = asinSearch.toLowerCase().split(',').map(t => t.trim()).filter(Boolean); result = result.filter(r => terms.some(t => r.asin.toLowerCase().includes(t))); } return result; }, [bsrData, selectedMarkets, selectedTopCats, selectedDetailCats, asinSearch]); // Determine active markets in filtered data for series generation const activeMarkets = useMemo(() => { const m = new Set(filteredData.map(r => r.market)); return Array.from(m).sort(); }, [filteredData]); // Aggregate Data for Charts const { topBsrChartData, detailBsrChartData, ratingChartData } = useMemo(() => { const byWeek = new Map(); filteredData.forEach(r => { if (!byWeek.has(r.week)) byWeek.set(r.week, []); byWeek.get(r.week)!.push(r); }); const sortedWeeks = Array.from(byWeek.keys()).sort((a, b) => a - b); const topBsrChartData: WeeklyChartPoint[] = []; const detailBsrChartData: WeeklyChartPoint[] = []; const ratingChartData: WeeklyChartPoint[] = []; sortedWeeks.forEach(week => { const row = byWeek.get(week)!; const weekLabel = `Week ${week}`; const topPoint: WeeklyChartPoint = { weekLabel }; const detailPoint: WeeklyChartPoint = { weekLabel }; const ratingPoint: WeeklyChartPoint = { weekLabel }; activeMarkets.forEach(m => { const marketRows = row.filter(r => r.market === m); // Top BSR const topBsrRows = marketRows.filter(r => r.topLevelBSR != null); topPoint[`${m}_bsr`] = topBsrRows.length > 0 ? Math.round(topBsrRows.reduce((sum, r) => sum + r.topLevelBSR!, 0) / topBsrRows.length) : null; // Detail BSR const detailBsrRows = marketRows.filter(r => r.detailLevelBSR != null); detailPoint[`${m}_bsr`] = detailBsrRows.length > 0 ? Math.round(detailBsrRows.reduce((sum, r) => sum + r.detailLevelBSR!, 0) / detailBsrRows.length) : null; // Rating const ratingRows = marketRows.filter(r => r.avgRating != null); ratingPoint[`${m}_rating`] = ratingRows.length > 0 ? Math.round((ratingRows.reduce((sum, r) => sum + r.avgRating!, 0) / ratingRows.length) * 10) / 10 : null; }); topBsrChartData.push(topPoint); detailBsrChartData.push(detailPoint); ratingChartData.push(ratingPoint); }); return { topBsrChartData, detailBsrChartData, ratingChartData }; }, [filteredData, activeMarkets]); if (bsrData.length === 0) { return (

No BSR Data Yet

Ensure BSR.xlsx is present and loading to see Top Level BSR, Detail Level BSR, and Average Rating trends.

); } return (
{/* Filters */}
setAsinSearch(e.target.value)} className="bg-slate-900 border border-slate-700 rounded-lg px-3 py-2 text-sm text-slate-200 placeholder-slate-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 w-64" /> {(selectedMarkets.length > 0 || selectedTopCats.length > 0 || selectedDetailCats.length > 0 || asinSearch) && ( )} {filteredData.length.toLocaleString()} records filtered
{/* Top Level BSR Trend Chart */}

Top Level BSR Trend (Weekly Avg)

Lower rank = better position. Averaged across filtered ASINs per market.

{activeMarkets.map(m => ( ))}
{/* Detail Level BSR Trend Chart */}

Detail Level BSR Trend (Weekly Avg)

Lower rank = better position. Averaged across filtered ASINs per market.

{activeMarkets.map(m => ( ))}
{/* Average Rating Chart */}

Average Rating (Weekly Avg)

Averaged across filtered ASINs per market.

{activeMarkets.map(m => ( ))}
); }; export default VendorDataView;