mirror of
https://github.com/christianvidalwolf-prog/Craze-Data-check.git
synced 2026-08-03 12:35:25 +02:00
Add column filters to Missing Data view and include Classification in editable columns
This commit is contained in:
@@ -115,6 +115,7 @@ export default function App() {
|
|||||||
const syncedData = await getAllSyncedRows(session?.access_token);
|
const syncedData = await getAllSyncedRows(session?.access_token);
|
||||||
|
|
||||||
const editableColumns = new Set([
|
const editableColumns = new Set([
|
||||||
|
COLUMNS.CLASSIFICATION,
|
||||||
COLUMNS.LONG_DE, COLUMNS.LONG_EN,
|
COLUMNS.LONG_DE, COLUMNS.LONG_EN,
|
||||||
COLUMNS.SHORT_DE, COLUMNS.SHORT_EN,
|
COLUMNS.SHORT_DE, COLUMNS.SHORT_EN,
|
||||||
COLUMNS.DETAILS_DE, COLUMNS.DETAILS_EN,
|
COLUMNS.DETAILS_DE, COLUMNS.DETAILS_EN,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { useState, useMemo } from 'react';
|
import React, { useState, useMemo } from 'react';
|
||||||
import { ExcelRow, COLUMNS } from '../types';
|
import { ExcelRow, COLUMNS } from '../types';
|
||||||
import { Search, ChevronDown, ChevronUp, X, Edit2, Save } from 'lucide-react';
|
import { Search, ChevronDown, ChevronUp, X, Edit2, Save, Filter, XCircle } from 'lucide-react';
|
||||||
import { cn } from '../lib/utils';
|
import { cn } from '../lib/utils';
|
||||||
|
|
||||||
interface MissingDataViewProps {
|
interface MissingDataViewProps {
|
||||||
@@ -52,6 +52,8 @@ export function MissingDataView({ data, headers, onSaveRow, onCaptureState }: Mi
|
|||||||
const [sortDesc, setSortDesc] = useState(false);
|
const [sortDesc, setSortDesc] = useState(false);
|
||||||
const [page, setPage] = useState(1);
|
const [page, setPage] = useState(1);
|
||||||
const [editing, setEditing] = useState<EditingState | null>(null);
|
const [editing, setEditing] = useState<EditingState | null>(null);
|
||||||
|
const [columnFilters, setColumnFilters] = useState<Record<number, string>>({});
|
||||||
|
const [showFilterMenu, setShowFilterMenu] = useState<number | null>(null);
|
||||||
const pageSize = 100;
|
const pageSize = 100;
|
||||||
|
|
||||||
const launchDateCol = useMemo(() =>
|
const launchDateCol = useMemo(() =>
|
||||||
@@ -87,6 +89,19 @@ export function MissingDataView({ data, headers, onSaveRow, onCaptureState }: Mi
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
(Object.entries(columnFilters) as [string, string][]).forEach(([colIdx, filterValue]) => {
|
||||||
|
if (!filterValue) 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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
if (sortCol !== null) {
|
if (sortCol !== null) {
|
||||||
result.sort((a, b) => {
|
result.sort((a, b) => {
|
||||||
const valA = a.row[sortCol];
|
const valA = a.row[sortCol];
|
||||||
@@ -101,7 +116,7 @@ export function MissingDataView({ data, headers, onSaveRow, onCaptureState }: Mi
|
|||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}, [data, activeTab, search, sortCol, sortDesc, launchDateCol]);
|
}, [data, activeTab, search, sortCol, sortDesc, launchDateCol, columnFilters]);
|
||||||
|
|
||||||
const paginatedData = useMemo(() => {
|
const paginatedData = useMemo(() => {
|
||||||
const start = (page - 1) * pageSize;
|
const start = (page - 1) * pageSize;
|
||||||
@@ -203,18 +218,45 @@ export function MissingDataView({ data, headers, onSaveRow, onCaptureState }: Mi
|
|||||||
<th
|
<th
|
||||||
key={col}
|
key={col}
|
||||||
style={{ width, minWidth: width }}
|
style={{ width, minWidth: width }}
|
||||||
className="px-3 py-3 font-medium border-r border-slate-700/30 select-none cursor-pointer hover:text-white"
|
className="px-2 py-2 font-medium border-r border-slate-700/30 relative"
|
||||||
onClick={() => handleSort(col)}
|
|
||||||
>
|
>
|
||||||
<span className="flex items-center gap-1">
|
<div
|
||||||
|
className="flex items-center gap-1 cursor-pointer select-none hover:text-white"
|
||||||
|
onClick={() => handleSort(col)}
|
||||||
|
>
|
||||||
{label}
|
{label}
|
||||||
{sortCol === col && (
|
{sortCol === col && (
|
||||||
sortDesc ? <ChevronDown className="w-3 h-3" /> : <ChevronUp className="w-3 h-3" />
|
sortDesc ? <ChevronDown className="w-3 h-3" /> : <ChevronUp className="w-3 h-3" />
|
||||||
)}
|
)}
|
||||||
</span>
|
</div>
|
||||||
|
<div className="mt-1 relative">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Filter..."
|
||||||
|
value={columnFilters[col] || ''}
|
||||||
|
onChange={(e) => {
|
||||||
|
setColumnFilters(prev => ({ ...prev, [col]: e.target.value }));
|
||||||
|
setPage(1);
|
||||||
|
}}
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
className="w-full px-1.5 py-0.5 bg-slate-800 border border-slate-600 rounded text-[10px] text-white placeholder-slate-500 focus:outline-none focus:border-indigo-500"
|
||||||
|
/>
|
||||||
|
{columnFilters[col] && (
|
||||||
|
<button
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
setColumnFilters(prev => { const n = { ...prev }; delete n[col]; return n; });
|
||||||
|
setPage(1);
|
||||||
|
}}
|
||||||
|
className="absolute right-1 top-1/2 -translate-y-1/2 text-slate-500 hover:text-white"
|
||||||
|
>
|
||||||
|
<XCircle className="w-3 h-3" />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</th>
|
</th>
|
||||||
))}
|
))}
|
||||||
<th className="px-3 py-3 font-medium text-right" style={{ width: 60 }}></th>
|
<th className="px-2 py-2 font-medium text-right" style={{ width: 60 }}></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody className="divide-y divide-slate-700/30">
|
<tbody className="divide-y divide-slate-700/30">
|
||||||
|
|||||||
Reference in New Issue
Block a user