2026-07-17 11:59:05 -03:00
|
|
|
import type { ReactNode } from "react";
|
|
|
|
|
import {
|
|
|
|
|
Paper,
|
|
|
|
|
Table,
|
|
|
|
|
TableBody,
|
|
|
|
|
TableCell,
|
|
|
|
|
TableContainer,
|
|
|
|
|
TableHead,
|
|
|
|
|
TableRow,
|
|
|
|
|
} from "@mui/material";
|
|
|
|
|
import { TableEmptyRow, TableLoadingRow } from "@/components/ui/table-state-rows";
|
2026-07-17 13:31:56 -03:00
|
|
|
|
|
|
|
|
export type SortDirection = "asc" | "desc" | "";
|
2026-07-17 11:59:05 -03:00
|
|
|
|
|
|
|
|
export type ResourceListColumn<T> = {
|
|
|
|
|
id: string;
|
|
|
|
|
header: ReactNode;
|
|
|
|
|
render: (row: T) => ReactNode;
|
|
|
|
|
sortable?: boolean;
|
|
|
|
|
headerClassName?: string;
|
|
|
|
|
cellClassName?: string;
|
|
|
|
|
align?: "left" | "center" | "right";
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
type ResourceListTableProps<T> = {
|
|
|
|
|
columns: ResourceListColumn<T>[];
|
|
|
|
|
rows: T[];
|
|
|
|
|
rowKey: (row: T) => string;
|
|
|
|
|
isLoading: boolean;
|
|
|
|
|
emptyMessage?: string;
|
|
|
|
|
sort?: { column: string; direction: SortDirection };
|
|
|
|
|
onSort?: (column: string) => void;
|
|
|
|
|
size?: "small" | "medium";
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
function sortIndicator(direction: SortDirection): string {
|
|
|
|
|
if (direction === "desc") {
|
|
|
|
|
return " ↓";
|
|
|
|
|
}
|
|
|
|
|
if (direction === "asc") {
|
|
|
|
|
return " ↑";
|
|
|
|
|
}
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function ResourceListTable<T>({
|
|
|
|
|
columns,
|
|
|
|
|
rows,
|
|
|
|
|
rowKey,
|
|
|
|
|
isLoading,
|
|
|
|
|
emptyMessage,
|
|
|
|
|
sort,
|
|
|
|
|
onSort,
|
|
|
|
|
size = "small",
|
|
|
|
|
}: ResourceListTableProps<T>) {
|
|
|
|
|
const colSpan = columns.length;
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<TableContainer component={Paper} variant="outlined">
|
|
|
|
|
<Table size={size}>
|
|
|
|
|
<TableHead>
|
|
|
|
|
<TableRow>
|
|
|
|
|
{columns.map((column) => (
|
|
|
|
|
<TableCell
|
|
|
|
|
key={column.id}
|
|
|
|
|
align={column.align}
|
|
|
|
|
className={`font-semibold ${column.sortable ? "cursor-pointer whitespace-nowrap" : ""} ${column.headerClassName ?? ""}`}
|
|
|
|
|
onClick={column.sortable && onSort ? () => onSort(column.id) : undefined}
|
|
|
|
|
>
|
|
|
|
|
{column.header}
|
|
|
|
|
{column.sortable && sort?.column === column.id ? sortIndicator(sort.direction) : ""}
|
|
|
|
|
</TableCell>
|
|
|
|
|
))}
|
|
|
|
|
</TableRow>
|
|
|
|
|
</TableHead>
|
|
|
|
|
<TableBody>
|
|
|
|
|
{isLoading ? (
|
|
|
|
|
<TableLoadingRow colSpan={colSpan} />
|
|
|
|
|
) : rows.length === 0 ? (
|
|
|
|
|
<TableEmptyRow colSpan={colSpan} message={emptyMessage} />
|
|
|
|
|
) : (
|
|
|
|
|
rows.map((row) => (
|
|
|
|
|
<TableRow key={rowKey(row)} hover>
|
|
|
|
|
{columns.map((column) => (
|
|
|
|
|
<TableCell key={column.id} align={column.align} className={column.cellClassName}>
|
|
|
|
|
{column.render(row)}
|
|
|
|
|
</TableCell>
|
|
|
|
|
))}
|
|
|
|
|
</TableRow>
|
|
|
|
|
))
|
|
|
|
|
)}
|
|
|
|
|
</TableBody>
|
|
|
|
|
</Table>
|
|
|
|
|
</TableContainer>
|
|
|
|
|
);
|
|
|
|
|
}
|