import React, { useState, useMemo } from 'react'; import { ExcelRow, COLUMNS } from '../types'; import { Search, ChevronDown, ChevronUp, X } from 'lucide-react'; import { cn } from '../lib/utils'; interface MissingDataViewProps { data: ExcelRow[]; headers: string[]; } function formatDateValue(val: any): string { if (val === null || val === undefined || val === '') return ''; if (typeof val === 'number') { if (val >= 25569 && val <= 60000) { const excelEpoch = new Date(1899, 11, 30); const date = new Date(excelEpoch.getTime() + val * 86400000); return date.toLocaleDateString('en-GB', { day: '2-digit', month: '2-digit', year: 'numeric' }); } return ''; } return String(val); } function isEmptyOrEpoch(val: any): boolean { if (val === null || val === undefined || val === '') return true; if (typeof val === 'number') { if (val === 0 || val === 1) return true; if (val >= 25569 && val <= 60000) { // valid date range — not empty return false; } // number outside date range — treat as empty return true; } const s = String(val).trim(); if (s === '' || s === '0' || s === '1') return true; if (s.endsWith('/1900')) return true; return false; } type TabType = 'missingClass' | 'missingLaunch'; export function MissingDataView({ data, headers }: MissingDataViewProps) { const [activeTab, setActiveTab] = useState('missingClass'); const [search, setSearch] = useState(''); const [sortCol, setSortCol] = useState(null); const [sortDesc, setSortDesc] = useState(false); const [page, setPage] = useState(1); const pageSize = 100; const launchDateCol = useMemo(() => headers.findIndex(h => h.toLowerCase().includes('launch')), [headers]); const readyToOrderCol = useMemo(() => headers.findIndex(h => h.toLowerCase().includes('ready')), [headers]); const filteredData = useMemo(() => { let result = data.map((row, index) => ({ row, index })); if (activeTab === 'missingClass') { result = result.filter(r => { const val = r.row[COLUMNS.CLASSIFICATION]; return !val || String(val).trim() === ''; }); } if (activeTab === 'missingLaunch') { result = result.filter(r => { const val = launchDateCol >= 0 ? r.row[launchDateCol] : undefined; return isEmptyOrEpoch(val); }); } if (search) { const s = search.toLowerCase(); result = result.filter(r => String(r.row[COLUMNS.ARTICLE_NO] || '').toLowerCase().includes(s) || String(r.row[COLUMNS.ARTICLE_NAME] || '').toLowerCase().includes(s) ); } if (sortCol !== null) { result.sort((a, b) => { const valA = a.row[sortCol]; const valB = b.row[sortCol]; if (typeof valA === 'number' && typeof valB === 'number') { return sortDesc ? valB - valA : valA - valB; } const sA = String(valA || ''); const sB = String(valB || ''); return sortDesc ? sB.localeCompare(sA) : sA.localeCompare(sB); }); } return result; }, [data, activeTab, search, sortCol, sortDesc, launchDateCol]); const paginatedData = useMemo(() => { const start = (page - 1) * pageSize; return filteredData.slice(start, start + pageSize); }, [filteredData, page]); const totalPages = Math.ceil(filteredData.length / pageSize); const handleSort = (col: number) => { if (sortCol === col) setSortDesc(d => !d); else { setSortCol(col); setSortDesc(false); } }; const tabs: { id: TabType; label: string }[] = [ { id: 'missingClass', label: 'Missing Classification' }, { id: 'missingLaunch', label: 'Missing Launch Date' }, ]; const launchHeader = launchDateCol >= 0 ? headers[launchDateCol] : 'Launch Date'; const readyHeader = readyToOrderCol >= 0 ? headers[readyToOrderCol] : 'Ready to Order'; const columns = [ { col: COLUMNS.ARTICLE_NO, label: 'SKU', width: 100 }, { col: COLUMNS.ARTICLE_NAME, label: 'Name', width: 220 }, { col: COLUMNS.CLASSIFICATION, label: 'Classification', width: 130 }, ...(launchDateCol >= 0 ? [{ col: launchDateCol, label: launchHeader, width: 130 }] : []), ...(readyToOrderCol >= 0 ? [{ col: readyToOrderCol, label: readyHeader, width: 130 }] : []), ]; return (
{tabs.map(tab => ( ))}
{ setSearch(e.target.value); setPage(1); }} className="w-full pl-9 pr-10 py-2 bg-slate-900/50 border border-slate-700 rounded-md text-sm text-white focus:outline-none focus:border-indigo-500 focus:ring-1 focus:ring-indigo-500" /> {search && ( )}
{filteredData.length} items
{columns.map(({ col, label, width }) => ( ))} {paginatedData.map(({ row, index }) => ( {launchDateCol >= 0 && ( )} {readyToOrderCol >= 0 && ( )} ))} {paginatedData.length === 0 && ( )}
handleSort(col)} > {label} {sortCol === col && ( sortDesc ? : )}
{row[COLUMNS.ARTICLE_NO]} {row[COLUMNS.ARTICLE_NAME]} {row[COLUMNS.CLASSIFICATION] && String(row[COLUMNS.CLASSIFICATION]).trim() !== '' ? ( {row[COLUMNS.CLASSIFICATION]} ) : ( Empty )} {isEmptyOrEpoch(row[launchDateCol]) ? ( Empty ) : ( formatDateValue(row[launchDateCol]) || String(row[launchDateCol]) )} {row[readyToOrderCol] !== null && row[readyToOrderCol] !== undefined && row[readyToOrderCol] !== '' ? ( formatDateValue(row[readyToOrderCol]) || String(row[readyToOrderCol]) ) : ( )}
No items found.
Showing {paginatedData.length} of {filteredData.length} items
Page {page} of {totalPages || 1}
); }