diff --git a/apps/cs/src/pages/menu/model/menu-interface.ts b/apps/cs/src/pages/menu/model/menu-interface.ts index 2463b511a..966cc7520 100644 --- a/apps/cs/src/pages/menu/model/menu-interface.ts +++ b/apps/cs/src/pages/menu/model/menu-interface.ts @@ -1,5 +1,16 @@ import { z } from "zod"; +export const MENU_TYPE = { + EMPTY: "EMPTY", + CONTENTS: "CONTENTS", + BOARD: "BOARD", + INSIDE: "INSIDE", + OUTSIDE: "OUTSIDE", + BOTTOM: "BOTTOM", +} as const; + +export type MenuType = (typeof MENU_TYPE)[keyof typeof MENU_TYPE]; + export interface Menu { menuCd: number; menuKornNm: string; @@ -19,6 +30,7 @@ export interface Menu { } export const menuFormSchema = z.object({ + siteId: z.string(), menuKornNm: z.string(), menuSubKornNm: z.string().nullish(), menuEngNm: z.string().nullish(), @@ -26,14 +38,21 @@ export const menuFormSchema = z.object({ levelNo: z.number(), urlPath: z.string().nullish(), iconNm: z.string().nullish(), - menuLnkgTypeCd: z.string().nullish(), + menuLnkgTypeCd: z.enum([ + MENU_TYPE.EMPTY, + MENU_TYPE.CONTENTS, + MENU_TYPE.BOARD, + MENU_TYPE.INSIDE, + MENU_TYPE.OUTSIDE, + MENU_TYPE.BOTTOM, + ]), menuLnkgSn: z.number().nullish(), - otptYn: z.string().nullish(), + otptYn: z.enum(["Y", "N"]).default("Y"), prntMenuCd: z.number().nullish(), sortSeq: z.number(), - evlUseYn: z.string().nullish(), + evlUseYn: z.enum(["Y", "N"]).nullish(), qrcd: z.string().nullish(), - useYn: z.string().nullish(), + useYn: z.enum(["Y", "N"]).default("Y"), }); export type MenuFormData = z.infer; diff --git a/apps/cs/src/pages/menu/ui/base-menu-form.tsx b/apps/cs/src/pages/menu/ui/base-menu-form.tsx new file mode 100644 index 000000000..98dfe4e92 --- /dev/null +++ b/apps/cs/src/pages/menu/ui/base-menu-form.tsx @@ -0,0 +1,121 @@ +import { type UseFormReturn } from "react-hook-form"; +import { + Input, + Label, + RadioGroup, + RadioGroupItem, + Table, + TableBody, + TableCell, + TableHead, + TableRow, +} from "@common/components"; +import { + FormItem, + FormControl, + FormField, + FormMessage, +} from "@/shared/ui/form"; +import { MENU_TYPE, type MenuFormData } from "../model/menu-interface"; + +interface BaseMenuFormProps { + form: UseFormReturn; +} + +export function BaseMenuForm({ form }: BaseMenuFormProps) { + return ( + + + + + + + + 메뉴명 + + ( + + + + + + + )} + /> + + + + 메뉴 영문명 + + ( + + + + + + + )} + /> + + + + 메뉴 유형 + + ( + + + +
+ {Object.entries(MENU_TYPE).map(([key, value]) => ( +
+ + +
+ ))} +
+
+
+ +
+ )} + /> +
+
+
+
+ ); +} diff --git a/apps/cs/src/pages/menu/ui/content-menu-form.tsx b/apps/cs/src/pages/menu/ui/content-menu-form.tsx new file mode 100644 index 000000000..3bea24ca7 --- /dev/null +++ b/apps/cs/src/pages/menu/ui/content-menu-form.tsx @@ -0,0 +1,198 @@ +import { type UseFormReturn } from "react-hook-form"; +import { + Input, + Label, + RadioGroup, + RadioGroupItem, + Table, + TableBody, + TableCell, + TableHead, + TableRow, +} from "@common/components"; +import { + FormItem, + FormControl, + FormField, + FormMessage, +} from "@/shared/ui/form"; +import type { MenuFormData } from "../model/menu-interface"; + +export function ContentMenuForm({ + form, +}: { + form: UseFormReturn; +}) { + return ( + + + + + + + + 콘텐츠 + +
+ {form.watch("menuLnkgSn")} +
+
+
+ + 메뉴 평가 사용 여부 + + ( + + + +
+
+ + +
+
+ + +
+
+
+
+ +
+ )} + /> +
+
+ + 메뉴 서브명 + + ( + + + + + + + )} + /> + + + + 메뉴 아이콘 + +
+ {form.watch("iconNm")} +
+
+
+ + 출력 여부 + + ( + + + +
+
+ + +
+
+ + +
+
+
+
+ +
+ )} + /> +
+
+ + 사용 여부 + + ( + + + +
+
+ + +
+
+ + +
+
+
+
+ +
+ )} + /> +
+
+ + QR코드 + + ( + + + +
+
+ + +
+
+ + +
+
+
+
+ +
+ )} + /> +
+
+
+
+ ); +} diff --git a/apps/cs/src/pages/menu/ui/empty-menu-form.tsx b/apps/cs/src/pages/menu/ui/empty-menu-form.tsx new file mode 100644 index 000000000..92794aa10 --- /dev/null +++ b/apps/cs/src/pages/menu/ui/empty-menu-form.tsx @@ -0,0 +1,118 @@ +import { type UseFormReturn } from "react-hook-form"; +import { + Input, + Label, + RadioGroup, + RadioGroupItem, + Table, + TableBody, + TableCell, + TableHead, + TableRow, +} from "@common/components"; +import { + FormItem, + FormControl, + FormField, + FormMessage, +} from "@/shared/ui/form"; +import type { MenuFormData } from "../model/menu-interface"; + +export function EmptyMenuForm({ form }: { form: UseFormReturn }) { + return ( + + + + + + + + 메뉴 서브명 + + ( + + + + + + + )} + /> + + + + 메뉴 아이콘 + +
+ {form.watch("iconNm")} +
+
+
+ + 출력 여부 + + ( + + + +
+
+ + +
+
+ + +
+
+
+
+ +
+ )} + /> +
+
+ + 사용 여부 + + ( + + + +
+
+ + +
+
+ + +
+
+
+
+ +
+ )} + /> +
+
+
+
+ ); +} diff --git a/apps/cs/src/pages/menu/ui/menu-form.tsx b/apps/cs/src/pages/menu/ui/menu-form.tsx new file mode 100644 index 000000000..a3daef358 --- /dev/null +++ b/apps/cs/src/pages/menu/ui/menu-form.tsx @@ -0,0 +1,99 @@ +import { useState } from "react"; +import { useConfirm } from "@frontend-opensource/use-react-hooks"; +import { type UseFormReturn } from "react-hook-form"; +import { Button } from "@common/components"; +import { Form } from "@/shared/ui/form"; +import { ConfirmDialog } from "@/shared/ui/confirm-dialog"; +import { MENU_TYPE, type MenuFormData } from "../model/menu-interface"; +import { BaseMenuForm } from "./base-menu-form"; +import { EmptyMenuForm } from "./empty-menu-form"; +import { ContentMenuForm } from "./content-menu-form"; + +interface MenuFormProps { + form: UseFormReturn; + menuId?: string; + handleSave: (data: MenuFormData) => Promise; + handleDelete?: () => Promise; +} + +export function MenuForm({ + form, + menuId, + handleSave, + handleDelete, +}: MenuFormProps) { + const isModifyMode = !!menuId; + const { confirm } = useConfirm(); + const [onConfirm, setOnConfirm] = useState(false); + + const menuType = form.watch("menuLnkgTypeCd"); + + const onSubmit = async (data: MenuFormData) => { + const confirmMessage = isModifyMode + ? "수정하시겠습니까?" + : "저장하시겠습니까?"; + + setOnConfirm(true); + if (await confirm(confirmMessage)) { + setOnConfirm(false); + handleSave(data); + } + setOnConfirm(false); + }; + + const onDelete = async () => { + setOnConfirm(true); + if (await confirm("삭제하시겠습니까?")) { + setOnConfirm(false); + handleDelete?.(); + } + setOnConfirm(false); + }; + + return ( + <> +
+ +
+
+

+ 메뉴 등록/수정 +

+
+
+ +
+ {renderMenuTypeComponent(menuType, form)} +
+
+
+
+ + {isModifyMode && ( + + )} +
+
+ + + + ); +} + +function renderMenuTypeComponent( + menuType: string, + form: UseFormReturn, +) { + switch (menuType) { + case MENU_TYPE.EMPTY: + return ; + case MENU_TYPE.CONTENTS: + return ; + default: + return null; + } +} diff --git a/apps/cs/src/pages/menu/ui/menu-page.tsx b/apps/cs/src/pages/menu/ui/menu-page.tsx index f7236616c..88bf8ecee 100644 --- a/apps/cs/src/pages/menu/ui/menu-page.tsx +++ b/apps/cs/src/pages/menu/ui/menu-page.tsx @@ -1,7 +1,9 @@ "use client"; import { useState } from "react"; +import { useForm } from "react-hook-form"; import { useQuery } from "@tanstack/react-query"; +import { zodResolver } from "@hookform/resolvers/zod"; import { Button, Select, @@ -10,11 +12,13 @@ import { SelectTrigger, SelectValue, } from "@common/components/ui"; -import { PageLayout } from "@/shared/ui/page-layout"; -import { ENDPOINT } from "@/shared/config"; import { SiteApi } from "@/entities/site"; +import { ENDPOINT } from "@/shared/config"; +import { PageLayout } from "@/shared/ui/page-layout"; import { MenuApi } from "../api/menu-service"; +import { menuFormSchema, type MenuFormData } from "../model/menu-interface"; import { MenuTree } from "./menu-tree"; +import { MenuForm } from "./menu-form"; function MenuPage() { const [siteId, setSiteId] = useState(""); @@ -22,15 +26,14 @@ function MenuPage() { const { data: treeData } = useQuery({ queryKey: [ENDPOINT.CMS_SERVICE.MENUS, siteId], queryFn: () => MenuApi.getMenuList(siteId), - select: (data) => { - return data.map((menu) => ({ + select: (data) => + data.map((menu) => ({ id: menu.menuCd, parent: menu.parentId ?? 0, text: menu.menuKornNm, droppable: true, data: menu, - })); - }, + })), enabled: !!siteId, }); @@ -44,9 +47,19 @@ function MenuPage() { })), }); + const form = useForm({ + resolver: zodResolver(menuFormSchema), + }); + + const handleSave = async (data: MenuFormData) => { + console.log(data); + }; + + const handleDelete = async () => {}; + return ( -
+