mirror of
https://github.com/christianvidalwolf-prog/CrazeAnalytix.git
synced 2026-08-03 13:05:24 +02:00
Add numeric column filters for Units, Spend and GV in Weekly Sales
This commit is contained in:
+81
-10
@@ -4,6 +4,7 @@ import { CombinedKPIs } from '../types';
|
||||
import { pivotWeeklySalesData, WeeklyPivotRow, PAN_EU_COUNTRIES, checkNumericConditions } from '../services/dataProcessor';
|
||||
import { StockBadge } from './StockBadge';
|
||||
import { InColumnStockFilter } from './InColumnStockFilter';
|
||||
import { NumericColumnFilter, NumericFilterConfig, passesNumericFilter } from './NumericColumnFilter';
|
||||
import { Top50Badge } from './Top50Badge';
|
||||
import { VendorStockBadge } from './VendorStockBadge';
|
||||
import { BuyBoxWarningBadge } from './BuyBoxWarningBadge';
|
||||
@@ -182,6 +183,26 @@ const WeeklyGrid: React.FC<WeeklyGridProps & { top50Mode: 'eu' | 'uk' }> = ({
|
||||
const [displayCount, setDisplayCount] = useState(50);
|
||||
const scrollContainerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// Numeric filters for Units, Spend, GV
|
||||
const [numericFilters, setNumericFilters] = useState<NumericFilterConfig[]>([]);
|
||||
|
||||
// Helper to get/set filter for a specific week+metric
|
||||
const getNumericFilter = useCallback((week: string, metric: 'units' | 'spend' | 'gv') => {
|
||||
return numericFilters.find(f => f.week === week && f.metric === metric) || null;
|
||||
}, [numericFilters]);
|
||||
|
||||
const setNumericFilter = useCallback((filter: NumericFilterConfig | null, week: string, metric: 'units' | 'spend' | 'gv') => {
|
||||
setNumericFilters(prev => {
|
||||
// Remove existing filter for this week+metric
|
||||
const filtered = prev.filter(f => !(f.week === week && f.metric === metric));
|
||||
// Add new filter if provided
|
||||
if (filter) {
|
||||
return [...filtered, filter];
|
||||
}
|
||||
return filtered;
|
||||
});
|
||||
}, []);
|
||||
|
||||
// Default sort: most recent week, descending, units
|
||||
const [sortConfig, setSortConfig] = useState<SortConfig>(() => {
|
||||
if (weeks.length > 0) {
|
||||
@@ -311,8 +332,25 @@ const WeeklyGrid: React.FC<WeeklyGridProps & { top50Mode: 'eu' | 'uk' }> = ({
|
||||
});
|
||||
}
|
||||
|
||||
// Numeric Filters (Units, Spend, GV)
|
||||
if (numericFilters.length > 0) {
|
||||
result = result.filter(row => {
|
||||
return numericFilters.every(filter => {
|
||||
let value = 0;
|
||||
if (filter.metric === 'units') {
|
||||
value = row.unitsByWeek[filter.week] || 0;
|
||||
} else if (filter.metric === 'spend') {
|
||||
value = row.spendByWeek[filter.week] || 0;
|
||||
} else if (filter.metric === 'gv') {
|
||||
value = row.gvByWeek?.[filter.week] || 0;
|
||||
}
|
||||
return passesNumericFilter(value, filter);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}, [rows, debouncedSearch, showOnlyTop50, top50Ranking, growthFilterMode, growthThreshold, sortConfig, weeks, top50Mode, wocFilter, velocityMap, vendorStockMap]);
|
||||
}, [rows, debouncedSearch, showOnlyTop50, top50Ranking, growthFilterMode, growthThreshold, sortConfig, weeks, top50Mode, wocFilter, velocityMap, vendorStockMap, numericFilters]);
|
||||
|
||||
// 2. Sort results
|
||||
const sortedRows = useMemo(() => {
|
||||
@@ -571,33 +609,66 @@ const WeeklyGrid: React.FC<WeeklyGridProps & { top50Mode: 'eu' | 'uk' }> = ({
|
||||
</div>
|
||||
|
||||
<div
|
||||
onClick={() => handleSort(week, 'units')}
|
||||
className={`flex-1 p-1.5 cursor-pointer hover:bg-indigo-500/10 transition-colors flex items-center justify-center gap-1 border-b border-white/5 ${sortConfig?.key === week && sortConfig.metric === 'units' ? 'bg-indigo-500/5 text-indigo-400' : 'text-slate-500 hover:text-slate-300'}`}
|
||||
className={`flex-1 p-1.5 transition-colors flex items-center justify-center gap-1 border-b border-white/5 ${sortConfig?.key === week && sortConfig.metric === 'units' ? 'bg-indigo-500/5 text-indigo-400' : 'text-slate-500'}`}
|
||||
>
|
||||
<span className="text-[9px]">Units</span>
|
||||
<span
|
||||
onClick={() => handleSort(week, 'units')}
|
||||
className="text-[9px] cursor-pointer hover:text-indigo-300"
|
||||
>
|
||||
Units
|
||||
</span>
|
||||
{sortConfig?.key === week && sortConfig.metric === 'units' && (
|
||||
<span className="text-xs font-bold leading-none">{sortConfig.direction === 'asc' ? '↑' : '↓'}</span>
|
||||
)}
|
||||
<NumericColumnFilter
|
||||
week={week}
|
||||
metric="units"
|
||||
currentFilter={getNumericFilter(week, 'units')}
|
||||
onFilterChange={(f) => setNumericFilter(f, week, 'units')}
|
||||
accentColor="indigo"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
onClick={() => handleSort(week, 'spend')}
|
||||
className={`flex-1 p-1.5 cursor-pointer hover:bg-amber-500/10 transition-colors flex items-center justify-center gap-1 border-b border-white/5 ${sortConfig?.key === week && sortConfig.metric === 'spend' ? 'bg-amber-500/5 text-amber-400' : 'text-slate-500 hover:text-slate-300'}`}
|
||||
className={`flex-1 p-1.5 transition-colors flex items-center justify-center gap-1 border-b border-white/5 ${sortConfig?.key === week && sortConfig.metric === 'spend' ? 'bg-amber-500/5 text-amber-400' : 'text-slate-500'}`}
|
||||
>
|
||||
<span className="text-[9px]">Spend</span>
|
||||
<span
|
||||
onClick={() => handleSort(week, 'spend')}
|
||||
className="text-[9px] cursor-pointer hover:text-amber-300"
|
||||
>
|
||||
Spend
|
||||
</span>
|
||||
{sortConfig?.key === week && sortConfig.metric === 'spend' && (
|
||||
<span className="text-xs font-bold leading-none">{sortConfig.direction === 'asc' ? '↑' : '↓'}</span>
|
||||
)}
|
||||
<NumericColumnFilter
|
||||
week={week}
|
||||
metric="spend"
|
||||
currentFilter={getNumericFilter(week, 'spend')}
|
||||
onFilterChange={(f) => setNumericFilter(f, week, 'spend')}
|
||||
accentColor="amber"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
onClick={() => handleSort(week, 'gv')}
|
||||
className={`flex-1 p-1.5 cursor-pointer hover:bg-teal-500/10 transition-colors flex items-center justify-center gap-1 ${sortConfig?.key === week && sortConfig.metric === 'gv' ? 'bg-teal-500/5 text-teal-400' : 'text-slate-500 hover:text-slate-300'}`}
|
||||
className={`flex-1 p-1.5 transition-colors flex items-center justify-center gap-1 ${sortConfig?.key === week && sortConfig.metric === 'gv' ? 'bg-teal-500/5 text-teal-400' : 'text-slate-500'}`}
|
||||
>
|
||||
<span className="text-[9px]">GV</span>
|
||||
<span
|
||||
onClick={() => handleSort(week, 'gv')}
|
||||
className="text-[9px] cursor-pointer hover:text-teal-300"
|
||||
>
|
||||
GV
|
||||
</span>
|
||||
{sortConfig?.key === week && sortConfig.metric === 'gv' && (
|
||||
<span className="text-xs font-bold leading-none">{sortConfig.direction === 'asc' ? '↑' : '↓'}</span>
|
||||
)}
|
||||
<NumericColumnFilter
|
||||
week={week}
|
||||
metric="gv"
|
||||
currentFilter={getNumericFilter(week, 'gv')}
|
||||
onFilterChange={(f) => setNumericFilter(f, week, 'gv')}
|
||||
accentColor="teal"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</th>
|
||||
|
||||
Reference in New Issue
Block a user