From 04ed774ff60a83c00711d4e8615cb4122954dba5 Mon Sep 17 00:00:00 2001
From: joonhoekim <26rote@gmail.com>
Date: Thu, 4 Dec 2025 19:46:55 +0900
Subject: (김준회) 메뉴 관리기능 초안 개발 (시딩 필요)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 lib/menu-v2/components/add-node-dialog.tsx | 186 +++++++++++++++++++++++++++++
 1 file changed, 186 insertions(+)
 create mode 100644 lib/menu-v2/components/add-node-dialog.tsx

(limited to 'lib/menu-v2/components/add-node-dialog.tsx')

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>
+  );
+}
-- 
cgit v1.2.3

