Spaces:
Sleeping
Sleeping
import { Fragment, useEffect } from "react"; | |
import { | |
ColumnDef, | |
flexRender, | |
getCoreRowModel, | |
getExpandedRowModel, | |
Row, | |
useReactTable, | |
} from "@tanstack/react-table"; | |
import { | |
Table, | |
TableHead, | |
TableHeaderCell, | |
TableBody, | |
TableRow, | |
TableCell, | |
} from "@tremor/react"; | |
interface DataTableProps<TData, TValue> { | |
data: TData[]; | |
columns: ColumnDef<TData, TValue>[]; | |
renderSubComponent: (props: { row: Row<TData> }) => React.ReactElement; | |
getRowCanExpand: (row: Row<TData>) => boolean; | |
isLoading?: boolean; | |
expandedRequestId?: string | null; | |
onRowExpand?: (requestId: string | null) => void; | |
setSelectedKeyIdInfoView?: (keyId: string | null) => void; | |
loadingMessage?: string; | |
noDataMessage?: string; | |
} | |
export function DataTable<TData extends { request_id: string }, TValue>({ | |
data = [], | |
columns, | |
getRowCanExpand, | |
renderSubComponent, | |
isLoading = false, | |
expandedRequestId, | |
onRowExpand, | |
loadingMessage = "🚅 Loading logs...", | |
noDataMessage = "No logs found", | |
}: DataTableProps<TData, TValue>) { | |
const table = useReactTable({ | |
data, | |
columns, | |
getRowCanExpand, | |
getCoreRowModel: getCoreRowModel(), | |
getExpandedRowModel: getExpandedRowModel(), | |
state: { | |
expanded: expandedRequestId | |
? data.reduce((acc, row, index) => { | |
if (row.request_id === expandedRequestId) { | |
acc[index] = true; | |
} | |
return acc; | |
}, {} as Record<string, boolean>) | |
: {}, | |
}, | |
onExpandedChange: (updater) => { | |
if (!onRowExpand) return; | |
// Get current expanded state | |
const currentExpanded = expandedRequestId | |
? data.reduce((acc, row, index) => { | |
if (row.request_id === expandedRequestId) { | |
acc[index] = true; | |
} | |
return acc; | |
}, {} as Record<string, boolean>) | |
: {}; | |
// Calculate new expanded state | |
const newExpanded = typeof updater === 'function' | |
? updater(currentExpanded) | |
: updater; | |
// If empty, it means we're closing the expanded row | |
if (Object.keys(newExpanded).length === 0) { | |
onRowExpand(null); | |
return; | |
} | |
// Find the request_id of the expanded row | |
const expandedIndex = Object.keys(newExpanded)[0]; | |
const expandedRow = expandedIndex !== undefined ? data[parseInt(expandedIndex)] : null; | |
// Call the onRowExpand callback with the request_id | |
onRowExpand(expandedRow ? expandedRow.request_id : null); | |
}, | |
}); | |
// No need for the useEffect here as we're handling everything in onExpandedChange | |
return ( | |
<div className="rounded-lg custom-border"> | |
<Table className="[&_td]:py-0.5 [&_th]:py-1"> | |
<TableHead> | |
{table.getHeaderGroups().map((headerGroup) => ( | |
<TableRow key={headerGroup.id}> | |
{headerGroup.headers.map((header) => { | |
return ( | |
<TableHeaderCell key={header.id} className="py-1 h-8"> | |
{header.isPlaceholder ? null : ( | |
flexRender( | |
header.column.columnDef.header, | |
header.getContext() | |
) | |
)} | |
</TableHeaderCell> | |
); | |
})} | |
</TableRow> | |
))} | |
</TableHead> | |
<TableBody> | |
{isLoading ? | |
<TableRow> | |
<TableCell colSpan={columns.length} className="h-8 text-center"> | |
<div className="text-center text-gray-500"> | |
<p>{loadingMessage}</p> | |
</div> | |
</TableCell> | |
</TableRow> | |
: table.getRowModel().rows.length > 0 ? | |
table.getRowModel().rows.map((row) => ( | |
<Fragment key={row.id}> | |
<TableRow className="h-8"> | |
{row.getVisibleCells().map((cell) => ( | |
<TableCell | |
key={cell.id} | |
className="py-0.5 max-h-8 overflow-hidden text-ellipsis whitespace-nowrap" | |
> | |
{flexRender( | |
cell.column.columnDef.cell, | |
cell.getContext() | |
)} | |
</TableCell> | |
))} | |
</TableRow> | |
{row.getIsExpanded() && ( | |
<TableRow> | |
<TableCell colSpan={row.getVisibleCells().length}> | |
{renderSubComponent({ row })} | |
</TableCell> | |
</TableRow> | |
)} | |
</Fragment> | |
)) | |
: <TableRow> | |
<TableCell colSpan={columns.length} className="h-8 text-center"> | |
<div className="text-center text-gray-500"> | |
<p>{noDataMessage}</p> | |
</div> | |
</TableCell> | |
</TableRow> | |
} | |
</TableBody> | |
</Table> | |
</div> | |
); | |
} | |