From e456595868e9e97234d08e3a9247b92f5b2ec40c Mon Sep 17 00:00:00 2001 From: bicochan Date: Thu, 10 Apr 2025 10:13:03 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EB=A9=94=EB=89=B4=20=EA=B6=8C=ED=95=9C?= =?UTF-8?q?=20=EA=B4=80=EB=A6=AC=20=EB=8B=A4=EC=9D=B4=EC=96=BC=EB=A1=9C?= =?UTF-8?q?=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 --- .../api/menu-authority-service.ts | 32 +++++ apps/cs/src/entities/menu-authority/index.ts | 1 + .../model/menu-authority-interface.ts | 41 ++++++ .../model/menu-authority-table-columns.tsx | 117 ++++++++++++++++++ .../ui/management-authority-dialog.tsx | 101 +++++++++++++++ .../pages/menu/model/content-table-colums.tsx | 22 ++-- apps/cs/src/pages/menu/ui/menu-form.tsx | 4 +- .../menu/ui/permissions-management-dialog.tsx | 22 ---- .../src/pages/template/ui/template-page.tsx | 2 +- apps/cs/src/shared/config/api.ts | 1 + 10 files changed, 306 insertions(+), 37 deletions(-) create mode 100644 apps/cs/src/entities/menu-authority/api/menu-authority-service.ts create mode 100644 apps/cs/src/entities/menu-authority/index.ts create mode 100644 apps/cs/src/entities/menu-authority/model/menu-authority-interface.ts create mode 100644 apps/cs/src/entities/menu-authority/model/menu-authority-table-columns.tsx create mode 100644 apps/cs/src/entities/menu-authority/ui/management-authority-dialog.tsx delete mode 100644 apps/cs/src/pages/menu/ui/permissions-management-dialog.tsx diff --git a/apps/cs/src/entities/menu-authority/api/menu-authority-service.ts b/apps/cs/src/entities/menu-authority/api/menu-authority-service.ts new file mode 100644 index 000000000..f9b01b4d8 --- /dev/null +++ b/apps/cs/src/entities/menu-authority/api/menu-authority-service.ts @@ -0,0 +1,32 @@ +import { client } from "@/shared/api"; +import { ENDPOINT } from "@/shared/config"; +import type { + MenuAuthorityList, + MenuAuthorityParams, + MenuAuthorityPayloadData, + MenuAuthorityService, +} from "../model/menu-authority-interface"; + +export class MenuAuthorityApiService implements MenuAuthorityService { + public async getMenuAuthorityList( + params: MenuAuthorityParams, + ): Promise { + const response = await client.get( + `${ENDPOINT.CMS_SERVICE.MENU_AUTHRTS}`, + { params }, + ); + return response.data; + } + + public async saveMenuAuthority( + data: MenuAuthorityPayloadData[], + ): Promise { + const response = await client.post( + `${ENDPOINT.CMS_SERVICE.MENU_AUTHRTS}`, + data, + ); + return response.data; + } +} + +export const MenuAuthorityApi = new MenuAuthorityApiService(); diff --git a/apps/cs/src/entities/menu-authority/index.ts b/apps/cs/src/entities/menu-authority/index.ts new file mode 100644 index 000000000..07433a098 --- /dev/null +++ b/apps/cs/src/entities/menu-authority/index.ts @@ -0,0 +1 @@ +export { ManagementAuthorityDialog } from "./ui/management-authority-dialog"; diff --git a/apps/cs/src/entities/menu-authority/model/menu-authority-interface.ts b/apps/cs/src/entities/menu-authority/model/menu-authority-interface.ts new file mode 100644 index 000000000..d6775838c --- /dev/null +++ b/apps/cs/src/entities/menu-authority/model/menu-authority-interface.ts @@ -0,0 +1,41 @@ +import { z } from "zod"; +import type { PageableList, PageableParams } from "@/shared/model"; + +export interface MenuAuthorityParams extends PageableParams { + groupId?: string; + menuCd?: number; + useYn?: string; +} + +export const menuAuthoritySchema = z.object({ + groupId: z.string(), + groupNm: z.string(), + menuCd: z.number(), + mngAuthrtYn: z.string(), + inqAuthrtYn: z.string(), + regAuthrtYn: z.string(), + mdfcnAuthrtYn: z.string(), + delAuthrtYn: z.string(), + useYn: z.string(), + mode: z.string(), +}); + +export const menuAuthorityPayloadSchema = menuAuthoritySchema.omit({ + groupNm: true, +}); + +export const menuAuthorityFormSchema = z.object({ + menuAuthority: z.array(menuAuthorityPayloadSchema), +}); + +export type MenuAuthority = z.infer; +export type MenuAuthorityList = PageableList; +export type MenuAuthorityPayloadData = z.infer< + typeof menuAuthorityPayloadSchema +>; +export type MenuAuthorityFormData = z.infer; + +export interface MenuAuthorityService { + getMenuAuthorityList(params: MenuAuthorityParams): Promise; + saveMenuAuthority(data: MenuAuthorityPayloadData[]): Promise; +} diff --git a/apps/cs/src/entities/menu-authority/model/menu-authority-table-columns.tsx b/apps/cs/src/entities/menu-authority/model/menu-authority-table-columns.tsx new file mode 100644 index 000000000..90bff735d --- /dev/null +++ b/apps/cs/src/entities/menu-authority/model/menu-authority-table-columns.tsx @@ -0,0 +1,117 @@ +import type { ColumnDef } from "@tanstack/react-table"; +import type { UseFormReturn } from "react-hook-form"; +import { Checkbox } from "@common/components/ui"; +import type { + MenuAuthority, + MenuAuthorityFormData, + MenuAuthorityPayloadData, +} from "./menu-authority-interface"; + +export const columns = ( + form: UseFormReturn, +): ColumnDef[] => { + const getCurrentRow = (i: number) => form.watch("menuAuthority")[i]; + + const handleCheckedChange = + (i: number, key: keyof MenuAuthorityPayloadData) => (checked: boolean) => { + if (getCurrentRow(i)?.mode === "R") { + form.setValue(`menuAuthority.${i}.mode`, "U"); + } + form.setValue(`menuAuthority.${i}.${key}`, checked ? "Y" : "N"); + }; + + return [ + { + accessorKey: "groupNm", + header: "그룹명", + }, + { + accessorKey: "mngAuthrtYn", + header: "관리권한", + cell: ({ row }) => { + return ( + { + if (checked) { + handleCheckedChange( + row.index, + "regAuthrtYn", + )(checked as boolean); + handleCheckedChange( + row.index, + "mdfcnAuthrtYn", + )(checked as boolean); + handleCheckedChange( + row.index, + "delAuthrtYn", + )(checked as boolean); + handleCheckedChange( + row.index, + "inqAuthrtYn", + )(checked as boolean); + } + handleCheckedChange(row.index, "mngAuthrtYn")(checked as boolean); + }} + /> + ); + }, + }, + { + accessorKey: "regAuthrtYn", + header: "등록권한", + cell: ({ row }) => { + return ( + { + if (checked) { + handleCheckedChange( + row.index, + "inqAuthrtYn", + )(checked as boolean); + } + handleCheckedChange(row.index, "regAuthrtYn")(checked as boolean); + }} + /> + ); + }, + }, + { + accessorKey: "mdfcnAuthrtYn", + header: "수정권한 ", + cell: ({ row }) => { + return ( + + ); + }, + }, + { + accessorKey: "delAuthrtYn", + header: "삭제권한", + cell: ({ row }) => { + return ( + + ); + }, + }, + { + accessorKey: "inqAuthrtYn", + header: "조회권한", + cell: ({ row }) => { + return ( + + ); + }, + }, + ]; +}; diff --git a/apps/cs/src/entities/menu-authority/ui/management-authority-dialog.tsx b/apps/cs/src/entities/menu-authority/ui/management-authority-dialog.tsx new file mode 100644 index 000000000..8b940634a --- /dev/null +++ b/apps/cs/src/entities/menu-authority/ui/management-authority-dialog.tsx @@ -0,0 +1,101 @@ +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useMutation, useQuery } from "@tanstack/react-query"; +import { getCoreRowModel, useReactTable } from "@tanstack/react-table"; +import { useConfirm } from "@frontend-opensource/use-react-hooks"; +import { + Button, + DataTable, + Dialog, + DialogClose, + DialogContent, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@common/components/ui"; +import { ENDPOINT } from "@/shared/config"; +import { useAlertStore } from "@/shared/lib/use-alert-store"; +import { + menuAuthorityFormSchema, + type MenuAuthorityFormData, + type MenuAuthorityPayloadData, +} from "../model/menu-authority-interface"; +import { columns } from "../model/menu-authority-table-columns"; +import { MenuAuthorityApi } from "../api/menu-authority-service"; + +type ManagementAuthorityDialogProps = { + menuCd?: number; +} & React.ComponentProps; + +function ManagementAuthorityDialog({ + menuCd, + ...props +}: ManagementAuthorityDialogProps) { + const { confirm } = useConfirm(); + const { setMessage: alert } = useAlertStore((state) => state); + + const { data: menuAuthorityList } = useQuery({ + queryKey: [ENDPOINT.CMS_SERVICE.MENU_AUTHRTS, menuCd], + queryFn: () => MenuAuthorityApi.getMenuAuthorityList({ menuCd }), + enabled: !!menuCd, + }); + + const { mutateAsync: saveMenuAuthority } = useMutation({ + mutationFn: (data: MenuAuthorityPayloadData[]) => + MenuAuthorityApi.saveMenuAuthority(data), + onSuccess: () => { + alert("저장되었습니다."); + }, + }); + + const form = useForm({ + resolver: zodResolver(menuAuthorityFormSchema), + values: { + menuAuthority: menuAuthorityList?.content ?? [], + }, + }); + + const table = useReactTable({ + data: menuAuthorityList?.content ?? [], + columns: columns(form), + getCoreRowModel: getCoreRowModel(), + }); + + const handleSave = async () => { + if (await confirm("저장하시겠습니까?")) { + const data = form.getValues("menuAuthority"); + const filteredData = data.filter((item) => item.mode !== "R"); + await saveMenuAuthority(filteredData); + } + }; + + return ( + + + + + + + 그룹별 메뉴 권한 + + + + + + + + + + + ); +} + +export { ManagementAuthorityDialog }; diff --git a/apps/cs/src/pages/menu/model/content-table-colums.tsx b/apps/cs/src/pages/menu/model/content-table-colums.tsx index b8bb28638..844055b73 100644 --- a/apps/cs/src/pages/menu/model/content-table-colums.tsx +++ b/apps/cs/src/pages/menu/model/content-table-colums.tsx @@ -1,20 +1,20 @@ -import { createColumnHelper } from "@tanstack/react-table"; +import type { ColumnDef } from "@tanstack/react-table"; import { Checkbox } from "@common/components/ui"; import type { Content } from "./content-interface"; -const columnHelper = createColumnHelper(); - -const columns = [ - columnHelper.accessor((_, index) => index + 1, { +export const columns: ColumnDef[] = [ + { id: "no", header: "NO", - }), - columnHelper.accessor("contsTtl", { + cell: ({ row }) => row.index + 1, + }, + { id: "contsTtl", header: "콘텐츠명", - }), + cell: ({ row }) => row.original.contsTtl, + }, // TODO: 최종수정자, 최종수정일, 승인일시 추가 - columnHelper.display({ + { id: "select", header: "선택", cell: ({ row }) => { @@ -26,7 +26,5 @@ const columns = [ /> ); }, - }), + }, ]; - -export { columns }; diff --git a/apps/cs/src/pages/menu/ui/menu-form.tsx b/apps/cs/src/pages/menu/ui/menu-form.tsx index 8ee61812a..0a45214eb 100644 --- a/apps/cs/src/pages/menu/ui/menu-form.tsx +++ b/apps/cs/src/pages/menu/ui/menu-form.tsx @@ -2,10 +2,10 @@ import type { UseFormReturn } from "react-hook-form"; import { useConfirm } from "@frontend-opensource/use-react-hooks"; import { Button } from "@common/components"; import { Form } from "@/shared/ui/form"; +import { ManagementAuthorityDialog } from "@/entities/menu-authority"; import type { MenuFormData } from "../model/menu-interface"; import { BaseMenuForm } from "./base-menu-form"; import { TypeMenuForm } from "./type-menu-form"; -import { PermissionsManagementDialog } from "./permissions-management-dialog"; interface MenuFormProps { form: UseFormReturn; @@ -69,7 +69,7 @@ function MenuForm({

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

- +
diff --git a/apps/cs/src/pages/menu/ui/permissions-management-dialog.tsx b/apps/cs/src/pages/menu/ui/permissions-management-dialog.tsx deleted file mode 100644 index c5f782cbb..000000000 --- a/apps/cs/src/pages/menu/ui/permissions-management-dialog.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { Button, Dialog, DialogTrigger } from "@common/components/ui"; - -type PermissionsManagementDialogProps = { - menuCd?: number; -} & React.ComponentProps; - -function PermissionsManagementDialog({ - ...props -}: PermissionsManagementDialogProps) { - return ( - - - - - - ); -} - -export { PermissionsManagementDialog }; diff --git a/apps/cs/src/pages/template/ui/template-page.tsx b/apps/cs/src/pages/template/ui/template-page.tsx index dbd96b56b..b107559fe 100644 --- a/apps/cs/src/pages/template/ui/template-page.tsx +++ b/apps/cs/src/pages/template/ui/template-page.tsx @@ -9,7 +9,7 @@ import { type PaginationState, } from "@tanstack/react-table"; import { Button, DataTable, Input, Label } from "@common/components"; -import { CodeSelect } from "@/entities/code/ui/code-select"; +import { CodeSelect } from "@/entities/code"; import { ENDPOINT } from "@/shared/config"; import { TemplateApi } from "../api/template-service"; import { columns } from "../model/template-table-columns"; diff --git a/apps/cs/src/shared/config/api.ts b/apps/cs/src/shared/config/api.ts index 4f1d5605c..6ed82dec1 100644 --- a/apps/cs/src/shared/config/api.ts +++ b/apps/cs/src/shared/config/api.ts @@ -21,6 +21,7 @@ export const ENDPOINT = { CODE: "/cms-service/api/v1/code/cache", MENUS: "/cms-service/api/v1/menus", MENU_ROLES: "/cms-service/api/v1/menu-roles", + MENU_AUTHRTS: "/cms-service/api/v1/menu-authrts", CONTENTS: "/cms-service/api/v1/contents", USER_ORGANIZATION: "/cms-service/api/v1/user-ognz", IMAGES: "/cms-service/api/v1/images",