Enhance Excel-style filters with numeric operators and integrate into GRID metrics (Sell Out, Units)

This commit is contained in:
Christian Vidal Wolf
2026-02-09 12:09:01 +01:00
parent d5bcbeda1a
commit 81c946b3e1
3 changed files with 75 additions and 14 deletions
+70 -13
View File
@@ -473,7 +473,7 @@ const DataGrid: React.FC<DataGridProps> = ({ data, filters, hasCustomerFilter, a
}
}
// 1. Filter
// 1. Filter (Legacy Row Filters)
if (rowFilters.length > 0) {
result = result.filter(row => {
return rowFilters.every(filter => {
@@ -512,6 +512,45 @@ const DataGrid: React.FC<DataGridProps> = ({ data, filters, hasCustomerFilter, a
});
}
// 1b. Filter (New Excel Column Filters for Metrics)
(Object.entries(columnFilters) as [string, ColumnFilterCondition][]).forEach(([key, condition]) => {
if (!condition) return;
if (!key.startsWith('total_')) return; // Metric filters in this view start with 'total_'
const isSellOut = key.includes('_sellOut_');
const year = key.split('_')[2];
if (condition.selectedValues && condition.selectedValues.length > 0) {
result = result.filter(row => {
const val = isSellOut ? row.totalsByYear[year]?.sellOut : row.totalsByYear[year]?.units;
return condition.selectedValues?.includes(String(val || 0));
});
}
if (condition.textFilter) {
const { operator, value } = condition.textFilter;
const filterNum = parseFloat(value);
if (isNaN(filterNum)) return;
result = result.filter(row => {
const rowVal = (isSellOut ? row.totalsByYear[year]?.sellOut : row.totalsByYear[year]?.units) || 0;
switch (operator) {
case 'equals': return rowVal === filterNum;
case 'notEquals': return rowVal !== filterNum;
case 'gt': return rowVal > filterNum;
case 'lt': return rowVal < filterNum;
case 'gte': return rowVal >= filterNum;
case 'lte': return rowVal <= filterNum;
// Text-like operators on numeric values (convert to string)
case 'contains': return String(rowVal).includes(value);
case 'startsWith': return String(rowVal).startsWith(value);
case 'endsWith': return String(rowVal).endsWith(value);
default: return true;
}
});
}
});
// 2. Sort
if (sortConfig.key) {
// Create a copy to avoid mutating the original array and ensure React detects the change
@@ -1244,20 +1283,38 @@ const DataGrid: React.FC<DataGridProps> = ({ data, filters, hasCustomerFilter, a
return (
<React.Fragment key={year}>
{/* Sell Out & Units */}
<th
className="px-4 py-3 border-b border-border text-right cursor-pointer hover:text-white bg-slate-950 min-w-[100px]"
onClick={() => requestSort(`total_sellOut_${year}`)}
>
<div className="flex items-center justify-end">
Sell Out {year} {getSortIcon(`total_sellOut_${year}`)}
<th className="px-4 py-3 border-b border-border text-right bg-slate-950 min-w-[120px]">
<div className="flex items-center justify-end gap-2">
<div
className="flex items-center cursor-pointer hover:text-white"
onClick={() => requestSort(`total_sellOut_${year}`)}
>
Sell Out {year} {getSortIcon(`total_sellOut_${year}`)}
</div>
<ExcelFilter
columnKey={`total_sellOut_${year}`}
title={`Sell Out ${year}`}
uniqueValues={Array.from(new Set(pivotRows.map(r => String(r.totalsByYear[year]?.sellOut || 0)))).sort((a, b) => parseFloat(a as string) - parseFloat(b as string))}
currentFilter={columnFilters[`total_sellOut_${year}`]}
onFilterChange={handleColumnFilterChange}
/>
</div>
</th>
<th
className="px-4 py-3 border-b border-border text-right cursor-pointer hover:text-white bg-slate-950 min-w-[80px]"
onClick={() => requestSort(`total_units_${year}`)}
>
<div className="flex items-center justify-end">
Units {year} {getSortIcon(`total_units_${year}`)}
<th className="px-4 py-3 border-b border-border text-right bg-slate-950 min-w-[100px]">
<div className="flex items-center justify-end gap-2">
<div
className="flex items-center cursor-pointer hover:text-white"
onClick={() => requestSort(`total_units_${year}`)}
>
Units {year} {getSortIcon(`total_units_${year}`)}
</div>
<ExcelFilter
columnKey={`total_units_${year}`}
title={`Units ${year}`}
uniqueValues={Array.from(new Set(pivotRows.map(r => String(r.totalsByYear[year]?.units || 0)))).sort((a, b) => parseFloat(a as string) - parseFloat(b as string))}
currentFilter={columnFilters[`total_units_${year}`]}
onFilterChange={handleColumnFilterChange}
/>
</div>
</th>