- Implemented asset and resource list query state helpers to manage loading, error, and availability states. - Created EntityListStatus component for displaying status messages and retry options based on entity list state. - Added EntityTableSkeleton component for rendering loading placeholders in tables. - Defined EntityListState type and entityListState function for consistent state management across components. - Developed comprehensive UI tests for list states using Playwright, covering various scenarios including loading, error handling, and responsiveness.
96 lines
3.0 KiB
TypeScript
96 lines
3.0 KiB
TypeScript
import { Button } from "@repo/shadcn-ui/components/button";
|
|
import type { Table as TableModel } from "@tanstack/react-table";
|
|
import { useId, useState } from "react";
|
|
|
|
import type { EntityColumnGroup, EntityColumnMetadata } from "../lib/entity-column-configuration";
|
|
import { entityListLabels } from "../lib/entity-list";
|
|
|
|
import { EntityTableColumnsDialog } from "./entity-table-columns-dialog";
|
|
import { SelectWithAction } from "./select-with-action";
|
|
|
|
export function EntityTableConfiguration<T>({
|
|
table,
|
|
locale,
|
|
onSort,
|
|
presentation = "full",
|
|
columnGroups,
|
|
columnMetadata,
|
|
onResetColumns,
|
|
columnsDisabled = false,
|
|
}: {
|
|
table: TableModel<T>;
|
|
locale: string;
|
|
onSort: () => void;
|
|
presentation?: "full" | "picker";
|
|
columnGroups?: readonly EntityColumnGroup[] | undefined;
|
|
columnMetadata?: readonly (EntityColumnMetadata & { id: string })[] | undefined;
|
|
onResetColumns?: (() => void) | undefined;
|
|
columnsDisabled?: boolean | undefined;
|
|
}) {
|
|
const id = useId();
|
|
const labels = entityListLabels(locale);
|
|
const [columnsOpen, setColumnsOpen] = useState(false);
|
|
const sorting = table.getState().sorting;
|
|
const columns = table.getAllLeafColumns().map((column) => ({
|
|
id: column.id,
|
|
label: String(column.columnDef.header),
|
|
value: column.getCanSort(),
|
|
}));
|
|
const setSorting = table.setSorting;
|
|
|
|
return (
|
|
<>
|
|
<div className="flex flex-wrap items-center gap-2">
|
|
<Button
|
|
disabled={columnsDisabled}
|
|
onClick={() => setColumnsOpen(true)}
|
|
size="sm"
|
|
variant="outline"
|
|
>
|
|
{labels.columns}
|
|
</Button>
|
|
<div
|
|
className={
|
|
presentation === "picker" ? "hidden @min-[32rem]/entity-table:block" : "contents"
|
|
}
|
|
>
|
|
<SelectWithAction
|
|
id={`${id}-sort`}
|
|
label={labels.sort}
|
|
onValueChange={(value) => {
|
|
const [column, direction] = value.split(":");
|
|
|
|
if (column && column !== "initial") {
|
|
setSorting([{ id: column, desc: direction === "desc" }]);
|
|
} else {
|
|
table.resetSorting();
|
|
}
|
|
onSort();
|
|
}}
|
|
options={[
|
|
{ value: "initial", label: labels.initial },
|
|
...columns
|
|
.filter((column) => column.value)
|
|
.flatMap((column) => [
|
|
{ value: `${column.id}:asc`, label: `${column.label} · ${labels.ascending}` },
|
|
{ value: `${column.id}:desc`, label: `${column.label} · ${labels.descending}` },
|
|
]),
|
|
]}
|
|
placeholder={labels.sort}
|
|
value={sorting[0] ? `${sorting[0].id}:${sorting[0].desc ? "desc" : "asc"}` : "initial"}
|
|
/>
|
|
</div>
|
|
</div>{" "}
|
|
<EntityTableColumnsDialog
|
|
columnGroups={columnGroups}
|
|
columnMetadata={columnMetadata}
|
|
locale={locale}
|
|
onOpenChange={setColumnsOpen}
|
|
onResetColumns={onResetColumns}
|
|
open={columnsOpen}
|
|
table={table}
|
|
/>
|
|
</>
|
|
);
|
|
}
|