diff --git a/src/components/DateFilterPopover.tsx b/src/components/DateFilterPopover.tsx new file mode 100644 index 0000000..f935700 --- /dev/null +++ b/src/components/DateFilterPopover.tsx @@ -0,0 +1,85 @@ +import React, { useState } from 'react'; +import { Calendar } from 'lucide-react'; +import { cn } from '../lib/utils'; + +interface DateFilterPopoverProps { + selectedRange: { start: string; end: string }; + onRangeChange: (range: { start: string; end: string }) => void; + onClose: () => void; +} + +export function DateFilterPopover({ selectedRange, onRangeChange, onClose }: DateFilterPopoverProps) { + const [startDate, setStartDate] = useState(selectedRange.start); + const [endDate, setEndDate] = useState(selectedRange.end); + + const handleApply = () => { + onRangeChange({ start: startDate, end: endDate }); + onClose(); + }; + + const handleClear = () => { + setStartDate(''); + setEndDate(''); + onRangeChange({ start: '', end: '' }); + onClose(); + }; + + const hasValue = startDate || endDate; + + return ( +
e.stopPropagation()} + > +
+ + Filter by Date Range +
+ +
+
+ + setStartDate(e.target.value)} + className="w-full bg-slate-900 border border-slate-700 rounded px-3 py-2 text-xs text-white focus:outline-none focus:border-blue-500" + /> +
+ +
+ + setEndDate(e.target.value)} + className="w-full bg-slate-900 border border-slate-700 rounded px-3 py-2 text-xs text-white focus:outline-none focus:border-blue-500" + /> +
+
+ +
+ +
+ + +
+
+
+ ); +} \ No newline at end of file diff --git a/src/components/MissingDataView.tsx b/src/components/MissingDataView.tsx index da24857..2ebf3e8 100644 --- a/src/components/MissingDataView.tsx +++ b/src/components/MissingDataView.tsx @@ -3,6 +3,7 @@ import { ExcelRow, COLUMNS } from '../types'; import { Search, ChevronDown, ChevronUp, X, Edit2, Save } from 'lucide-react'; import { cn } from '../lib/utils'; import { ColumnFilterPopover } from './ColumnFilterPopover'; +import { DateFilterPopover } from './DateFilterPopover'; interface MissingDataViewProps { data: ExcelRow[]; @@ -72,6 +73,7 @@ export function MissingDataView({ data, headers, onSaveRow, onCaptureState }: Mi const [page, setPage] = useState(1); const [editing, setEditing] = useState(null); const [columnFilters, setColumnFilters] = useState>({}); + const [dateFilters, setDateFilters] = useState>({}); const [openFilter, setOpenFilter] = useState(null); const pageSize = 100; @@ -137,6 +139,7 @@ export function MissingDataView({ data, headers, onSaveRow, onCaptureState }: Mi (Object.entries(columnFilters) as [string, string[]][]).forEach(([colIdx, filterValues]) => { if (!filterValues || filterValues.length === 0) return; const colIdxNum = parseInt(colIdx); + if (colIdxNum === launchDateCol || colIdxNum === readyToOrderCol) return; result = result.filter(r => { const val: any = r.row[colIdxNum]; const displayVal = colIdxNum === launchDateCol || colIdxNum === readyToOrderCol @@ -146,6 +149,29 @@ export function MissingDataView({ data, headers, onSaveRow, onCaptureState }: Mi }); }); + (Object.entries(dateFilters) as [string, { start: string; end: string }][]).forEach(([colIdx, range]) => { + if (!range.start && !range.end) return; + const colIdxNum = parseInt(colIdx); + result = result.filter(r => { + const val = r.row[colIdxNum]; + const dateStr = formatDateValue(val) || String(val ?? ''); + if (!dateStr) return false; + const parts = dateStr.split('/'); + if (parts.length !== 3) return false; + const rowDate = new Date(`${parts[2]}-${parts[1]}-${parts[0]}`); + if (isNaN(rowDate.getTime())) return false; + if (range.start) { + const startDate = new Date(range.start); + if (rowDate < startDate) return false; + } + if (range.end) { + const endDate = new Date(range.end); + if (rowDate > endDate) return false; + } + return true; + }); + }); + if (sortCol !== null) { result.sort((a, b) => { const valA = a.row[sortCol]; @@ -269,31 +295,61 @@ export function MissingDataView({ data, headers, onSaveRow, onCaptureState }: Mi )}
- - {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)} - /> + {col === launchDateCol || col === readyToOrderCol ? ( + <> + + {openFilter === col && ( + setDateFilters(prev => ({ ...prev, [col]: range }))} + onClose={() => setOpenFilter(null)} + /> + )} + + ) : ( + <> + + {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)} + /> + )} + )}