Fix Vendor filters: add year, date range, and ASIN search

- Add year filter with multi-select dropdown
- Add date range pickers (from/to)
- Fix tags filter using ilike for partial matching
- Add clear filters button
- Update supabase service with proper year filtering

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
Christian Vidal Wolf
2026-02-20 14:01:05 +01:00
co-authored by Qwen-Coder
parent ae6b8bfb9d
commit a4919691b2
2 changed files with 69 additions and 2 deletions
+51 -1
View File
@@ -109,11 +109,15 @@ const VendorDataView: React.FC = () => {
// Filter options
const [availableMarkets, setAvailableMarkets] = useState<string[]>([]);
const [availableTags, setAvailableTags] = useState<string[]>([]);
const [availableYears, setAvailableYears] = useState<number[]>([]);
// Selected filters
const [selectedMarkets, setSelectedMarkets] = useState<string[]>([]);
const [selectedTags, setSelectedTags] = useState<string[]>([]);
const [selectedYears, setSelectedYears] = useState<number[]>([]);
const [asinSearch, setAsinSearch] = useState('');
const [dateFrom, setDateFrom] = useState('');
const [dateTo, setDateTo] = useState('');
// Load filter options on mount
useEffect(() => {
@@ -122,6 +126,14 @@ const VendorDataView: React.FC = () => {
const opts = await fetchVendorFilterOptions();
setAvailableMarkets(opts.markets);
setAvailableTags(opts.tags);
// Extract years from date range
if (opts.dateRange) {
const minYear = new Date(opts.dateRange.min).getFullYear();
const maxYear = new Date(opts.dateRange.max).getFullYear();
const years: number[] = [];
for (let y = minYear; y <= maxYear; y++) years.push(y);
setAvailableYears(years);
}
} catch (e: any) {
console.error('Failed to load vendor filter options', e);
}
@@ -138,6 +150,9 @@ const VendorDataView: React.FC = () => {
markets: selectedMarkets.length > 0 ? selectedMarkets : undefined,
tags: selectedTags.length > 0 ? selectedTags : undefined,
asins: asinSearch.trim() ? asinSearch.split(',').map(a => a.trim()).filter(Boolean) : undefined,
dateFrom: dateFrom || undefined,
dateTo: dateTo || undefined,
years: selectedYears.length > 0 ? selectedYears : undefined,
});
setVendorRows(data);
} catch (e: any) {
@@ -145,7 +160,7 @@ const VendorDataView: React.FC = () => {
} finally {
setLoading(false);
}
}, [selectedMarkets, selectedTags, asinSearch]);
}, [selectedMarkets, selectedTags, selectedYears, asinSearch, dateFrom, dateTo]);
useEffect(() => {
loadData();
@@ -197,6 +212,26 @@ const VendorDataView: React.FC = () => {
selected={selectedTags}
onChange={setSelectedTags}
/>
<MultiSelectDropdown
label="Year"
options={availableYears.map(String)}
selected={selectedYears.map(String)}
onChange={(vals) => setSelectedYears(vals.map(v => parseInt(v)))}
/>
<input
type="date"
value={dateFrom}
onChange={(e) => setDateFrom(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"
placeholder="From date"
/>
<input
type="date"
value={dateTo}
onChange={(e) => setDateTo(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"
placeholder="To date"
/>
<input
type="text"
placeholder="Search ASINs (comma-separated)"
@@ -204,6 +239,21 @@ const VendorDataView: React.FC = () => {
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 || selectedTags.length > 0 || selectedYears.length > 0 || dateFrom || dateTo || asinSearch) && (
<button
onClick={() => {
setSelectedMarkets([]);
setSelectedTags([]);
setSelectedYears([]);
setDateFrom('');
setDateTo('');
setAsinSearch('');
}}
className="text-xs text-slate-400 hover:text-white underline"
>
Clear filters
</button>
)}
{loading && (
<div className="flex items-center gap-2 text-slate-400 text-sm">
<div className="w-4 h-4 border-2 border-slate-600 border-t-indigo-400 rounded-full animate-spin"></div>