"use client"; import { Check, ChevronsUpDown, FileText, FolderTree, Loader2, Package, Palette, Save, Settings, Trash2, } from "lucide-react"; import { useEffect, useRef, useState } from "react"; import { deleteCatalogPage, updateCatalogPage } from "@/actions/catalog"; import { CatalogImagePicker } from "@/components/admin/catalog/catalog-image-picker"; import { CatalogIcon, LAYOUT_COLORS, } from "@/components/admin/catalog/catalog-visuals"; import { IconPicker } from "@/components/admin/catalog/icon-picker"; import { ImagePreview } from "@/components/admin/catalog/image-preview"; import { useConfirmDialog } from "@/components/admin/confirm-dialog"; import Link from "@/components/link"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, } from "@/components/ui/command"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Popover, PopoverContent, PopoverTrigger, } from "@/components/ui/popover"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Switch } from "@/components/ui/switch"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Textarea } from "@/components/ui/textarea"; import { useServerAction } from "@/hooks/use-server-action"; import { CATALOG_LAYOUTS, type CatalogLayout, LAYOUT_DESCRIPTIONS, } from "@/lib/catalog-layouts"; import { cn } from "@/lib/utils"; interface CatalogPageData { id: number; caption: string; parentId: number; pageLayout: string; enabled: string; visible: string; minRank: number; clubOnly: string; orderNum: number; iconImage: number; iconColor: number; pageHeadline: string; pageTeaser: string; pageSpecial: string | null; pageText1: string | null; pageText2: string | null; pageTextDetails: string | null; pageTextTeaser: string | null; } interface CatalogPageFormProps { catalogPage: CatalogPageData; childPages: { id: number; caption: string; enabled: string }[]; canEdit: boolean; allPages: { id: number; caption: string }[]; } export function CatalogPageForm({ catalogPage, childPages, canEdit, allPages, }: CatalogPageFormProps) { const { isPending, run } = useServerAction(); const { confirm, dialog: confirmDialog } = useConfirmDialog(); const formRef = useRef(null); const [form, setForm] = useState({ caption: catalogPage.caption, parentId: catalogPage.parentId, pageLayout: catalogPage.pageLayout, enabled: catalogPage.enabled as "0" | "1", visible: catalogPage.visible as "0" | "1", minRank: catalogPage.minRank, clubOnly: catalogPage.clubOnly as "0" | "1", orderNum: catalogPage.orderNum, iconImage: catalogPage.iconImage, iconColor: catalogPage.iconColor, pageHeadline: catalogPage.pageHeadline, pageTeaser: catalogPage.pageTeaser, pageSpecial: catalogPage.pageSpecial || "", pageText1: catalogPage.pageText1 || "", pageText2: catalogPage.pageText2 || "", pageTextDetails: catalogPage.pageTextDetails || "", pageTextTeaser: catalogPage.pageTextTeaser || "", }); // Ctrl+S / Cmd+S to save useEffect(() => { if (!canEdit) return; function onKeyDown(e: KeyboardEvent) { if ((e.metaKey || e.ctrlKey) && e.key === "s") { e.preventDefault(); formRef.current?.requestSubmit(); } } document.addEventListener("keydown", onKeyDown); return () => document.removeEventListener("keydown", onKeyDown); }, [canEdit]); function update( key: K, value: (typeof form)[K], ) { setForm((prev) => ({ ...prev, [key]: value })); } function handleSubmit(e: React.FormEvent) { e.preventDefault(); run(() => updateCatalogPage({ id: catalogPage.id, ...form }), { successMessage: "Page updated and catalog synced.", errorMessage: "Failed to update.", }); } async function handleDelete() { const childCount = childPages.length; const description = childCount > 0 ? `Delete this catalog page? Its ${childCount} child page(s) will be moved to the parent. All items on this page will be deleted.` : "Delete this catalog page and all its items?"; const ok = await confirm({ title: "Delete catalog page", description, confirmLabel: "Delete", }); if (!ok) return; run(() => deleteCatalogPage({ id: catalogPage.id }), { successMessage: "Page deleted.", redirectTo: "/ase/economy/catalog", }); } const layoutColorClass = LAYOUT_COLORS[form.pageLayout] || "bg-muted text-muted-foreground"; return ( <> {confirmDialog}
{/* ── Left column (2/3): tabs ─────────────────────────── */}
General Texts Appearance {/* ── General ──────────────────────────────────── */}
update("caption", e.target.value)} disabled={!canEdit} />
update("parentId", id)} pages={allPages} currentPageId={catalogPage.id} disabled={!canEdit} />
update("pageLayout", v)} disabled={!canEdit} />

{LAYOUT_DESCRIPTIONS[ form.pageLayout as CatalogLayout ] ?? "Custom layout"}

update( "orderNum", parseInt(e.target.value, 10) || 0, ) } disabled={!canEdit} />
update("minRank", parseInt(e.target.value, 10) || 1) } disabled={!canEdit} />
{/* ── Texts ────────────────────────────────────── */}
update("pageHeadline", v)} type="header" label="Headline image" disabled={!canEdit} />
update("pageTeaser", v)} type="teaser" label="Teaser image" disabled={!canEdit} />
update("pageText1", v)} disabled={!canEdit} rows={4} max={5000} /> update("pageText2", v)} disabled={!canEdit} rows={4} max={5000} />
update("pageTextDetails", v)} disabled={!canEdit} rows={3} max={5000} /> update("pageTextTeaser", v)} disabled={!canEdit} rows={3} max={5000} />
update("pageSpecial", v)} disabled={!canEdit} rows={2} max={2048} />
{/* ── Appearance ───────────────────────────────── */}
update("iconImage", id)} disabled={!canEdit} />

Icon shown in the catalog tree navigation.

update( "iconColor", parseInt(e.target.value, 10) || 0, ) } disabled={!canEdit} />

Legacy tint value. Leave at default unless you need to match a specific theme.

{/* Child pages list */} {childPages.length > 0 && ( Child Pages ({childPages.length})
{childPages.map((child) => ( {child.caption} #{child.id} {child.enabled === "1" ? "Enabled" : "Disabled"} ))}
)}
{/* ── Right column (1/3): status sticky ────────────────── */}
Status & Preview {/* Preview */}

{form.caption || "Untitled"}

{form.pageLayout}
{/* Toggles */}
update("enabled", v ? "1" : "0")} disabled={!canEdit} /> update("visible", v ? "1" : "0")} disabled={!canEdit} /> update("clubOnly", v ? "1" : "0")} disabled={!canEdit} />
{/* Stats */}
{childPages.length} sub #{catalogPage.id}
{/* Actions */} {canEdit && (

Press Ctrl+S to save

)}
); } // ── Toggle row ───────────────────────────────────────────────── function ToggleRow({ id, label, description, checked, onChange, disabled, }: { id: string; label: string; description: string; checked: boolean; onChange: (v: boolean) => void; disabled?: boolean; }) { return (

{description}

); } function TextareaField({ id, label, value, onChange, disabled, rows = 3, max, }: { id: string; label: string; value: string; onChange: (v: string) => void; disabled?: boolean; rows?: number; max?: number; }) { return (
{max && ( {value.length}/{max} )}