import React, { useState, useMemo } from 'react'; import { ExcelRow, COLUMNS } from '../types'; import { Search, Filter, Edit2, ChevronDown, ChevronUp } from 'lucide-react'; import { cn } from '../lib/utils'; interface ProductDescriptionsProps { data: ExcelRow[]; onEdit: (index: number) => void; } type TabType = 'all' | 'missingDeLong' | 'missingEnLong' | 'missingDeShort' | 'missingEnShort' | 'complete' | 'incomplete'; export function ProductDescriptions({ data, onEdit }: ProductDescriptionsProps) { const [activeTab, setActiveTab] = useState('all'); const [search, setSearch] = useState(''); const [lineFilter, setLineFilter] = useState(''); const [licenseFilter, setLicenseFilter] = useState(''); const [sortCol, setSortCol] = useState(null); const [sortDesc, setSortDesc] = useState(false); const [pageSize, setPageSize] = useState(25); const [page, setPage] = useState(1); const lines = useMemo(() => Array.from(new Set(data.map(r => r[COLUMNS.LINE]).filter(Boolean))), [data]); const licenses = useMemo(() => Array.from(new Set(data.map(r => r[COLUMNS.LICENSE]).filter(Boolean))), [data]); const filteredData = useMemo(() => { let result = data.map((row, index) => ({ row, index })); // Tab filter if (activeTab === 'missingDeLong') result = result.filter(r => !r.row[COLUMNS.LONG_DE]); if (activeTab === 'missingEnLong') result = result.filter(r => !r.row[COLUMNS.LONG_EN]); if (activeTab === 'missingDeShort') result = result.filter(r => !r.row[COLUMNS.SHORT_DE]); if (activeTab === 'missingEnShort') result = result.filter(r => !r.row[COLUMNS.SHORT_EN]); if (activeTab === 'complete') result = result.filter(r => r.row[COLUMNS.LONG_DE] && r.row[COLUMNS.LONG_EN] && r.row[COLUMNS.SHORT_DE] && r.row[COLUMNS.SHORT_EN]); if (activeTab === 'incomplete') result = result.filter(r => !r.row[COLUMNS.LONG_DE] || !r.row[COLUMNS.LONG_EN] || !r.row[COLUMNS.SHORT_DE] || !r.row[COLUMNS.SHORT_EN]); // Search filter 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) ); } // Dropdown filters if (lineFilter) result = result.filter(r => r.row[COLUMNS.LINE] === lineFilter); if (licenseFilter) result = result.filter(r => r.row[COLUMNS.LICENSE] === licenseFilter); // Sorting if (sortCol !== null) { result.sort((a, b) => { const valA = String(a.row[sortCol] || ''); const valB = String(b.row[sortCol] || ''); return sortDesc ? valB.localeCompare(valA) : valA.localeCompare(valB); }); } return result; }, [data, activeTab, search, lineFilter, licenseFilter, sortCol, sortDesc]); const paginatedData = useMemo(() => { const start = (page - 1) * pageSize; return filteredData.slice(start, start + pageSize); }, [filteredData, page, pageSize]); const totalPages = Math.ceil(filteredData.length / pageSize); const handleSort = (col: number) => { if (sortCol === col) { setSortDesc(!sortDesc); } else { setSortCol(col); setSortDesc(false); } }; const getRowColor = (row: ExcelRow) => { // EOL Rule: OOC Classification and 0 or negative stock (Item Available) const classification = String(row[COLUMNS.CLASSIFICATION] || '').toUpperCase().trim(); const stock = Number(row[COLUMNS.ITEM_AVAILABLE] || 0); if (classification === 'OOC' && stock <= 0) { return 'bg-yellow-500/10 hover:bg-yellow-500/20'; // EOL Highlight } const fields = [row[COLUMNS.LONG_DE], row[COLUMNS.LONG_EN], row[COLUMNS.SHORT_DE], row[COLUMNS.SHORT_EN]]; const filled = fields.filter(Boolean).length; if (filled === 4) return 'bg-green-900/10 hover:bg-green-900/20'; if (filled === 0) return 'bg-red-900/10 hover:bg-red-900/20'; return 'bg-yellow-900/10 hover:bg-yellow-900/20'; }; const Badge = ({ content, row }: { content: any, row: ExcelRow }) => { if (content) return ; // EOL Exception: OOC and stock <= 0 const classification = String(row[COLUMNS.CLASSIFICATION] || '').toUpperCase().trim(); const stock = Number(row[COLUMNS.ITEM_AVAILABLE] || 0); if (classification === 'OOC' && stock <= 0) { return EOL not neccessary; } return ✗ Missing; }; const tabs: { id: TabType; label: string }[] = [ { id: 'all', label: 'All Products' }, { id: 'missingDeLong', label: 'Missing DE Long' }, { id: 'missingEnLong', label: 'Missing EN Long' }, { id: 'missingDeShort', label: 'Missing DE Short' }, { id: 'missingEnShort', label: 'Missing EN Short' }, { id: 'complete', label: 'Complete' }, { id: 'incomplete', label: 'Incomplete' }, ]; return (
{tabs.map(tab => ( ))}
{ setSearch(e.target.value); setPage(1); }} className="w-full pl-9 pr-4 py-2 bg-slate-900 border border-slate-700 rounded-md text-sm text-white focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500" />
{[ { col: COLUMNS.ARTICLE_NO, label: 'Article No.' }, { col: COLUMNS.ARTICLE_NAME, label: 'Article Name' }, { col: COLUMNS.LINE, label: 'Line' }, { col: COLUMNS.LICENSE, label: 'License' }, { col: COLUMNS.LONG_DE, label: 'Long DE' }, { col: COLUMNS.SHORT_EN, label: 'Short EN' }, ].map(({ col, label }) => ( ))} {paginatedData.map(({ row, index }) => ( ))} {paginatedData.length === 0 && ( )}
handleSort(col)} >
{label} {sortCol === col && ( sortDesc ? : )}
Actions
{row[COLUMNS.ARTICLE_NO]} {row[COLUMNS.ARTICLE_NAME]} {row[COLUMNS.LINE]} {row[COLUMNS.LICENSE]}
No products found matching the criteria.
Showing {Math.min((page - 1) * pageSize + 1, filteredData.length)} to {Math.min(page * pageSize, filteredData.length)} of {filteredData.length} entries
Page {page} of {totalPages || 1}
); }