import React, { useMemo, useState, useEffect } from 'react'; import { Edit2, Trash2, Filter, Columns, Layers, Download, ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from 'lucide-react'; import AdvancedFiltersModal from './AdvancedFiltersModal'; const ExcelTable = ({ data, columns, filterDefs = [], onEdit, onDelete, onRowClick, loading = false, pageSize: initialPageSize = 50, selectedIds = [], onSelectionChange, rowKey = 'id' }) => { const [showFilters, setShowFilters] = useState(false); const [filters, setFilters] = useState({}); const [sortConfig, setSortConfig] = useState({ key: null, direction: 'asc' }); // Pagination State const [currentPage, setCurrentPage] = useState(1); const [pageSize, setPageSize] = useState(initialPageSize); // ... (existing useMemo logic) ... // Helper para lidar com seleção // 1. Extract Unique Values... const options = React.useMemo(() => { const opts = {}; const getUnique = (key) => [...new Set(data.map(item => item[key]).filter(Boolean))].sort(); filterDefs.forEach(def => { if (def.type === 'select') { opts[def.field] = getUnique(def.field); } }); return opts; }, [data, filterDefs]); // 2. Process Data... const processedData = React.useMemo(() => { let result = [...data]; // Filter Object.keys(filters).forEach(key => { if (filters[key]) { result = result.filter(item => { const itemValue = String(item[key] || '').toLowerCase(); const filterValue = String(filters[key]).toLowerCase(); return itemValue.includes(filterValue); }); } }); // Sort if (sortConfig.key) { result.sort((a, b) => { const valA = a[sortConfig.key] || ''; const valB = b[sortConfig.key] || ''; if (valA < valB) return sortConfig.direction === 'asc' ? -1 : 1; if (valA > valB) return sortConfig.direction === 'asc' ? 1 : -1; return 0; }); } return result; }, [data, filters, sortConfig]); // 3. Paginate... const paginatedData = React.useMemo(() => { const startIndex = (currentPage - 1) * pageSize; return processedData.slice(startIndex, startIndex + pageSize); }, [processedData, currentPage, pageSize]); // Reset page... useEffect(() => { setCurrentPage(1); }, [filters, sortConfig, pageSize]); // Helper para lidar com seleção (Movido para cá para ter acesso a processedData) const handleSelectAll = (e) => { if (!onSelectionChange) return; if (e.target.checked) { const allIds = processedData.map(item => item[rowKey]); onSelectionChange(allIds); } else { onSelectionChange([]); } }; const handleSelectRow = (id) => { if (!onSelectionChange) return; const newSelected = selectedIds.includes(id) ? selectedIds.filter(x => x !== id) : [...selectedIds, id]; onSelectionChange(newSelected); }; const isAllSelected = processedData && processedData.length > 0 && processedData.every(item => selectedIds.includes(item[rowKey])); const isIndeterminate = processedData && processedData.some(item => selectedIds.includes(item[rowKey])) && !isAllSelected; const totalPages = Math.ceil(processedData.length / pageSize); const handleSort = (key) => { setSortConfig(current => ({ key, direction: current.key === key && current.direction === 'asc' ? 'desc' : 'asc' })); }; const handleApplyFilters = (newFilters) => { setFilters(newFilters); setShowFilters(false); }; return ( <>
{/* Loading Overlay */} {loading && (
Carregando Dados...
)} {/* 1. Top Toolbar */}
{/* Left Actions */}
{/* Filter Button */} {/* Actions Button (Green Highlight) - Commented as requested */}
{/* Right Actions - Commented out as requested
*/}
{/* 2. Table Header (The Complex Part) */}
{/* Checkbox Header */} {onEdit || onDelete ? ( ) : null} {/* Dynamic Columns */} {columns.map((col, idx) => ( ))} {/* Spacer for scrollbar */} {/* 3. Table Body - Displaying Paginated Data */} {paginatedData.map((row, idx) => ( { // Não disparar onRowClick se clicar em checkbox, botões ou links if (e.target.closest('input[type="checkbox"]') || e.target.closest('button') || e.target.closest('a')) { return; } onRowClick && onRowClick(row); }} className={`h-[44px] border-b border-slate-100 dark:border-[#252525] hover:bg-slate-50 dark:hover:bg-[#202020] transition-colors group text-sm ${idx % 2 === 0 ? 'bg-white dark:bg-[#151515]' : 'bg-slate-50/50 dark:bg-[#181818]'} ${idx === 0 ? '!bg-slate-100 dark:!bg-[#2b2b2b]' : ''} ${onRowClick ? 'cursor-pointer' : ''}`}> {/* Checkbox Cell */} {/* Actions Cell */} {(onEdit || onDelete) ? ( ) : null} {/* Data Cells */} {columns.map((col, cIdx) => ( ))} ))} {/* Empty rows filler if needed */} {!loading && paginatedData.length === 0 && ( )}
{ if (input) input.indeterminate = isIndeterminate; }} onChange={handleSelectAll} disabled={!onSelectionChange} className="appearance-none w-3.5 h-3.5 border-2 border-slate-300 dark:border-slate-600 rounded-sm bg-white dark:bg-[#2a2a2a] checked:bg-orange-500 checked:border-orange-500 transition-all cursor-pointer relative checked:bg-[url('data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2214%22%20height%3D%2214%22%20viewBox%3D%220%200%2014%2014%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M2.5%207L5.5%2010L11.5%204%22%20stroke%3D%22%23FFF%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%2F%3E%3C%2Fsvg%3E')] bg-center bg-no-repeat bg-[length:70%]" />
Ações
handleSort(col.field)} className="h-[40px] border-r border-slate-200 dark:border-[#333] bg-slate-50 dark:bg-[#1b1b1b] px-3 relative group hover:bg-slate-100 dark:hover:bg-[#222] transition-colors cursor-pointer select-none" style={{ width: col.width, minWidth: col.width }} >
{col.header}
{/* Sort/Menu Icons */}
{/* Column Resizer Handle */}
e.stopPropagation()} />
handleSelectRow(row[rowKey])} disabled={!onSelectionChange} className="appearance-none w-3.5 h-3.5 border-2 border-slate-300 dark:border-slate-600 rounded-sm bg-white dark:bg-[#2a2a2a] checked:bg-orange-500 checked:border-orange-500 transition-all cursor-pointer relative checked:bg-[url('data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2214%22%20height%3D%2214%22%20viewBox%3D%220%200%2014%2014%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M2.5%207L5.5%2010L11.5%204%22%20stroke%3D%22%23FFF%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%2F%3E%3C%2Fsvg%3E')] bg-center bg-no-repeat bg-[length:70%]" />
{onDelete && ( )}
{col.render ? col.render(row) : ( {row[col.field] || '-'} )}
{filters && Object.keys(filters).length > 0 ? 'Nenhum registro encontrado para os filtros selecionados.' : 'Nenhum registro disponível.'}
{/* 4. Footer Steps (Pagination Controls) */}
{/* Left: Totals */}
Total: {processedData.length}
Página: {currentPage} / {totalPages || 1}
{/* Right: Pagination Controls */}
Exibindo {Math.min((currentPage - 1) * pageSize + 1, processedData.length)} a {Math.min(currentPage * pageSize, processedData.length)}
{/* First Page */} {/* Prev Page */} {/* Manual Page Buttons (Simple logic) */}
{/* Next Page */} {/* Last Page */}
setShowFilters(false)} onApply={handleApplyFilters} options={options} initialFilters={filters} config={filterDefs} /> ); }; export default ExcelTable;