mirror of
https://github.com/christianvidalwolf-prog/CrazeAnalytix.git
synced 2026-08-03 12:45:23 +02:00
Enhance Excel-style filters with numeric operators and integrate into GRID metrics (Sell Out, Units)
This commit is contained in:
+70
-13
@@ -473,7 +473,7 @@ const DataGrid: React.FC<DataGridProps> = ({ data, filters, hasCustomerFilter, a
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Filter
|
// 1. Filter (Legacy Row Filters)
|
||||||
if (rowFilters.length > 0) {
|
if (rowFilters.length > 0) {
|
||||||
result = result.filter(row => {
|
result = result.filter(row => {
|
||||||
return rowFilters.every(filter => {
|
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
|
// 2. Sort
|
||||||
if (sortConfig.key) {
|
if (sortConfig.key) {
|
||||||
// Create a copy to avoid mutating the original array and ensure React detects the change
|
// 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 (
|
return (
|
||||||
<React.Fragment key={year}>
|
<React.Fragment key={year}>
|
||||||
{/* Sell Out & Units */}
|
{/* Sell Out & Units */}
|
||||||
<th
|
<th className="px-4 py-3 border-b border-border text-right bg-slate-950 min-w-[120px]">
|
||||||
className="px-4 py-3 border-b border-border text-right cursor-pointer hover:text-white bg-slate-950 min-w-[100px]"
|
<div className="flex items-center justify-end gap-2">
|
||||||
onClick={() => requestSort(`total_sellOut_${year}`)}
|
<div
|
||||||
>
|
className="flex items-center cursor-pointer hover:text-white"
|
||||||
<div className="flex items-center justify-end">
|
onClick={() => requestSort(`total_sellOut_${year}`)}
|
||||||
Sell Out {year} {getSortIcon(`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>
|
</div>
|
||||||
</th>
|
</th>
|
||||||
<th
|
<th className="px-4 py-3 border-b border-border text-right bg-slate-950 min-w-[100px]">
|
||||||
className="px-4 py-3 border-b border-border text-right cursor-pointer hover:text-white bg-slate-950 min-w-[80px]"
|
<div className="flex items-center justify-end gap-2">
|
||||||
onClick={() => requestSort(`total_units_${year}`)}
|
<div
|
||||||
>
|
className="flex items-center cursor-pointer hover:text-white"
|
||||||
<div className="flex items-center justify-end">
|
onClick={() => requestSort(`total_units_${year}`)}
|
||||||
Units {year} {getSortIcon(`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>
|
</div>
|
||||||
</th>
|
</th>
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ const OPERATORS = [
|
|||||||
{ label: 'No comienza por', value: 'notStartsWith' },
|
{ label: 'No comienza por', value: 'notStartsWith' },
|
||||||
{ label: 'Termina con', value: 'endsWith' },
|
{ label: 'Termina con', value: 'endsWith' },
|
||||||
{ label: 'No termina con', value: 'notEndsWith' },
|
{ label: 'No termina con', value: 'notEndsWith' },
|
||||||
|
{ label: 'Mayor que', value: 'gt' },
|
||||||
|
{ label: 'Menor que', value: 'lt' },
|
||||||
|
{ label: 'Mayor o igual que', value: 'gte' },
|
||||||
|
{ label: 'Menor o igual que', value: 'lte' },
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export const ExcelFilter: React.FC<ExcelFilterProps> = ({
|
export const ExcelFilter: React.FC<ExcelFilterProps> = ({
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export interface FilterState {
|
|||||||
|
|
||||||
export interface ColumnFilterCondition {
|
export interface ColumnFilterCondition {
|
||||||
textFilter?: {
|
textFilter?: {
|
||||||
operator: 'equals' | 'notEquals' | 'contains' | 'notContains' | 'startsWith' | 'notStartsWith' | 'endsWith' | 'notEndsWith';
|
operator: 'equals' | 'notEquals' | 'contains' | 'notContains' | 'startsWith' | 'notStartsWith' | 'endsWith' | 'notEndsWith' | 'gt' | 'lt' | 'gte' | 'lte';
|
||||||
value: string;
|
value: string;
|
||||||
};
|
};
|
||||||
selectedValues?: string[];
|
selectedValues?: string[];
|
||||||
|
|||||||
Reference in New Issue
Block a user