From 4533a98ddfb09f9cf804a9a79300b5512bf14020 Mon Sep 17 00:00:00 2001 From: Christian Vidal Wolf Date: Sun, 12 Apr 2026 22:27:04 +0200 Subject: [PATCH] Add multi-select filters to Missing Data view using ColumnFilterPopover --- src/components/MissingDataView.tsx | 86 +++++++++++------------------- 1 file changed, 31 insertions(+), 55 deletions(-) diff --git a/src/components/MissingDataView.tsx b/src/components/MissingDataView.tsx index a817c84..450b604 100644 --- a/src/components/MissingDataView.tsx +++ b/src/components/MissingDataView.tsx @@ -1,7 +1,8 @@ import React, { useState, useMemo } from 'react'; import { ExcelRow, COLUMNS } from '../types'; -import { Search, ChevronDown, ChevronUp, X, Edit2, Save, Filter, XCircle, ChevronLeft, ChevronRight } from 'lucide-react'; +import { Search, ChevronDown, ChevronUp, X, Edit2, Save } from 'lucide-react'; import { cn } from '../lib/utils'; +import { ColumnFilterPopover } from './ColumnFilterPopover'; interface MissingDataViewProps { data: ExcelRow[]; @@ -52,9 +53,8 @@ export function MissingDataView({ data, headers, onSaveRow, onCaptureState }: Mi const [sortDesc, setSortDesc] = useState(false); const [page, setPage] = useState(1); const [editing, setEditing] = useState(null); - const [columnFilters, setColumnFilters] = useState>({}); - const [showFilterMenu, setShowFilterMenu] = useState(null); - const [filterSearch, setFilterSearch] = useState>({}); + const [columnFilters, setColumnFilters] = useState>({}); + const [openFilter, setOpenFilter] = useState(null); const pageSize = 100; const launchDateCol = useMemo(() => @@ -92,12 +92,10 @@ export function MissingDataView({ data, headers, onSaveRow, onCaptureState }: Mi return result; }, [data, columns, launchDateCol, readyToOrderCol]); - const getDisplayValues = (col: number) => { + const getUniqueValues = (col: number): string[] => { const values = columnUniqueValues[col]; if (!values) return []; - const searchVal = filterSearch[col]?.toLowerCase() || ''; - const arr = Array.from(values).sort(); - return searchVal ? arr.filter((v: string) => v.toLowerCase().includes(searchVal)) : arr; + return Array.from(values).sort() as string[]; }; const filteredData = useMemo(() => { @@ -118,16 +116,15 @@ export function MissingDataView({ data, headers, onSaveRow, onCaptureState }: Mi ); } - (Object.entries(columnFilters) as [string, string][]).forEach(([colIdx, filterValue]) => { - if (!filterValue) return; + (Object.entries(columnFilters) as [string, string[]][]).forEach(([colIdx, filterValues]) => { + if (!filterValues || filterValues.length === 0) return; const colIdxNum = parseInt(colIdx); - const filterLower = filterValue.toLowerCase(); result = result.filter(r => { const val: any = r.row[colIdxNum]; const displayVal = colIdxNum === launchDateCol || colIdxNum === readyToOrderCol ? formatDateValue(val) || String(val ?? '') : String(val ?? ''); - return displayVal.toLowerCase().includes(filterLower); + return filterValues.includes(displayVal); }); }); @@ -235,9 +232,9 @@ export function MissingDataView({ data, headers, onSaveRow, onCaptureState }: Mi {columns.map(({ col, label, width }) => { - const isOpen = showFilterMenu === col; - const hasFilter = columnFilters[col]; - const displayValues = getDisplayValues(col); + const selectedFilters = columnFilters[col] || []; + const allValues = getUniqueValues(col); + const filterCount = selectedFilters.length; return (
- {isOpen && ( -
-
- setFilterSearch(prev => ({ ...prev, [col]: e.target.value }))} - onClick={(e) => e.stopPropagation()} - className="w-full px-2 py-1 bg-slate-900 border border-slate-700 rounded text-xs text-white placeholder-slate-500 focus:outline-none focus:border-indigo-500" - /> -
-
- - {displayValues.map((val) => ( - - ))} - {displayValues.length === 0 && ( -
No values
- )} -
-
+ {openFilter === col && ( + setColumnFilters(prev => { + const current = prev[col] || []; + if (current.includes(val)) { + return { ...prev, [col]: current.filter(v => v !== val) }; + } + return { ...prev, [col]: [...current, val] }; + })} + onSelectAll={(vals) => setColumnFilters(prev => ({ ...prev, [col]: vals }))} + onClear={() => { setColumnFilters(prev => { const n = { ...prev }; delete n[col]; return n; }); }} + onClose={() => setOpenFilter(null)} + /> )}