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/edit-node-dialog.tsx | 215 ++++++++++++++++++++++++++++
 1 file changed, 215 insertions(+)
 create mode 100644 lib/menu-v2/components/edit-node-dialog.tsx

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

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

