Files
kandev/apps/web/components/ui/data-table.tsx
T
João Salavisa f09e938673 feat(i18n): localize the last batch of live surfaces before Office (#2357)
Migrates the eleven remaining files that `i18next/no-literal-string` flagged
outside `app/office`, `app/demo` and the task-create surfaces owned by #2326:
the session prepare panel, the onboarding agents step, the host system-metrics
readout, the system-health issues dialog, the shared panel search bar, the
mermaid diagram block, the settings agent card, both release-notes surfaces,
and the shared data-table pair.

Findings drop 884 -> 826 (-58) and the guard allowlist grows 442 -> 453, file
by file rather than by directory since every one of these directories still
holds un-migrated files.

Defects repaired beyond the codemod's output:

- `data-table-pagination.tsx` rendered `{count} results` as a bare JSX
  fragment, invisible to both plural gates. It is now one interpolated key
  with `_one`/`_other`, as are the health-dialog issue count and the
  release-notes "N new releases" helper.
- Two mixed literal+expression blocks were half-converted, rendering
  identically in English while being structurally untranslatable. Both were
  rebuilt as a single message.
- The codemod re-sorted `settings.json` into `localeCompare` order and
  re-encoded every non-ASCII character to `\uXXXX` under a comment claiming it
  matched the existing files. No catalog is stored escaped and only `common`
  is in that order, so both halves were reverted; all four catalogs are
  additions-only.
- Copy in default parameters and plain helpers is invisible to the lint rule,
  so `Search…`, the six prepare-panel header labels, the setup-script step name
  and error, the metric labels, `lastUpdatedText` and `buildDescription` were
  migrated by hand.

`task:preparingEnvironment` was already taken by #2326 holding "Preparing
environment" without the ellipsis this surface renders, so the prepare panel
uses `preparingEnvironmentEllipsis` rather than silently changing its copy.

Units, wire ids, the `{workspaceId}` URL token, the `"ellipsis"` page sentinel
and every mermaid theme/CSS value stay English as protocol.
2026-08-07 11:34:35 +02:00

115 lines
3.5 KiB
TypeScript

"use client";
import {
type ColumnDef,
type PaginationState,
flexRender,
getCoreRowModel,
useReactTable,
} from "@tanstack/react-table";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@kandev/ui/table";
import { DataTablePagination } from "./data-table-pagination";
import { useTranslation } from "react-i18next";
interface DataTableProps<TData, TValue> {
columns: ColumnDef<TData, TValue>[];
data: TData[];
pageCount?: number;
rowCount?: number;
pagination?: PaginationState;
onPaginationChange?: (pagination: PaginationState) => void;
isLoading?: boolean;
onRowClick?: (row: TData) => void;
}
export function DataTable<TData, TValue>({
columns,
data,
pageCount = -1,
rowCount,
pagination,
onPaginationChange,
isLoading = false,
onRowClick,
}: DataTableProps<TData, TValue>) {
const { t } = useTranslation();
// eslint-disable-next-line react-hooks/incompatible-library -- TanStack Table's API is designed this way
const table = useReactTable({
data,
columns,
pageCount,
rowCount,
state: {
pagination,
},
onPaginationChange: onPaginationChange
? (updater) => {
const newPagination = typeof updater === "function" ? updater(pagination!) : updater;
onPaginationChange(newPagination);
}
: undefined,
getCoreRowModel: getCoreRowModel(),
manualPagination: true,
});
return (
<div className="space-y-4">
<div className="rounded-md border">
<Table>
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(header.column.columnDef.header, header.getContext())}
</TableHead>
))}
</TableRow>
))}
</TableHeader>
<TableBody>
{(() => {
if (isLoading) {
return (
<TableRow>
<TableCell colSpan={columns.length} className="h-24 text-center">
{t("common:loadingEllipsis")}
</TableCell>
</TableRow>
);
}
if (table.getRowModel().rows?.length) {
return table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
data-state={row.getIsSelected() && "selected"}
className={onRowClick ? "cursor-pointer hover:bg-muted/50" : undefined}
onClick={() => onRowClick?.(row.original)}
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</TableCell>
))}
</TableRow>
));
}
return (
<TableRow>
<TableCell colSpan={columns.length} className="h-24 text-center">
{t("common:noResults")}
</TableCell>
</TableRow>
);
})()}
</TableBody>
</Table>
</div>
{pagination && onPaginationChange && <DataTablePagination table={table} />}
</div>
);
}