This commit is contained in:
1 parent
8efd032cc6
commit
df38dccbf1
735 files changed
+128321
-120870
No files matched your search
@@ -11,157 +11,186 @@ import { useServerAction } from "@/hooks/use-server-action";
|
||||
import type { DataTableColumn, PaginatedResult } from "@/types";
|
||||
|
||||
interface CatalogPageRow {
|
||||
id: number;
|
||||
caption: string;
|
||||
parentId: number;
|
||||
parentCaption: string;
|
||||
pageLayout: string;
|
||||
enabled: string;
|
||||
visible: string;
|
||||
minRank: number;
|
||||
orderNum: number;
|
||||
itemCount: number;
|
||||
childCount: number;
|
||||
id: number;
|
||||
caption: string;
|
||||
parentId: number;
|
||||
parentCaption: string;
|
||||
pageLayout: string;
|
||||
enabled: string;
|
||||
visible: string;
|
||||
minRank: number;
|
||||
orderNum: number;
|
||||
itemCount: number;
|
||||
childCount: number;
|
||||
}
|
||||
|
||||
interface CatalogTableProps {
|
||||
data: PaginatedResult<CatalogPageRow>;
|
||||
canEdit: boolean;
|
||||
catalogType?: "normal" | "bc";
|
||||
data: PaginatedResult<CatalogPageRow>;
|
||||
canEdit: boolean;
|
||||
catalogType?: "normal" | "bc";
|
||||
}
|
||||
|
||||
export function CatalogTable({ data, canEdit, catalogType = "normal" }: CatalogTableProps) {
|
||||
const { run, isPending } = useServerAction();
|
||||
const isBc = catalogType === "bc";
|
||||
export function CatalogTable({
|
||||
data,
|
||||
canEdit,
|
||||
catalogType = "normal",
|
||||
}: CatalogTableProps) {
|
||||
const { run, isPending } = useServerAction();
|
||||
const isBc = catalogType === "bc";
|
||||
|
||||
// Parent URLs stay in the same catalog; root takes you to the catalog index.
|
||||
const detailHref = (id: number) => (isBc ? `/admin/catalog/builder-club/${id}` : `/admin/catalog/${id}`);
|
||||
// Parent URLs stay in the same catalog; root takes you to the catalog index.
|
||||
const detailHref = (id: number) =>
|
||||
isBc ? `/admin/catalog/builder-club/${id}` : `/admin/catalog/${id}`;
|
||||
|
||||
function handleToggle(id: number, action: "toggleEnabled" | "toggleVisible") {
|
||||
if (isBc) {
|
||||
const field = action === "toggleEnabled" ? "enabled" : "visible";
|
||||
run(() => toggleBcPage({ id, field }), { successMessage: "Updated." });
|
||||
} else {
|
||||
run(() => toggleCatalogPage({ id, action }), { successMessage: "Updated." });
|
||||
}
|
||||
}
|
||||
function handleToggle(id: number, action: "toggleEnabled" | "toggleVisible") {
|
||||
if (isBc) {
|
||||
const field = action === "toggleEnabled" ? "enabled" : "visible";
|
||||
run(() => toggleBcPage({ id, field }), { successMessage: "Updated." });
|
||||
} else {
|
||||
run(() => toggleCatalogPage({ id, action }), {
|
||||
successMessage: "Updated.",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const columns: DataTableColumn<CatalogPageRow>[] = [
|
||||
{ key: "id", label: "ID", sortable: true },
|
||||
{
|
||||
key: "caption",
|
||||
label: "Caption",
|
||||
sortable: true,
|
||||
render: (value, row) => (
|
||||
<Link href={detailHref(row.id)} className="font-medium text-primary hover:underline">
|
||||
{String(value)}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "parentId",
|
||||
label: "Parent",
|
||||
sortable: true,
|
||||
render: (_, row) =>
|
||||
row.parentId === -1 ? (
|
||||
<span className="text-xs text-muted-foreground">Root</span>
|
||||
) : (
|
||||
<Link href={detailHref(row.parentId)} className="text-xs hover:underline">
|
||||
{row.parentCaption}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "pageLayout",
|
||||
label: "Layout",
|
||||
render: (value) => <Badge variant="secondary">{String(value)}</Badge>,
|
||||
},
|
||||
{
|
||||
key: "enabled",
|
||||
label: "Enabled",
|
||||
sortable: true,
|
||||
filterKey: "filter_enabled",
|
||||
filterOptions: [
|
||||
{ label: "Enabled", value: "1" },
|
||||
{ label: "Disabled", value: "0" },
|
||||
],
|
||||
render: (value, row) =>
|
||||
canEdit ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleToggle(row.id, "toggleEnabled")}
|
||||
disabled={isPending}
|
||||
className="cursor-pointer disabled:opacity-50"
|
||||
>
|
||||
<Badge variant={value === "1" ? "default" : "outline"}>{value === "1" ? "Yes" : "No"}</Badge>
|
||||
</button>
|
||||
) : (
|
||||
<Badge variant={value === "1" ? "default" : "outline"}>{value === "1" ? "Yes" : "No"}</Badge>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "visible",
|
||||
label: "Visible",
|
||||
sortable: true,
|
||||
filterKey: "filter_visible",
|
||||
filterOptions: [
|
||||
{ label: "Visible", value: "1" },
|
||||
{ label: "Hidden", value: "0" },
|
||||
],
|
||||
render: (value, row) =>
|
||||
canEdit ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleToggle(row.id, "toggleVisible")}
|
||||
disabled={isPending}
|
||||
className="cursor-pointer disabled:opacity-50"
|
||||
>
|
||||
<Badge variant={value === "1" ? "default" : "outline"}>{value === "1" ? "Yes" : "No"}</Badge>
|
||||
</button>
|
||||
) : (
|
||||
<Badge variant={value === "1" ? "default" : "outline"}>{value === "1" ? "Yes" : "No"}</Badge>
|
||||
),
|
||||
},
|
||||
...(isBc
|
||||
? []
|
||||
: [
|
||||
{
|
||||
key: "minRank" as const,
|
||||
label: "Min Rank",
|
||||
sortable: true,
|
||||
} satisfies DataTableColumn<CatalogPageRow>,
|
||||
]),
|
||||
{
|
||||
key: "itemCount",
|
||||
label: "Items",
|
||||
render: (value) => <span>{String(value)}</span>,
|
||||
},
|
||||
{
|
||||
key: "childCount",
|
||||
label: "Children",
|
||||
render: (value) => <span>{String(value)}</span>,
|
||||
},
|
||||
{
|
||||
key: "actions",
|
||||
label: "Actions",
|
||||
render: (_, row) => (
|
||||
<Button variant="ghost" size="icon" asChild>
|
||||
<Link href={detailHref(row.id)}>
|
||||
<Pencil className="h-4 w-4" />
|
||||
</Link>
|
||||
</Button>
|
||||
),
|
||||
},
|
||||
];
|
||||
const columns: DataTableColumn<CatalogPageRow>[] = [
|
||||
{ key: "id", label: "ID", sortable: true },
|
||||
{
|
||||
key: "caption",
|
||||
label: "Caption",
|
||||
sortable: true,
|
||||
render: (value, row) => (
|
||||
<Link
|
||||
href={detailHref(row.id)}
|
||||
className="font-medium text-primary hover:underline"
|
||||
>
|
||||
{String(value)}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "parentId",
|
||||
label: "Parent",
|
||||
sortable: true,
|
||||
render: (_, row) =>
|
||||
row.parentId === -1 ? (
|
||||
<span className="text-xs text-muted-foreground">Root</span>
|
||||
) : (
|
||||
<Link
|
||||
href={detailHref(row.parentId)}
|
||||
className="text-xs hover:underline"
|
||||
>
|
||||
{row.parentCaption}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "pageLayout",
|
||||
label: "Layout",
|
||||
render: (value) => <Badge variant="secondary">{String(value)}</Badge>,
|
||||
},
|
||||
{
|
||||
key: "enabled",
|
||||
label: "Enabled",
|
||||
sortable: true,
|
||||
filterKey: "filter_enabled",
|
||||
filterOptions: [
|
||||
{ label: "Enabled", value: "1" },
|
||||
{ label: "Disabled", value: "0" },
|
||||
],
|
||||
render: (value, row) =>
|
||||
canEdit ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleToggle(row.id, "toggleEnabled")}
|
||||
disabled={isPending}
|
||||
className="cursor-pointer disabled:opacity-50"
|
||||
>
|
||||
<Badge variant={value === "1" ? "default" : "outline"}>
|
||||
{value === "1" ? "Yes" : "No"}
|
||||
</Badge>
|
||||
</button>
|
||||
) : (
|
||||
<Badge variant={value === "1" ? "default" : "outline"}>
|
||||
{value === "1" ? "Yes" : "No"}
|
||||
</Badge>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "visible",
|
||||
label: "Visible",
|
||||
sortable: true,
|
||||
filterKey: "filter_visible",
|
||||
filterOptions: [
|
||||
{ label: "Visible", value: "1" },
|
||||
{ label: "Hidden", value: "0" },
|
||||
],
|
||||
render: (value, row) =>
|
||||
canEdit ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleToggle(row.id, "toggleVisible")}
|
||||
disabled={isPending}
|
||||
className="cursor-pointer disabled:opacity-50"
|
||||
>
|
||||
<Badge variant={value === "1" ? "default" : "outline"}>
|
||||
{value === "1" ? "Yes" : "No"}
|
||||
</Badge>
|
||||
</button>
|
||||
) : (
|
||||
<Badge variant={value === "1" ? "default" : "outline"}>
|
||||
{value === "1" ? "Yes" : "No"}
|
||||
</Badge>
|
||||
),
|
||||
},
|
||||
...(isBc
|
||||
? []
|
||||
: [
|
||||
{
|
||||
key: "minRank" as const,
|
||||
label: "Min Rank",
|
||||
sortable: true,
|
||||
} satisfies DataTableColumn<CatalogPageRow>,
|
||||
]),
|
||||
{
|
||||
key: "itemCount",
|
||||
label: "Items",
|
||||
render: (value) => <span>{String(value)}</span>,
|
||||
},
|
||||
{
|
||||
key: "childCount",
|
||||
label: "Children",
|
||||
render: (value) => <span>{String(value)}</span>,
|
||||
},
|
||||
{
|
||||
key: "actions",
|
||||
label: "Actions",
|
||||
render: (_, row) => (
|
||||
<Button variant="ghost" size="icon" asChild>
|
||||
<Link href={detailHref(row.id)}>
|
||||
<Pencil className="h-4 w-4" />
|
||||
</Link>
|
||||
</Button>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<DataTable
|
||||
data={data}
|
||||
columns={columns}
|
||||
searchPlaceholder="Search by caption, layout or item..."
|
||||
bulkActions={
|
||||
canEdit ? [{ label: "Delete Selected", value: "delete", variant: "destructive" }] : undefined
|
||||
}
|
||||
/>
|
||||
);
|
||||
return (
|
||||
<DataTable
|
||||
data={data}
|
||||
columns={columns}
|
||||
searchPlaceholder="Search by caption, layout or item..."
|
||||
bulkActions={
|
||||
canEdit
|
||||
? [
|
||||
{
|
||||
label: "Delete Selected",
|
||||
value: "delete",
|
||||
variant: "destructive",
|
||||
},
|
||||
]
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user