import { ReactNode, useState } from "react"; import { Table, Button, Menu, Checkbox, Group, Text, Pagination, Loader, Center, Paper, Badge, } from "@mantine/core"; import { IconRefresh, IconEye, IconInbox } from "@tabler/icons-react"; import { useTranslation } from "react-i18next"; export interface AdvancedColumn { header: ReactNode; /** Dot-path into the row, used when no `cell` is given (e.g. "expectation.name"). */ accessorKey?: string; cell?: (ctx: { row: { original: T }; value: unknown }) => ReactNode; size?: number; align?: "left" | "center" | "right"; /** Whether column starts visible. Default true. */ enabled?: boolean; } interface AdvancedTableProps { columns: AdvancedColumn[]; data: T[]; tableName: string; /** Total item count on the server (drives pagination), not data.length. */ itemCount: number; /** 0-based current page. */ pageIndex: number; onPageChange: (pageIndex: number) => void; pageSize?: number; refresh?: () => void; /** Server-side search — debounced internally. Omit to hide the search box. */ onSearchChange?: (q: string) => void; isLoading?: boolean; emptyText?: string; } function getByPath(obj: unknown, path?: string): unknown { if (!path) return undefined; return path .split(".") .reduce( (acc, key) => acc && typeof acc === "object" ? (acc as Record)[key] : undefined, obj, ); } export function AdvancedTable({ columns, data, tableName, itemCount, pageIndex, onPageChange, pageSize = 10, refresh, isLoading = false, emptyText, }: AdvancedTableProps) { const { t } = useTranslation(); const [visible, setVisible] = useState( columns.map((c) => c.enabled ?? true), ); const toggleColumn = (i: number) => setVisible((prev) => { if (prev[i] && prev.filter(Boolean).length === 1) return prev; // keep at least one column visible return prev.map((v, idx) => (idx === i ? !v : v)); }); const shownColumns = columns.filter((_, i) => visible[i] ?? true); return ( {""} {refresh && ( )} {t("common.toggleColumns", "Toggle columns")} {columns.map((col, i) => ( toggleColumn(i)}> ))} {shownColumns.map((col, i) => ( {col.header} ))} {isLoading ? (
) : data.length === 0 ? (
{emptyText ?? t("common.noResult", "No results")}
) : ( data.map((row, rowIndex) => ( {shownColumns.map((col, i) => { const value = getByPath(row, col.accessorKey); return ( {col.cell ? col.cell({ row: { original: row }, value }) : ((value as ReactNode) ?? "-")} ); })} )) )}
{itemCount > pageSize && ( onPageChange(page - 1)} size="sm" /> )}
); }