import React from 'react'; import { ArrowUpDown, ArrowUp, ArrowDown } from 'lucide-react'; import type { VisibilityMode } from '@/hooks/useTableVisibility'; import { useMediaQuery } from '@/hooks/useMediaQuery'; export interface ColumnDef { key: string; label: string; render: (item: T) => React.ReactNode; sortable?: boolean; visibilityLevel: VisibilityMode; } export interface RowGroup { label: string; rows: T[]; } interface EntityTableProps { columns: ColumnDef[]; groups: RowGroup[]; pinnedRows: T[]; pinnedLabel: string; showPinned: boolean; selectedId: number | null; onRowClick: (id: number) => void; sortKey: string; sortDir: 'asc' | 'desc'; onSort: (key: string) => void; visibilityMode: VisibilityMode; loading?: boolean; mobileCardRender?: (item: T) => React.ReactNode; } const LEVEL_ORDER: VisibilityMode[] = ['essential', 'filtered', 'all']; function isVisible(colLevel: VisibilityMode, mode: VisibilityMode): boolean { return LEVEL_ORDER.indexOf(colLevel) <= LEVEL_ORDER.indexOf(mode); } function SkeletonRow({ colCount }: { colCount: number }) { return ( {Array.from({ length: colCount }).map((_, i) => (
))} ); } function SortIcon({ sortKey, sortDir, colKey, }: { sortKey: string; sortDir: 'asc' | 'desc'; colKey: string; }) { if (sortKey !== colKey) return ; return sortDir === 'asc' ? ( ) : ( ); } function DataRow({ item, visibleColumns, selectedId, onRowClick, }: { item: T; visibleColumns: ColumnDef[]; selectedId: number | null; onRowClick: (id: number) => void; }) { return ( onRowClick(item.id)} onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); onRowClick(item.id); } }} tabIndex={0} role="row" aria-selected={selectedId === item.id} > {visibleColumns.map((col) => ( {col.render(item)} ))} ); } function SectionHeader({ label, colCount }: { label: string; colCount: number }) { return ( {label} ); } export function EntityTable({ columns, groups, pinnedRows, pinnedLabel, showPinned, selectedId, onRowClick, sortKey, sortDir, onSort, visibilityMode, loading = false, mobileCardRender, }: EntityTableProps) { const visibleColumns = columns.filter((col) => isVisible(col.visibilityLevel, visibilityMode)); const colCount = visibleColumns.length; const showPinnedSection = showPinned && pinnedRows.length > 0; const isMobile = useMediaQuery('(max-width: 767px)'); if (isMobile && mobileCardRender) { return (
{loading ? ( Array.from({ length: 6 }).map((_, i) => (
)) ) : ( <> {showPinnedSection && ( <>

{pinnedLabel}

{pinnedRows.map((item) => (
onRowClick(item.id)} className="cursor-pointer" role="button" tabIndex={0} onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); onRowClick(item.id); } }}> {mobileCardRender(item)}
))} )} {groups.map((group) => ( {group.rows.length > 0 && ( <>

{group.label}

{group.rows.map((item) => (
onRowClick(item.id)} className="cursor-pointer" role="button" tabIndex={0} onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); onRowClick(item.id); } }}> {mobileCardRender(item)}
))} )}
))} )}
); } return (
{visibleColumns.map((col) => ( ))} {loading ? ( Array.from({ length: 6 }).map((_, i) => ) ) : ( <> {showPinnedSection && ( <> {pinnedRows.map((item) => ( ))} )} {groups.map((group) => ( {group.rows.length > 0 && ( <> {group.rows.map((item) => ( ))} )} ))} )}
{col.sortable ? ( ) : ( col.label )}
); }