Files
Epicnabbo-Catalogus-Updated…/src/app/admin/catalog/catalog-actions.tsx
T
SimoandCursor 8d1cf86105 feat(catalog): SSR stats in Visual Manager, honest deletes, newPageParent
Pass DB totals into StatsBar, mirror Catalog Structure delete copy, and open New Page with ?newPageParent= for shop and BC.

Co-authored-by: Cursor <[email protected]>
2026-07-18 21:14:55 +02:00

159 lines
4.0 KiB
TypeScript

"use client";
import { Loader2, Plus } from "lucide-react";
import { useRouter } from "next/navigation";
import { useEffect, useState } from "react";
import { toast } from "sonner";
import { createCatalogPage } from "@/actions/catalog";
import { createBcPage } from "@/actions/catalog-bc";
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { useServerAction } from "@/hooks/use-server-action";
import { CATALOG_LAYOUTS } from "@/lib/catalog-layouts";
interface CatalogActionsProps {
catalogType?: "normal" | "bc";
/** Prefill parent and auto-open create dialog (from ?newPageParent=). */
initialParentId?: string | null;
}
export function CatalogActions({
catalogType = "normal",
initialParentId = null,
}: CatalogActionsProps) {
const { run, isPending } = useServerAction();
const router = useRouter();
const [open, setOpen] = useState(false);
const [caption, setCaption] = useState("");
const [parentId, setParentId] = useState("-1");
const [pageLayout, setPageLayout] = useState("default_3x3");
const isBc = catalogType === "bc";
useEffect(() => {
if (initialParentId == null || initialParentId === "") return;
const n = Number.parseInt(initialParentId, 10);
if (!Number.isFinite(n)) return;
setParentId(String(n));
setOpen(true);
if (typeof window === "undefined") return;
const url = new URL(window.location.href);
if (url.searchParams.has("newPageParent")) {
url.searchParams.delete("newPageParent");
const qs = url.searchParams.toString();
router.replace(`${url.pathname}${qs ? `?${qs}` : ""}`, {
scroll: false,
});
}
}, [initialParentId, router]);
function handleCreate() {
if (!caption.trim()) {
toast.error("Caption is required");
return;
}
const input = {
caption: caption.trim(),
parentId: parseInt(parentId, 10),
pageLayout,
};
const fn = isBc
? () => createBcPage(input)
: () => createCatalogPage(input);
run(fn, {
successMessage: `Catalog page "${caption.trim()}" created.`,
onSuccess: (data) => {
setOpen(false);
setCaption("");
setParentId("-1");
if (data.id) {
window.location.href = isBc
? `/admin/catalog/builder-club/${data.id}`
: `/admin/catalog/${data.id}`;
}
},
});
}
return (
<Dialog open={open} onOpenChange={setOpen}>
<Button type="button" onClick={() => setOpen(true)}>
<Plus className="mr-2 h-4 w-4" />
New Page
</Button>
<DialogContent>
<DialogHeader>
<DialogTitle>
{isBc ? "Create Builder Club Page" : "Create Catalog Page"}
</DialogTitle>
</DialogHeader>
<div className="space-y-4">
<div className="space-y-2">
<Label htmlFor="caption">Caption</Label>
<Input
id="caption"
value={caption}
onChange={(e) => setCaption(e.target.value)}
placeholder="Page name"
/>
</div>
<div className="space-y-2">
<Label htmlFor="parentId">Parent ID (-1 = root)</Label>
<Input
id="parentId"
type="number"
value={parentId}
onChange={(e) => setParentId(e.target.value)}
/>
</div>
<div className="space-y-2">
<Label>Layout</Label>
<Select value={pageLayout} onValueChange={setPageLayout}>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
{CATALOG_LAYOUTS.map((layout) => (
<SelectItem key={layout} value={layout}>
{layout}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<Button
onClick={handleCreate}
disabled={isPending}
className="w-full"
>
{isPending ? (
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
) : (
<Plus className="mr-2 h-4 w-4" />
)}
Create Page
</Button>
</div>
</DialogContent>
</Dialog>
);
}