summaryrefslogtreecommitdiff
path: root/lib/menu-v2/components/add-node-dialog.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'lib/menu-v2/components/add-node-dialog.tsx')
-rw-r--r--lib/menu-v2/components/add-node-dialog.tsx186
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>
+ );
+}