f09e938673
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.
115 lines
3.5 KiB
TypeScript
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>
|
|
);
|
|
}
|