fix(vendor): replace ASIN text input with MultiSelectDropdown for proper filtering

The free-text ASIN search was unclear and not working as expected.
Replace it with a proper MultiSelectDropdown (same as Market/Category
filters) — selecting an ASIN now strictly filters chart data to that
ASIN only.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
christian.vidal
2026-03-02 20:16:27 +01:00
co-authored by Claude Sonnet 4.6
parent 7f5aa1b812
commit a6b7503c82
+17 -16
View File
@@ -24,24 +24,27 @@ interface ChartPoint {
const VendorDataView: React.FC<VendorDataViewProps> = ({ bsrData }) => { const VendorDataView: React.FC<VendorDataViewProps> = ({ bsrData }) => {
// Filters // Filters
const [selectedMarkets, setSelectedMarkets] = useState<string[]>([]); const [selectedMarkets, setSelectedMarkets] = useState<string[]>([]);
const [selectedAsins, setSelectedAsins] = useState<string[]>([]);
const [selectedTopCats, setSelectedTopCats] = useState<string[]>([]); const [selectedTopCats, setSelectedTopCats] = useState<string[]>([]);
const [selectedDetailCats, setSelectedDetailCats] = useState<string[]>([]); const [selectedDetailCats, setSelectedDetailCats] = useState<string[]>([]);
const [asinSearch, setAsinSearch] = useState('');
// Extract available filter options from the dataset // Extract available filter options from the dataset
const { availableMarkets, availableTopCats, availableDetailCats } = useMemo(() => { const { availableMarkets, availableAsins, availableTopCats, availableDetailCats } = useMemo(() => {
const markets = new Set<string>(); const markets = new Set<string>();
const asins = new Set<string>();
const topCats = new Set<string>(); const topCats = new Set<string>();
const detailCats = new Set<string>(); const detailCats = new Set<string>();
bsrData.forEach(r => { bsrData.forEach(r => {
if (r.market) markets.add(r.market); if (r.market) markets.add(r.market);
if (r.asin) asins.add(r.asin);
if (r.topLevelName) topCats.add(r.topLevelName); if (r.topLevelName) topCats.add(r.topLevelName);
if (r.detailLevelName) detailCats.add(r.detailLevelName); if (r.detailLevelName) detailCats.add(r.detailLevelName);
}); });
return { return {
availableMarkets: Array.from(markets).sort(), availableMarkets: Array.from(markets).sort(),
availableAsins: Array.from(asins).sort(),
availableTopCats: Array.from(topCats).sort(), availableTopCats: Array.from(topCats).sort(),
availableDetailCats: Array.from(detailCats).sort() availableDetailCats: Array.from(detailCats).sort()
}; };
@@ -54,19 +57,18 @@ const VendorDataView: React.FC<VendorDataViewProps> = ({ bsrData }) => {
if (selectedMarkets.length > 0) { if (selectedMarkets.length > 0) {
result = result.filter(r => selectedMarkets.includes(r.market)); result = result.filter(r => selectedMarkets.includes(r.market));
} }
if (selectedAsins.length > 0) {
result = result.filter(r => selectedAsins.includes(r.asin));
}
if (selectedTopCats.length > 0) { if (selectedTopCats.length > 0) {
result = result.filter(r => r.topLevelName && selectedTopCats.includes(r.topLevelName)); result = result.filter(r => r.topLevelName && selectedTopCats.includes(r.topLevelName));
} }
if (selectedDetailCats.length > 0) { if (selectedDetailCats.length > 0) {
result = result.filter(r => r.detailLevelName && selectedDetailCats.includes(r.detailLevelName)); 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; return result;
}, [bsrData, selectedMarkets, selectedTopCats, selectedDetailCats, asinSearch]); }, [bsrData, selectedMarkets, selectedAsins, selectedTopCats, selectedDetailCats]);
// Determine active markets in filtered data for series generation // Determine active markets in filtered data for series generation
const activeMarkets = useMemo(() => { const activeMarkets = useMemo(() => {
@@ -172,6 +174,12 @@ const VendorDataView: React.FC<VendorDataViewProps> = ({ bsrData }) => {
selected={selectedMarkets} selected={selectedMarkets}
onChange={setSelectedMarkets} onChange={setSelectedMarkets}
/> />
<MultiSelectDropdown
label="ASIN"
options={availableAsins}
selected={selectedAsins}
onChange={setSelectedAsins}
/>
<MultiSelectDropdown <MultiSelectDropdown
label="Top Level Category" label="Top Level Category"
options={availableTopCats} options={availableTopCats}
@@ -184,20 +192,13 @@ const VendorDataView: React.FC<VendorDataViewProps> = ({ bsrData }) => {
selected={selectedDetailCats} selected={selectedDetailCats}
onChange={setSelectedDetailCats} onChange={setSelectedDetailCats}
/> />
<input {(selectedMarkets.length > 0 || selectedAsins.length > 0 || selectedTopCats.length > 0 || selectedDetailCats.length > 0) && (
type="text"
placeholder="Search ASINs (comma-separated)"
value={asinSearch}
onChange={e => 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) && (
<button <button
onClick={() => { onClick={() => {
setSelectedMarkets([]); setSelectedMarkets([]);
setSelectedAsins([]);
setSelectedTopCats([]); setSelectedTopCats([]);
setSelectedDetailCats([]); setSelectedDetailCats([]);
setAsinSearch('');
}} }}
className="text-xs text-slate-400 hover:text-white underline" className="text-xs text-slate-400 hover:text-white underline"
> >