// @components/table.tsx import React, { useState, useMemo } from "react"; import Pagination from "./pagination"; export interface Header { key: keyof T; label: string; render?: (value: T[keyof T], row: T) => React.ReactNode; width?: string; className?: string; } interface TableProps { headers: Header[]; data: T[]; rowsPerPage?: number; onRowClick?: (rowData: T) => void; rowKey?: (row: T) => string | number; className?: { table?: string; row?: string; cell?: string; header?: string; }; controlledPage?: number; onPageChange?: (page: number) => void; } export default function Table({ headers, data, rowsPerPage = 10, onRowClick, rowKey, className, controlledPage, onPageChange, }: TableProps) { const [sortConfig, setSortConfig] = useState<{ key: keyof T; direction: "asc" | "desc"; } | null>(null); const [internalPage, setInternalPage] = useState(1); const currentPage = controlledPage ?? internalPage; const setPage = onPageChange ?? setInternalPage; const handleSort = (key: keyof T) => { let direction: "asc" | "desc" = "asc"; if ( sortConfig && sortConfig.key === key && sortConfig.direction === "asc" ) { direction = "desc"; } setSortConfig({ key, direction }); }; const sortedData = useMemo(() => { if (!sortConfig) return data; return [...data].sort((a, b) => { if (a[sortConfig.key]! < b[sortConfig.key]!) return sortConfig.direction === "asc" ? -1 : 1; if (a[sortConfig.key]! > b[sortConfig.key]!) return sortConfig.direction === "asc" ? 1 : -1; return 0; }); }, [data, sortConfig]); const totalPages = Math.ceil(sortedData.length / rowsPerPage); const paginatedData = sortedData.slice( (currentPage - 1) * rowsPerPage, currentPage * rowsPerPage ); return (
{headers.map((header, index) => ( ))} {headers.map((header, index) => ( ))} {paginatedData.length === 0 ? ( ) : ( paginatedData.map((row, rowIndex) => ( onRowClick?.(row)} className={`${onRowClick ? "clickable-row" : ""} ${ className?.row || "" }`} > {headers.map((header, cellIndex) => ( ))} )) )}
handleSort(header.key)} tabIndex={0} aria-sort={ sortConfig?.key === header.key ? sortConfig.direction === "asc" ? "ascending" : "descending" : "none" } style={{ width: header.width }} className={`sortable ${className?.header || ""} ${ header.className || "" } ${ sortConfig?.key === header.key ? `sorted-${sortConfig.direction}` : "" }`} > {header.label} {sortConfig?.key === header.key && ( )}
No hay datos disponibles
{header.render ? header.render(row[header.key], row) : (row[header.key] as React.ReactNode)}
{totalPages > 1 && ( )}
); } interface TablaSkeletonProps { rows?: number; columns?: number; } export function TableSkeleton({ rows = 5, columns = 5 }: TablaSkeletonProps) { return ( {Array.from({ length: columns }).map((_, index) => ( ))} {Array.from({ length: rows }).map((_, rowIndex) => ( {Array.from({ length: columns }).map((_, colIndex) => ( ))} ))}
); }