mirror of
https://github.com/christianvidalwolf-prog/Craze-Data-check.git
synced 2026-08-03 16:55:24 +02:00
Add date range filter with calendar picker for Launch Date and Ready to Order
This commit is contained in:
@@ -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<EditingState | null>(null);
|
||||
const [columnFilters, setColumnFilters] = useState<Record<number, string[]>>({});
|
||||
const [dateFilters, setDateFilters] = useState<Record<number, { start: string; end: string }>>({});
|
||||
const [openFilter, setOpenFilter] = useState<number | null>(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
|
||||
)}
|
||||
</div>
|
||||
<div className="mt-1 relative">
|
||||
<button
|
||||
onClick={(e) => { e.stopPropagation(); setOpenFilter(openFilter === col ? null : col); }}
|
||||
className={cn(
|
||||
"w-full flex items-center justify-between px-1.5 py-0.5 bg-slate-800 border rounded text-[10px] transition-colors",
|
||||
filterCount > 0 ? "border-indigo-500 text-white" : "border-slate-600 text-slate-400 hover:border-slate-500"
|
||||
)}
|
||||
>
|
||||
<span className="truncate">{filterCount > 0 ? `${filterCount} selected` : 'Filter...'}</span>
|
||||
<ChevronDown className={cn("w-3 h-3 transition-transform", openFilter === col && "rotate-180")} />
|
||||
</button>
|
||||
{openFilter === col && (
|
||||
<ColumnFilterPopover
|
||||
uniqueValues={allValues}
|
||||
selectedValues={selectedFilters}
|
||||
onToggle={(val) => 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 ? (
|
||||
<>
|
||||
<button
|
||||
onClick={(e) => { e.stopPropagation(); setOpenFilter(openFilter === col ? null : col); }}
|
||||
className={cn(
|
||||
"w-full flex items-center justify-between px-1.5 py-0.5 bg-slate-800 border rounded text-[10px] transition-colors",
|
||||
dateFilters[col]?.start || dateFilters[col]?.end
|
||||
? "border-indigo-500 text-white"
|
||||
: "border-slate-600 text-slate-400 hover:border-slate-500"
|
||||
)}
|
||||
>
|
||||
<span className="truncate">
|
||||
{dateFilters[col]?.start || dateFilters[col]?.end
|
||||
? `${dateFilters[col].start ? dateFilters[col].start : '...'} - ${dateFilters[col].end ? dateFilters[col].end : '...'}`
|
||||
: 'Filter...'}
|
||||
</span>
|
||||
<ChevronDown className={cn("w-3 h-3 transition-transform", openFilter === col && "rotate-180")} />
|
||||
</button>
|
||||
{openFilter === col && (
|
||||
<DateFilterPopover
|
||||
selectedRange={dateFilters[col] || { start: '', end: '' }}
|
||||
onRangeChange={(range) => setDateFilters(prev => ({ ...prev, [col]: range }))}
|
||||
onClose={() => setOpenFilter(null)}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<button
|
||||
onClick={(e) => { e.stopPropagation(); setOpenFilter(openFilter === col ? null : col); }}
|
||||
className={cn(
|
||||
"w-full flex items-center justify-between px-1.5 py-0.5 bg-slate-800 border rounded text-[10px] transition-colors",
|
||||
filterCount > 0 ? "border-indigo-500 text-white" : "border-slate-600 text-slate-400 hover:border-slate-500"
|
||||
)}
|
||||
>
|
||||
<span className="truncate">{filterCount > 0 ? `${filterCount} selected` : 'Filter...'}</span>
|
||||
<ChevronDown className={cn("w-3 h-3 transition-transform", openFilter === col && "rotate-180")} />
|
||||
</button>
|
||||
{openFilter === col && (
|
||||
<ColumnFilterPopover
|
||||
uniqueValues={allValues}
|
||||
selectedValues={selectedFilters}
|
||||
onToggle={(val) => 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)}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</th>
|
||||
|
||||
Reference in New Issue
Block a user