Sifa professional network frontend (Next.js, React, TailwindCSS)
sifa.id/
1interface DataTableProps {
2 columns: { key: string; label: string }[];
3 rows: Record<string, string | number | boolean | null>[];
4 caption?: string;
5}
6
7export function DataTable({ columns, rows, caption }: DataTableProps) {
8 if (!rows.length) return null;
9
10 return (
11 <details className="mt-4">
12 <summary className="inline-flex cursor-pointer items-center rounded-md border border-border px-3 py-1.5 text-xs font-medium text-muted-foreground transition-colors hover:bg-secondary hover:text-foreground">
13 Show data as table
14 </summary>
15 <div className="mt-2 overflow-x-auto">
16 <table className="w-full border-collapse text-sm">
17 {caption && <caption className="sr-only">{caption}</caption>}
18 <thead>
19 <tr>
20 {columns.map((col) => (
21 <th
22 key={col.key}
23 className="border-b border-border px-3 py-2 text-left font-medium text-foreground"
24 >
25 {col.label}
26 </th>
27 ))}
28 </tr>
29 </thead>
30 <tbody>
31 {rows.map((row, i) => (
32 <tr key={i} className={i % 2 === 1 ? 'bg-secondary/50' : undefined}>
33 {columns.map((col) => (
34 <td
35 key={col.key}
36 className="border-b border-border px-3 py-2 text-muted-foreground"
37 >
38 {formatCell(row[col.key])}
39 </td>
40 ))}
41 </tr>
42 ))}
43 </tbody>
44 </table>
45 </div>
46 </details>
47 );
48}
49
50function formatCell(value: string | number | boolean | null | undefined): string {
51 if (value === null || value === undefined) return '--';
52 if (typeof value === 'boolean') return value ? 'Yes' : 'No';
53 return String(value);
54}