diff options
Diffstat (limited to 'lib/menu-v2/components/edit-node-dialog.tsx')
| -rw-r--r-- | lib/menu-v2/components/edit-node-dialog.tsx | 215 |
1 files changed, 215 insertions, 0 deletions
diff --git a/lib/menu-v2/components/edit-node-dialog.tsx b/lib/menu-v2/components/edit-node-dialog.tsx new file mode 100644 index 00000000..9631a611 --- /dev/null +++ b/lib/menu-v2/components/edit-node-dialog.tsx @@ -0,0 +1,215 @@ +"use client"; + +import { useEffect } from "react"; +import { useForm } from "react-hook-form"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { Textarea } from "@/components/ui/textarea"; +import { Switch } from "@/components/ui/switch"; +import type { MenuTreeNode, UpdateNodeInput } from "../types"; + +interface EditNodeDialogProps { + open: boolean; + onOpenChange: (open: boolean) => void; + node: MenuTreeNode | null; + onSave: (nodeId: number, data: UpdateNodeInput) => Promise<void>; +} + +interface FormData { + titleKo: string; + titleEn: string; + descriptionKo: string; + descriptionEn: string; + scrId: string; + isActive: boolean; +} + +export function EditNodeDialog({ + open, + onOpenChange, + node, + onSave, +}: EditNodeDialogProps) { + const { + register, + handleSubmit, + reset, + setValue, + watch, + formState: { isSubmitting }, + } = useForm<FormData>({ + defaultValues: { + titleKo: "", + titleEn: "", + descriptionKo: "", + descriptionEn: "", + scrId: "", + isActive: true, + }, + }); + + const isActive = watch("isActive"); + + useEffect(() => { + if (node) { + reset({ + titleKo: node.titleKo, + titleEn: node.titleEn || "", + descriptionKo: node.descriptionKo || "", + descriptionEn: node.descriptionEn || "", + scrId: node.scrId || "", + isActive: node.isActive, + }); + } + }, [node, reset]); + + const onSubmit = async (data: FormData) => { + if (!node) return; + + await onSave(node.id, { + titleKo: data.titleKo, + titleEn: data.titleEn || undefined, + descriptionKo: data.descriptionKo || undefined, + descriptionEn: data.descriptionEn || undefined, + scrId: data.scrId || undefined, + isActive: data.isActive, + }); + + onOpenChange(false); + }; + + const getTypeLabel = () => { + switch (node?.nodeType) { + case "menu_group": + return "Menu Group"; + case "group": + return "Group"; + case "menu": + return "Menu"; + case "additional": + return "Additional Menu"; + default: + return "Node"; + } + }; + + const showMenuFields = node?.nodeType === "menu" || node?.nodeType === "additional"; + + return ( + <Dialog open={open} onOpenChange={onOpenChange}> + <DialogContent className="max-w-lg"> + <DialogHeader> + <DialogTitle>Edit {getTypeLabel()}</DialogTitle> + <DialogDescription> + {node?.menuPath && ( + <span className="text-xs text-muted-foreground">{node.menuPath}</span> + )} + </DialogDescription> + </DialogHeader> + + <form onSubmit={handleSubmit(onSubmit)} className="space-y-4"> + <div className="grid gap-4"> + {/* Korean Name */} + <div className="grid gap-2"> + <Label htmlFor="titleKo">Name (Korean) *</Label> + <Input + id="titleKo" + {...register("titleKo", { required: true })} + placeholder="Project List" + /> + </div> + + {/* English Name */} + <div className="grid gap-2"> + <Label htmlFor="titleEn">Name (English)</Label> + <Input + id="titleEn" + {...register("titleEn")} + placeholder="Project List" + /> + </div> + + {/* Korean Description */} + {showMenuFields && ( + <div className="grid gap-2"> + <Label htmlFor="descriptionKo">Description (Korean)</Label> + <Textarea + id="descriptionKo" + {...register("descriptionKo")} + placeholder="Project list from MDG (C)" + rows={2} + /> + </div> + )} + + {/* English Description */} + {showMenuFields && ( + <div className="grid gap-2"> + <Label htmlFor="descriptionEn">Description (English)</Label> + <Textarea + id="descriptionEn" + {...register("descriptionEn")} + placeholder="Project list from MDG (C)" + rows={2} + /> + </div> + )} + + {/* Permission SCR_ID */} + {showMenuFields && ( + <div className="grid gap-2"> + <Label htmlFor="scrId">Permission SCR_ID (EVCP only)</Label> + <Input + id="scrId" + {...register("scrId")} + placeholder="SCR_001" + /> + <p className="text-xs text-muted-foreground"> + Linked with Oracle DB SCR_ID. If empty, auto-matched by URL. + </p> + </div> + )} + + {/* Active Status */} + <div className="flex items-center justify-between"> + <div className="space-y-0.5"> + <Label htmlFor="isActive">Show in Menu</Label> + <p className="text-xs text-muted-foreground"> + When disabled, hidden from the navigation menu. + </p> + </div> + <Switch + id="isActive" + checked={isActive} + onCheckedChange={(checked) => setValue("isActive", checked)} + /> + </div> + </div> + + <DialogFooter> + <Button + type="button" + variant="outline" + onClick={() => onOpenChange(false)} + > + Cancel + </Button> + <Button type="submit" disabled={isSubmitting}> + {isSubmitting ? "Saving..." : "Save"} + </Button> + </DialogFooter> + </form> + </DialogContent> + </Dialog> + ); +} + |
