import { useState, type ReactNode } from "react"; import { Hash, Loader2 } from "lucide-react"; import { Dialog, DialogClose, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Button } from "@/components/ui/button"; import { Textarea } from "@/components/ui/textarea"; import type { CreateDropdownSettingDto, DropdownSetting, UpdateDropdownSettingDto, } from "@/types/dropdownSettings"; import { useCreateDropdownSetting, useUpdateDropdownSetting, } from "@/hooks/useDropdownSettings"; export interface EditDropdownSettingDialogProps { mode?: "create" | "edit"; setting?: DropdownSetting; /** Optional trigger element. When omitted, the dialog renders content only and is fully controlled. */ children?: ReactNode; /** Controlled open state. When provided, internal state is ignored. */ open?: boolean; onOpenChange?: (open: boolean) => void; } function parsePermissions(raw: string): string[] { return raw .split(",") .map((s) => s.trim()) .filter(Boolean); } export default function EditDropdownSettingDialog({ mode = "create", setting, children, open: openProp, onOpenChange, }: EditDropdownSettingDialogProps) { const isEdit = mode === "edit"; const isControlled = openProp !== undefined; const [internalOpen, setInternalOpen] = useState(false); const open = isControlled ? openProp : internalOpen; const setOpen = (next: boolean) => { if (!isControlled) setInternalOpen(next); onOpenChange?.(next); }; const [code, setCode] = useState(setting?.code ?? ""); const [label, setLabel] = useState(setting?.label ?? ""); const [description, setDescription] = useState(setting?.description ?? ""); const [icon, setIcon] = useState(setting?.meta?.icon ?? ""); const [color, setColor] = useState(setting?.meta?.color ?? ""); const [permissions, setPermissions] = useState( setting?.meta?.permissions?.join(", ") ?? "", ); const [version, setVersion] = useState(setting?.meta?.version ?? "1.0"); const [multiple, setMultiple] = useState(setting?.multiple ?? false); const [searchable, setSearchable] = useState( setting?.meta?.searchable ?? false, ); const [clearable, setClearable] = useState( setting?.meta?.clearable ?? false, ); const [error, setError] = useState(null); const createMutation = useCreateDropdownSetting(); const updateMutation = useUpdateDropdownSetting(); const pending = createMutation.isPending || updateMutation.isPending; const reset = () => { setCode(setting?.code ?? ""); setLabel(setting?.label ?? ""); setDescription(setting?.description ?? ""); setIcon(setting?.meta?.icon ?? ""); setColor(setting?.meta?.color ?? ""); setPermissions(setting?.meta?.permissions?.join(", ") ?? ""); setVersion(setting?.meta?.version ?? "1.0"); setMultiple(setting?.multiple ?? false); setSearchable(setting?.meta?.searchable ?? false); setClearable(setting?.meta?.clearable ?? false); setError(null); }; const buildPayload = (): CreateDropdownSettingDto => ({ code: code.trim(), label: label.trim(), description: description.trim() || undefined, multiple, meta: { ...(icon.trim() ? { icon: icon.trim() } : {}), ...(color.trim() ? { color: color.trim() } : {}), searchable, clearable, ...(version.trim() ? { version: version.trim() } : {}), permissions: parsePermissions(permissions), }, }); const handleSubmit = () => { setError(null); if (!code.trim() || !label.trim()) { setError("Code and label are required."); return; } if (!/^[a-z][a-z0-9_]*$/i.test(code.trim())) { setError( "Code must start with a letter and contain only letters, digits, or underscores.", ); return; } const payload = buildPayload(); const onDone = () => { setOpen(false); if (!isEdit) reset(); }; const onError = (err: unknown) => { setError( err instanceof Error ? err.message : "Something went wrong. Try again.", ); }; if (isEdit && setting) { // Update DTO omits `code` (immutable); strip it before sending. const { code: _unused, ...updateDto } = payload; void _unused; updateMutation.mutate( { id: setting.id, dto: updateDto as UpdateDropdownSettingDto }, { onSuccess: onDone, onError }, ); } else { createMutation.mutate(payload, { onSuccess: onDone, onError }); } }; return ( { setOpen(next); if (!next) reset(); }} > {children ? {children} : null} {isEdit ? "Edit Dropdown Setting" : "New Dropdown Setting"} {isEdit ? "Update the metadata for this dropdown setting." : "Define a new dynamic dropdown that admins can manage."}
setCode(e.target.value)} placeholder="e.g. cargo_type" className="pl-10 font-mono" disabled={isEdit} />

{isEdit ? "Code is immutable after creation." : "Stable identifier used in code. Use snake_case."}

setLabel(e.target.value)} placeholder="e.g. Cargo Type" />