diff options
Diffstat (limited to 'lib/menu-v2/components/add-node-dialog.tsx')
| -rw-r--r-- | lib/menu-v2/components/add-node-dialog.tsx | 186 |
1 files changed, 186 insertions, 0 deletions
diff --git a/lib/menu-v2/components/add-node-dialog.tsx b/lib/menu-v2/components/add-node-dialog.tsx new file mode 100644 index 00000000..b6762820 --- /dev/null +++ b/lib/menu-v2/components/add-node-dialog.tsx @@ -0,0 +1,186 @@ +"use client"; + +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 type { + MenuDomain, + CreateMenuGroupInput, + CreateGroupInput, + CreateTopLevelMenuInput +} from "../types"; + +type DialogType = "menu_group" | "group" | "top_level_menu"; + +interface AddNodeDialogProps { + open: boolean; + onOpenChange: (open: boolean) => void; + type: DialogType; + domain: MenuDomain; + parentId?: number; // group 생성 시 필요 + onSave: (data: CreateMenuGroupInput | CreateGroupInput | CreateTopLevelMenuInput) => Promise<void>; +} + +interface FormData { + titleKo: string; + titleEn: string; + menuPath: string; +} + +export function AddNodeDialog({ + open, + onOpenChange, + type, + domain, + parentId, + onSave, +}: AddNodeDialogProps) { + const { + register, + handleSubmit, + reset, + formState: { isSubmitting, errors }, + } = useForm<FormData>({ + defaultValues: { + titleKo: "", + titleEn: "", + menuPath: "", + }, + }); + + const getTitle = () => { + switch (type) { + case "menu_group": + return "Add Menu Group"; + case "group": + return "Add Group"; + case "top_level_menu": + return "Add Top-Level Menu"; + default: + return "Add"; + } + }; + + const getDescription = () => { + switch (type) { + case "menu_group": + return "A dropdown trigger displayed in the header navigation."; + case "group": + return "Groups menus within a menu group."; + case "top_level_menu": + return "A single link displayed in the header navigation."; + default: + return ""; + } + }; + + const onSubmit = async (data: FormData) => { + let saveData: CreateMenuGroupInput | CreateGroupInput | CreateTopLevelMenuInput; + + if (type === "menu_group") { + saveData = { + titleKo: data.titleKo, + titleEn: data.titleEn || undefined, + }; + } else if (type === "group" && parentId) { + saveData = { + parentId, + titleKo: data.titleKo, + titleEn: data.titleEn || undefined, + }; + } else if (type === "top_level_menu") { + saveData = { + titleKo: data.titleKo, + titleEn: data.titleEn || undefined, + menuPath: data.menuPath, + }; + } else { + return; + } + + await onSave(saveData); + reset(); + onOpenChange(false); + }; + + const handleClose = () => { + reset(); + onOpenChange(false); + }; + + return ( + <Dialog open={open} onOpenChange={handleClose}> + <DialogContent className="max-w-md"> + <DialogHeader> + <DialogTitle>{getTitle()}</DialogTitle> + <DialogDescription>{getDescription()}</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: "Name is required" })} + placeholder="Master Data" + /> + {errors.titleKo && ( + <p className="text-xs text-destructive">{errors.titleKo.message}</p> + )} + </div> + + {/* English Name */} + <div className="grid gap-2"> + <Label htmlFor="titleEn">Name (English)</Label> + <Input + id="titleEn" + {...register("titleEn")} + placeholder="Master Data" + /> + </div> + + {/* Menu Path for Top-Level Menu */} + {type === "top_level_menu" && ( + <div className="grid gap-2"> + <Label htmlFor="menuPath">Menu Path *</Label> + <Input + id="menuPath" + {...register("menuPath", { + required: type === "top_level_menu" ? "Path is required" : false + })} + placeholder={`/${domain}/dashboard`} + /> + {errors.menuPath && ( + <p className="text-xs text-destructive">{errors.menuPath.message}</p> + )} + <p className="text-xs text-muted-foreground"> + e.g., /{domain}/report, /{domain}/faq + </p> + </div> + )} + </div> + + <DialogFooter> + <Button type="button" variant="outline" onClick={handleClose}> + Cancel + </Button> + <Button type="submit" disabled={isSubmitting}> + {isSubmitting ? "Creating..." : "Create"} + </Button> + </DialogFooter> + </form> + </DialogContent> + </Dialog> + ); +} |
