fix: add Item to Logistic filter and apply missing safety checks to dimension filters

This commit is contained in:
Christian Vidal Wolf
2026-04-24 20:21:45 +02:00
parent dcbaaea055
commit d50ecc9506
+43 -12
View File
@@ -95,6 +95,7 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit,
const [articleNoColFilter, setArticleNoColFilter] = usePersistentState<{ terms: string[]; op: 'and' | 'or' }>('pricing-articleNoColFilter', { terms: [''], op: 'and' }); const [articleNoColFilter, setArticleNoColFilter] = usePersistentState<{ terms: string[]; op: 'and' | 'or' }>('pricing-articleNoColFilter', { terms: [''], op: 'and' });
const [globalAdvancedFilter, setGlobalAdvancedFilter] = usePersistentState<{ terms: string[]; op: 'and' | 'or' }>('pricing-globalAdvancedFilter', { terms: [''], op: 'and' }); const [globalAdvancedFilter, setGlobalAdvancedFilter] = usePersistentState<{ terms: string[]; op: 'and' | 'or' }>('pricing-globalAdvancedFilter', { terms: [''], op: 'and' });
const [unitsOuterFilter, setUnitsOuterFilter] = usePersistentState<string[]>('pricing-unitsOuterFilter', []); const [unitsOuterFilter, setUnitsOuterFilter] = usePersistentState<string[]>('pricing-unitsOuterFilter', []);
const [itemToLogisticFilter, setItemToLogisticFilter] = usePersistentState<string[]>('pricing-itemToLogisticFilter', []);
const [outerWFilter, setOuterWFilter] = usePersistentState<string[]>('pricing-outerWFilter', []); const [outerWFilter, setOuterWFilter] = usePersistentState<string[]>('pricing-outerWFilter', []);
const [outerLFilter, setOuterLFilter] = usePersistentState<string[]>('pricing-outerLFilter', []); const [outerLFilter, setOuterLFilter] = usePersistentState<string[]>('pricing-outerLFilter', []);
const [outerHFilter, setOuterHFilter] = usePersistentState<string[]>('pricing-outerHFilter', []); const [outerHFilter, setOuterHFilter] = usePersistentState<string[]>('pricing-outerHFilter', []);
@@ -307,6 +308,10 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit,
Array.from(new Set(data.map(r => String(r[COLUMNS.OUTER_H] || '')))).filter(v => v).sort((a, b) => Number(a) - Number(b)), Array.from(new Set(data.map(r => String(r[COLUMNS.OUTER_H] || '')))).filter(v => v).sort((a, b) => Number(a) - Number(b)),
[data]); [data]);
const uniqueItemToLogistic = useMemo(() =>
Array.from(new Set(data.map(r => String(r[COLUMNS.ITEM_TO_LOGISTIC] || '')))).filter(v => v).sort(),
[data]);
// ── Filtered rows ───────────────────────────────────────────────────────── // ── Filtered rows ─────────────────────────────────────────────────────────
const filteredRows = useMemo(() => { const filteredRows = useMemo(() => {
let result = analyzedRows; let result = analyzedRows;
@@ -399,6 +404,9 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit,
if ((outerHFilter || []).length > 0) { if ((outerHFilter || []).length > 0) {
result = result.filter(r => (outerHFilter || []).includes(String(r.row[COLUMNS.OUTER_H] || ''))); result = result.filter(r => (outerHFilter || []).includes(String(r.row[COLUMNS.OUTER_H] || '')));
} }
if ((itemToLogisticFilter || []).length > 0) {
result = result.filter(r => (itemToLogisticFilter || []).includes(String(r.row[COLUMNS.ITEM_TO_LOGISTIC] || '')));
}
// Dynamic column filters (SRP, container) // Dynamic column filters (SRP, container)
(Object.keys(dynamicColFilters) as string[]).forEach(colIdx => { (Object.keys(dynamicColFilters) as string[]).forEach(colIdx => {
@@ -1342,6 +1350,29 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit,
<span className="flex items-center gap-1"> <span className="flex items-center gap-1">
{isPinned('itemToLogistic') && <Pin className="w-3 h-3 text-blue-400 shrink-0" />} {isPinned('itemToLogistic') && <Pin className="w-3 h-3 text-blue-400 shrink-0" />}
Item to Logistic <SortIcon current={sortConfig.key === 'itemToLogistic' ? sortConfig.direction : null} /></span> Item to Logistic <SortIcon current={sortConfig.key === 'itemToLogistic' ? sortConfig.direction : null} /></span>
<button
id="filter-trigger-logistic"
onClick={(e) => { e.stopPropagation(); setOpenFilter(openFilter === 'logistic' ? null : 'logistic'); }}
className={cn(
'ml-1 p-0.5 rounded hover:bg-slate-700 transition-colors shrink-0 filter-trigger-btn',
(itemToLogisticFilter || []).length > 0 ? 'text-pink-400 bg-pink-400/10' : 'text-slate-500 opacity-0 group-hover:opacity-100'
)}
>
<Filter className="w-3 h-3" />
</button>
</span>
{openFilter === 'logistic' && (
<ColumnFilterPopover
triggerId="filter-trigger-logistic"
uniqueValues={uniqueItemToLogistic || []}
selectedValues={itemToLogisticFilter || []}
onToggle={val => setItemToLogisticFilter(prev => prev.includes(val) ? prev.filter(v => v !== val) : [...prev, val])}
onSelectAll={vals => setItemToLogisticFilter(vals)}
onClear={() => { setItemToLogisticFilter([]); setOpenFilter(null); }}
onClose={() => setOpenFilter(null)}
zIndex={50}
/>
)}
<ResizeHandle onMouseDown={e => { e.stopPropagation(); handleResizeStart(e, 'itemToLogistic', columnWidths.itemToLogistic); }} /> <ResizeHandle onMouseDown={e => { e.stopPropagation(); handleResizeStart(e, 'itemToLogistic', columnWidths.itemToLogistic); }} />
</th> </th>
@@ -1416,7 +1447,7 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit,
onClick={(e) => { e.stopPropagation(); setOpenFilter(openFilter === 'unitsOuter' ? null : 'unitsOuter'); }} onClick={(e) => { e.stopPropagation(); setOpenFilter(openFilter === 'unitsOuter' ? null : 'unitsOuter'); }}
className={cn( className={cn(
'p-0.5 rounded hover:bg-slate-700 transition-colors shrink-0 filter-trigger-btn', 'p-0.5 rounded hover:bg-slate-700 transition-colors shrink-0 filter-trigger-btn',
unitsOuterFilter.length > 0 ? 'text-blue-400 bg-blue-400/10' : 'text-slate-500 opacity-0 group-hover:opacity-100' (unitsOuterFilter || []).length > 0 ? 'text-blue-400 bg-blue-400/10' : 'text-slate-500 opacity-0 group-hover:opacity-100'
)} )}
> >
<Filter className="w-3 h-3" /> <Filter className="w-3 h-3" />
@@ -1425,8 +1456,8 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit,
{openFilter === 'unitsOuter' && ( {openFilter === 'unitsOuter' && (
<ColumnFilterPopover <ColumnFilterPopover
triggerId="filter-trigger-unitsOuter" triggerId="filter-trigger-unitsOuter"
uniqueValues={uniqueUnitsOuter} uniqueValues={uniqueUnitsOuter || []}
selectedValues={unitsOuterFilter} selectedValues={unitsOuterFilter || []}
onToggle={val => setUnitsOuterFilter(prev => prev.includes(val) ? prev.filter(v => v !== val) : [...prev, val])} onToggle={val => setUnitsOuterFilter(prev => prev.includes(val) ? prev.filter(v => v !== val) : [...prev, val])}
onSelectAll={vals => setUnitsOuterFilter(vals)} onSelectAll={vals => setUnitsOuterFilter(vals)}
onClear={() => { setUnitsOuterFilter([]); setOpenFilter(null); }} onClear={() => { setUnitsOuterFilter([]); setOpenFilter(null); }}
@@ -1455,7 +1486,7 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit,
onClick={(e) => { e.stopPropagation(); setOpenFilter(openFilter === 'outerW' ? null : 'outerW'); }} onClick={(e) => { e.stopPropagation(); setOpenFilter(openFilter === 'outerW' ? null : 'outerW'); }}
className={cn( className={cn(
'p-0.5 rounded hover:bg-slate-700 transition-colors shrink-0 filter-trigger-btn', 'p-0.5 rounded hover:bg-slate-700 transition-colors shrink-0 filter-trigger-btn',
outerWFilter.length > 0 ? 'text-blue-400 bg-blue-400/10' : 'text-slate-500 opacity-0 group-hover:opacity-100' (outerWFilter || []).length > 0 ? 'text-blue-400 bg-blue-400/10' : 'text-slate-500 opacity-0 group-hover:opacity-100'
)} )}
> >
<Filter className="w-3 h-3" /> <Filter className="w-3 h-3" />
@@ -1464,8 +1495,8 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit,
{openFilter === 'outerW' && ( {openFilter === 'outerW' && (
<ColumnFilterPopover <ColumnFilterPopover
triggerId="filter-trigger-outerW" triggerId="filter-trigger-outerW"
uniqueValues={uniqueOuterW} uniqueValues={uniqueOuterW || []}
selectedValues={outerWFilter} selectedValues={outerWFilter || []}
onToggle={val => setOuterWFilter(prev => prev.includes(val) ? prev.filter(v => v !== val) : [...prev, val])} onToggle={val => setOuterWFilter(prev => prev.includes(val) ? prev.filter(v => v !== val) : [...prev, val])}
onSelectAll={vals => setOuterWFilter(vals)} onSelectAll={vals => setOuterWFilter(vals)}
onClear={() => { setOuterWFilter([]); setOpenFilter(null); }} onClear={() => { setOuterWFilter([]); setOpenFilter(null); }}
@@ -1494,7 +1525,7 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit,
onClick={(e) => { e.stopPropagation(); setOpenFilter(openFilter === 'outerL' ? null : 'outerL'); }} onClick={(e) => { e.stopPropagation(); setOpenFilter(openFilter === 'outerL' ? null : 'outerL'); }}
className={cn( className={cn(
'p-0.5 rounded hover:bg-slate-700 transition-colors shrink-0 filter-trigger-btn', 'p-0.5 rounded hover:bg-slate-700 transition-colors shrink-0 filter-trigger-btn',
outerLFilter.length > 0 ? 'text-blue-400 bg-blue-400/10' : 'text-slate-500 opacity-0 group-hover:opacity-100' (outerLFilter || []).length > 0 ? 'text-blue-400 bg-blue-400/10' : 'text-slate-500 opacity-0 group-hover:opacity-100'
)} )}
> >
<Filter className="w-3 h-3" /> <Filter className="w-3 h-3" />
@@ -1503,8 +1534,8 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit,
{openFilter === 'outerL' && ( {openFilter === 'outerL' && (
<ColumnFilterPopover <ColumnFilterPopover
triggerId="filter-trigger-outerL" triggerId="filter-trigger-outerL"
uniqueValues={uniqueOuterL} uniqueValues={uniqueOuterL || []}
selectedValues={outerLFilter} selectedValues={outerLFilter || []}
onToggle={val => setOuterLFilter(prev => prev.includes(val) ? prev.filter(v => v !== val) : [...prev, val])} onToggle={val => setOuterLFilter(prev => prev.includes(val) ? prev.filter(v => v !== val) : [...prev, val])}
onSelectAll={vals => setOuterLFilter(vals)} onSelectAll={vals => setOuterLFilter(vals)}
onClear={() => { setOuterLFilter([]); setOpenFilter(null); }} onClear={() => { setOuterLFilter([]); setOpenFilter(null); }}
@@ -1533,7 +1564,7 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit,
onClick={(e) => { e.stopPropagation(); setOpenFilter(openFilter === 'outerH' ? null : 'outerH'); }} onClick={(e) => { e.stopPropagation(); setOpenFilter(openFilter === 'outerH' ? null : 'outerH'); }}
className={cn( className={cn(
'p-0.5 rounded hover:bg-slate-700 transition-colors shrink-0 filter-trigger-btn', 'p-0.5 rounded hover:bg-slate-700 transition-colors shrink-0 filter-trigger-btn',
outerHFilter.length > 0 ? 'text-blue-400 bg-blue-400/10' : 'text-slate-500 opacity-0 group-hover:opacity-100' (outerHFilter || []).length > 0 ? 'text-blue-400 bg-blue-400/10' : 'text-slate-500 opacity-0 group-hover:opacity-100'
)} )}
> >
<Filter className="w-3 h-3" /> <Filter className="w-3 h-3" />
@@ -1542,8 +1573,8 @@ export function PricingView({ data, headers, onSaveRow, onCaptureState, onEdit,
{openFilter === 'outerH' && ( {openFilter === 'outerH' && (
<ColumnFilterPopover <ColumnFilterPopover
triggerId="filter-trigger-outerH" triggerId="filter-trigger-outerH"
uniqueValues={uniqueOuterH} uniqueValues={uniqueOuterH || []}
selectedValues={outerHFilter} selectedValues={outerHFilter || []}
onToggle={val => setOuterHFilter(prev => prev.includes(val) ? prev.filter(v => v !== val) : [...prev, val])} onToggle={val => setOuterHFilter(prev => prev.includes(val) ? prev.filter(v => v !== val) : [...prev, val])}
onSelectAll={vals => setOuterHFilter(vals)} onSelectAll={vals => setOuterHFilter(vals)}
onClear={() => { setOuterHFilter([]); setOpenFilter(null); }} onClear={() => { setOuterHFilter([]); setOpenFilter(null); }}