OverviewTable.tsx72 lines · main
1import { Loader2 } from 'lucide-react'
2import { ReactNode } from 'react'
3import { cn, Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from 'ui'
4
5import NoDataPlaceholder from '@/components/ui/Charts/NoDataPlaceholder'
6
7export type OverviewTableColumn<T> = {
8 key: keyof T | string
9 header: string
10 render?: (row: T) => ReactNode
11 className?: string
12}
13
14export interface OverviewTable<T> {
15 columns: OverviewTableColumn<T>[]
16 data: T[]
17 isLoading?: boolean
18 emptyMessage?: string
19}
20
21export function OverviewTable<T>({ columns, data, isLoading, emptyMessage }: OverviewTable<T>) {
22 const hasData = !isLoading && data.length > 0
23
24 return (
25 <Table className="border-t mt-4">
26 {hasData && (
27 <TableHeader>
28 <TableRow>
29 {columns.map((col) => (
30 <TableHead key={String(col.key)} className={cn(col.className)}>
31 {col.header}
32 </TableHead>
33 ))}
34 </TableRow>
35 </TableHeader>
36 )}
37 <TableBody className="text-foreground">
38 {isLoading ? (
39 <TableRow className="[&>td]:hover:bg-transparent">
40 <TableCell colSpan={columns.length} className="text-center text-foreground-light">
41 <div className="flex items-center justify-center gap-2 py-4">
42 <Loader2 className="size-4 animate-spin" />
43 </div>
44 </TableCell>
45 </TableRow>
46 ) : data.length === 0 ? (
47 <TableRow className="[&>td]:hover:bg-transparent">
48 <TableCell colSpan={columns.length} className="text-center text-foreground-light">
49 <div className="p-2 pt-0">
50 <NoDataPlaceholder
51 size="normal"
52 message={emptyMessage || 'No data available'}
53 isFullHeight
54 />
55 </div>
56 </TableCell>
57 </TableRow>
58 ) : (
59 (data as unknown as T[]).map((row, idx) => (
60 <TableRow key={idx}>
61 {columns.map((col) => (
62 <TableCell key={String(col.key)} className={cn(col.className)}>
63 {col.render ? col.render(row) : (row as any)[col.key as string]}
64 </TableCell>
65 ))}
66 </TableRow>
67 ))
68 )}
69 </TableBody>
70 </Table>
71 )
72}