From 5b4e67421433d3958c3cda7b6df8951d27e9dc64 Mon Sep 17 00:00:00 2001 From: bicochan Date: Wed, 26 Mar 2025 11:00:24 +0900 Subject: [PATCH 01/24] =?UTF-8?q?rename:=20base=20bottom=20form=20?= =?UTF-8?q?=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/{base-bottom-menu-form.tsx => base-type-form.tsx} | 6 +----- apps/cs/src/pages/menu/ui/board-menu-form.tsx | 4 ++-- apps/cs/src/pages/menu/ui/bottom-menu-form.tsx | 4 ++-- apps/cs/src/pages/menu/ui/content-menu-form.tsx | 4 ++-- apps/cs/src/pages/menu/ui/empty-menu-form.tsx | 4 ++-- apps/cs/src/pages/menu/ui/inside-menu-form.tsx | 4 ++-- apps/cs/src/pages/menu/ui/menu-form.tsx | 7 ++----- apps/cs/src/pages/menu/ui/outside-menu-form.tsx | 4 ++-- 8 files changed, 15 insertions(+), 22 deletions(-) rename apps/cs/src/pages/menu/ui/{base-bottom-menu-form.tsx => base-type-form.tsx} (97%) diff --git a/apps/cs/src/pages/menu/ui/base-bottom-menu-form.tsx b/apps/cs/src/pages/menu/ui/base-type-form.tsx similarity index 97% rename from apps/cs/src/pages/menu/ui/base-bottom-menu-form.tsx rename to apps/cs/src/pages/menu/ui/base-type-form.tsx index d8afc344d..3d0c57eb7 100644 --- a/apps/cs/src/pages/menu/ui/base-bottom-menu-form.tsx +++ b/apps/cs/src/pages/menu/ui/base-type-form.tsx @@ -17,11 +17,7 @@ import { } from "@/shared/ui/form"; import type { MenuFormData } from "../model/menu-interface"; -export function BaseBottomMenuForm({ - form, -}: { - form: UseFormReturn; -}) { +export function BaseTypeForm({ form }: { form: UseFormReturn }) { const handleIconClick = (icon: string) => { form.setValue("iconNm", icon); }; diff --git a/apps/cs/src/pages/menu/ui/board-menu-form.tsx b/apps/cs/src/pages/menu/ui/board-menu-form.tsx index a63fe24ff..91ebc68d3 100644 --- a/apps/cs/src/pages/menu/ui/board-menu-form.tsx +++ b/apps/cs/src/pages/menu/ui/board-menu-form.tsx @@ -16,7 +16,7 @@ import { FormMessage, } from "@/shared/ui/form"; import type { MenuFormData } from "../model/menu-interface"; -import { BaseBottomMenuForm } from "./base-bottom-menu-form"; +import { BaseTypeForm } from "./base-type-form"; export function BoardMenuForm({ form }: { form: UseFormReturn }) { return ( @@ -97,7 +97,7 @@ export function BoardMenuForm({ form }: { form: UseFormReturn }) { - + QR코드 diff --git a/apps/cs/src/pages/menu/ui/bottom-menu-form.tsx b/apps/cs/src/pages/menu/ui/bottom-menu-form.tsx index 88199147b..5214f3a13 100644 --- a/apps/cs/src/pages/menu/ui/bottom-menu-form.tsx +++ b/apps/cs/src/pages/menu/ui/bottom-menu-form.tsx @@ -1,7 +1,7 @@ import { type UseFormReturn } from "react-hook-form"; import { Table, TableBody } from "@common/components"; import type { MenuFormData } from "../model/menu-interface"; -import { BaseBottomMenuForm } from "./base-bottom-menu-form"; +import { BaseTypeForm } from "./base-type-form"; export function BottomMenuForm({ form, @@ -15,7 +15,7 @@ export function BottomMenuForm({ - + ); diff --git a/apps/cs/src/pages/menu/ui/content-menu-form.tsx b/apps/cs/src/pages/menu/ui/content-menu-form.tsx index 65e806f25..c5abc36bf 100644 --- a/apps/cs/src/pages/menu/ui/content-menu-form.tsx +++ b/apps/cs/src/pages/menu/ui/content-menu-form.tsx @@ -16,7 +16,7 @@ import { FormMessage, } from "@/shared/ui/form"; import type { MenuFormData } from "../model/menu-interface"; -import { BaseBottomMenuForm } from "./base-bottom-menu-form"; +import { BaseTypeForm } from "./base-type-form"; export function ContentMenuForm({ form, @@ -101,7 +101,7 @@ export function ContentMenuForm({ - + 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 index 1cdaaed97..d65ffd602 100644 --- a/apps/cs/src/pages/menu/ui/empty-menu-form.tsx +++ b/apps/cs/src/pages/menu/ui/empty-menu-form.tsx @@ -1,7 +1,7 @@ import { type UseFormReturn } from "react-hook-form"; import { Table, TableBody } from "@common/components"; import type { MenuFormData } from "../model/menu-interface"; -import { BaseBottomMenuForm } from "./base-bottom-menu-form"; +import { BaseTypeForm } from "./base-type-form"; export function EmptyMenuForm({ form }: { form: UseFormReturn }) { return ( @@ -11,7 +11,7 @@ export function EmptyMenuForm({ form }: { form: UseFormReturn }) { - + ); diff --git a/apps/cs/src/pages/menu/ui/inside-menu-form.tsx b/apps/cs/src/pages/menu/ui/inside-menu-form.tsx index 8e63a2615..03325dc7b 100644 --- a/apps/cs/src/pages/menu/ui/inside-menu-form.tsx +++ b/apps/cs/src/pages/menu/ui/inside-menu-form.tsx @@ -1,7 +1,7 @@ import { type UseFormReturn } from "react-hook-form"; import { Table, TableBody } from "@common/components"; import type { MenuFormData } from "../model/menu-interface"; -import { BaseBottomMenuForm } from "./base-bottom-menu-form"; +import { BaseTypeForm } from "./base-type-form"; export function InsideMenuForm({ form, @@ -15,7 +15,7 @@ export function InsideMenuForm({ - + ); diff --git a/apps/cs/src/pages/menu/ui/menu-form.tsx b/apps/cs/src/pages/menu/ui/menu-form.tsx index 894927ba0..5fce6d13d 100644 --- a/apps/cs/src/pages/menu/ui/menu-form.tsx +++ b/apps/cs/src/pages/menu/ui/menu-form.tsx @@ -67,7 +67,7 @@ export function MenuForm({
- {renderMenuTypeComponent(menuType, form)} + {renderTypeForm(menuType, form)}
@@ -88,10 +88,7 @@ export function MenuForm({ ); } -function renderMenuTypeComponent( - menuType: string, - form: UseFormReturn, -) { +function renderTypeForm(menuType: string, form: UseFormReturn) { switch (menuType) { case MENU_TYPE.EMPTY: return ; diff --git a/apps/cs/src/pages/menu/ui/outside-menu-form.tsx b/apps/cs/src/pages/menu/ui/outside-menu-form.tsx index b92977a83..750a00a21 100644 --- a/apps/cs/src/pages/menu/ui/outside-menu-form.tsx +++ b/apps/cs/src/pages/menu/ui/outside-menu-form.tsx @@ -1,7 +1,7 @@ import { type UseFormReturn } from "react-hook-form"; import { Table, TableBody } from "@common/components"; import type { MenuFormData } from "../model/menu-interface"; -import { BaseBottomMenuForm } from "./base-bottom-menu-form"; +import { BaseTypeForm } from "./base-type-form"; export function OutsideMenuForm({ form, @@ -15,7 +15,7 @@ export function OutsideMenuForm({ - + ); -- 2.47.1.windows.1 From d4f7cf140e2e446a7e47a5216c491a387700d16d Mon Sep 17 00:00:00 2001 From: bicochan Date: Wed, 26 Mar 2025 15:07:53 +0900 Subject: [PATCH 02/24] =?UTF-8?q?feat:=20=EB=A9=94=EB=89=B4=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=20=EB=B0=8F=20=EC=88=98=EC=A0=95=20=EC=9E=91=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/cs/src/app/styles/components.css | 80 ++++++++++++++----- apps/cs/src/pages/menu/api/menu-service.ts | 15 +++- .../cs/src/pages/menu/model/menu-interface.ts | 7 +- apps/cs/src/pages/menu/ui/base-menu-form.tsx | 25 +++--- apps/cs/src/pages/menu/ui/base-type-form.tsx | 4 +- apps/cs/src/pages/menu/ui/board-menu-form.tsx | 4 +- .../cs/src/pages/menu/ui/bottom-menu-form.tsx | 8 +- .../src/pages/menu/ui/content-menu-form.tsx | 8 +- apps/cs/src/pages/menu/ui/empty-menu-form.tsx | 4 +- .../cs/src/pages/menu/ui/inside-menu-form.tsx | 8 +- apps/cs/src/pages/menu/ui/menu-form.tsx | 27 ++++--- apps/cs/src/pages/menu/ui/menu-node.tsx | 14 +++- apps/cs/src/pages/menu/ui/menu-page.tsx | 59 ++++++++++++-- apps/cs/src/pages/menu/ui/menu-tree.tsx | 18 +++-- .../src/pages/menu/ui/outside-menu-form.tsx | 8 +- 15 files changed, 202 insertions(+), 87 deletions(-) diff --git a/apps/cs/src/app/styles/components.css b/apps/cs/src/app/styles/components.css index c1eb79210..17cd78834 100644 --- a/apps/cs/src/app/styles/components.css +++ b/apps/cs/src/app/styles/components.css @@ -9,43 +9,84 @@ color: #666; } -.card_width10 {width:calc(10% - 12px);} +.card_width10 { + width: calc(10% - 12px); +} -.card_width20 {width:calc(20% - 12px);} +.card_width20 { + width: calc(20% - 12px); +} -.card_width25 {width:calc(25% - 12px);} +.card_width25 { + width: calc(25% - 12px); +} -.card_width30 {width:calc(30% - 12px);} +.card_width30 { + width: calc(30% - 12px); +} -.card_width33 {width:calc(33.333% - 12px);} +.card_width33 { + width: calc(33.333% - 12px); +} -.card_width40 {width:calc(40% - 12px);} +.card_width40 { + width: calc(40% - 12px); +} -.card_width45 {width:calc(45% - 12px);} +.card_width45 { + width: calc(45% - 12px); +} -.card_width50 {width:calc(50% - 12px);} +.card_width50 { + width: calc(50% - 12px); +} -.card_width60 {width:calc(60% - 12px);} +.card_width60 { + width: calc(60% - 12px); +} -.card_width66 {width:calc(66.666% - 12px);} +.card_width66 { + width: calc(66.666% - 12px); +} -.card_width70 {width:calc(70% - 12px);} +.card_width70 { + width: calc(70% - 12px); +} -.card_width75 {width:calc(75% - 12px);} +.card_width75 { + width: calc(75% - 12px); +} -.card_width80 {width:calc(80% - 12px);} +.card_width80 { + width: calc(80% - 12px); +} -.card_width90 {width:calc(90% - 12px);} +.card_width90 { + width: calc(90% - 12px); +} -.card_width100 {width:calc(100% - 12px);} +.card_width100 { + width: calc(100% - 12px); +} -.card_nopadding {padding:0 !important;} +.card_nopadding { + padding: 0 !important; +} -.card_nomargin {width:100%;margin:0 !important;padding:0 !important;} +.card_nomargin { + width: 100%; + margin: 0 !important; + padding: 0 !important; +} -.topline {border-top: 1px solid #ccc;} +.topline { + border-top: 1px solid #ccc; +} -.bottomline {border-bottom: 1px solid #e6eef3;background: #fff;} +.bottomline { + border-bottom: 1px solid #e6eef3; + background: #fff; +} .card-border { border: 1px solid #dbe2e6; @@ -54,6 +95,7 @@ } .card-header { + margin-top: -0.6rem; margin-left: -0.6rem; margin-right: -0.6rem; display: flex; diff --git a/apps/cs/src/pages/menu/api/menu-service.ts b/apps/cs/src/pages/menu/api/menu-service.ts index 4fe6c845a..4825da594 100644 --- a/apps/cs/src/pages/menu/api/menu-service.ts +++ b/apps/cs/src/pages/menu/api/menu-service.ts @@ -1,8 +1,21 @@ import { client } from "@/shared/api"; import { ENDPOINT } from "@/shared/config"; -import type { Menu, MenuService, TreeMenu } from "../model/menu-interface"; +import type { + Menu, + MenuFormData, + MenuService, + TreeMenu, +} from "../model/menu-interface"; export class MenuApiService implements MenuService { + public async createMenu(data: MenuFormData): Promise { + const response = await client.post( + `${ENDPOINT.CMS_SERVICE.MENUS}`, + data, + ); + return response.data; + } + public async getTreeMenuList(siteId: string): Promise { const response = await client.get( `${ENDPOINT.CMS_SERVICE.MENUS}/${siteId}/tree`, diff --git a/apps/cs/src/pages/menu/model/menu-interface.ts b/apps/cs/src/pages/menu/model/menu-interface.ts index fc22c462c..dfc8de878 100644 --- a/apps/cs/src/pages/menu/model/menu-interface.ts +++ b/apps/cs/src/pages/menu/model/menu-interface.ts @@ -53,17 +53,18 @@ export const menuFormSchema = z.object({ MENU_TYPE.BOTTOM, ]), menuLnkgSn: z.number().nullish(), - otptYn: z.enum(["Y", "N"]).default("Y").nullish(), + otptYn: z.string().default("Y").nullish(), prntMenuCd: z.number().nullish(), sortSeq: z.number(), - evlUseYn: z.enum(["Y", "N"]).nullish(), + evlUseYn: z.string().default("Y").nullish(), qrcd: z.string().nullish(), - useYn: z.enum(["Y", "N"]).default("Y").nullish(), + useYn: z.string().default("Y").nullish(), }); export type MenuFormData = z.infer; export interface MenuService { + createMenu: (data: MenuFormData) => Promise; getTreeMenuList: (siteId: string) => Promise; modifyTreeMenu: (siteId: string, data: TreeMenu[]) => Promise; modifyMenuName: (menuCd: number, name: string) => Promise; diff --git a/apps/cs/src/pages/menu/ui/base-menu-form.tsx b/apps/cs/src/pages/menu/ui/base-menu-form.tsx index 9af6dd6f1..62a602a33 100644 --- a/apps/cs/src/pages/menu/ui/base-menu-form.tsx +++ b/apps/cs/src/pages/menu/ui/base-menu-form.tsx @@ -20,10 +20,10 @@ import { MENU_TYPE, type MenuFormData } from "../model/menu-interface"; interface BaseMenuFormProps { form: UseFormReturn; - menuId?: number; + menuCd?: number; } -export function BaseMenuForm({ form, menuId }: BaseMenuFormProps) { +function BaseMenuForm({ form, menuCd }: BaseMenuFormProps) { return ( @@ -31,14 +31,16 @@ export function BaseMenuForm({ form, menuId }: BaseMenuFormProps) { - - 메뉴ID - -
- {menuId} -
-
-
+ {menuCd && ( + + 메뉴ID + +
+ {menuCd} +
+
+
+ )} 메뉴명 @@ -50,6 +52,7 @@ export function BaseMenuForm({ form, menuId }: BaseMenuFormProps) { @@ -128,3 +131,5 @@ export function BaseMenuForm({ form, menuId }: BaseMenuFormProps) {
); } + +export { BaseMenuForm }; diff --git a/apps/cs/src/pages/menu/ui/base-type-form.tsx b/apps/cs/src/pages/menu/ui/base-type-form.tsx index 3d0c57eb7..107e92b60 100644 --- a/apps/cs/src/pages/menu/ui/base-type-form.tsx +++ b/apps/cs/src/pages/menu/ui/base-type-form.tsx @@ -17,7 +17,7 @@ import { } from "@/shared/ui/form"; import type { MenuFormData } from "../model/menu-interface"; -export function BaseTypeForm({ form }: { form: UseFormReturn }) { +function BaseTypeForm({ form }: { form: UseFormReturn }) { const handleIconClick = (icon: string) => { form.setValue("iconNm", icon); }; @@ -124,3 +124,5 @@ export function BaseTypeForm({ form }: { form: UseFormReturn }) { ); } + +export { BaseTypeForm }; diff --git a/apps/cs/src/pages/menu/ui/board-menu-form.tsx b/apps/cs/src/pages/menu/ui/board-menu-form.tsx index 91ebc68d3..cfd59b563 100644 --- a/apps/cs/src/pages/menu/ui/board-menu-form.tsx +++ b/apps/cs/src/pages/menu/ui/board-menu-form.tsx @@ -18,7 +18,7 @@ import { import type { MenuFormData } from "../model/menu-interface"; import { BaseTypeForm } from "./base-type-form"; -export function BoardMenuForm({ form }: { form: UseFormReturn }) { +function BoardMenuForm({ form }: { form: UseFormReturn }) { return ( @@ -142,3 +142,5 @@ export function BoardMenuForm({ form }: { form: UseFormReturn }) {
); } + +export { BoardMenuForm }; diff --git a/apps/cs/src/pages/menu/ui/bottom-menu-form.tsx b/apps/cs/src/pages/menu/ui/bottom-menu-form.tsx index 5214f3a13..63069aac5 100644 --- a/apps/cs/src/pages/menu/ui/bottom-menu-form.tsx +++ b/apps/cs/src/pages/menu/ui/bottom-menu-form.tsx @@ -3,11 +3,7 @@ import { Table, TableBody } from "@common/components"; import type { MenuFormData } from "../model/menu-interface"; import { BaseTypeForm } from "./base-type-form"; -export function BottomMenuForm({ - form, -}: { - form: UseFormReturn; -}) { +function BottomMenuForm({ form }: { form: UseFormReturn }) { return ( @@ -20,3 +16,5 @@ export function BottomMenuForm({
); } + +export { BottomMenuForm }; diff --git a/apps/cs/src/pages/menu/ui/content-menu-form.tsx b/apps/cs/src/pages/menu/ui/content-menu-form.tsx index c5abc36bf..153eede5b 100644 --- a/apps/cs/src/pages/menu/ui/content-menu-form.tsx +++ b/apps/cs/src/pages/menu/ui/content-menu-form.tsx @@ -18,11 +18,7 @@ import { import type { MenuFormData } from "../model/menu-interface"; import { BaseTypeForm } from "./base-type-form"; -export function ContentMenuForm({ - form, -}: { - form: UseFormReturn; -}) { +function ContentMenuForm({ form }: { form: UseFormReturn }) { return ( @@ -146,3 +142,5 @@ export function ContentMenuForm({
); } + +export { ContentMenuForm }; diff --git a/apps/cs/src/pages/menu/ui/empty-menu-form.tsx b/apps/cs/src/pages/menu/ui/empty-menu-form.tsx index d65ffd602..4c97e1c66 100644 --- a/apps/cs/src/pages/menu/ui/empty-menu-form.tsx +++ b/apps/cs/src/pages/menu/ui/empty-menu-form.tsx @@ -3,7 +3,7 @@ import { Table, TableBody } from "@common/components"; import type { MenuFormData } from "../model/menu-interface"; import { BaseTypeForm } from "./base-type-form"; -export function EmptyMenuForm({ form }: { form: UseFormReturn }) { +function EmptyMenuForm({ form }: { form: UseFormReturn }) { return ( @@ -16,3 +16,5 @@ export function EmptyMenuForm({ form }: { form: UseFormReturn }) {
); } + +export { EmptyMenuForm }; diff --git a/apps/cs/src/pages/menu/ui/inside-menu-form.tsx b/apps/cs/src/pages/menu/ui/inside-menu-form.tsx index 03325dc7b..4b894690c 100644 --- a/apps/cs/src/pages/menu/ui/inside-menu-form.tsx +++ b/apps/cs/src/pages/menu/ui/inside-menu-form.tsx @@ -3,11 +3,7 @@ import { Table, TableBody } from "@common/components"; import type { MenuFormData } from "../model/menu-interface"; import { BaseTypeForm } from "./base-type-form"; -export function InsideMenuForm({ - form, -}: { - form: UseFormReturn; -}) { +function InsideMenuForm({ form }: { form: UseFormReturn }) { return ( @@ -20,3 +16,5 @@ export function InsideMenuForm({
); } + +export { InsideMenuForm }; diff --git a/apps/cs/src/pages/menu/ui/menu-form.tsx b/apps/cs/src/pages/menu/ui/menu-form.tsx index 5fce6d13d..2db4c979a 100644 --- a/apps/cs/src/pages/menu/ui/menu-form.tsx +++ b/apps/cs/src/pages/menu/ui/menu-form.tsx @@ -12,21 +12,18 @@ import { BoardMenuForm } from "./board-menu-form"; import { InsideMenuForm } from "./inside-menu-form"; import { OutsideMenuForm } from "./outside-menu-form"; import { BottomMenuForm } from "./bottom-menu-form"; +import { PermissionsManagementDialog } from "./permissions-management-dialog"; interface MenuFormProps { form: UseFormReturn; - menuId?: number; - handleSave: (data: MenuFormData) => Promise; - handleDelete?: () => Promise; + menuCd?: number; + handleSave: (data: MenuFormData) => void; + handleDelete?: () => void; } -export function MenuForm({ - form, - menuId, - handleSave, - handleDelete, -}: MenuFormProps) { - const isModifyMode = !!menuId; +function MenuForm({ form, menuCd, handleSave, handleDelete }: MenuFormProps) { + const isModifyMode = !!menuCd; + const { confirm } = useConfirm(); const [onConfirm, setOnConfirm] = useState(false); @@ -53,6 +50,7 @@ export function MenuForm({ }; const menuType = form.watch("menuLnkgTypeCd"); + const isSubmitDisabled = !form.watch("siteId"); return ( <> @@ -61,18 +59,19 @@ export function MenuForm({

- 메뉴 등록/수정 + {isModifyMode ? "메뉴 수정" : "메뉴 등록"}

+
- +
{renderTypeForm(menuType, form)}
- {isModifyMode && ( @@ -106,3 +105,5 @@ function renderTypeForm(menuType: string, form: UseFormReturn) { return null; } } + +export { MenuForm }; diff --git a/apps/cs/src/pages/menu/ui/menu-node.tsx b/apps/cs/src/pages/menu/ui/menu-node.tsx index f8a150633..827e480d4 100644 --- a/apps/cs/src/pages/menu/ui/menu-node.tsx +++ b/apps/cs/src/pages/menu/ui/menu-node.tsx @@ -1,10 +1,11 @@ import { useState } from "react"; -import { useMutation } from "@tanstack/react-query"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; import type { NodeModel } from "@minoru/react-dnd-treeview"; -import { cn } from "@common/components/lib"; import { Button, Input } from "@common/components/ui"; -import type { Menu } from "../model/menu-interface"; +import { cn } from "@common/components/lib"; +import { ENDPOINT } from "@/shared/config"; import { MenuApi } from "../api/menu-service"; +import type { Menu } from "../model/menu-interface"; type MenuNodeProps = { node: NodeModel; @@ -26,11 +27,18 @@ function MenuNode({ const { id, text, data } = node; const indent = depth * 10 + 5; + const queryClient = useQueryClient(); + const { mutateAsync: modifyMenuName } = useMutation({ mutationFn: (nodeName: string) => { const encodedName = encodeURIComponent(nodeName); return MenuApi.modifyMenuName(id as number, encodedName); }, + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: [ENDPOINT.CMS_SERVICE.MENUS], + }); + }, }); const [editMode, setEditMode] = useState(false); diff --git a/apps/cs/src/pages/menu/ui/menu-page.tsx b/apps/cs/src/pages/menu/ui/menu-page.tsx index 5cc63b627..f159cbcf2 100644 --- a/apps/cs/src/pages/menu/ui/menu-page.tsx +++ b/apps/cs/src/pages/menu/ui/menu-page.tsx @@ -2,8 +2,9 @@ import { useState } from "react"; import { useForm } from "react-hook-form"; -import { useQuery } from "@tanstack/react-query"; import { zodResolver } from "@hookform/resolvers/zod"; +import type { NodeModel } from "@minoru/react-dnd-treeview"; +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { Button, Select, @@ -15,6 +16,8 @@ import { import { SiteApi } from "@/entities/site"; import { ENDPOINT } from "@/shared/config"; import { PageLayout } from "@/shared/ui/page-layout"; +import { useAlertStore } from "@/shared/lib/use-alert-store"; +import type { Menu, MenuType } from "../model/menu-interface"; import { MenuApi } from "../api/menu-service"; import { MENU_TYPE, @@ -26,12 +29,23 @@ import { MenuForm } from "./menu-form"; import { generateTreeData } from "../lib/utils"; function MenuPage() { + const queryClient = useQueryClient(); + const { setMessage: alert } = useAlertStore((state) => state); + const [siteId, setSiteId] = useState(""); + const [selectedMenu, setSelectedMenu] = useState(null); const { data: treeData } = useQuery({ queryKey: [ENDPOINT.CMS_SERVICE.MENUS, siteId], queryFn: () => MenuApi.getTreeMenuList(siteId), - select: (data) => generateTreeData(data), + select: (data) => { + const treeData = generateTreeData(data); + const defaultSortSeq = + treeData.filter((node) => node.data?.levelNo === 1).length + 1; + form.setValue("sortSeq", defaultSortSeq); + + return treeData; + }, enabled: !!siteId, }); @@ -45,15 +59,40 @@ function MenuPage() { })), }); + const { mutate: createMenu } = useMutation({ + mutationFn: (data: MenuFormData) => MenuApi.createMenu(data), + onSuccess: () => { + alert("저장되었습니다."); + queryClient.invalidateQueries({ + queryKey: [ENDPOINT.CMS_SERVICE.MENUS], + }); + }, + }); + const form = useForm({ resolver: zodResolver(menuFormSchema), defaultValues: { menuLnkgTypeCd: MENU_TYPE.EMPTY, }, + values: { + ...selectedMenu, + siteId, + sortSeq: selectedMenu?.sortSeq ?? 1, + levelNo: selectedMenu?.levelNo ?? 1, + menuKornNm: selectedMenu?.menuKornNm ?? "", + menuLnkgTypeCd: + (selectedMenu?.menuLnkgTypeCd as MenuType) ?? MENU_TYPE.EMPTY, + }, }); - const handleSave = async (data: MenuFormData) => { - console.log(data); + const handleSiteIdChange = (value: string) => { + setSiteId(value); + form.setValue("siteId", value); + }; + + const handleNodeSelect = (node: NodeModel) => { + if (!node.data) return; + setSelectedMenu(node.data); }; const handleDelete = async () => {}; @@ -62,7 +101,7 @@ function MenuPage() {
- @@ -85,7 +124,12 @@ function MenuPage() {
- +
)} @@ -93,7 +137,8 @@ function MenuPage() {
diff --git a/apps/cs/src/pages/menu/ui/menu-tree.tsx b/apps/cs/src/pages/menu/ui/menu-tree.tsx index 629dd1252..02f7ffbba 100644 --- a/apps/cs/src/pages/menu/ui/menu-tree.tsx +++ b/apps/cs/src/pages/menu/ui/menu-tree.tsx @@ -1,6 +1,5 @@ "use client"; -import { useState } from "react"; import { DndProvider } from "react-dnd"; import { useMutation, useQueryClient } from "@tanstack/react-query"; import { @@ -18,9 +17,16 @@ import { MenuNode } from "./menu-node"; type MenuTreeProps = { treeData: NodeModel[]; siteId: string; + selectedMenu: Menu | null; + onSelectNode: (node: NodeModel) => void; }; -function MenuTree({ treeData, siteId }: MenuTreeProps) { +function MenuTree({ + treeData, + siteId, + selectedMenu, + onSelectNode, +}: MenuTreeProps) { const queryClient = useQueryClient(); const { mutate: modifyTreeMenu } = useMutation({ @@ -33,10 +39,6 @@ function MenuTree({ treeData, siteId }: MenuTreeProps) { }, }); - const [selectedNode, setSelectedNode] = useState | null>( - null, - ); - return ( )} onDrop={handleDropNode(modifyTreeMenu)} diff --git a/apps/cs/src/pages/menu/ui/outside-menu-form.tsx b/apps/cs/src/pages/menu/ui/outside-menu-form.tsx index 750a00a21..6649dd979 100644 --- a/apps/cs/src/pages/menu/ui/outside-menu-form.tsx +++ b/apps/cs/src/pages/menu/ui/outside-menu-form.tsx @@ -3,11 +3,7 @@ import { Table, TableBody } from "@common/components"; import type { MenuFormData } from "../model/menu-interface"; import { BaseTypeForm } from "./base-type-form"; -export function OutsideMenuForm({ - form, -}: { - form: UseFormReturn; -}) { +function OutsideMenuForm({ form }: { form: UseFormReturn }) { return ( @@ -20,3 +16,5 @@ export function OutsideMenuForm({
); } + +export { OutsideMenuForm }; -- 2.47.1.windows.1 From f03b07738e0e747e2dc5b915b082fbfac8a17509 Mon Sep 17 00:00:00 2001 From: bicochan Date: Wed, 26 Mar 2025 15:35:15 +0900 Subject: [PATCH 03/24] =?UTF-8?q?feat:=20=EB=A9=94=EB=89=B4=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EC=9E=91=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/cs/src/pages/menu/api/menu-service.ts | 11 ++++ .../cs/src/pages/menu/model/menu-interface.ts | 8 +++ apps/cs/src/pages/menu/ui/menu-form.tsx | 63 ++++++++++++++----- apps/cs/src/pages/menu/ui/menu-page.tsx | 19 +++++- 4 files changed, 83 insertions(+), 18 deletions(-) diff --git a/apps/cs/src/pages/menu/api/menu-service.ts b/apps/cs/src/pages/menu/api/menu-service.ts index 4825da594..6d22dcc58 100644 --- a/apps/cs/src/pages/menu/api/menu-service.ts +++ b/apps/cs/src/pages/menu/api/menu-service.ts @@ -16,6 +16,17 @@ export class MenuApiService implements MenuService { return response.data; } + public async modifyMenu( + menuCd: number, + data: Omit, + ): Promise { + const response = await client.put( + `${ENDPOINT.CMS_SERVICE.MENUS}/${menuCd}`, + data, + ); + return response.data; + } + public async getTreeMenuList(siteId: string): Promise { const response = await client.get( `${ENDPOINT.CMS_SERVICE.MENUS}/${siteId}/tree`, diff --git a/apps/cs/src/pages/menu/model/menu-interface.ts b/apps/cs/src/pages/menu/model/menu-interface.ts index dfc8de878..99c132bd6 100644 --- a/apps/cs/src/pages/menu/model/menu-interface.ts +++ b/apps/cs/src/pages/menu/model/menu-interface.ts @@ -59,12 +59,20 @@ export const menuFormSchema = z.object({ evlUseYn: z.string().default("Y").nullish(), qrcd: z.string().nullish(), useYn: z.string().default("Y").nullish(), + menuLnkgSnNm: z.string().nullish(), + menuPicUserUnqId: z.string().nullish(), + menuPicUserUnqIdNm: z.string().nullish(), + menuPicDsgnYmd: z.string().nullish(), }); export type MenuFormData = z.infer; export interface MenuService { createMenu: (data: MenuFormData) => Promise; + modifyMenu: ( + menuCd: number, + data: Omit, + ) => Promise; getTreeMenuList: (siteId: string) => Promise; modifyTreeMenu: (siteId: string, data: TreeMenu[]) => Promise; modifyMenuName: (menuCd: number, name: string) => Promise; diff --git a/apps/cs/src/pages/menu/ui/menu-form.tsx b/apps/cs/src/pages/menu/ui/menu-form.tsx index 2db4c979a..e85204e17 100644 --- a/apps/cs/src/pages/menu/ui/menu-form.tsx +++ b/apps/cs/src/pages/menu/ui/menu-form.tsx @@ -17,36 +17,65 @@ import { PermissionsManagementDialog } from "./permissions-management-dialog"; interface MenuFormProps { form: UseFormReturn; menuCd?: number; - handleSave: (data: MenuFormData) => void; + handleCreate: (data: MenuFormData) => void; + handleModify: ({ + menuCd, + data, + }: { + menuCd: number; + data: Omit; + }) => void; handleDelete?: () => void; } -function MenuForm({ form, menuCd, handleSave, handleDelete }: MenuFormProps) { +function MenuForm({ + form, + menuCd, + handleCreate, + handleModify, + handleDelete, +}: MenuFormProps) { const isModifyMode = !!menuCd; const { confirm } = useConfirm(); const [onConfirm, setOnConfirm] = useState(false); - const onSubmit = async (data: MenuFormData) => { - const confirmMessage = isModifyMode - ? "수정하시겠습니까?" - : "저장하시겠습니까?"; - + const handleConfirm = async (fn: () => Promise) => { setOnConfirm(true); - if (await confirm(confirmMessage)) { - setOnConfirm(false); - handleSave(data); - } + await fn(); setOnConfirm(false); }; - const onDelete = async () => { - setOnConfirm(true); - if (await confirm("삭제하시겠습니까?")) { - setOnConfirm(false); - handleDelete?.(); + const onSubmit = async (data: MenuFormData) => { + if (isModifyMode) { + onModify(data); + } else { + onCreate(data); } - setOnConfirm(false); + }; + + const onCreate = (data: MenuFormData) => { + handleConfirm(async () => { + if (await confirm("저장하시겠습니까?")) { + handleCreate(data); + } + }); + }; + + const onModify = (data: MenuFormData) => { + handleConfirm(async () => { + if (await confirm("수정하시겠습니까?")) { + handleModify({ menuCd: menuCd!, data }); + } + }); + }; + + const onDelete = () => { + handleConfirm(async () => { + if (await confirm("삭제하시겠습니까?")) { + handleDelete?.(); + } + }); }; const menuType = form.watch("menuLnkgTypeCd"); diff --git a/apps/cs/src/pages/menu/ui/menu-page.tsx b/apps/cs/src/pages/menu/ui/menu-page.tsx index f159cbcf2..edd1910fd 100644 --- a/apps/cs/src/pages/menu/ui/menu-page.tsx +++ b/apps/cs/src/pages/menu/ui/menu-page.tsx @@ -69,6 +69,22 @@ function MenuPage() { }, }); + const { mutate: modifyMenu } = useMutation({ + mutationFn: ({ + menuCd, + data, + }: { + menuCd: number; + data: Omit; + }) => MenuApi.modifyMenu(menuCd, data), + onSuccess: () => { + alert("수정되었습니다."); + queryClient.invalidateQueries({ + queryKey: [ENDPOINT.CMS_SERVICE.MENUS], + }); + }, + }); + const form = useForm({ resolver: zodResolver(menuFormSchema), defaultValues: { @@ -138,7 +154,8 @@ function MenuPage() {
-- 2.47.1.windows.1 From b26e7e3d1043a92970a49573d0bb9af72b584790 Mon Sep 17 00:00:00 2001 From: bicochan Date: Wed, 26 Mar 2025 17:00:35 +0900 Subject: [PATCH 04/24] =?UTF-8?q?feat:=20=EB=A9=94=EB=89=B4=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=20=EB=B0=8F=20=EC=88=98=EC=A0=95=20=EC=9E=91=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/cs/src/pages/menu/api/menu-service.ts | 14 ++++++-- apps/cs/src/pages/menu/lib/utils.ts | 35 ++++++++++--------- .../cs/src/pages/menu/model/menu-interface.ts | 32 +++++++++-------- apps/cs/src/pages/menu/ui/menu-form.tsx | 12 +++---- apps/cs/src/pages/menu/ui/menu-node.tsx | 6 ++-- apps/cs/src/pages/menu/ui/menu-page.tsx | 26 +++++++++----- apps/cs/src/pages/menu/ui/menu-tree.tsx | 12 +++---- 7 files changed, 78 insertions(+), 59 deletions(-) diff --git a/apps/cs/src/pages/menu/api/menu-service.ts b/apps/cs/src/pages/menu/api/menu-service.ts index 6d22dcc58..cdb64b521 100644 --- a/apps/cs/src/pages/menu/api/menu-service.ts +++ b/apps/cs/src/pages/menu/api/menu-service.ts @@ -5,9 +5,17 @@ import type { MenuFormData, MenuService, TreeMenu, + TreeNode, } from "../model/menu-interface"; export class MenuApiService implements MenuService { + public async getMenuInfo(menuCd: number): Promise { + const response = await client.get( + `${ENDPOINT.CMS_SERVICE.MENUS}/${menuCd}`, + ); + return response.data; + } + public async createMenu(data: MenuFormData): Promise { const response = await client.post( `${ENDPOINT.CMS_SERVICE.MENUS}`, @@ -27,14 +35,14 @@ export class MenuApiService implements MenuService { return response.data; } - public async getTreeMenuList(siteId: string): Promise { - const response = await client.get( + public async getTreeMenuList(siteId: string): Promise { + const response = await client.get( `${ENDPOINT.CMS_SERVICE.MENUS}/${siteId}/tree`, ); return response.data; } - public async modifyTreeMenu(siteId: string, data: TreeMenu[]): Promise { + public async modifyTreeMenu(siteId: string, data: TreeNode[]): Promise { const response = await client.put( `${ENDPOINT.CMS_SERVICE.MENUS}/${siteId}/tree`, data, diff --git a/apps/cs/src/pages/menu/lib/utils.ts b/apps/cs/src/pages/menu/lib/utils.ts index 333ebad94..a567cf96f 100644 --- a/apps/cs/src/pages/menu/lib/utils.ts +++ b/apps/cs/src/pages/menu/lib/utils.ts @@ -1,9 +1,9 @@ import type { DropOptions, NodeModel } from "@minoru/react-dnd-treeview"; -import type { Menu, TreeMenu } from "../model/menu-interface"; +import type { TreeMenu, TreeNode } from "../model/menu-interface"; -export const generateTreeData = (data: Menu[]): NodeModel[] => { - return data.reduce[]>((acc, menu) => { - const node: NodeModel = { +export const generateTreeData = (data: TreeMenu[]): NodeModel[] => { + return data.reduce[]>((acc, menu) => { + const node: NodeModel = { id: menu.menuCd ?? 0, parent: menu.parentId ?? 0, text: menu.menuKornNm, @@ -22,9 +22,9 @@ export const generateTreeData = (data: Menu[]): NodeModel[] => { }; export const generateNewTreeData = ( - treeData: NodeModel[], - options: DropOptions, -): TreeMenu | null => { + treeData: NodeModel[], + options: DropOptions, +): TreeNode | null => { const { dragSourceId, dragSource, dropTargetId, dropTarget } = options; if (!dragSource?.data) return null; @@ -38,7 +38,7 @@ export const generateNewTreeData = ( (node) => node.parent === dragSourceId && node.data, ); - const baseNode = (node: NodeModel): TreeMenu => { + const baseNode = (node: NodeModel): TreeNode => { if (!node.data) return {}; return { menuCd: node.data?.menuCd, @@ -47,20 +47,20 @@ export const generateNewTreeData = ( }; }; - const treeMenu: TreeMenu = { + const treeNode: TreeNode = { ...baseNode(dragSource), parentId: dropTargetId === 0 ? null : (dropTargetId as number), sortSeq: newSortSeq, levelNo: (dropTarget?.data?.levelNo ?? 0) + 1, }; - const result: TreeMenu = { - ...treeMenu, + const result: TreeNode = { + ...treeNode, children: childNodes.map((child) => ({ ...baseNode(child), parentId: child.data?.parentId === 0 ? null : (dropTargetId as number), sortSeq: child.data?.sortSeq, - levelNo: (treeMenu?.levelNo ?? 0) + 1, + levelNo: (treeNode?.levelNo ?? 0) + 1, })), }; @@ -68,21 +68,22 @@ export const generateNewTreeData = ( }; export const handleSelectNode = - (callback: (node: NodeModel) => void) => (node: NodeModel) => { + (callback: (node: NodeModel) => void) => + (node: NodeModel) => { callback(node); }; export const handleDropNode = - (callback: (newTree: TreeMenu) => void) => - (newTree: NodeModel[], options: DropOptions) => { + (callback: (newTree: TreeNode) => void) => + (newTree: NodeModel[], options: DropOptions) => { const newTreeData = generateNewTreeData(newTree, options); if (!newTreeData) return; callback(newTreeData); }; export const handleNodeTextChange = - (treeData: NodeModel[]) => - (callback: (newTree: NodeModel[]) => void) => + (treeData: NodeModel[]) => + (callback: (newTree: NodeModel[]) => void) => (id: NodeModel["id"], value: string) => { const newTree = treeData.map((node) => { if (node.id === id) { diff --git a/apps/cs/src/pages/menu/model/menu-interface.ts b/apps/cs/src/pages/menu/model/menu-interface.ts index 99c132bd6..31f319c82 100644 --- a/apps/cs/src/pages/menu/model/menu-interface.ts +++ b/apps/cs/src/pages/menu/model/menu-interface.ts @@ -1,31 +1,31 @@ import { z } from "zod"; export const MENU_TYPE = { - EMPTY: "EMPTY", - CONTENTS: "CONTENTS", - BOARD: "BOARD", - INSIDE: "INSIDE", - OUTSIDE: "OUTSIDE", - BOTTOM: "BOTTOM", + 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 TreeMenu { +export interface TreeNode { menuCd?: number; name?: string; sortSeq?: number; parentId?: number | null; levelNo?: number; icon?: string; - children?: TreeMenu[]; + children?: TreeNode[]; } -export interface Menu extends Omit { +export interface TreeMenu extends Omit { menuKornNm: string; menuEngNm: string; iconNm: string; - children: Menu[]; + children: TreeMenu[]; urlPath: string; menuLnkgTypeCd: string; menuLnkgSn: number; @@ -53,12 +53,12 @@ export const menuFormSchema = z.object({ MENU_TYPE.BOTTOM, ]), menuLnkgSn: z.number().nullish(), - otptYn: z.string().default("Y").nullish(), + otptYn: z.string().default("Y"), prntMenuCd: z.number().nullish(), sortSeq: z.number(), - evlUseYn: z.string().default("Y").nullish(), + evlUseYn: z.string().default("Y"), qrcd: z.string().nullish(), - useYn: z.string().default("Y").nullish(), + useYn: z.string().default("Y"), menuLnkgSnNm: z.string().nullish(), menuPicUserUnqId: z.string().nullish(), menuPicUserUnqIdNm: z.string().nullish(), @@ -66,14 +66,16 @@ export const menuFormSchema = z.object({ }); export type MenuFormData = z.infer; +export type Menu = MenuFormData & { menuCd: number }; export interface MenuService { + getMenuInfo: (menuCd: number) => Promise; createMenu: (data: MenuFormData) => Promise; modifyMenu: ( menuCd: number, data: Omit, ) => Promise; - getTreeMenuList: (siteId: string) => Promise; - modifyTreeMenu: (siteId: string, data: TreeMenu[]) => Promise; + getTreeMenuList: (siteId: string) => Promise; + modifyTreeMenu: (siteId: string, data: TreeNode[]) => Promise; modifyMenuName: (menuCd: number, name: string) => Promise; } diff --git a/apps/cs/src/pages/menu/ui/menu-form.tsx b/apps/cs/src/pages/menu/ui/menu-form.tsx index e85204e17..f42e3d725 100644 --- a/apps/cs/src/pages/menu/ui/menu-form.tsx +++ b/apps/cs/src/pages/menu/ui/menu-form.tsx @@ -48,22 +48,22 @@ function MenuForm({ const onSubmit = async (data: MenuFormData) => { if (isModifyMode) { - onModify(data); + await onModify(data); } else { - onCreate(data); + await onCreate(data); } }; - const onCreate = (data: MenuFormData) => { - handleConfirm(async () => { + const onCreate = async (data: MenuFormData) => { + await handleConfirm(async () => { if (await confirm("저장하시겠습니까?")) { handleCreate(data); } }); }; - const onModify = (data: MenuFormData) => { - handleConfirm(async () => { + const onModify = async (data: MenuFormData) => { + await handleConfirm(async () => { if (await confirm("수정하시겠습니까?")) { handleModify({ menuCd: menuCd!, data }); } diff --git a/apps/cs/src/pages/menu/ui/menu-node.tsx b/apps/cs/src/pages/menu/ui/menu-node.tsx index 827e480d4..d938f0df0 100644 --- a/apps/cs/src/pages/menu/ui/menu-node.tsx +++ b/apps/cs/src/pages/menu/ui/menu-node.tsx @@ -5,15 +5,15 @@ import { Button, Input } from "@common/components/ui"; import { cn } from "@common/components/lib"; import { ENDPOINT } from "@/shared/config"; import { MenuApi } from "../api/menu-service"; -import type { Menu } from "../model/menu-interface"; +import type { TreeMenu } from "../model/menu-interface"; type MenuNodeProps = { - node: NodeModel; + node: NodeModel; depth: number; isOpen: boolean; isSelected: boolean; onToggle: (id: NodeModel["id"]) => void; - onSelect: (node: NodeModel) => void; + onSelect: (node: NodeModel) => void; }; function MenuNode({ diff --git a/apps/cs/src/pages/menu/ui/menu-page.tsx b/apps/cs/src/pages/menu/ui/menu-page.tsx index edd1910fd..57fdec1f1 100644 --- a/apps/cs/src/pages/menu/ui/menu-page.tsx +++ b/apps/cs/src/pages/menu/ui/menu-page.tsx @@ -17,7 +17,7 @@ import { SiteApi } from "@/entities/site"; import { ENDPOINT } from "@/shared/config"; import { PageLayout } from "@/shared/ui/page-layout"; import { useAlertStore } from "@/shared/lib/use-alert-store"; -import type { Menu, MenuType } from "../model/menu-interface"; +import type { TreeMenu } from "../model/menu-interface"; import { MenuApi } from "../api/menu-service"; import { MENU_TYPE, @@ -33,7 +33,13 @@ function MenuPage() { const { setMessage: alert } = useAlertStore((state) => state); const [siteId, setSiteId] = useState(""); - const [selectedMenu, setSelectedMenu] = useState(null); + const [selectedMenu, setSelectedMenu] = useState(null); + + const { data: menuInfo } = useQuery({ + queryKey: [ENDPOINT.CMS_SERVICE.MENUS, selectedMenu?.menuCd], + queryFn: () => MenuApi.getMenuInfo(selectedMenu?.menuCd ?? 0), + enabled: !!selectedMenu?.menuCd, + }); const { data: treeData } = useQuery({ queryKey: [ENDPOINT.CMS_SERVICE.MENUS, siteId], @@ -91,13 +97,15 @@ function MenuPage() { menuLnkgTypeCd: MENU_TYPE.EMPTY, }, values: { - ...selectedMenu, + ...menuInfo, siteId, - sortSeq: selectedMenu?.sortSeq ?? 1, - levelNo: selectedMenu?.levelNo ?? 1, - menuKornNm: selectedMenu?.menuKornNm ?? "", - menuLnkgTypeCd: - (selectedMenu?.menuLnkgTypeCd as MenuType) ?? MENU_TYPE.EMPTY, + sortSeq: menuInfo?.sortSeq ?? 1, + levelNo: menuInfo?.levelNo ?? 1, + menuKornNm: menuInfo?.menuKornNm ?? "", + menuLnkgTypeCd: menuInfo?.menuLnkgTypeCd ?? MENU_TYPE.EMPTY, + otptYn: menuInfo?.otptYn ?? "Y", + evlUseYn: menuInfo?.evlUseYn ?? "Y", + useYn: menuInfo?.useYn ?? "Y", }, }); @@ -106,7 +114,7 @@ function MenuPage() { form.setValue("siteId", value); }; - const handleNodeSelect = (node: NodeModel) => { + const handleNodeSelect = (node: NodeModel) => { if (!node.data) return; setSelectedMenu(node.data); }; diff --git a/apps/cs/src/pages/menu/ui/menu-tree.tsx b/apps/cs/src/pages/menu/ui/menu-tree.tsx index 02f7ffbba..a7d6b0fd3 100644 --- a/apps/cs/src/pages/menu/ui/menu-tree.tsx +++ b/apps/cs/src/pages/menu/ui/menu-tree.tsx @@ -10,15 +10,15 @@ import { } from "@minoru/react-dnd-treeview"; import { ENDPOINT } from "@/shared/config"; import { MenuApi } from "../api/menu-service"; -import type { Menu, TreeMenu } from "../model/menu-interface"; +import type { TreeMenu, TreeNode } from "../model/menu-interface"; import { handleDropNode, handleSelectNode } from "../lib/utils"; import { MenuNode } from "./menu-node"; type MenuTreeProps = { - treeData: NodeModel[]; + treeData: NodeModel[]; siteId: string; - selectedMenu: Menu | null; - onSelectNode: (node: NodeModel) => void; + selectedMenu: TreeMenu | null; + onSelectNode: (node: NodeModel) => void; }; function MenuTree({ @@ -30,7 +30,7 @@ function MenuTree({ const queryClient = useQueryClient(); const { mutate: modifyTreeMenu } = useMutation({ - mutationFn: (newTree: TreeMenu) => + mutationFn: (newTree: TreeNode) => MenuApi.modifyTreeMenu(siteId, [newTree]), onSuccess: () => { queryClient.invalidateQueries({ @@ -44,7 +44,7 @@ function MenuTree({ , { depth, isOpen, onToggle }) => ( + render={(node: NodeModel, { depth, isOpen, onToggle }) => ( Date: Wed, 26 Mar 2025 17:46:05 +0900 Subject: [PATCH 05/24] =?UTF-8?q?refactor:=20=ED=8A=B8=EB=A6=AC=20?= =?UTF-8?q?=EB=A9=94=EB=89=B4=20=EC=88=9C=EC=84=9C=20=EB=B3=80=EA=B2=BD=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/cs/src/pages/menu/lib/utils.ts | 8 +++++++- apps/cs/src/pages/menu/ui/menu-page.tsx | 6 +++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/apps/cs/src/pages/menu/lib/utils.ts b/apps/cs/src/pages/menu/lib/utils.ts index a567cf96f..304c7166a 100644 --- a/apps/cs/src/pages/menu/lib/utils.ts +++ b/apps/cs/src/pages/menu/lib/utils.ts @@ -31,8 +31,14 @@ export const generateNewTreeData = ( const siblings = treeData.filter((node) => node.parent === dropTargetId); + const dragSourceIndex = siblings.findIndex( + (node) => node.id === dragSourceId, + ); + + const prevSibling = siblings[dragSourceIndex - 1]; + const newSortSeq = - siblings.findIndex((sibling) => sibling.id === dragSourceId) + 1; + dragSourceIndex === 0 ? 1 : (prevSibling?.data?.sortSeq ?? 0) + 1; const childNodes = treeData.filter( (node) => node.parent === dragSourceId && node.data, diff --git a/apps/cs/src/pages/menu/ui/menu-page.tsx b/apps/cs/src/pages/menu/ui/menu-page.tsx index 57fdec1f1..3219291ab 100644 --- a/apps/cs/src/pages/menu/ui/menu-page.tsx +++ b/apps/cs/src/pages/menu/ui/menu-page.tsx @@ -116,6 +116,10 @@ function MenuPage() { const handleNodeSelect = (node: NodeModel) => { if (!node.data) return; + if (node.data.menuCd === selectedMenu?.menuCd) { + setSelectedMenu(null); + return; + } setSelectedMenu(node.data); }; @@ -123,7 +127,7 @@ function MenuPage() { return ( -
+
+
+ + +
+ + ); +} + +export { EditableNode }; diff --git a/apps/cs/src/pages/menu/ui/empty-menu-form.tsx b/apps/cs/src/pages/menu/ui/empty-menu-form.tsx index 4c97e1c66..78339d90f 100644 --- a/apps/cs/src/pages/menu/ui/empty-menu-form.tsx +++ b/apps/cs/src/pages/menu/ui/empty-menu-form.tsx @@ -1,7 +1,7 @@ import { type UseFormReturn } from "react-hook-form"; import { Table, TableBody } from "@common/components"; import type { MenuFormData } from "../model/menu-interface"; -import { BaseTypeForm } from "./base-type-form"; +import { BaseTypeMenuForm } from "./base-type-menu-form"; function EmptyMenuForm({ form }: { form: UseFormReturn }) { return ( @@ -11,7 +11,7 @@ function EmptyMenuForm({ form }: { form: UseFormReturn }) { - + ); diff --git a/apps/cs/src/pages/menu/ui/inside-menu-form.tsx b/apps/cs/src/pages/menu/ui/inside-menu-form.tsx index 4b894690c..f2fed53cf 100644 --- a/apps/cs/src/pages/menu/ui/inside-menu-form.tsx +++ b/apps/cs/src/pages/menu/ui/inside-menu-form.tsx @@ -1,7 +1,7 @@ import { type UseFormReturn } from "react-hook-form"; import { Table, TableBody } from "@common/components"; import type { MenuFormData } from "../model/menu-interface"; -import { BaseTypeForm } from "./base-type-form"; +import { BaseTypeMenuForm } from "./base-type-menu-form"; function InsideMenuForm({ form }: { form: UseFormReturn }) { return ( @@ -11,7 +11,7 @@ function InsideMenuForm({ form }: { form: UseFormReturn }) { - + ); diff --git a/apps/cs/src/pages/menu/ui/menu-form.tsx b/apps/cs/src/pages/menu/ui/menu-form.tsx index f42e3d725..13a910c23 100644 --- a/apps/cs/src/pages/menu/ui/menu-form.tsx +++ b/apps/cs/src/pages/menu/ui/menu-form.tsx @@ -95,7 +95,7 @@ function MenuForm({
- {renderTypeForm(menuType, form)} + {renderTypeMenuForm(menuType, form)}
@@ -116,7 +116,10 @@ function MenuForm({ ); } -function renderTypeForm(menuType: string, form: UseFormReturn) { +function renderTypeMenuForm( + menuType: string, + form: UseFormReturn, +) { switch (menuType) { case MENU_TYPE.EMPTY: return ; diff --git a/apps/cs/src/pages/menu/ui/menu-node.tsx b/apps/cs/src/pages/menu/ui/menu-node.tsx index d938f0df0..add24f3af 100644 --- a/apps/cs/src/pages/menu/ui/menu-node.tsx +++ b/apps/cs/src/pages/menu/ui/menu-node.tsx @@ -1,11 +1,12 @@ import { useState } from "react"; import { useMutation, useQueryClient } from "@tanstack/react-query"; import type { NodeModel } from "@minoru/react-dnd-treeview"; -import { Button, Input } from "@common/components/ui"; +import { Button } from "@common/components/ui"; import { cn } from "@common/components/lib"; import { ENDPOINT } from "@/shared/config"; import { MenuApi } from "../api/menu-service"; import type { TreeMenu } from "../model/menu-interface"; +import { EditableNode } from "./editable-node"; type MenuNodeProps = { node: NodeModel; @@ -52,6 +53,11 @@ function MenuNode({ setEditMode(false); }; + const handleCancel = () => { + setEditMode(false); + setNodeName(text); + }; + return (
-
- {editMode ? ( - <> - setNodeName(e.target.value)} - className="h-[2.5rem] !text-[1.3rem] text-foreground" - /> - - ) : ( - <> + {editMode ? ( + setNodeName(e.target.value)} + onSave={handleSave} + onCancel={handleCancel} + /> + ) : ( + <> +
{data?.children?.length && ( )} {text} - - )} -
- {editMode ? ( -
+
- -
- ) : ( - + )}
); diff --git a/apps/cs/src/pages/menu/ui/menu-page.tsx b/apps/cs/src/pages/menu/ui/menu-page.tsx index 3219291ab..8e13fee10 100644 --- a/apps/cs/src/pages/menu/ui/menu-page.tsx +++ b/apps/cs/src/pages/menu/ui/menu-page.tsx @@ -141,7 +141,7 @@ function MenuPage() { ))} - {treeData && ( + {siteId && ( <>
- +
); } diff --git a/apps/cs/src/pages/menu/ui/menu-node.tsx b/apps/cs/src/pages/menu/ui/menu-node.tsx index add24f3af..bce0f8ac2 100644 --- a/apps/cs/src/pages/menu/ui/menu-node.tsx +++ b/apps/cs/src/pages/menu/ui/menu-node.tsx @@ -1,6 +1,6 @@ -import { useState } from "react"; +import { useEffect, useState } from "react"; import { useMutation, useQueryClient } from "@tanstack/react-query"; -import type { NodeModel } from "@minoru/react-dnd-treeview"; +import type { NodeModel, TreeMethods } from "@minoru/react-dnd-treeview"; import { Button } from "@common/components/ui"; import { cn } from "@common/components/lib"; import { ENDPOINT } from "@/shared/config"; @@ -13,6 +13,8 @@ type MenuNodeProps = { depth: number; isOpen: boolean; isSelected: boolean; + isAdditionalParent: boolean; + treeMethods: TreeMethods | null; onToggle: (id: NodeModel["id"]) => void; onSelect: (node: NodeModel) => void; }; @@ -22,6 +24,8 @@ function MenuNode({ depth, isOpen, isSelected, + isAdditionalParent, + treeMethods, onToggle, onSelect, }: MenuNodeProps) { @@ -58,6 +62,10 @@ function MenuNode({ setNodeName(text); }; + useEffect(() => { + if (isAdditionalParent) treeMethods?.open(id); + }, [isAdditionalParent]); + return (
(null); + const [additionalNode, setAdditionalNode] = + useState | null>(null); const { data: menuInfo } = useQuery({ queryKey: [ENDPOINT.CMS_SERVICE.MENUS, selectedMenu?.menuCd], @@ -65,7 +67,7 @@ function MenuPage() { })), }); - const { mutate: createMenu } = useMutation({ + const { mutateAsync: createMenu } = useMutation({ mutationFn: (data: MenuFormData) => MenuApi.createMenu(data), onSuccess: () => { alert("저장되었습니다."); @@ -99,8 +101,8 @@ function MenuPage() { values: { ...menuInfo, siteId, - sortSeq: menuInfo?.sortSeq ?? 1, levelNo: menuInfo?.levelNo ?? 1, + sortSeq: menuInfo?.sortSeq ?? 1, menuKornNm: menuInfo?.menuKornNm ?? "", menuLnkgTypeCd: menuInfo?.menuLnkgTypeCd ?? MENU_TYPE.EMPTY, otptYn: menuInfo?.otptYn ?? "Y", @@ -109,21 +111,64 @@ function MenuPage() { }, }); + const additionalNodeForm = useForm({ + resolver: zodResolver(menuFormSchema), + defaultValues: { + menuLnkgTypeCd: MENU_TYPE.EMPTY, + levelNo: 1, + sortSeq: 1, + otptYn: "Y", + evlUseYn: "Y", + useYn: "Y", + }, + }); + + const handleAdditionalNodeSave = async () => { + await createMenu({ + ...additionalNodeForm.getValues(), + prntMenuCd: additionalNode?.parent as number, + }); + setAdditionalNode(null); + }; + + const handleAdditionalNodeCancel = () => { + additionalNodeForm.setValue("menuKornNm", ""); + setAdditionalNode(null); + }; + + const handleDelete = async () => {}; + const handleSiteIdChange = (value: string) => { - setSiteId(value); form.setValue("siteId", value); + additionalNodeForm.setValue("siteId", value); + + setSiteId(value); + setSelectedMenu(null); }; const handleNodeSelect = (node: NodeModel) => { if (!node.data) return; if (node.data.menuCd === selectedMenu?.menuCd) { setSelectedMenu(null); + additionalNodeForm.setValue("levelNo", 1); return; } setSelectedMenu(node.data); + additionalNodeForm.setValue("levelNo", (node.data.levelNo ?? 0) + 1); }; - const handleDelete = async () => {}; + const handleAddMenu = () => { + if (additionalNode) return; + setAdditionalNode({ + id: -1, + parent: selectedMenu?.menuCd ?? 0, + text: "", + }); + }; + + const tree = additionalNode + ? [...(treeData ?? []), ...(additionalNode ? [additionalNode] : [])] + : (treeData ?? []); return ( @@ -144,19 +189,32 @@ function MenuPage() { {siteId && ( <>
- -
diff --git a/apps/cs/src/pages/menu/ui/menu-tree.tsx b/apps/cs/src/pages/menu/ui/menu-tree.tsx index a7d6b0fd3..59f55b4c2 100644 --- a/apps/cs/src/pages/menu/ui/menu-tree.tsx +++ b/apps/cs/src/pages/menu/ui/menu-tree.tsx @@ -1,33 +1,44 @@ "use client"; +import { useRef } from "react"; import { DndProvider } from "react-dnd"; +import type { UseFormReturn } from "react-hook-form"; import { useMutation, useQueryClient } from "@tanstack/react-query"; import { Tree, MultiBackend, getBackendOptions, type NodeModel, + type TreeMethods, } from "@minoru/react-dnd-treeview"; import { ENDPOINT } from "@/shared/config"; import { MenuApi } from "../api/menu-service"; -import type { TreeMenu, TreeNode } from "../model/menu-interface"; +import type { MenuFormData, TreeMenu, TreeNode } from "../model/menu-interface"; import { handleDropNode, handleSelectNode } from "../lib/utils"; import { MenuNode } from "./menu-node"; +import { EditableNode } from "./editable-node"; type MenuTreeProps = { - treeData: NodeModel[]; + tree: NodeModel[]; siteId: string; selectedMenu: TreeMenu | null; + additionalNodeForm: UseFormReturn; onSelectNode: (node: NodeModel) => void; + onAdditionalNodeSave: () => void; + onAdditionalNodeCancel: () => void; }; function MenuTree({ - treeData, + tree, siteId, selectedMenu, + additionalNodeForm, onSelectNode, + onAdditionalNodeSave, + onAdditionalNodeCancel, }: MenuTreeProps) { const queryClient = useQueryClient(); + const treeMethods = useRef(null); const { mutate: modifyTreeMenu } = useMutation({ mutationFn: (newTree: TreeNode) => @@ -39,21 +50,46 @@ function MenuTree({ }, }); + const additionalNode = tree.find((n) => n.id === -1); + + const isAdditionalParent = (node: NodeModel): boolean => { + if (!additionalNode) return false; + return additionalNode?.parent === node.id; + }; + return ( , { depth, isOpen, onToggle }) => ( - - )} + ref={treeMethods} + render={(node: NodeModel, { depth, isOpen, onToggle }) => { + if (node.id === -1) { + return ( + + additionalNodeForm.setValue("menuKornNm", e.target.value) + } + depth={depth} + onSave={onAdditionalNodeSave} + onCancel={onAdditionalNodeCancel} + /> + ); + } + return ( + + ); + }} onDrop={handleDropNode(modifyTreeMenu)} classes={{ root: "p-4", -- 2.47.1.windows.1 From 0fa026e11b0fcf87df47c7d3e22aa27d9917317f Mon Sep 17 00:00:00 2001 From: bicochan Date: Thu, 27 Mar 2025 14:59:16 +0900 Subject: [PATCH 08/24] =?UTF-8?q?fix:=20prntMenuCd=20=EC=97=86=EC=9D=84=20?= =?UTF-8?q?=EA=B2=BD=EC=9A=B0=20null=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/cs/src/pages/menu/ui/menu-page.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/apps/cs/src/pages/menu/ui/menu-page.tsx b/apps/cs/src/pages/menu/ui/menu-page.tsx index 7125710aa..18837dc26 100644 --- a/apps/cs/src/pages/menu/ui/menu-page.tsx +++ b/apps/cs/src/pages/menu/ui/menu-page.tsx @@ -124,10 +124,15 @@ function MenuPage() { }); const handleAdditionalNodeSave = async () => { + const prntMenuCd = !additionalNode?.parent + ? null + : (additionalNode?.parent as number); + await createMenu({ ...additionalNodeForm.getValues(), - prntMenuCd: additionalNode?.parent as number, + prntMenuCd, }); + additionalNodeForm.setValue("menuKornNm", ""); setAdditionalNode(null); }; @@ -167,7 +172,7 @@ function MenuPage() { }; const tree = additionalNode - ? [...(treeData ?? []), ...(additionalNode ? [additionalNode] : [])] + ? [...(additionalNode ? [additionalNode] : []), ...(treeData ?? [])] : (treeData ?? []); return ( -- 2.47.1.windows.1 From ae5597b1f330fcca8dcc61c310162afa2b548c40 Mon Sep 17 00:00:00 2001 From: bicochan Date: Thu, 27 Mar 2025 15:23:01 +0900 Subject: [PATCH 09/24] =?UTF-8?q?feat:=20=EB=A9=94=EB=89=B4=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EB=A1=9C=EC=A7=81=20=EC=9E=91=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/cs/src/pages/menu/api/menu-service.ts | 7 + .../cs/src/pages/menu/model/menu-interface.ts | 1 + apps/cs/src/pages/menu/ui/menu-form.tsx | 4 +- apps/cs/src/pages/menu/ui/menu-node.tsx | 5 +- apps/cs/src/pages/menu/ui/menu-page.tsx | 159 ++++++++++-------- 5 files changed, 105 insertions(+), 71 deletions(-) diff --git a/apps/cs/src/pages/menu/api/menu-service.ts b/apps/cs/src/pages/menu/api/menu-service.ts index cdb64b521..4e03f6cdf 100644 --- a/apps/cs/src/pages/menu/api/menu-service.ts +++ b/apps/cs/src/pages/menu/api/menu-service.ts @@ -35,6 +35,13 @@ export class MenuApiService implements MenuService { return response.data; } + public async deleteMenu(menuCd: number): Promise { + const response = await client.delete( + `${ENDPOINT.CMS_SERVICE.MENUS}/${menuCd}`, + ); + return response.data; + } + public async getTreeMenuList(siteId: string): Promise { const response = await client.get( `${ENDPOINT.CMS_SERVICE.MENUS}/${siteId}/tree`, diff --git a/apps/cs/src/pages/menu/model/menu-interface.ts b/apps/cs/src/pages/menu/model/menu-interface.ts index 91043a76c..2498dde5f 100644 --- a/apps/cs/src/pages/menu/model/menu-interface.ts +++ b/apps/cs/src/pages/menu/model/menu-interface.ts @@ -75,6 +75,7 @@ export interface MenuService { menuCd: number, data: Omit, ) => Promise; + deleteMenu: (menuCd: number) => Promise; getTreeMenuList: (siteId: string) => Promise; modifyTreeMenu: (siteId: string, data: TreeNode[]) => Promise; modifyMenuName: (menuCd: number, name: string) => Promise; diff --git a/apps/cs/src/pages/menu/ui/menu-form.tsx b/apps/cs/src/pages/menu/ui/menu-form.tsx index 13a910c23..922c2bb78 100644 --- a/apps/cs/src/pages/menu/ui/menu-form.tsx +++ b/apps/cs/src/pages/menu/ui/menu-form.tsx @@ -25,7 +25,7 @@ interface MenuFormProps { menuCd: number; data: Omit; }) => void; - handleDelete?: () => void; + handleDelete?: (menuCd: number) => void; } function MenuForm({ @@ -73,7 +73,7 @@ function MenuForm({ const onDelete = () => { handleConfirm(async () => { if (await confirm("삭제하시겠습니까?")) { - handleDelete?.(); + handleDelete?.(menuCd!); } }); }; diff --git a/apps/cs/src/pages/menu/ui/menu-node.tsx b/apps/cs/src/pages/menu/ui/menu-node.tsx index bce0f8ac2..b2e595923 100644 --- a/apps/cs/src/pages/menu/ui/menu-node.tsx +++ b/apps/cs/src/pages/menu/ui/menu-node.tsx @@ -90,7 +90,10 @@ function MenuNode({ "diveicon di-arrow-forward mr-2 text-base text-label", isOpen && "rotate-90", )} - onClick={handleToggle} + onClick={(e) => { + e.stopPropagation(); + handleToggle(); + }} /> )} {text} diff --git a/apps/cs/src/pages/menu/ui/menu-page.tsx b/apps/cs/src/pages/menu/ui/menu-page.tsx index 18837dc26..12b4de492 100644 --- a/apps/cs/src/pages/menu/ui/menu-page.tsx +++ b/apps/cs/src/pages/menu/ui/menu-page.tsx @@ -4,6 +4,7 @@ import { useState } from "react"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import type { NodeModel } from "@minoru/react-dnd-treeview"; +import { useConfirm } from "@frontend-opensource/use-react-hooks"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { Button, @@ -16,6 +17,7 @@ import { import { SiteApi } from "@/entities/site"; import { ENDPOINT } from "@/shared/config"; import { PageLayout } from "@/shared/ui/page-layout"; +import { ConfirmDialog } from "@/shared/ui/confirm-dialog"; import { useAlertStore } from "@/shared/lib/use-alert-store"; import type { TreeMenu } from "../model/menu-interface"; import { MenuApi } from "../api/menu-service"; @@ -31,8 +33,10 @@ import { generateTreeData } from "../lib/utils"; function MenuPage() { const queryClient = useQueryClient(); const { setMessage: alert } = useAlertStore((state) => state); + const { confirm } = useConfirm(); const [siteId, setSiteId] = useState(""); + const [onConfirm, setOnConfirm] = useState(false); const [selectedMenu, setSelectedMenu] = useState(null); const [additionalNode, setAdditionalNode] = useState | null>(null); @@ -72,7 +76,7 @@ function MenuPage() { onSuccess: () => { alert("저장되었습니다."); queryClient.invalidateQueries({ - queryKey: [ENDPOINT.CMS_SERVICE.MENUS], + queryKey: [ENDPOINT.CMS_SERVICE.MENUS, siteId], }); }, }); @@ -88,7 +92,17 @@ function MenuPage() { onSuccess: () => { alert("수정되었습니다."); queryClient.invalidateQueries({ - queryKey: [ENDPOINT.CMS_SERVICE.MENUS], + queryKey: [ENDPOINT.CMS_SERVICE.MENUS, siteId], + }); + }, + }); + + const { mutate: deleteMenu } = useMutation({ + mutationFn: (menuCd: number) => MenuApi.deleteMenu(menuCd), + onSuccess: () => { + alert("삭제되었습니다."); + queryClient.invalidateQueries({ + queryKey: [ENDPOINT.CMS_SERVICE.MENUS, siteId], }); }, }); @@ -132,17 +146,14 @@ function MenuPage() { ...additionalNodeForm.getValues(), prntMenuCd, }); + resetAdditionalNode(); + }; + + const resetAdditionalNode = () => { additionalNodeForm.setValue("menuKornNm", ""); setAdditionalNode(null); }; - const handleAdditionalNodeCancel = () => { - additionalNodeForm.setValue("menuKornNm", ""); - setAdditionalNode(null); - }; - - const handleDelete = async () => {}; - const handleSiteIdChange = (value: string) => { form.setValue("siteId", value); additionalNodeForm.setValue("siteId", value); @@ -162,6 +173,14 @@ function MenuPage() { additionalNodeForm.setValue("levelNo", (node.data.levelNo ?? 0) + 1); }; + const handleNodeDelete = async () => { + setOnConfirm(true); + if (await confirm("삭제하시겠습니까?")) { + setOnConfirm(false); + deleteMenu(selectedMenu!.menuCd!); + } + }; + const handleAddMenu = () => { if (additionalNode) return; setAdditionalNode({ @@ -176,66 +195,70 @@ function MenuPage() { : (treeData ?? []); return ( - -
-
- - {siteId && ( - <> -
- - -
-
- -
- - )} + <> + +
+
+ + {siteId && ( + <> +
+ + +
+
+ +
+ + )} +
+
+ +
-
- -
-
- + + + ); } -- 2.47.1.windows.1 From 8b0939ff41feec5863f9a6603acda6580446fd18 Mon Sep 17 00:00:00 2001 From: bicochan Date: Thu, 27 Mar 2025 16:16:59 +0900 Subject: [PATCH 10/24] =?UTF-8?q?feat:=20=EB=A9=94=EB=89=B4=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=EB=B3=84=20=ED=8F=BC=20=EC=9E=91=EC=97=85(=EB=82=B4?= =?UTF-8?q?=EB=B6=80=EB=A7=81=ED=81=AC,=20=EC=99=B8=EB=B6=80=EB=A7=81?= =?UTF-8?q?=ED=81=AC,=20=ED=95=98=EB=8B=A8=EB=A9=94=EB=89=B4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cs/src/pages/menu/model/menu-interface.ts | 5 ++- .../src/pages/menu/ui/base-type-menu-form.tsx | 2 +- .../cs/src/pages/menu/ui/inside-menu-form.tsx | 38 ++++++++++++++++++- apps/cs/src/pages/menu/ui/menu-page.tsx | 1 + .../src/pages/menu/ui/outside-menu-form.tsx | 38 ++++++++++++++++++- 5 files changed, 80 insertions(+), 4 deletions(-) diff --git a/apps/cs/src/pages/menu/model/menu-interface.ts b/apps/cs/src/pages/menu/model/menu-interface.ts index 2498dde5f..47df6f749 100644 --- a/apps/cs/src/pages/menu/model/menu-interface.ts +++ b/apps/cs/src/pages/menu/model/menu-interface.ts @@ -66,7 +66,10 @@ export const menuFormSchema = z.object({ }); export type MenuFormData = z.infer; -export type Menu = MenuFormData & { menuCd: number }; +export type Menu = Omit & { + menuCd: number; + iconName?: string; +}; export interface MenuService { getMenuInfo: (menuCd: number) => Promise; diff --git a/apps/cs/src/pages/menu/ui/base-type-menu-form.tsx b/apps/cs/src/pages/menu/ui/base-type-menu-form.tsx index 9835f1e3a..5f7c3bb45 100644 --- a/apps/cs/src/pages/menu/ui/base-type-menu-form.tsx +++ b/apps/cs/src/pages/menu/ui/base-type-menu-form.tsx @@ -37,7 +37,7 @@ function BaseTypeMenuForm({ form }: { form: UseFormReturn }) { {...field} value={field.value ?? ""} className="!text-[1.3rem]" - placeholder="메뉴 서브명을 입력하십시오." + placeholder="사이트맵에서 다른 이름으로 사용할 경우 입력하십시오." /> diff --git a/apps/cs/src/pages/menu/ui/inside-menu-form.tsx b/apps/cs/src/pages/menu/ui/inside-menu-form.tsx index f2fed53cf..f671c7d26 100644 --- a/apps/cs/src/pages/menu/ui/inside-menu-form.tsx +++ b/apps/cs/src/pages/menu/ui/inside-menu-form.tsx @@ -1,5 +1,18 @@ import { type UseFormReturn } from "react-hook-form"; -import { Table, TableBody } from "@common/components"; +import { + Input, + Table, + TableBody, + TableCell, + TableHead, + TableRow, +} from "@common/components"; +import { + FormField, + FormItem, + FormControl, + FormMessage, +} from "@/shared/ui/form"; import type { MenuFormData } from "../model/menu-interface"; import { BaseTypeMenuForm } from "./base-type-menu-form"; @@ -11,6 +24,29 @@ function InsideMenuForm({ form }: { form: UseFormReturn }) { + + 연결 URL + + ( + + + + + + + )} + /> + + + {/* TODO: 연결 형태 추가 */} diff --git a/apps/cs/src/pages/menu/ui/menu-page.tsx b/apps/cs/src/pages/menu/ui/menu-page.tsx index 12b4de492..f06f7a773 100644 --- a/apps/cs/src/pages/menu/ui/menu-page.tsx +++ b/apps/cs/src/pages/menu/ui/menu-page.tsx @@ -122,6 +122,7 @@ function MenuPage() { otptYn: menuInfo?.otptYn ?? "Y", evlUseYn: menuInfo?.evlUseYn ?? "Y", useYn: menuInfo?.useYn ?? "Y", + iconNm: menuInfo?.iconName, }, }); diff --git a/apps/cs/src/pages/menu/ui/outside-menu-form.tsx b/apps/cs/src/pages/menu/ui/outside-menu-form.tsx index ac72a2892..035d2dbda 100644 --- a/apps/cs/src/pages/menu/ui/outside-menu-form.tsx +++ b/apps/cs/src/pages/menu/ui/outside-menu-form.tsx @@ -1,5 +1,18 @@ import { type UseFormReturn } from "react-hook-form"; -import { Table, TableBody } from "@common/components"; +import { + Input, + Table, + TableBody, + TableCell, + TableHead, + TableRow, +} from "@common/components"; +import { + FormField, + FormItem, + FormControl, + FormMessage, +} from "@/shared/ui/form"; import type { MenuFormData } from "../model/menu-interface"; import { BaseTypeMenuForm } from "./base-type-menu-form"; @@ -11,6 +24,29 @@ function OutsideMenuForm({ form }: { form: UseFormReturn }) { + + 연결 URL + + ( + + + + + + + )} + /> + + + {/* TODO: 연결 형태 추가 */} -- 2.47.1.windows.1 From e32d0ba1447e4b8bb3de0795c0f698065b8d0858 Mon Sep 17 00:00:00 2001 From: bicochan Date: Thu, 27 Mar 2025 16:17:42 +0900 Subject: [PATCH 11/24] =?UTF-8?q?feat:=20=EB=A9=94=EB=89=B4=20=EA=B6=8C?= =?UTF-8?q?=ED=95=9C=EA=B4=80=EB=A6=AC=20=EB=8B=A4=EC=9D=B4=EC=96=BC?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=20=EC=9E=91=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../menu/ui/permissions-management-dialog.tsx | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 apps/cs/src/pages/menu/ui/permissions-management-dialog.tsx diff --git a/apps/cs/src/pages/menu/ui/permissions-management-dialog.tsx b/apps/cs/src/pages/menu/ui/permissions-management-dialog.tsx new file mode 100644 index 000000000..c5f782cbb --- /dev/null +++ b/apps/cs/src/pages/menu/ui/permissions-management-dialog.tsx @@ -0,0 +1,22 @@ +import { Button, Dialog, DialogTrigger } from "@common/components/ui"; + +type PermissionsManagementDialogProps = { + menuCd?: number; +} & React.ComponentProps; + +function PermissionsManagementDialog({ + ...props +}: PermissionsManagementDialogProps) { + return ( + + + + + + ); +} + +export { PermissionsManagementDialog }; -- 2.47.1.windows.1 From 89f31c4c6b7c529982cda1138b5eb1f875edba9e Mon Sep 17 00:00:00 2001 From: bicochan Date: Thu, 27 Mar 2025 16:29:40 +0900 Subject: [PATCH 12/24] =?UTF-8?q?chore:=20=EC=9D=B8=ED=84=B0=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=8A=A4=20=EB=84=A4=EC=9D=B4=EB=B0=8D=20=EA=B7=9C?= =?UTF-8?q?=EC=B9=99=20=EB=B3=80=EA=B2=BD('I'=20prefix=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/cs/src/entities/site/api/site-service.ts | 10 +++++----- apps/cs/src/entities/site/index.ts | 4 ++-- apps/cs/src/entities/site/model/site-interface.ts | 14 +++++++------- apps/cs/src/pages/site/api/domain-service.ts | 4 ++-- apps/cs/src/pages/site/api/ip-service.ts | 4 ++-- apps/cs/src/pages/site/model/domain-interface.ts | 6 +++--- apps/cs/src/pages/site/model/ip-interface.ts | 6 +++--- apps/cs/src/pages/site/model/table-columns.tsx | 4 ++-- apps/cs/src/pages/site/ui/site-page.tsx | 6 +++--- apps/cs/src/shared/model/api-interface.ts | 2 +- 10 files changed, 30 insertions(+), 30 deletions(-) diff --git a/apps/cs/src/entities/site/api/site-service.ts b/apps/cs/src/entities/site/api/site-service.ts index 629bf5f01..65f6a95a0 100644 --- a/apps/cs/src/entities/site/api/site-service.ts +++ b/apps/cs/src/entities/site/api/site-service.ts @@ -4,13 +4,13 @@ import { siteFormSchema, type SiteService, type SiteList, - type ISiteDetail, - type ISiteParams, + type SiteDetail, + type SiteParams, type SiteFormData, } from "../model/site-interface"; export class SiteApiService implements SiteService { - public async getSiteList(params: ISiteParams): Promise { + public async getSiteList(params: SiteParams): Promise { const response = await client.get( `${ENDPOINT.CMS_SERVICE.SITES}`, { params }, @@ -18,8 +18,8 @@ export class SiteApiService implements SiteService { return response.data; } - public async siteDetail(siteId: string): Promise { - const response = await client.get( + public async siteDetail(siteId: string): Promise { + const response = await client.get( `${ENDPOINT.CMS_SERVICE.SITES}/${siteId}`, ); return response.data; diff --git a/apps/cs/src/entities/site/index.ts b/apps/cs/src/entities/site/index.ts index 985b9346a..c7b437a64 100644 --- a/apps/cs/src/entities/site/index.ts +++ b/apps/cs/src/entities/site/index.ts @@ -1,7 +1,7 @@ export { SiteApi } from "./api/site-service"; export { siteFormSchema, - type ISiteParams, - type ISite, + type SiteParams, + type Site, type SiteFormData, } from "./model/site-interface"; diff --git a/apps/cs/src/entities/site/model/site-interface.ts b/apps/cs/src/entities/site/model/site-interface.ts index ae14297ae..7fb34670e 100644 --- a/apps/cs/src/entities/site/model/site-interface.ts +++ b/apps/cs/src/entities/site/model/site-interface.ts @@ -1,13 +1,13 @@ import { z } from "zod"; -import type { IPageableParams, PageableList } from "@/shared/model"; +import type { PageableParams, PageableList } from "@/shared/model"; -export interface ISiteParams extends IPageableParams { +export interface SiteParams extends PageableParams { siteNm?: string; siteKndCd?: string; useYn?: string; } -export interface ISite { +export interface Site { siteId: string; siteNm: string; siteKindCode: string; @@ -19,7 +19,7 @@ export interface ISite { dmnAddr: string; } -export interface ISiteDetail extends SiteFormData { +export interface SiteDetail extends SiteFormData { siteId: string; siteKindNm: string; basicSiteYn: string; @@ -49,11 +49,11 @@ export const siteFormSchema = z.object({ }); export type SiteFormData = z.infer; -export type SiteList = PageableList; +export type SiteList = PageableList; export interface SiteService { - getSiteList(params: ISiteParams): Promise; - siteDetail(siteId: string): Promise; + getSiteList(params: SiteParams): Promise; + siteDetail(siteId: string): Promise; modifySite(siteId: string, data: SiteFormData): Promise; createSite(data: SiteFormData): Promise; } diff --git a/apps/cs/src/pages/site/api/domain-service.ts b/apps/cs/src/pages/site/api/domain-service.ts index 7cc36363f..a8d2acfab 100644 --- a/apps/cs/src/pages/site/api/domain-service.ts +++ b/apps/cs/src/pages/site/api/domain-service.ts @@ -4,11 +4,11 @@ import type { Domain, DomainService, DomainList, - IDomainParams, + DomainParams, } from "../model/domain-interface"; export class DomainApiService implements DomainService { - public async getDomainList(params: IDomainParams): Promise { + public async getDomainList(params: DomainParams): Promise { const response = await client.get( `${ENDPOINT.CMS_SERVICE.DOMAINS}`, { params }, diff --git a/apps/cs/src/pages/site/api/ip-service.ts b/apps/cs/src/pages/site/api/ip-service.ts index 748676b21..903f0407e 100644 --- a/apps/cs/src/pages/site/api/ip-service.ts +++ b/apps/cs/src/pages/site/api/ip-service.ts @@ -1,9 +1,9 @@ import { client } from "@/shared/api"; import { ENDPOINT } from "@/shared/config"; -import type { IIpParams, IP, IpList, IpService } from "../model/ip-interface"; +import type { IpParams, IP, IpList, IpService } from "../model/ip-interface"; export class IpApiService implements IpService { - public async getIpList(params: IIpParams): Promise { + public async getIpList(params: IpParams): Promise { const response = await client.get(`${ENDPOINT.CMS_SERVICE.IPS}`, { params, }); diff --git a/apps/cs/src/pages/site/model/domain-interface.ts b/apps/cs/src/pages/site/model/domain-interface.ts index 5f1076275..d9e3886c7 100644 --- a/apps/cs/src/pages/site/model/domain-interface.ts +++ b/apps/cs/src/pages/site/model/domain-interface.ts @@ -1,7 +1,7 @@ import { z } from "zod"; -import type { IPageableParams, PageableList } from "@/shared/model"; +import type { PageableParams, PageableList } from "@/shared/model"; -export interface IDomainParams extends IPageableParams { +export interface DomainParams extends PageableParams { keywordType?: string; keyword?: string; siteId?: string; @@ -26,6 +26,6 @@ export type DomainFormData = z.infer; export type DomainList = PageableList; export interface DomainService { - getDomainList(params: IDomainParams): Promise; + getDomainList(params: DomainParams): Promise; saveDomainList(body: Domain[]): Promise; } diff --git a/apps/cs/src/pages/site/model/ip-interface.ts b/apps/cs/src/pages/site/model/ip-interface.ts index db1d393f1..63c458b2a 100644 --- a/apps/cs/src/pages/site/model/ip-interface.ts +++ b/apps/cs/src/pages/site/model/ip-interface.ts @@ -1,7 +1,7 @@ import { z } from "zod"; -import type { IPageableParams, PageableList } from "@/shared/model"; +import type { PageableParams, PageableList } from "@/shared/model"; -export interface IIpParams extends IPageableParams { +export interface IpParams extends PageableParams { siteId: string; ipAddr?: string; prmYn?: string; @@ -24,6 +24,6 @@ export type IPFormData = z.infer; export type IpList = PageableList; export interface IpService { - getIpList(params: IIpParams): Promise; + getIpList(params: IpParams): Promise; saveIpList(body: IP[]): Promise; } diff --git a/apps/cs/src/pages/site/model/table-columns.tsx b/apps/cs/src/pages/site/model/table-columns.tsx index 2a07ce4b1..8a2d852cd 100644 --- a/apps/cs/src/pages/site/model/table-columns.tsx +++ b/apps/cs/src/pages/site/model/table-columns.tsx @@ -1,8 +1,8 @@ import dayjs from "dayjs"; import { createColumnHelper } from "@tanstack/react-table"; -import type { ISite } from "@/entities/site"; +import type { Site } from "@/entities/site"; -const columnHelper = createColumnHelper(); +const columnHelper = createColumnHelper(); const columns = [ columnHelper.accessor((_, index) => index + 1, { diff --git a/apps/cs/src/pages/site/ui/site-page.tsx b/apps/cs/src/pages/site/ui/site-page.tsx index 33431f316..b5210df61 100644 --- a/apps/cs/src/pages/site/ui/site-page.tsx +++ b/apps/cs/src/pages/site/ui/site-page.tsx @@ -7,7 +7,7 @@ import { useEffect, useState } from "react"; import { useQuery } from "@tanstack/react-query"; import { getCoreRowModel, useReactTable } from "@tanstack/react-table"; import { Button, DataTable, Input } from "@common/components"; -import { SiteApi, type ISite, type ISiteParams } from "@/entities/site"; +import { SiteApi, type Site, type SiteParams } from "@/entities/site"; import { ENDPOINT } from "@/shared/config"; import { PageLayout } from "@/shared/ui/page-layout"; import { columns } from "../model/table-columns"; @@ -20,7 +20,7 @@ function SitePage() { enabled: false, }); - const [siteQuery, setSiteQuery] = useState({ + const [siteQuery, setSiteQuery] = useState({ siteNm: "", }); @@ -35,7 +35,7 @@ function SitePage() { refetch(); }; - const handleRowClick = (row: ISite) => { + const handleRowClick = (row: Site) => { router.push(`/site/register/${row.siteId}`); }; diff --git a/apps/cs/src/shared/model/api-interface.ts b/apps/cs/src/shared/model/api-interface.ts index 5aa4cd09b..f1eaa6b3c 100644 --- a/apps/cs/src/shared/model/api-interface.ts +++ b/apps/cs/src/shared/model/api-interface.ts @@ -27,7 +27,7 @@ export interface PageableList { empty: boolean; } -export interface IPageableParams { +export interface PageableParams { page?: number; size?: number; sort?: string[]; -- 2.47.1.windows.1 From 050ce8148170172dd62524c8b7adcd51e5747a09 Mon Sep 17 00:00:00 2001 From: bicochan Date: Thu, 27 Mar 2025 16:33:39 +0900 Subject: [PATCH 13/24] =?UTF-8?q?chore:=20PageableList=20=EC=A0=9C?= =?UTF-8?q?=EB=84=88=EB=A6=AD=20=ED=83=80=EC=9E=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/cs/src/entities/site/model/site-interface.ts | 2 +- apps/cs/src/pages/site/model/domain-interface.ts | 2 +- apps/cs/src/pages/site/model/ip-interface.ts | 2 +- apps/cs/src/shared/model/api-interface.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/cs/src/entities/site/model/site-interface.ts b/apps/cs/src/entities/site/model/site-interface.ts index 7fb34670e..ef83a53c1 100644 --- a/apps/cs/src/entities/site/model/site-interface.ts +++ b/apps/cs/src/entities/site/model/site-interface.ts @@ -49,7 +49,7 @@ export const siteFormSchema = z.object({ }); export type SiteFormData = z.infer; -export type SiteList = PageableList; +export type SiteList = PageableList; export interface SiteService { getSiteList(params: SiteParams): Promise; diff --git a/apps/cs/src/pages/site/model/domain-interface.ts b/apps/cs/src/pages/site/model/domain-interface.ts index d9e3886c7..e93f86bc1 100644 --- a/apps/cs/src/pages/site/model/domain-interface.ts +++ b/apps/cs/src/pages/site/model/domain-interface.ts @@ -23,7 +23,7 @@ export const domainFormSchema = z.object({ export type Domain = z.infer; export type DomainFormData = z.infer; -export type DomainList = PageableList; +export type DomainList = PageableList; export interface DomainService { getDomainList(params: DomainParams): Promise; diff --git a/apps/cs/src/pages/site/model/ip-interface.ts b/apps/cs/src/pages/site/model/ip-interface.ts index 63c458b2a..427fbfe57 100644 --- a/apps/cs/src/pages/site/model/ip-interface.ts +++ b/apps/cs/src/pages/site/model/ip-interface.ts @@ -21,7 +21,7 @@ export const ipFormSchema = z.object({ export type IP = z.infer; export type IPFormData = z.infer; -export type IpList = PageableList; +export type IpList = PageableList; export interface IpService { getIpList(params: IpParams): Promise; diff --git a/apps/cs/src/shared/model/api-interface.ts b/apps/cs/src/shared/model/api-interface.ts index f1eaa6b3c..133c0c401 100644 --- a/apps/cs/src/shared/model/api-interface.ts +++ b/apps/cs/src/shared/model/api-interface.ts @@ -14,7 +14,7 @@ interface PageableConfig { } export interface PageableList { - content: T; + content: T[]; pageable: PageableConfig; sort: SortConfig; totalPages: number; -- 2.47.1.windows.1 From c221a4537e3706c7b8335deb0e37ba1a828c3a78 Mon Sep 17 00:00:00 2001 From: bicochan Date: Thu, 27 Mar 2025 18:20:29 +0900 Subject: [PATCH 14/24] =?UTF-8?q?feat:=20=EC=BD=98=ED=85=90=EC=B8=A0=20?= =?UTF-8?q?=EB=A9=94=EB=89=B4=20=EB=93=B1=EB=A1=9D=20=EC=9E=91=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/cs/src/pages/menu/api/content-service.ts | 19 +++++ .../src/pages/menu/model/content-interface.ts | 23 ++++++ .../cs/src/pages/menu/model/menu-interface.ts | 12 +-- apps/cs/src/pages/menu/model/table-colums.tsx | 32 ++++++++ apps/cs/src/pages/menu/ui/content-dialog.tsx | 73 +++++++++++++++++++ .../src/pages/menu/ui/content-menu-form.tsx | 46 ++++-------- apps/cs/src/shared/config/api.ts | 1 + .../common/components/src/ui/table/index.tsx | 11 +-- 8 files changed, 169 insertions(+), 48 deletions(-) create mode 100644 apps/cs/src/pages/menu/api/content-service.ts create mode 100644 apps/cs/src/pages/menu/model/content-interface.ts create mode 100644 apps/cs/src/pages/menu/model/table-colums.tsx create mode 100644 apps/cs/src/pages/menu/ui/content-dialog.tsx diff --git a/apps/cs/src/pages/menu/api/content-service.ts b/apps/cs/src/pages/menu/api/content-service.ts new file mode 100644 index 000000000..2f76baec8 --- /dev/null +++ b/apps/cs/src/pages/menu/api/content-service.ts @@ -0,0 +1,19 @@ +import { client } from "@/shared/api"; +import { ENDPOINT } from "@/shared/config"; +import type { + ContentService, + ContentParams, + ContentList, +} from "../model/content-interface"; + +export class ContentApiService implements ContentService { + public async getContentList(params: ContentParams): Promise { + const response = await client.get( + `${ENDPOINT.CMS_SERVICE.CONTENTS}`, + { params }, + ); + return response.data; + } +} + +export const ContentApi = new ContentApiService(); diff --git a/apps/cs/src/pages/menu/model/content-interface.ts b/apps/cs/src/pages/menu/model/content-interface.ts new file mode 100644 index 000000000..dc789d059 --- /dev/null +++ b/apps/cs/src/pages/menu/model/content-interface.ts @@ -0,0 +1,23 @@ +import type { PageableList, PageableParams } from "@/shared/model"; + +export interface ContentParams extends PageableParams { + siteId?: string; + menuKornNm?: string; + urlPath?: string; + useYn?: string; +} + +export interface Content { + contsSn: number; + contsTtl: string; + aprvContsVer: number; + useYn: string; + rgtr: string; + regDt: string; +} + +export type ContentList = PageableList; + +export interface ContentService { + getContentList(params: ContentParams): Promise; +} diff --git a/apps/cs/src/pages/menu/model/menu-interface.ts b/apps/cs/src/pages/menu/model/menu-interface.ts index 47df6f749..fcee4c505 100644 --- a/apps/cs/src/pages/menu/model/menu-interface.ts +++ b/apps/cs/src/pages/menu/model/menu-interface.ts @@ -1,12 +1,12 @@ import { z } from "zod"; export const MENU_TYPE = { - EMPTY: "empty", - CONTENTS: "contents", - BOARD: "board", - INSIDE: "inside", - OUTSIDE: "outside", - BOTTOM: "bottom", + EMPTY: "EMPTY", + CONTENTS: "CONTENTS", + BOARD: "BOARD", + INSIDE: "INSIDE", + OUTSIDE: "OUTSIDE", + BOTTOM: "BOTTOM", } as const; export type MenuType = (typeof MENU_TYPE)[keyof typeof MENU_TYPE]; diff --git a/apps/cs/src/pages/menu/model/table-colums.tsx b/apps/cs/src/pages/menu/model/table-colums.tsx new file mode 100644 index 000000000..b24b506fd --- /dev/null +++ b/apps/cs/src/pages/menu/model/table-colums.tsx @@ -0,0 +1,32 @@ +import { createColumnHelper } from "@tanstack/react-table"; +import { Checkbox } from "@common/components/ui"; +import type { Content } from "../model/content-interface"; + +const columnHelper = createColumnHelper(); + +const columns = [ + columnHelper.accessor((_, index) => index + 1, { + id: "no", + header: "NO", + }), + columnHelper.accessor("contsTtl", { + id: "contsTtl", + header: "콘텐츠명", + }), + // TODO: 최종수정자, 최종수정일, 승인일시 추가 + columnHelper.display({ + id: "select", + header: "선택", + cell: ({ row }) => { + return ( + + ); + }, + }), +]; + +export { columns }; diff --git a/apps/cs/src/pages/menu/ui/content-dialog.tsx b/apps/cs/src/pages/menu/ui/content-dialog.tsx new file mode 100644 index 000000000..e6aedbb00 --- /dev/null +++ b/apps/cs/src/pages/menu/ui/content-dialog.tsx @@ -0,0 +1,73 @@ +import { useEffect, useState } from "react"; +import { useQuery } from "@tanstack/react-query"; +import { + getCoreRowModel, + useReactTable, + type RowSelectionState, +} from "@tanstack/react-table"; +import { + Button, + DataTable, + Dialog, + DialogTrigger, + DialogContent, + DialogHeader, + DialogTitle, +} from "@common/components/ui"; +import { ENDPOINT } from "@/shared/config"; +import { ContentApi } from "../api/content-service"; +import type { Content } from "../model/content-interface"; +import { columns } from "../model/table-colums"; + +type ContentDialogProps = { + handleContentSelect: (content: Content) => void; +} & React.ComponentProps; + +function ContentDialog({ handleContentSelect, ...props }: ContentDialogProps) { + const [rowSelection, setRowSelection] = useState({}); + + const { data: contentList } = useQuery({ + queryKey: [ENDPOINT.CMS_SERVICE.CONTENTS], + queryFn: () => + ContentApi.getContentList({ useYn: "Y", page: 0, size: 100 }), + }); + + const table = useReactTable({ + data: contentList?.content ?? [], + columns, + state: { + rowSelection, + }, + enableMultiRowSelection: false, + getCoreRowModel: getCoreRowModel(), + onRowSelectionChange: setRowSelection, + }); + + useEffect(() => { + const contentData = table.getSelectedRowModel().rows[0] + ?.original as Content; + if (!contentData) return; + handleContentSelect(contentData); + }, [table.getSelectedRowModel().rows]); + + return ( + + + + + + + 콘텐츠 선택 + +
+ +
+
+
+ ); +} + +export { ContentDialog }; diff --git a/apps/cs/src/pages/menu/ui/content-menu-form.tsx b/apps/cs/src/pages/menu/ui/content-menu-form.tsx index 0905a594b..7ce40d940 100644 --- a/apps/cs/src/pages/menu/ui/content-menu-form.tsx +++ b/apps/cs/src/pages/menu/ui/content-menu-form.tsx @@ -16,9 +16,16 @@ import { FormMessage, } from "@/shared/ui/form"; import type { MenuFormData } from "../model/menu-interface"; +import type { Content } from "../model/content-interface"; import { BaseTypeMenuForm } from "./base-type-menu-form"; +import { ContentDialog } from "./content-dialog"; function ContentMenuForm({ form }: { form: UseFormReturn }) { + const handleContentSelect = (content: Content) => { + form.setValue("menuLnkgSn", content.contsSn); + form.setValue("menuLnkgSnNm", content.contsTtl); + }; + return ( @@ -29,42 +36,15 @@ function ContentMenuForm({ form }: { form: UseFormReturn }) { 콘텐츠 -
- {form.watch("menuLnkgSn")} +
+
+ {form.watch("menuLnkgSnNm")} +
+
- - 연결 형태 - - ( - - - -
-
- - -
-
- - -
-
-
-
- -
- )} - /> -
-
+ {/* TODO: 연결 형태 추가 */} 메뉴 평가 사용 여부 diff --git a/apps/cs/src/shared/config/api.ts b/apps/cs/src/shared/config/api.ts index e07b6c9ff..b938ceaa9 100644 --- a/apps/cs/src/shared/config/api.ts +++ b/apps/cs/src/shared/config/api.ts @@ -14,6 +14,7 @@ export const ENDPOINT = { IPS: "/cms-service/api/v1/site-ips", DOMAINS: "/cms-service/api/v1/site-dmns", MENUS: "/cms-service/api/v1/menus", + CONTENTS: "/cms-service/api/v1/contents", }, }; diff --git a/packages/common/components/src/ui/table/index.tsx b/packages/common/components/src/ui/table/index.tsx index 797f48dfd..46c8db39c 100644 --- a/packages/common/components/src/ui/table/index.tsx +++ b/packages/common/components/src/ui/table/index.tsx @@ -79,11 +79,7 @@ const TableHead = React.forwardRef< HTMLTableCellElement, React.ThHTMLAttributes >(({ className, ...props }, ref) => ( -
+ )); TableHead.displayName = "TableHead"; @@ -93,10 +89,7 @@ const TableCell = React.forwardRef< >(({ className, ...props }, ref) => ( )); -- 2.47.1.windows.1 From cb5e90c2770832e87087f2ea30ae65fcca3b2ec3 Mon Sep 17 00:00:00 2001 From: bicochan Date: Fri, 28 Mar 2025 09:22:07 +0900 Subject: [PATCH 15/24] =?UTF-8?q?rename:=20=ED=8F=BC=20=EB=B0=8F=20?= =?UTF-8?q?=EB=8B=A4=EC=9D=B4=EC=96=BC=EB=A1=9C=EA=B7=B8=20=ED=8F=B4?= =?UTF-8?q?=EB=8D=94=20=EA=B5=AC=EB=B6=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/cs/src/pages/menu/ui/{ => dialog}/content-dialog.tsx | 6 +++--- .../menu/ui/{ => dialog}/permissions-management-dialog.tsx | 0 apps/cs/src/pages/menu/ui/{ => form}/base-menu-form.tsx | 2 +- .../cs/src/pages/menu/ui/{ => form}/base-type-menu-form.tsx | 2 +- apps/cs/src/pages/menu/ui/{ => form}/board-menu-form.tsx | 2 +- apps/cs/src/pages/menu/ui/{ => form}/bottom-menu-form.tsx | 2 +- apps/cs/src/pages/menu/ui/{ => form}/content-menu-form.tsx | 6 +++--- apps/cs/src/pages/menu/ui/{ => form}/empty-menu-form.tsx | 2 +- apps/cs/src/pages/menu/ui/{ => form}/inside-menu-form.tsx | 2 +- apps/cs/src/pages/menu/ui/{ => form}/menu-form.tsx | 4 ++-- apps/cs/src/pages/menu/ui/{ => form}/outside-menu-form.tsx | 2 +- 11 files changed, 15 insertions(+), 15 deletions(-) rename apps/cs/src/pages/menu/ui/{ => dialog}/content-dialog.tsx (91%) rename apps/cs/src/pages/menu/ui/{ => dialog}/permissions-management-dialog.tsx (100%) rename apps/cs/src/pages/menu/ui/{ => form}/base-menu-form.tsx (98%) rename apps/cs/src/pages/menu/ui/{ => form}/base-type-menu-form.tsx (98%) rename apps/cs/src/pages/menu/ui/{ => form}/board-menu-form.tsx (98%) rename apps/cs/src/pages/menu/ui/{ => form}/bottom-menu-form.tsx (88%) rename apps/cs/src/pages/menu/ui/{ => form}/content-menu-form.tsx (95%) rename apps/cs/src/pages/menu/ui/{ => form}/empty-menu-form.tsx (88%) rename apps/cs/src/pages/menu/ui/{ => form}/inside-menu-form.tsx (95%) rename apps/cs/src/pages/menu/ui/{ => form}/menu-form.tsx (96%) rename apps/cs/src/pages/menu/ui/{ => form}/outside-menu-form.tsx (95%) diff --git a/apps/cs/src/pages/menu/ui/content-dialog.tsx b/apps/cs/src/pages/menu/ui/dialog/content-dialog.tsx similarity index 91% rename from apps/cs/src/pages/menu/ui/content-dialog.tsx rename to apps/cs/src/pages/menu/ui/dialog/content-dialog.tsx index e6aedbb00..7c99637bf 100644 --- a/apps/cs/src/pages/menu/ui/content-dialog.tsx +++ b/apps/cs/src/pages/menu/ui/dialog/content-dialog.tsx @@ -15,9 +15,9 @@ import { DialogTitle, } from "@common/components/ui"; import { ENDPOINT } from "@/shared/config"; -import { ContentApi } from "../api/content-service"; -import type { Content } from "../model/content-interface"; -import { columns } from "../model/table-colums"; +import { ContentApi } from "../../api/content-service"; +import type { Content } from "../../model/content-interface"; +import { columns } from "../../model/table-colums"; type ContentDialogProps = { handleContentSelect: (content: Content) => void; diff --git a/apps/cs/src/pages/menu/ui/permissions-management-dialog.tsx b/apps/cs/src/pages/menu/ui/dialog/permissions-management-dialog.tsx similarity index 100% rename from apps/cs/src/pages/menu/ui/permissions-management-dialog.tsx rename to apps/cs/src/pages/menu/ui/dialog/permissions-management-dialog.tsx diff --git a/apps/cs/src/pages/menu/ui/base-menu-form.tsx b/apps/cs/src/pages/menu/ui/form/base-menu-form.tsx similarity index 98% rename from apps/cs/src/pages/menu/ui/base-menu-form.tsx rename to apps/cs/src/pages/menu/ui/form/base-menu-form.tsx index 62a602a33..7e58a63eb 100644 --- a/apps/cs/src/pages/menu/ui/base-menu-form.tsx +++ b/apps/cs/src/pages/menu/ui/form/base-menu-form.tsx @@ -16,7 +16,7 @@ import { FormField, FormMessage, } from "@/shared/ui/form"; -import { MENU_TYPE, type MenuFormData } from "../model/menu-interface"; +import { MENU_TYPE, type MenuFormData } from "../../model/menu-interface"; interface BaseMenuFormProps { form: UseFormReturn; diff --git a/apps/cs/src/pages/menu/ui/base-type-menu-form.tsx b/apps/cs/src/pages/menu/ui/form/base-type-menu-form.tsx similarity index 98% rename from apps/cs/src/pages/menu/ui/base-type-menu-form.tsx rename to apps/cs/src/pages/menu/ui/form/base-type-menu-form.tsx index 5f7c3bb45..58047ff3c 100644 --- a/apps/cs/src/pages/menu/ui/base-type-menu-form.tsx +++ b/apps/cs/src/pages/menu/ui/form/base-type-menu-form.tsx @@ -15,7 +15,7 @@ import { FormField, FormMessage, } from "@/shared/ui/form"; -import type { MenuFormData } from "../model/menu-interface"; +import type { MenuFormData } from "../../model/menu-interface"; function BaseTypeMenuForm({ form }: { form: UseFormReturn }) { const handleIconClick = (icon: string) => { diff --git a/apps/cs/src/pages/menu/ui/board-menu-form.tsx b/apps/cs/src/pages/menu/ui/form/board-menu-form.tsx similarity index 98% rename from apps/cs/src/pages/menu/ui/board-menu-form.tsx rename to apps/cs/src/pages/menu/ui/form/board-menu-form.tsx index df435e2e5..ec2b15378 100644 --- a/apps/cs/src/pages/menu/ui/board-menu-form.tsx +++ b/apps/cs/src/pages/menu/ui/form/board-menu-form.tsx @@ -15,7 +15,7 @@ import { FormField, FormMessage, } from "@/shared/ui/form"; -import type { MenuFormData } from "../model/menu-interface"; +import type { MenuFormData } from "../../model/menu-interface"; import { BaseTypeMenuForm } from "./base-type-menu-form"; function BoardMenuForm({ form }: { form: UseFormReturn }) { diff --git a/apps/cs/src/pages/menu/ui/bottom-menu-form.tsx b/apps/cs/src/pages/menu/ui/form/bottom-menu-form.tsx similarity index 88% rename from apps/cs/src/pages/menu/ui/bottom-menu-form.tsx rename to apps/cs/src/pages/menu/ui/form/bottom-menu-form.tsx index 88565210b..1a1b196f9 100644 --- a/apps/cs/src/pages/menu/ui/bottom-menu-form.tsx +++ b/apps/cs/src/pages/menu/ui/form/bottom-menu-form.tsx @@ -1,6 +1,6 @@ import { type UseFormReturn } from "react-hook-form"; import { Table, TableBody } from "@common/components"; -import type { MenuFormData } from "../model/menu-interface"; +import type { MenuFormData } from "../../model/menu-interface"; import { BaseTypeMenuForm } from "./base-type-menu-form"; function BottomMenuForm({ form }: { form: UseFormReturn }) { diff --git a/apps/cs/src/pages/menu/ui/content-menu-form.tsx b/apps/cs/src/pages/menu/ui/form/content-menu-form.tsx similarity index 95% rename from apps/cs/src/pages/menu/ui/content-menu-form.tsx rename to apps/cs/src/pages/menu/ui/form/content-menu-form.tsx index 7ce40d940..ed16547f8 100644 --- a/apps/cs/src/pages/menu/ui/content-menu-form.tsx +++ b/apps/cs/src/pages/menu/ui/form/content-menu-form.tsx @@ -15,10 +15,10 @@ import { FormField, FormMessage, } from "@/shared/ui/form"; -import type { MenuFormData } from "../model/menu-interface"; -import type { Content } from "../model/content-interface"; +import type { MenuFormData } from "../../model/menu-interface"; +import type { Content } from "../../model/content-interface"; +import { ContentDialog } from "../dialog/content-dialog"; import { BaseTypeMenuForm } from "./base-type-menu-form"; -import { ContentDialog } from "./content-dialog"; function ContentMenuForm({ form }: { form: UseFormReturn }) { const handleContentSelect = (content: Content) => { diff --git a/apps/cs/src/pages/menu/ui/empty-menu-form.tsx b/apps/cs/src/pages/menu/ui/form/empty-menu-form.tsx similarity index 88% rename from apps/cs/src/pages/menu/ui/empty-menu-form.tsx rename to apps/cs/src/pages/menu/ui/form/empty-menu-form.tsx index 78339d90f..1246a22ae 100644 --- a/apps/cs/src/pages/menu/ui/empty-menu-form.tsx +++ b/apps/cs/src/pages/menu/ui/form/empty-menu-form.tsx @@ -1,6 +1,6 @@ import { type UseFormReturn } from "react-hook-form"; import { Table, TableBody } from "@common/components"; -import type { MenuFormData } from "../model/menu-interface"; +import type { MenuFormData } from "../../model/menu-interface"; import { BaseTypeMenuForm } from "./base-type-menu-form"; function EmptyMenuForm({ form }: { form: UseFormReturn }) { diff --git a/apps/cs/src/pages/menu/ui/inside-menu-form.tsx b/apps/cs/src/pages/menu/ui/form/inside-menu-form.tsx similarity index 95% rename from apps/cs/src/pages/menu/ui/inside-menu-form.tsx rename to apps/cs/src/pages/menu/ui/form/inside-menu-form.tsx index f671c7d26..f0569cfe6 100644 --- a/apps/cs/src/pages/menu/ui/inside-menu-form.tsx +++ b/apps/cs/src/pages/menu/ui/form/inside-menu-form.tsx @@ -13,7 +13,7 @@ import { FormControl, FormMessage, } from "@/shared/ui/form"; -import type { MenuFormData } from "../model/menu-interface"; +import type { MenuFormData } from "../../model/menu-interface"; import { BaseTypeMenuForm } from "./base-type-menu-form"; function InsideMenuForm({ form }: { form: UseFormReturn }) { diff --git a/apps/cs/src/pages/menu/ui/menu-form.tsx b/apps/cs/src/pages/menu/ui/form/menu-form.tsx similarity index 96% rename from apps/cs/src/pages/menu/ui/menu-form.tsx rename to apps/cs/src/pages/menu/ui/form/menu-form.tsx index 922c2bb78..9a09b7c81 100644 --- a/apps/cs/src/pages/menu/ui/menu-form.tsx +++ b/apps/cs/src/pages/menu/ui/form/menu-form.tsx @@ -4,7 +4,7 @@ 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 { 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"; @@ -12,7 +12,7 @@ import { BoardMenuForm } from "./board-menu-form"; import { InsideMenuForm } from "./inside-menu-form"; import { OutsideMenuForm } from "./outside-menu-form"; import { BottomMenuForm } from "./bottom-menu-form"; -import { PermissionsManagementDialog } from "./permissions-management-dialog"; +import { PermissionsManagementDialog } from "../dialog/permissions-management-dialog"; interface MenuFormProps { form: UseFormReturn; diff --git a/apps/cs/src/pages/menu/ui/outside-menu-form.tsx b/apps/cs/src/pages/menu/ui/form/outside-menu-form.tsx similarity index 95% rename from apps/cs/src/pages/menu/ui/outside-menu-form.tsx rename to apps/cs/src/pages/menu/ui/form/outside-menu-form.tsx index 035d2dbda..4e8a402f3 100644 --- a/apps/cs/src/pages/menu/ui/outside-menu-form.tsx +++ b/apps/cs/src/pages/menu/ui/form/outside-menu-form.tsx @@ -13,7 +13,7 @@ import { FormControl, FormMessage, } from "@/shared/ui/form"; -import type { MenuFormData } from "../model/menu-interface"; +import type { MenuFormData } from "../../model/menu-interface"; import { BaseTypeMenuForm } from "./base-type-menu-form"; function OutsideMenuForm({ form }: { form: UseFormReturn }) { -- 2.47.1.windows.1 From 6a784cecf3b7c7c557e4264412e33dc749be625a Mon Sep 17 00:00:00 2001 From: bicochan Date: Fri, 28 Mar 2025 10:53:16 +0900 Subject: [PATCH 16/24] =?UTF-8?q?feat:=20=EC=BD=98=ED=85=90=EC=B8=A0=20?= =?UTF-8?q?=EA=B2=80=EC=83=89=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/pages/menu/model/content-interface.ts | 5 ++- .../pages/menu/ui/dialog/content-dialog.tsx | 31 ++++++++++++++++--- apps/cs/src/pages/menu/ui/menu-page.tsx | 4 +-- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/apps/cs/src/pages/menu/model/content-interface.ts b/apps/cs/src/pages/menu/model/content-interface.ts index dc789d059..8d59b1516 100644 --- a/apps/cs/src/pages/menu/model/content-interface.ts +++ b/apps/cs/src/pages/menu/model/content-interface.ts @@ -1,9 +1,8 @@ import type { PageableList, PageableParams } from "@/shared/model"; export interface ContentParams extends PageableParams { - siteId?: string; - menuKornNm?: string; - urlPath?: string; + contsTtl?: string; + aprvContsVer?: number; useYn?: string; } diff --git a/apps/cs/src/pages/menu/ui/dialog/content-dialog.tsx b/apps/cs/src/pages/menu/ui/dialog/content-dialog.tsx index 7c99637bf..00ad8810a 100644 --- a/apps/cs/src/pages/menu/ui/dialog/content-dialog.tsx +++ b/apps/cs/src/pages/menu/ui/dialog/content-dialog.tsx @@ -13,6 +13,7 @@ import { DialogContent, DialogHeader, DialogTitle, + Input, } from "@common/components/ui"; import { ENDPOINT } from "@/shared/config"; import { ContentApi } from "../../api/content-service"; @@ -25,11 +26,13 @@ type ContentDialogProps = { function ContentDialog({ handleContentSelect, ...props }: ContentDialogProps) { const [rowSelection, setRowSelection] = useState({}); + const [contsTtl, setContsTtl] = useState(""); - const { data: contentList } = useQuery({ + const { data: contentList, refetch } = useQuery({ queryKey: [ENDPOINT.CMS_SERVICE.CONTENTS], queryFn: () => - ContentApi.getContentList({ useYn: "Y", page: 0, size: 100 }), + ContentApi.getContentList({ contsTtl, useYn: "Y", page: 0, size: 100 }), + enabled: false, }); const table = useReactTable({ @@ -50,6 +53,10 @@ function ContentDialog({ handleContentSelect, ...props }: ContentDialogProps) { handleContentSelect(contentData); }, [table.getSelectedRowModel().rows]); + useEffect(() => { + refetch(); + }, []); + return ( @@ -60,10 +67,24 @@ function ContentDialog({ handleContentSelect, ...props }: ContentDialogProps) { - 콘텐츠 선택 + 콘텐츠 조회 -
- +
+
+ setContsTtl(e.target.value)} + /> + +
+
+ +
diff --git a/apps/cs/src/pages/menu/ui/menu-page.tsx b/apps/cs/src/pages/menu/ui/menu-page.tsx index f06f7a773..c1a83ab3e 100644 --- a/apps/cs/src/pages/menu/ui/menu-page.tsx +++ b/apps/cs/src/pages/menu/ui/menu-page.tsx @@ -27,7 +27,7 @@ import { type MenuFormData, } from "../model/menu-interface"; import { MenuTree } from "./menu-tree"; -import { MenuForm } from "./menu-form"; +import { MenuForm } from "./form/menu-form"; import { generateTreeData } from "../lib/utils"; function MenuPage() { @@ -119,10 +119,10 @@ function MenuPage() { sortSeq: menuInfo?.sortSeq ?? 1, menuKornNm: menuInfo?.menuKornNm ?? "", menuLnkgTypeCd: menuInfo?.menuLnkgTypeCd ?? MENU_TYPE.EMPTY, + iconNm: menuInfo?.iconName, otptYn: menuInfo?.otptYn ?? "Y", evlUseYn: menuInfo?.evlUseYn ?? "Y", useYn: menuInfo?.useYn ?? "Y", - iconNm: menuInfo?.iconName, }, }); -- 2.47.1.windows.1 From 572f18cc1a2e8bb0ce20dbdd5ac5b821253e7d83 Mon Sep 17 00:00:00 2001 From: bicochan Date: Fri, 28 Mar 2025 15:22:14 +0900 Subject: [PATCH 17/24] =?UTF-8?q?feat:=20=EC=BD=98=ED=85=90=EC=B8=A0=20?= =?UTF-8?q?=EB=A9=94=EB=89=B4=20=EB=93=B1=EB=A1=9D=20=EC=9E=91=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pages/menu/api/organization-service.ts | 17 ++ apps/cs/src/pages/menu/lib/utils.ts | 68 ++++++- .../menu/model/organization-interface.ts | 18 ++ .../menu/ui/dialog/organization-dialog.tsx | 182 ++++++++++++++++++ .../pages/menu/ui/form/content-menu-form.tsx | 50 ++++- apps/cs/src/pages/menu/ui/menu-page.tsx | 4 +- apps/cs/src/shared/config/api.ts | 1 + 7 files changed, 331 insertions(+), 9 deletions(-) create mode 100644 apps/cs/src/pages/menu/api/organization-service.ts create mode 100644 apps/cs/src/pages/menu/model/organization-interface.ts create mode 100644 apps/cs/src/pages/menu/ui/dialog/organization-dialog.tsx diff --git a/apps/cs/src/pages/menu/api/organization-service.ts b/apps/cs/src/pages/menu/api/organization-service.ts new file mode 100644 index 000000000..9ef3b55fa --- /dev/null +++ b/apps/cs/src/pages/menu/api/organization-service.ts @@ -0,0 +1,17 @@ +import { client } from "@/shared/api"; +import { ENDPOINT } from "@/shared/config"; +import type { + Organization, + OrganizationService, +} from "../model/organization-interface"; + +export class OrganizationApiService implements OrganizationService { + public async getOrganizationUserTreeList(): Promise { + const response = await client.get( + `${ENDPOINT.CMS_SERVICE.USER_OGNZ}/treeuser`, + ); + return response.data; + } +} + +export const OrganizationApi = new OrganizationApiService(); diff --git a/apps/cs/src/pages/menu/lib/utils.ts b/apps/cs/src/pages/menu/lib/utils.ts index 304c7166a..b1dbafe5f 100644 --- a/apps/cs/src/pages/menu/lib/utils.ts +++ b/apps/cs/src/pages/menu/lib/utils.ts @@ -1,7 +1,10 @@ import type { DropOptions, NodeModel } from "@minoru/react-dnd-treeview"; import type { TreeMenu, TreeNode } from "../model/menu-interface"; +import type { Organization, User } from "../model/organization-interface"; -export const generateTreeData = (data: TreeMenu[]): NodeModel[] => { +export const generateMenuTreeData = ( + data: TreeMenu[], +): NodeModel[] => { return data.reduce[]>((acc, menu) => { const node: NodeModel = { id: menu.menuCd ?? 0, @@ -14,14 +17,14 @@ export const generateTreeData = (data: TreeMenu[]): NodeModel[] => { acc.push(node); if (menu.children?.length) { - acc.push(...generateTreeData(menu.children)); + acc.push(...generateMenuTreeData(menu.children)); } return acc; }, []); }; -export const generateNewTreeData = ( +export const generateNewMenuTreeData = ( treeData: NodeModel[], options: DropOptions, ): TreeNode | null => { @@ -82,7 +85,7 @@ export const handleSelectNode = export const handleDropNode = (callback: (newTree: TreeNode) => void) => (newTree: NodeModel[], options: DropOptions) => { - const newTreeData = generateNewTreeData(newTree, options); + const newTreeData = generateNewMenuTreeData(newTree, options); if (!newTreeData) return; callback(newTreeData); }; @@ -103,3 +106,60 @@ export const handleNodeTextChange = }); callback(newTree); }; + +export const generateOrganizationTreeData = ( + organizations: Organization[], +): NodeModel[] => { + return organizations.reduce[]>((acc, org) => { + const orgNode: NodeModel = { + id: org.ognzId, + parent: org.upOgnzId ?? 0, + text: org.ognzNm, + data: org, + droppable: false, + }; + + acc.push(orgNode); + + if (org.children?.length) { + acc.push(...generateOrganizationTreeData(org.children)); + } + + if (org.users?.length) { + acc.push( + ...org.users.map((user) => ({ + id: user.userUnqId, + parent: org.ognzId, + text: user.userNm, + data: user, + droppable: false, + })), + ); + } + + return acc; + }, []); +}; + +export const filterOrganizationTreeDataByKeyword = ( + treeData: NodeModel[], + keyword: string, +): NodeModel[] => { + return Array.from( + treeData.reduce((matchedNodes, node) => { + if (!node.text.includes(keyword)) return matchedNodes; + + matchedNodes.add(node); + + let parentId = node.parent; + while (parentId) { + const parentNode = treeData.find((n) => n.id === parentId); + if (!parentNode) break; + matchedNodes.add(parentNode); + parentId = parentNode.parent; + } + + return matchedNodes; + }, new Set>()), + ); +}; diff --git a/apps/cs/src/pages/menu/model/organization-interface.ts b/apps/cs/src/pages/menu/model/organization-interface.ts new file mode 100644 index 000000000..86a8b8e31 --- /dev/null +++ b/apps/cs/src/pages/menu/model/organization-interface.ts @@ -0,0 +1,18 @@ +export interface User { + userUnqId: string; + userNm: string; + jbpsSeCd: string; + jbgdSeCd: string; +} + +export interface Organization { + ognzId: string; + upOgnzId: string; + ognzNm: string; + users: User[]; + children: Organization[]; +} + +export interface OrganizationService { + getOrganizationUserTreeList(): Promise; +} diff --git a/apps/cs/src/pages/menu/ui/dialog/organization-dialog.tsx b/apps/cs/src/pages/menu/ui/dialog/organization-dialog.tsx new file mode 100644 index 000000000..47fecb1ba --- /dev/null +++ b/apps/cs/src/pages/menu/ui/dialog/organization-dialog.tsx @@ -0,0 +1,182 @@ +"use client"; + +import { useEffect, useState } from "react"; +import { useQuery } from "@tanstack/react-query"; +import { DndProvider } from "react-dnd"; +import { + Tree, + MultiBackend, + getBackendOptions, + type NodeModel, +} from "@minoru/react-dnd-treeview"; +import { + Button, + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogTrigger, + Input, +} from "@common/components/ui"; +import { cn } from "@common/components/lib"; +import { ENDPOINT } from "@/shared/config"; +import { OrganizationApi } from "../../api/organization-service"; +import type { Organization, User } from "../../model/organization-interface"; +import { + filterOrganizationTreeDataByKeyword, + generateOrganizationTreeData, +} from "../../lib/utils"; + +type TreeModel = Organization | User; + +interface OrganizationDialogProps { + handleUserSelect: (user: User) => void; +} + +function OrganizationDialog({ handleUserSelect }: OrganizationDialogProps) { + const [keyword, setKeyword] = useState(""); + const [tree, setTree] = useState[]>([]); + const [selectedNode, setSelectedNode] = useState | null>( + null, + ); + + const { data: treeData } = useQuery({ + queryKey: [ENDPOINT.CMS_SERVICE.USER_OGNZ], + queryFn: () => OrganizationApi.getOrganizationUserTreeList(), + select: (data) => generateOrganizationTreeData(data), + }); + + const handleSelectNode = (node: NodeModel) => { + setSelectedNode(node); + handleUserSelect(node.data as User); + }; + + useEffect(() => { + setTree(treeData ?? []); + }, [treeData]); + + return ( + + + + + + + 담당자 조회 + +
+
+ setKeyword(e.target.value)} + /> + +
+
+ + , + { depth, isOpen, onToggle }, + ) => ( + + )} + classes={{ + container: "flex flex-col", + }} + sort={false} + onDrop={() => {}} + /> + +
+
+
+
+ ); +} + +interface TreeNodeProps { + node: NodeModel; + depth: number; + isOpen: boolean; + selectedNode: NodeModel | null; + onToggle: (id: NodeModel["id"]) => void; + onSelect: (node: NodeModel) => void; +} + +const ORGANIZATION_PREFIX = "OGNZ"; + +function TreeNode({ + node, + depth, + isOpen, + selectedNode, + onToggle, + onSelect, +}: TreeNodeProps) { + const { id, text, data } = node; + const indent = depth * 15 + 5; + + const handleToggle = () => onToggle(id); + const handleSelect = () => { + if (id.toString().startsWith(ORGANIZATION_PREFIX)) return; + onSelect(node); + }; + + const isSelected = selectedNode?.id === id; + + return ( +
+
+
+ {data && ("children" in data || "users" in data) && ( + + )} + {text} +
+ {isSelected && } +
+
+ ); +} + +export { OrganizationDialog }; diff --git a/apps/cs/src/pages/menu/ui/form/content-menu-form.tsx b/apps/cs/src/pages/menu/ui/form/content-menu-form.tsx index ed16547f8..1e74109f3 100644 --- a/apps/cs/src/pages/menu/ui/form/content-menu-form.tsx +++ b/apps/cs/src/pages/menu/ui/form/content-menu-form.tsx @@ -1,5 +1,7 @@ -import { type UseFormReturn } from "react-hook-form"; +import dayjs from "dayjs"; +import type { UseFormReturn } from "react-hook-form"; import { + DatePicker, Label, RadioGroup, RadioGroupItem, @@ -17,7 +19,9 @@ import { } from "@/shared/ui/form"; import type { MenuFormData } from "../../model/menu-interface"; import type { Content } from "../../model/content-interface"; +import type { User } from "../../model/organization-interface"; import { ContentDialog } from "../dialog/content-dialog"; +import { OrganizationDialog } from "../dialog/organization-dialog"; import { BaseTypeMenuForm } from "./base-type-menu-form"; function ContentMenuForm({ form }: { form: UseFormReturn }) { @@ -26,6 +30,16 @@ function ContentMenuForm({ form }: { form: UseFormReturn }) { form.setValue("menuLnkgSnNm", content.contsTtl); }; + const handleUserSelect = (user: User) => { + form.setValue("menuPicUserUnqId", user.userUnqId); + form.setValue("menuPicUserUnqIdNm", user.userNm); + }; + + const handleDateSelect = (date: Date | undefined) => { + if (!date) return; + form.setValue("menuPicDsgnYmd", dayjs(date).format("YYYYMMDD")); + }; + return ( @@ -113,8 +127,38 @@ function ContentMenuForm({ form }: { form: UseFormReturn }) { 담당자 -
- {form.watch("menuLnkgSn")} +
+
+
+ {form.watch("menuPicUserUnqIdNm")} +
+ +
+
+ 지정일: + ( + + + { + field.onChange(date); + handleDateSelect(date); + }} + /> + + + )} + /> +
diff --git a/apps/cs/src/pages/menu/ui/menu-page.tsx b/apps/cs/src/pages/menu/ui/menu-page.tsx index c1a83ab3e..6a9580856 100644 --- a/apps/cs/src/pages/menu/ui/menu-page.tsx +++ b/apps/cs/src/pages/menu/ui/menu-page.tsx @@ -28,7 +28,7 @@ import { } from "../model/menu-interface"; import { MenuTree } from "./menu-tree"; import { MenuForm } from "./form/menu-form"; -import { generateTreeData } from "../lib/utils"; +import { generateMenuTreeData } from "../lib/utils"; function MenuPage() { const queryClient = useQueryClient(); @@ -51,7 +51,7 @@ function MenuPage() { queryKey: [ENDPOINT.CMS_SERVICE.MENUS, siteId], queryFn: () => MenuApi.getTreeMenuList(siteId), select: (data) => { - const treeData = generateTreeData(data); + const treeData = generateMenuTreeData(data); const defaultSortSeq = treeData.filter((node) => node.data?.levelNo === 1).length + 1; form.setValue("sortSeq", defaultSortSeq); diff --git a/apps/cs/src/shared/config/api.ts b/apps/cs/src/shared/config/api.ts index b938ceaa9..f57b79adf 100644 --- a/apps/cs/src/shared/config/api.ts +++ b/apps/cs/src/shared/config/api.ts @@ -15,6 +15,7 @@ export const ENDPOINT = { DOMAINS: "/cms-service/api/v1/site-dmns", MENUS: "/cms-service/api/v1/menus", CONTENTS: "/cms-service/api/v1/contents", + USER_OGNZ: "/cms-service/api/v1/user-ognz", }, }; -- 2.47.1.windows.1 From f59320d2ff4258a3c5986277f66954d01723aa47 Mon Sep 17 00:00:00 2001 From: bicochan Date: Fri, 28 Mar 2025 16:38:29 +0900 Subject: [PATCH 18/24] =?UTF-8?q?refactor:=20=EB=A9=94=EB=89=B4=20?= =?UTF-8?q?=ED=8F=BC=20=ED=8F=B4=EB=8D=94=20=EA=B5=AC=EC=A1=B0=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit rename: 다이얼로그 폴더 제거 chore: 참조 경로 수정 --- .../cs/src/pages/menu/model/menu-interface.ts | 1 + .../menu/ui/{form => }/base-menu-form.tsx | 9 +- .../menu/ui/{dialog => }/content-dialog.tsx | 6 +- .../menu/ui/form/base-type-menu-form.tsx | 128 ------- .../pages/menu/ui/form/board-menu-form.tsx | 146 -------- .../pages/menu/ui/form/bottom-menu-form.tsx | 20 -- .../pages/menu/ui/form/content-menu-form.tsx | 170 --------- .../pages/menu/ui/form/empty-menu-form.tsx | 20 -- .../pages/menu/ui/form/inside-menu-form.tsx | 56 --- .../pages/menu/ui/form/outside-menu-form.tsx | 56 --- .../pages/menu/ui/{form => }/menu-form.tsx | 39 +-- apps/cs/src/pages/menu/ui/menu-page.tsx | 2 +- .../ui/{dialog => }/organization-dialog.tsx | 6 +- .../permissions-management-dialog.tsx | 0 apps/cs/src/pages/menu/ui/type-menu-form.tsx | 331 ++++++++++++++++++ 15 files changed, 347 insertions(+), 643 deletions(-) rename apps/cs/src/pages/menu/ui/{form => }/base-menu-form.tsx (92%) rename apps/cs/src/pages/menu/ui/{dialog => }/content-dialog.tsx (93%) delete mode 100644 apps/cs/src/pages/menu/ui/form/base-type-menu-form.tsx delete mode 100644 apps/cs/src/pages/menu/ui/form/board-menu-form.tsx delete mode 100644 apps/cs/src/pages/menu/ui/form/bottom-menu-form.tsx delete mode 100644 apps/cs/src/pages/menu/ui/form/content-menu-form.tsx delete mode 100644 apps/cs/src/pages/menu/ui/form/empty-menu-form.tsx delete mode 100644 apps/cs/src/pages/menu/ui/form/inside-menu-form.tsx delete mode 100644 apps/cs/src/pages/menu/ui/form/outside-menu-form.tsx rename apps/cs/src/pages/menu/ui/{form => }/menu-form.tsx (69%) rename apps/cs/src/pages/menu/ui/{dialog => }/organization-dialog.tsx (96%) rename apps/cs/src/pages/menu/ui/{dialog => }/permissions-management-dialog.tsx (100%) create mode 100644 apps/cs/src/pages/menu/ui/type-menu-form.tsx diff --git a/apps/cs/src/pages/menu/model/menu-interface.ts b/apps/cs/src/pages/menu/model/menu-interface.ts index fcee4c505..51258fc49 100644 --- a/apps/cs/src/pages/menu/model/menu-interface.ts +++ b/apps/cs/src/pages/menu/model/menu-interface.ts @@ -59,6 +59,7 @@ export const menuFormSchema = z.object({ evlUseYn: z.string().default("Y"), qrcd: z.string().nullish(), useYn: z.string().default("Y"), + newWndwYn: z.string().nullish(), menuLnkgSnNm: z.string().nullish(), menuPicUserUnqId: z.string().nullish(), menuPicUserUnqIdNm: z.string().nullish(), diff --git a/apps/cs/src/pages/menu/ui/form/base-menu-form.tsx b/apps/cs/src/pages/menu/ui/base-menu-form.tsx similarity index 92% rename from apps/cs/src/pages/menu/ui/form/base-menu-form.tsx rename to apps/cs/src/pages/menu/ui/base-menu-form.tsx index 7e58a63eb..62d69bb38 100644 --- a/apps/cs/src/pages/menu/ui/form/base-menu-form.tsx +++ b/apps/cs/src/pages/menu/ui/base-menu-form.tsx @@ -16,7 +16,7 @@ import { FormField, FormMessage, } from "@/shared/ui/form"; -import { MENU_TYPE, type MenuFormData } from "../../model/menu-interface"; +import { MENU_TYPE, type MenuFormData } from "../model/menu-interface"; interface BaseMenuFormProps { form: UseFormReturn; @@ -104,11 +104,8 @@ function BaseMenuForm({ form, menuCd }: BaseMenuFormProps) { key={key} className="flex items-center space-x-2" > - -
- - - - - - - 게시판 - -
- {form.watch("menuLnkgSn")} -
-
-
- - 연결 형태 - - ( - - - -
-
- - -
-
- - -
-
-
-
- -
- )} - /> -
-
- - 메뉴 평가 사용 여부 - - ( - - - -
-
- - -
-
- - -
-
-
-
- -
- )} - /> -
-
- - - - - QR코드 - - ( - - - -
-
- - -
-
- - -
-
-
-
- -
- )} - /> -
-
- - 담당자 - -
- {form.watch("menuLnkgSn")} -
-
-
-
-
- ); -} - -export { BoardMenuForm }; diff --git a/apps/cs/src/pages/menu/ui/form/bottom-menu-form.tsx b/apps/cs/src/pages/menu/ui/form/bottom-menu-form.tsx deleted file mode 100644 index 1a1b196f9..000000000 --- a/apps/cs/src/pages/menu/ui/form/bottom-menu-form.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { type UseFormReturn } from "react-hook-form"; -import { Table, TableBody } from "@common/components"; -import type { MenuFormData } from "../../model/menu-interface"; -import { BaseTypeMenuForm } from "./base-type-menu-form"; - -function BottomMenuForm({ form }: { form: UseFormReturn }) { - return ( - - - - - - - - -
- ); -} - -export { BottomMenuForm }; diff --git a/apps/cs/src/pages/menu/ui/form/content-menu-form.tsx b/apps/cs/src/pages/menu/ui/form/content-menu-form.tsx deleted file mode 100644 index 1e74109f3..000000000 --- a/apps/cs/src/pages/menu/ui/form/content-menu-form.tsx +++ /dev/null @@ -1,170 +0,0 @@ -import dayjs from "dayjs"; -import type { UseFormReturn } from "react-hook-form"; -import { - DatePicker, - 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"; -import type { Content } from "../../model/content-interface"; -import type { User } from "../../model/organization-interface"; -import { ContentDialog } from "../dialog/content-dialog"; -import { OrganizationDialog } from "../dialog/organization-dialog"; -import { BaseTypeMenuForm } from "./base-type-menu-form"; - -function ContentMenuForm({ form }: { form: UseFormReturn }) { - const handleContentSelect = (content: Content) => { - form.setValue("menuLnkgSn", content.contsSn); - form.setValue("menuLnkgSnNm", content.contsTtl); - }; - - const handleUserSelect = (user: User) => { - form.setValue("menuPicUserUnqId", user.userUnqId); - form.setValue("menuPicUserUnqIdNm", user.userNm); - }; - - const handleDateSelect = (date: Date | undefined) => { - if (!date) return; - form.setValue("menuPicDsgnYmd", dayjs(date).format("YYYYMMDD")); - }; - - return ( - - - - - - - - 콘텐츠 - -
-
- {form.watch("menuLnkgSnNm")} -
- -
-
-
- {/* TODO: 연결 형태 추가 */} - - 메뉴 평가 사용 여부 - - ( - - - -
-
- - -
-
- - -
-
-
-
- -
- )} - /> -
-
- - - - - QR코드 - - ( - - - -
-
- - -
-
- - -
-
-
-
- -
- )} - /> -
-
- - 담당자 - -
-
-
- {form.watch("menuPicUserUnqIdNm")} -
- -
-
- 지정일: - ( - - - { - field.onChange(date); - handleDateSelect(date); - }} - /> - - - )} - /> -
-
-
-
-
-
- ); -} - -export { ContentMenuForm }; diff --git a/apps/cs/src/pages/menu/ui/form/empty-menu-form.tsx b/apps/cs/src/pages/menu/ui/form/empty-menu-form.tsx deleted file mode 100644 index 1246a22ae..000000000 --- a/apps/cs/src/pages/menu/ui/form/empty-menu-form.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { type UseFormReturn } from "react-hook-form"; -import { Table, TableBody } from "@common/components"; -import type { MenuFormData } from "../../model/menu-interface"; -import { BaseTypeMenuForm } from "./base-type-menu-form"; - -function EmptyMenuForm({ form }: { form: UseFormReturn }) { - return ( - - - - - - - - -
- ); -} - -export { EmptyMenuForm }; diff --git a/apps/cs/src/pages/menu/ui/form/inside-menu-form.tsx b/apps/cs/src/pages/menu/ui/form/inside-menu-form.tsx deleted file mode 100644 index f0569cfe6..000000000 --- a/apps/cs/src/pages/menu/ui/form/inside-menu-form.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import { type UseFormReturn } from "react-hook-form"; -import { - Input, - Table, - TableBody, - TableCell, - TableHead, - TableRow, -} from "@common/components"; -import { - FormField, - FormItem, - FormControl, - FormMessage, -} from "@/shared/ui/form"; -import type { MenuFormData } from "../../model/menu-interface"; -import { BaseTypeMenuForm } from "./base-type-menu-form"; - -function InsideMenuForm({ form }: { form: UseFormReturn }) { - return ( - - - - - - - - 연결 URL - - ( - - - - - - - )} - /> - - - {/* TODO: 연결 형태 추가 */} - - -
- ); -} - -export { InsideMenuForm }; diff --git a/apps/cs/src/pages/menu/ui/form/outside-menu-form.tsx b/apps/cs/src/pages/menu/ui/form/outside-menu-form.tsx deleted file mode 100644 index 4e8a402f3..000000000 --- a/apps/cs/src/pages/menu/ui/form/outside-menu-form.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import { type UseFormReturn } from "react-hook-form"; -import { - Input, - Table, - TableBody, - TableCell, - TableHead, - TableRow, -} from "@common/components"; -import { - FormField, - FormItem, - FormControl, - FormMessage, -} from "@/shared/ui/form"; -import type { MenuFormData } from "../../model/menu-interface"; -import { BaseTypeMenuForm } from "./base-type-menu-form"; - -function OutsideMenuForm({ form }: { form: UseFormReturn }) { - return ( - - - - - - - - 연결 URL - - ( - - - - - - - )} - /> - - - {/* TODO: 연결 형태 추가 */} - - -
- ); -} - -export { OutsideMenuForm }; diff --git a/apps/cs/src/pages/menu/ui/form/menu-form.tsx b/apps/cs/src/pages/menu/ui/menu-form.tsx similarity index 69% rename from apps/cs/src/pages/menu/ui/form/menu-form.tsx rename to apps/cs/src/pages/menu/ui/menu-form.tsx index 9a09b7c81..48e7589be 100644 --- a/apps/cs/src/pages/menu/ui/form/menu-form.tsx +++ b/apps/cs/src/pages/menu/ui/menu-form.tsx @@ -1,18 +1,13 @@ import { useState } from "react"; +import type { UseFormReturn } from "react-hook-form"; 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 type { MenuFormData } from "../model/menu-interface"; import { BaseMenuForm } from "./base-menu-form"; -import { EmptyMenuForm } from "./empty-menu-form"; -import { ContentMenuForm } from "./content-menu-form"; -import { BoardMenuForm } from "./board-menu-form"; -import { InsideMenuForm } from "./inside-menu-form"; -import { OutsideMenuForm } from "./outside-menu-form"; -import { BottomMenuForm } from "./bottom-menu-form"; -import { PermissionsManagementDialog } from "../dialog/permissions-management-dialog"; +import { TypeMenuForm } from "./type-menu-form"; +import { PermissionsManagementDialog } from "./permissions-management-dialog"; interface MenuFormProps { form: UseFormReturn; @@ -94,9 +89,7 @@ function MenuForm({
-
- {renderTypeMenuForm(menuType, form)} -
+
@@ -116,26 +109,4 @@ function MenuForm({ ); } -function renderTypeMenuForm( - menuType: string, - form: UseFormReturn, -) { - switch (menuType) { - case MENU_TYPE.EMPTY: - return ; - case MENU_TYPE.CONTENTS: - return ; - case MENU_TYPE.BOARD: - return ; - case MENU_TYPE.INSIDE: - return ; - case MENU_TYPE.OUTSIDE: - return ; - case MENU_TYPE.BOTTOM: - return ; - default: - return null; - } -} - export { MenuForm }; diff --git a/apps/cs/src/pages/menu/ui/menu-page.tsx b/apps/cs/src/pages/menu/ui/menu-page.tsx index 6a9580856..87227fedd 100644 --- a/apps/cs/src/pages/menu/ui/menu-page.tsx +++ b/apps/cs/src/pages/menu/ui/menu-page.tsx @@ -27,7 +27,7 @@ import { type MenuFormData, } from "../model/menu-interface"; import { MenuTree } from "./menu-tree"; -import { MenuForm } from "./form/menu-form"; +import { MenuForm } from "./menu-form"; import { generateMenuTreeData } from "../lib/utils"; function MenuPage() { diff --git a/apps/cs/src/pages/menu/ui/dialog/organization-dialog.tsx b/apps/cs/src/pages/menu/ui/organization-dialog.tsx similarity index 96% rename from apps/cs/src/pages/menu/ui/dialog/organization-dialog.tsx rename to apps/cs/src/pages/menu/ui/organization-dialog.tsx index 47fecb1ba..ceb4cbbcf 100644 --- a/apps/cs/src/pages/menu/ui/dialog/organization-dialog.tsx +++ b/apps/cs/src/pages/menu/ui/organization-dialog.tsx @@ -20,12 +20,12 @@ import { } from "@common/components/ui"; import { cn } from "@common/components/lib"; import { ENDPOINT } from "@/shared/config"; -import { OrganizationApi } from "../../api/organization-service"; -import type { Organization, User } from "../../model/organization-interface"; +import { OrganizationApi } from "../api/organization-service"; +import type { Organization, User } from "../model/organization-interface"; import { filterOrganizationTreeDataByKeyword, generateOrganizationTreeData, -} from "../../lib/utils"; +} from "../lib/utils"; type TreeModel = Organization | User; diff --git a/apps/cs/src/pages/menu/ui/dialog/permissions-management-dialog.tsx b/apps/cs/src/pages/menu/ui/permissions-management-dialog.tsx similarity index 100% rename from apps/cs/src/pages/menu/ui/dialog/permissions-management-dialog.tsx rename to apps/cs/src/pages/menu/ui/permissions-management-dialog.tsx diff --git a/apps/cs/src/pages/menu/ui/type-menu-form.tsx b/apps/cs/src/pages/menu/ui/type-menu-form.tsx new file mode 100644 index 000000000..86080c555 --- /dev/null +++ b/apps/cs/src/pages/menu/ui/type-menu-form.tsx @@ -0,0 +1,331 @@ +import dayjs from "dayjs"; +import { type UseFormReturn } from "react-hook-form"; +import { + DatePicker, + IconFinder, + Input, + Label, + RadioGroup, + RadioGroupItem, + Table, + TableBody, + TableCell, + TableHead, + TableRow, +} from "@common/components"; +import { + FormItem, + FormControl, + FormField, + FormMessage, +} from "@/shared/ui/form"; +import type { Content } from "../model/content-interface"; +import type { User } from "../model/organization-interface"; +import type { MenuFormData, MenuType } from "../model/menu-interface"; +import { OrganizationDialog } from "./organization-dialog"; +import { ContentDialog } from "./content-dialog"; + +type TypeMenuFormProps = { + form: UseFormReturn; + menuType: MenuType; +}; + +function TypeMenuForm({ form, menuType }: TypeMenuFormProps) { + const handleIconClick = (icon: string) => { + form.setValue("iconNm", icon); + }; + + const handleContentSelect = (content: Content) => { + form.setValue("menuLnkgSn", content.contsSn); + form.setValue("menuLnkgSnNm", content.contsTtl); + }; + + const handleUserSelect = (user: User) => { + form.setValue("menuPicUserUnqId", user.userUnqId); + form.setValue("menuPicUserUnqIdNm", user.userNm); + }; + + const handleDateSelect = (date: Date | undefined) => { + if (!date) return; + form.setValue("menuPicDsgnYmd", dayjs(date).format("YYYYMMDD")); + }; + + return ( + + + + + + + {menuType === "CONTENTS" && ( + + 콘텐츠 + +
+
+ {form.watch("menuLnkgSnNm")} +
+ +
+
+
+ )} + {menuType === "BOARD" && ( + + 게시판 + +
+ {form.watch("menuLnkgSnNm")} +
+
+
+ )} + {(menuType === "CONTENTS" || + menuType === "BOARD" || + menuType === "INSIDE" || + menuType === "OUTSIDE") && ( + + 연결 형태 + + ( + + + +
+
+ + +
+
+ + +
+
+
+
+ +
+ )} + /> +
+
+ )} + {(menuType === "CONTENTS" || menuType === "BOARD") && ( + + 메뉴 평가 사용 여부 + + ( + + + +
+
+ + +
+
+ + +
+
+
+
+ +
+ )} + /> +
+
+ )} + + {/* 메뉴 유형 공통 폼 S */} + + 메뉴 서브명 + + ( + + + + + + + )} + /> + + + + 메뉴 아이콘 + +
+
+ {form.watch("iconNm")} +
+ +
+
+
+ + 출력 여부 + + ( + + + +
+
+ + +
+
+ + +
+
+
+
+ +
+ )} + /> +
+
+ + 사용 여부 + + ( + + + +
+
+ + +
+
+ + +
+
+
+
+ +
+ )} + /> +
+
+ {/* 메뉴 유형 공통 폼 E */} + + {(menuType === "CONTENTS" || menuType === "BOARD") && ( + <> + + QR코드 + + ( + + + +
+
+ + +
+
+ + +
+
+
+
+ +
+ )} + /> +
+
+ + 담당자 + +
+
+
+ {form.watch("menuPicUserUnqIdNm")} +
+ +
+
+ 지정일: + ( + + + { + field.onChange(date); + handleDateSelect(date); + }} + /> + + + )} + /> +
+
+
+
+ + )} +
+
+ ); +} + +export { TypeMenuForm }; -- 2.47.1.windows.1 From 701d042d4f8f39158cb63bb99fdb378a12469353 Mon Sep 17 00:00:00 2001 From: bicochan Date: Fri, 28 Mar 2025 16:44:32 +0900 Subject: [PATCH 19/24] =?UTF-8?q?rename:=20=EC=BD=98=ED=85=90=EC=B8=A0=20?= =?UTF-8?q?=EB=B0=8F=20=EB=8B=B4=EB=8B=B9=EC=9E=90=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EB=8B=A4=EC=9D=B4=EC=96=BC=EB=A1=9C=EA=B7=B8=20=EB=A6=AC?= =?UTF-8?q?=EB=84=A4=EC=9D=B4=EB=B0=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...{content-dialog.tsx => content-search-dialog.tsx} | 9 ++++++--- ...ion-dialog.tsx => organization-search-dialog.tsx} | 8 +++++--- apps/cs/src/pages/menu/ui/type-menu-form.tsx | 12 ++++++++---- 3 files changed, 19 insertions(+), 10 deletions(-) rename apps/cs/src/pages/menu/ui/{content-dialog.tsx => content-search-dialog.tsx} (94%) rename apps/cs/src/pages/menu/ui/{organization-dialog.tsx => organization-search-dialog.tsx} (96%) diff --git a/apps/cs/src/pages/menu/ui/content-dialog.tsx b/apps/cs/src/pages/menu/ui/content-search-dialog.tsx similarity index 94% rename from apps/cs/src/pages/menu/ui/content-dialog.tsx rename to apps/cs/src/pages/menu/ui/content-search-dialog.tsx index 2bb711833..60ee794b8 100644 --- a/apps/cs/src/pages/menu/ui/content-dialog.tsx +++ b/apps/cs/src/pages/menu/ui/content-search-dialog.tsx @@ -20,11 +20,14 @@ import { ContentApi } from "../api/content-service"; import type { Content } from "../model/content-interface"; import { columns } from "../model/table-colums"; -type ContentDialogProps = { +type ContentSearchDialogProps = { handleContentSelect: (content: Content) => void; } & React.ComponentProps; -function ContentDialog({ handleContentSelect, ...props }: ContentDialogProps) { +function ContentSearchDialog({ + handleContentSelect, + ...props +}: ContentSearchDialogProps) { const [rowSelection, setRowSelection] = useState({}); const [contsTtl, setContsTtl] = useState(""); @@ -91,4 +94,4 @@ function ContentDialog({ handleContentSelect, ...props }: ContentDialogProps) { ); } -export { ContentDialog }; +export { ContentSearchDialog }; diff --git a/apps/cs/src/pages/menu/ui/organization-dialog.tsx b/apps/cs/src/pages/menu/ui/organization-search-dialog.tsx similarity index 96% rename from apps/cs/src/pages/menu/ui/organization-dialog.tsx rename to apps/cs/src/pages/menu/ui/organization-search-dialog.tsx index ceb4cbbcf..9e0637082 100644 --- a/apps/cs/src/pages/menu/ui/organization-dialog.tsx +++ b/apps/cs/src/pages/menu/ui/organization-search-dialog.tsx @@ -29,11 +29,13 @@ import { type TreeModel = Organization | User; -interface OrganizationDialogProps { +interface OrganizationSearchDialogProps { handleUserSelect: (user: User) => void; } -function OrganizationDialog({ handleUserSelect }: OrganizationDialogProps) { +function OrganizationSearchDialog({ + handleUserSelect, +}: OrganizationSearchDialogProps) { const [keyword, setKeyword] = useState(""); const [tree, setTree] = useState[]>([]); const [selectedNode, setSelectedNode] = useState | null>( @@ -179,4 +181,4 @@ function TreeNode({ ); } -export { OrganizationDialog }; +export { OrganizationSearchDialog }; diff --git a/apps/cs/src/pages/menu/ui/type-menu-form.tsx b/apps/cs/src/pages/menu/ui/type-menu-form.tsx index 86080c555..209cadb1a 100644 --- a/apps/cs/src/pages/menu/ui/type-menu-form.tsx +++ b/apps/cs/src/pages/menu/ui/type-menu-form.tsx @@ -22,8 +22,8 @@ import { import type { Content } from "../model/content-interface"; import type { User } from "../model/organization-interface"; import type { MenuFormData, MenuType } from "../model/menu-interface"; -import { OrganizationDialog } from "./organization-dialog"; -import { ContentDialog } from "./content-dialog"; +import { OrganizationSearchDialog } from "./organization-search-dialog"; +import { ContentSearchDialog } from "./content-search-dialog"; type TypeMenuFormProps = { form: UseFormReturn; @@ -65,7 +65,9 @@ function TypeMenuForm({ form, menuType }: TypeMenuFormProps) {
{form.watch("menuLnkgSnNm")}
- +
@@ -291,7 +293,9 @@ function TypeMenuForm({ form, menuType }: TypeMenuFormProps) {
{form.watch("menuPicUserUnqIdNm")}
- +
지정일: -- 2.47.1.windows.1 From cd455fa343115dadeb547a3c2c059c5e2e5f039a Mon Sep 17 00:00:00 2001 From: bicochan Date: Fri, 28 Mar 2025 18:17:13 +0900 Subject: [PATCH 20/24] =?UTF-8?q?feat:=20=EA=B2=8C=EC=8B=9C=ED=8C=90=20?= =?UTF-8?q?=EB=A9=94=EB=89=B4=20=EB=93=B1=EB=A1=9D=20=EC=9E=91=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/entities/board/api/board-service.ts | 21 ++++ apps/cs/src/entities/board/index.ts | 2 + .../entities/board/model/board-interface.ts | 42 ++++++++ .../pages/menu/model/board-table-columns.tsx | 43 +++++++++ .../src/pages/menu/ui/board-search-dialog.tsx | 95 +++++++++++++++++++ apps/cs/src/pages/menu/ui/type-menu-form.tsx | 21 +++- apps/cs/src/shared/config/api.ts | 3 + 7 files changed, 225 insertions(+), 2 deletions(-) create mode 100644 apps/cs/src/entities/board/api/board-service.ts create mode 100644 apps/cs/src/entities/board/index.ts create mode 100644 apps/cs/src/entities/board/model/board-interface.ts create mode 100644 apps/cs/src/pages/menu/model/board-table-columns.tsx create mode 100644 apps/cs/src/pages/menu/ui/board-search-dialog.tsx diff --git a/apps/cs/src/entities/board/api/board-service.ts b/apps/cs/src/entities/board/api/board-service.ts new file mode 100644 index 000000000..9cf29599b --- /dev/null +++ b/apps/cs/src/entities/board/api/board-service.ts @@ -0,0 +1,21 @@ +import { client } from "@/shared/api"; +import { ENDPOINT } from "@/shared/config"; +import type { + BoardPopupList, + BoardPopupParams, + BoardService, +} from "../model/board-interface"; + +export class BoardApiService implements BoardService { + public async getBoardPopupList( + params: BoardPopupParams, + ): Promise { + const response = await client.get( + `${ENDPOINT.BOARD_SERVICE.BBS}`, + { params }, + ); + return response.data; + } +} + +export const BoardApi = new BoardApiService(); diff --git a/apps/cs/src/entities/board/index.ts b/apps/cs/src/entities/board/index.ts new file mode 100644 index 000000000..2173bb4a0 --- /dev/null +++ b/apps/cs/src/entities/board/index.ts @@ -0,0 +1,2 @@ +export { BoardApi } from "./api/board-service"; +export { type BoardPopup } from "./model/board-interface"; diff --git a/apps/cs/src/entities/board/model/board-interface.ts b/apps/cs/src/entities/board/model/board-interface.ts new file mode 100644 index 000000000..942840d74 --- /dev/null +++ b/apps/cs/src/entities/board/model/board-interface.ts @@ -0,0 +1,42 @@ +import type { PageableList, PageableParams } from "@/shared/model"; + +export const BOARD_TYPE = { + BBSNORMAL: "BBSNORMAL", + BBSFAQ: "BBSFAQ", + BBSIMAGE: "BBSIMAGE", + BBSMOVIE: "BBSMOVIE", +} as const; + +export type BoardType = (typeof BOARD_TYPE)[keyof typeof BOARD_TYPE]; + +export interface BoardPopupParams extends PageableParams { + bbsNm?: string; +} + +export interface BoardPopup { + bbsId: number; + bbsTypeCd: BoardType; + bbsTypeNm: string; + bbsNm: string; + bbsExpln: string; + rfrncUseYn: string; + atflUseYn: string; + atflMaxCnt: number; + atflMaxFileSz: number; + ansUseYn: string; + cmntUseYn: string; + dgstfnEvlUseYn: string; + smsUseYn: string; + upndFixUseYn: string; + pstgPrdUseYn: string; + popupUseYn: string; + delYn: string; + regDt: string; + pstCnt: number; +} + +export type BoardPopupList = PageableList; + +export interface BoardService { + getBoardPopupList: (params: BoardPopupParams) => Promise; +} diff --git a/apps/cs/src/pages/menu/model/board-table-columns.tsx b/apps/cs/src/pages/menu/model/board-table-columns.tsx new file mode 100644 index 000000000..f144ac158 --- /dev/null +++ b/apps/cs/src/pages/menu/model/board-table-columns.tsx @@ -0,0 +1,43 @@ +import { createColumnHelper } from "@tanstack/react-table"; +import { Checkbox } from "@common/components/ui"; +import type { BoardPopup } from "@/entities/board"; + +const columnHelper = createColumnHelper(); + +const columns = [ + columnHelper.accessor((_, index) => index + 1, { + id: "no", + header: "NO", + }), + columnHelper.accessor("bbsNm", { + id: "bbsNm", + header: "게시판명", + }), + columnHelper.accessor("bbsTypeNm", { + id: "bbsTypeNm", + header: "게시판유형", + }), + columnHelper.accessor("regDt", { + id: "regDt", + header: "최초생성일", + }), + columnHelper.accessor("pstCnt", { + id: "pstCnt", + header: "게시물건수", + }), + columnHelper.display({ + id: "select", + header: "선택", + cell: ({ row }) => { + return ( + + ); + }, + }), +]; + +export { columns }; diff --git a/apps/cs/src/pages/menu/ui/board-search-dialog.tsx b/apps/cs/src/pages/menu/ui/board-search-dialog.tsx new file mode 100644 index 000000000..531e17c86 --- /dev/null +++ b/apps/cs/src/pages/menu/ui/board-search-dialog.tsx @@ -0,0 +1,95 @@ +import { useEffect, useState } from "react"; +import { useQuery } from "@tanstack/react-query"; +import { + getCoreRowModel, + useReactTable, + type RowSelectionState, +} from "@tanstack/react-table"; +import { + Button, + DataTable, + Dialog, + DialogTrigger, + DialogContent, + DialogHeader, + DialogTitle, + Input, +} from "@common/components/ui"; +import { ENDPOINT } from "@/shared/config"; +import { BoardApi, type BoardPopup } from "@/entities/board"; +import { columns } from "../model/board-table-columns"; + +type BoardSearchDialogProps = { + handleBoardSelect: (board: BoardPopup) => void; +} & React.ComponentProps; + +function BoardSearchDialog({ + handleBoardSelect, + ...props +}: BoardSearchDialogProps) { + const [rowSelection, setRowSelection] = useState({}); + const [bbsNm, setBbsNm] = useState(""); + + const { data: boardList, refetch } = useQuery({ + queryKey: [ENDPOINT.BOARD_SERVICE.BBS], + queryFn: () => BoardApi.getBoardPopupList({ bbsNm }), + enabled: false, + }); + + const table = useReactTable({ + data: boardList?.content ?? [], + columns, + state: { + rowSelection, + }, + enableMultiRowSelection: false, + getCoreRowModel: getCoreRowModel(), + onRowSelectionChange: setRowSelection, + }); + + useEffect(() => { + const boardData = table.getSelectedRowModel().rows[0] + ?.original as BoardPopup; + if (!boardData) return; + handleBoardSelect(boardData); + }, [table.getSelectedRowModel().rows]); + + useEffect(() => { + refetch(); + }, []); + + return ( + + + + + + + 게시판 조회 + +
+
+ setBbsNm(e.target.value)} + /> + +
+
+ +
+
+
+
+ ); +} + +export { BoardSearchDialog }; diff --git a/apps/cs/src/pages/menu/ui/type-menu-form.tsx b/apps/cs/src/pages/menu/ui/type-menu-form.tsx index 209cadb1a..e02d6dacc 100644 --- a/apps/cs/src/pages/menu/ui/type-menu-form.tsx +++ b/apps/cs/src/pages/menu/ui/type-menu-form.tsx @@ -19,11 +19,13 @@ import { FormField, FormMessage, } from "@/shared/ui/form"; +import type { BoardPopup } from "@/entities/board"; import type { Content } from "../model/content-interface"; import type { User } from "../model/organization-interface"; import type { MenuFormData, MenuType } from "../model/menu-interface"; import { OrganizationSearchDialog } from "./organization-search-dialog"; import { ContentSearchDialog } from "./content-search-dialog"; +import { BoardSearchDialog } from "./board-search-dialog"; type TypeMenuFormProps = { form: UseFormReturn; @@ -45,6 +47,11 @@ function TypeMenuForm({ form, menuType }: TypeMenuFormProps) { form.setValue("menuPicUserUnqIdNm", user.userNm); }; + const handleBoardSelect = (board: BoardPopup) => { + form.setValue("menuLnkgSn", board.bbsId); + form.setValue("menuLnkgSnNm", board.bbsNm); + }; + const handleDateSelect = (date: Date | undefined) => { if (!date) return; form.setValue("menuPicDsgnYmd", dayjs(date).format("YYYYMMDD")); @@ -63,6 +70,8 @@ function TypeMenuForm({ form, menuType }: TypeMenuFormProps) {
+ {!form.watch("menuLnkgSnNm") && + "메뉴에 표시될 콘텐츠를 조회 후 선택하십시오."} {form.watch("menuLnkgSnNm")}
게시판 -
- {form.watch("menuLnkgSnNm")} +
+
+ {!form.watch("menuLnkgSnNm") && + "메뉴에 표시될 게시판을 조회 후 선택하십시오."} + {form.watch("menuLnkgSnNm")} +
+
@@ -180,6 +194,7 @@ function TypeMenuForm({ form, menuType }: TypeMenuFormProps) {
+ {!form.watch("iconNm") && "아이콘을 조회 후 선택하십시오."} {form.watch("iconNm")}
@@ -291,6 +306,8 @@ function TypeMenuForm({ form, menuType }: TypeMenuFormProps) {
+ {!form.watch("menuPicUserUnqIdNm") && + "메뉴 담당자를 조회 후 선택하십시오."} {form.watch("menuPicUserUnqIdNm")}
Date: Fri, 28 Mar 2025 18:23:52 +0900 Subject: [PATCH 21/24] =?UTF-8?q?feat:=20=EB=82=B4=EB=B6=80/=EC=99=B8?= =?UTF-8?q?=EB=B6=80=EB=A7=81=ED=81=AC=20=EB=A9=94=EB=89=B4=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=20=EC=9E=91=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/cs/src/pages/menu/ui/type-menu-form.tsx | 28 ++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/apps/cs/src/pages/menu/ui/type-menu-form.tsx b/apps/cs/src/pages/menu/ui/type-menu-form.tsx index e02d6dacc..5081a7e51 100644 --- a/apps/cs/src/pages/menu/ui/type-menu-form.tsx +++ b/apps/cs/src/pages/menu/ui/type-menu-form.tsx @@ -96,6 +96,34 @@ function TypeMenuForm({ form, menuType }: TypeMenuFormProps) { )} + {(menuType === "INSIDE" || menuType === "OUTSIDE") && ( + + 연결 URL + + ( + + + + + + + )} + /> + + + )} {(menuType === "CONTENTS" || menuType === "BOARD" || menuType === "INSIDE" || -- 2.47.1.windows.1 From 8b9d493ccd77427b75ef45d904fc1dc5e8aeb908 Mon Sep 17 00:00:00 2001 From: bicochan Date: Fri, 28 Mar 2025 18:32:53 +0900 Subject: [PATCH 22/24] =?UTF-8?q?feat:=20=ED=8A=B8=EB=A6=AC=20=EB=A9=94?= =?UTF-8?q?=EB=89=B4=20=EC=A0=84=EC=B2=B4=20=ED=8E=BC=EC=B9=A8/=EC=A0=91?= =?UTF-8?q?=EA=B8=B0=20=EA=B8=B0=EB=8A=A5=20=EC=9E=91=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/cs/src/pages/menu/ui/menu-tree.tsx | 131 ++++++++++++++---------- 1 file changed, 78 insertions(+), 53 deletions(-) diff --git a/apps/cs/src/pages/menu/ui/menu-tree.tsx b/apps/cs/src/pages/menu/ui/menu-tree.tsx index 59f55b4c2..b5782ec89 100644 --- a/apps/cs/src/pages/menu/ui/menu-tree.tsx +++ b/apps/cs/src/pages/menu/ui/menu-tree.tsx @@ -11,6 +11,7 @@ import { type NodeModel, type TreeMethods, } from "@minoru/react-dnd-treeview"; +import { Button } from "@common/components/ui"; import { ENDPOINT } from "@/shared/config"; import { MenuApi } from "../api/menu-service"; import type { MenuFormData, TreeMenu, TreeNode } from "../model/menu-interface"; @@ -58,63 +59,87 @@ function MenuTree({ }; return ( - - , { depth, isOpen, onToggle }) => { - if (node.id === -1) { + <> + + , { depth, isOpen, onToggle }) => { + if (node.id === -1) { + return ( + + additionalNodeForm.setValue("menuKornNm", e.target.value) + } + depth={depth} + onSave={onAdditionalNodeSave} + onCancel={onAdditionalNodeCancel} + /> + ); + } return ( - - additionalNodeForm.setValue("menuKornNm", e.target.value) - } + ); - } - return ( - - ); - }} - onDrop={handleDropNode(modifyTreeMenu)} - classes={{ - root: "p-4", - container: "flex flex-col", - dropTarget: "!bg-[rgba(32,148,250,0.5)] rounded-sm", - placeholder: "relative", - }} - sort={false} - dropTargetOffset={5} - insertDroppableFirst={false} - canDrop={(_, { dragSource, dropTargetId }) => { - if (dragSource?.parent === dropTargetId) { - return true; - } - }} - placeholderRender={(_, { depth }) => { - return ( -
- ); - }} - /> - + }} + onDrop={handleDropNode(modifyTreeMenu)} + classes={{ + root: "p-4", + container: "flex flex-col", + dropTarget: "!bg-[rgba(32,148,250,0.5)] rounded-sm", + placeholder: "relative", + }} + sort={false} + dropTargetOffset={5} + insertDroppableFirst={false} + canDrop={(_, { dragSource, dropTargetId }) => { + if (dragSource?.parent === dropTargetId) { + return true; + } + }} + placeholderRender={(_, { depth }) => { + return ( +
+ ); + }} + /> + +
+ + +
+ ); } -- 2.47.1.windows.1 From 84519658c1f6066d25c270afdeda61b3b17da64d Mon Sep 17 00:00:00 2001 From: bicochan Date: Fri, 28 Mar 2025 18:50:18 +0900 Subject: [PATCH 23/24] =?UTF-8?q?design:=20=ED=8A=B8=EB=A6=AC=20=EB=A9=94?= =?UTF-8?q?=EB=89=B4=20=EC=B5=9C=EB=8C=80=20=EB=86=92=EC=9D=B4=20=EC=A7=80?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/cs/src/pages/menu/ui/menu-page.tsx | 2 +- apps/cs/src/pages/menu/ui/menu-tree.tsx | 111 +++++++++++++----------- 2 files changed, 59 insertions(+), 54 deletions(-) diff --git a/apps/cs/src/pages/menu/ui/menu-page.tsx b/apps/cs/src/pages/menu/ui/menu-page.tsx index 87227fedd..52a63eb8a 100644 --- a/apps/cs/src/pages/menu/ui/menu-page.tsx +++ b/apps/cs/src/pages/menu/ui/menu-page.tsx @@ -198,7 +198,7 @@ function MenuPage() { return ( <> -
+