diff --git a/apps/common/features/app/(with-base-layout)/(contents)/layout.tsx b/apps/common/features/app/(with-base-layout)/(contents)/layout.tsx index 86007388a..934be4d6b 100644 --- a/apps/common/features/app/(with-base-layout)/(contents)/layout.tsx +++ b/apps/common/features/app/(with-base-layout)/(contents)/layout.tsx @@ -2,7 +2,7 @@ import { useQuery } from "@tanstack/react-query"; import { MenuApi } from "@/entities/menu"; -import { getCookie } from "@/shared/lib"; +import { useMenuStore } from "@/shared/lib"; import { ENDPOINT } from "@/shared/config"; import { PageLayout } from "@/shared/ui/page-layout"; @@ -11,11 +11,12 @@ export default function ContentsLayout({ }: { children: React.ReactNode; }) { - const menuCd = getCookie("menu-code-key"); + const currentMenuInfo = useMenuStore((state) => state.currentMenuInfo); const { data } = useQuery({ - queryKey: [ENDPOINT.CMS_SERVICE.MENUS, menuCd], - queryFn: () => MenuApi.getMenuInfo(menuCd!), - enabled: !!menuCd, + queryKey: [ENDPOINT.CMS_SERVICE.MENUS, currentMenuInfo?.menuCd], + queryFn: () => + MenuApi.getMenuInfo(currentMenuInfo?.menuCd.toString() ?? ""), + enabled: !!currentMenuInfo, }); const menuPaths = data?.menuPath?.split(">").map((path) => path.trim()); diff --git a/apps/common/features/app/(with-base-layout)/(post)/layout.tsx b/apps/common/features/app/(with-base-layout)/(post)/layout.tsx index 023b912cb..7f4b1e26b 100644 --- a/apps/common/features/app/(with-base-layout)/(post)/layout.tsx +++ b/apps/common/features/app/(with-base-layout)/(post)/layout.tsx @@ -2,7 +2,7 @@ import { MenuApi } from "@/entities/menu"; import { ENDPOINT } from "@/shared/config"; -import { getCookie, useMenuStore } from "@/shared/lib"; +import { useMenuStore } from "@/shared/lib"; import { PageLayout } from "@/shared/ui/page-layout"; import { useQuery } from "@tanstack/react-query"; @@ -11,13 +11,12 @@ export default function PostLayout({ }: { children: React.ReactNode; }) { - const bbsInfo = useMenuStore((state) => state.bbsInfo); - - const menuCd = getCookie("menu-code-key"); + const { bbsInfo, currentMenuInfo } = useMenuStore((state) => state); const { data } = useQuery({ - queryKey: [ENDPOINT.CMS_SERVICE.MENUS, menuCd], - queryFn: () => MenuApi.getMenuInfo(menuCd!), - enabled: !!menuCd, + queryKey: [ENDPOINT.CMS_SERVICE.MENUS, currentMenuInfo?.menuCd], + queryFn: () => + MenuApi.getMenuInfo(currentMenuInfo?.menuCd.toString() ?? ""), + enabled: !!currentMenuInfo, }); const menuPaths = data?.menuPath?.split(">").map((path) => path.trim()); diff --git a/apps/common/features/src/entities/content/api/contents-service.ts b/apps/common/features/src/entities/content/api/contents-service.ts index f1822ce77..7a55ad03d 100644 --- a/apps/common/features/src/entities/content/api/contents-service.ts +++ b/apps/common/features/src/entities/content/api/contents-service.ts @@ -21,9 +21,11 @@ export class ContentsApiService implements ContentsService { public async getLatestApprovedContentsInfo( contsSn: number, + menuCd: string, ): Promise { const response = await client.get( `${ENDPOINT.CMS_SERVICE.CONTENTS}/${contsSn}`, + { params: { menuCd } }, ); return response.data; } diff --git a/apps/common/features/src/entities/content/model/contents-interface.ts b/apps/common/features/src/entities/content/model/contents-interface.ts index 9e906d5f1..d30840164 100644 --- a/apps/common/features/src/entities/content/model/contents-interface.ts +++ b/apps/common/features/src/entities/content/model/contents-interface.ts @@ -65,6 +65,7 @@ export const contentsDetailSchema = z.object({ aprvDt: z.string(), autzr: z.string(), useYn: z.string(), + menuPicUserUnqIdNm: z.string().nullish(), }); export const contentsFormSchema = contentsDetailSchema.omit({ @@ -81,7 +82,10 @@ export type ContentsVersionList = PageableList; export interface ContentsService { getContentsList(params: ContentsParams): Promise; - getLatestApprovedContentsInfo(contsSn: number): Promise; + getLatestApprovedContentsInfo( + contsSn: number, + menuCd: string, + ): Promise; getContentsVersionList( params: ContentsVersionParams, ): Promise; diff --git a/apps/common/features/src/pages/contents/api/menu-evaluation-service.ts b/apps/common/features/src/pages/contents/api/menu-evaluation-service.ts new file mode 100644 index 000000000..963b7ad19 --- /dev/null +++ b/apps/common/features/src/pages/contents/api/menu-evaluation-service.ts @@ -0,0 +1,59 @@ +import { client } from "@/shared/api"; +import { ENDPOINT } from "@/shared/config"; +import type { + MenuEvaluation, + MenuEvaluationDetail, + MenuEvaluationParams, + MenuEvaluationService, + MenuEvaluationCreateRequestDto, + MenuEvaluationUpdateRequestDto, + PageMenuEvaluationListResponseDto, +} from "../model/menu-evaluation-interface"; + +export class MenuEvaluationApiService implements MenuEvaluationService { + public async getMenuEvaluationList( + params: MenuEvaluationParams, + ): Promise { + const response = await client.get( + `${ENDPOINT.CMS_SERVICE.MENU_EVALUATION}`, + { params }, + ); + return response.data; + } + + public async getMenuEvaluation( + menuEvlSn: number, + ): Promise { + const response = await client.get( + `${ENDPOINT.CMS_SERVICE.MENU_EVALUATION}/${menuEvlSn}`, + ); + return response.data; + } + + public async createMenuEvaluation( + data: MenuEvaluationCreateRequestDto, + ): Promise { + const response = await client.post( + `${ENDPOINT.CMS_SERVICE.MENU_EVALUATION}`, + data, + ); + return response.data; + } + + public async updateMenuEvaluation( + menuEvlSn: number, + data: MenuEvaluationUpdateRequestDto, + ): Promise { + const response = await client.put( + `${ENDPOINT.CMS_SERVICE.MENU_EVALUATION}/${menuEvlSn}`, + data, + ); + return response.data; + } + + public async deleteMenuEvaluation(menuEvlSn: number): Promise { + await client.delete(`${ENDPOINT.CMS_SERVICE.MENU_EVALUATION}/${menuEvlSn}`); + } +} + +export const MenuEvaluationApi = new MenuEvaluationApiService(); diff --git a/apps/common/features/src/pages/contents/api/menu-modification-demand-service.ts b/apps/common/features/src/pages/contents/api/menu-modification-demand-service.ts new file mode 100644 index 000000000..9961e0946 --- /dev/null +++ b/apps/common/features/src/pages/contents/api/menu-modification-demand-service.ts @@ -0,0 +1,66 @@ +import { client } from "@/shared/api"; +import { ENDPOINT } from "@/shared/config"; +import type { + MenuModificationDemand, + MenuModificationDemandCreateRequestDto, + MenuModificationDemandDetail, + MenuModificationDemandParams, + MenuModificationDemandService, + MenuModificationDemandUpdateRequestDto, + PageMenuModificationDemandListResponseDto, +} from "../model/menu-modification-demand-interface"; + +export class MenuModificationDemandApiService + implements MenuModificationDemandService +{ + public async getMenuModificationDemandList( + params: MenuModificationDemandParams, + ): Promise { + const response = + await client.get( + `${ENDPOINT.CMS_SERVICE.MENU_MODIFICATION_DEMAND}`, + { params }, + ); + return response.data; + } + + public async getMenuModificationDemand( + mdfcnRcptNo: number, + ): Promise { + const response = await client.get( + `${ENDPOINT.CMS_SERVICE.MENU_MODIFICATION_DEMAND}/${mdfcnRcptNo}`, + ); + return response.data; + } + + public async createMenuModificationDemand( + data: MenuModificationDemandCreateRequestDto, + ): Promise { + const response = await client.post( + `${ENDPOINT.CMS_SERVICE.MENU_MODIFICATION_DEMAND}`, + data, + ); + return response.data; + } + + public async updateMenuModificationDemand( + mdfcnRcptNo: number, + data: MenuModificationDemandUpdateRequestDto, + ): Promise { + const response = await client.put( + `${ENDPOINT.CMS_SERVICE.MENU_MODIFICATION_DEMAND}/${mdfcnRcptNo}`, + data, + ); + return response.data; + } + + public async deleteMenuModificationDemand( + mdfcnRcptNo: number, + ): Promise { + await client.delete( + `${ENDPOINT.CMS_SERVICE.MENU_MODIFICATION_DEMAND}/${mdfcnRcptNo}`, + ); + } +} + +export const MenuModificationDemandApi = new MenuModificationDemandApiService(); diff --git a/apps/common/features/src/pages/contents/model/menu-evaluation-interface.ts b/apps/common/features/src/pages/contents/model/menu-evaluation-interface.ts new file mode 100644 index 000000000..c9166f56c --- /dev/null +++ b/apps/common/features/src/pages/contents/model/menu-evaluation-interface.ts @@ -0,0 +1,49 @@ +import type { PageableList, PageableParams } from "@/shared/model"; + +export interface MenuEvaluationParams extends PageableParams { + menuCd?: number; + siteId?: string; + evlIp?: string; +} + +export interface MenuEvaluation { + evlSn: number; + menuCd: number; + siteId: string; + evlDt: string; + evlScr: number; +} + +export interface MenuEvaluationDetail extends MenuEvaluation { + evlIp: string; + userUnqId: string; +} + +export interface MenuEvaluationCreateRequestDto { + menuCd: number; + siteId: string; + evlIp?: string; + evlScr: number; + userUnqId: string; +} + +export interface MenuEvaluationUpdateRequestDto { + evlScr: number; +} + +export type PageMenuEvaluationListResponseDto = PageableList; + +export interface MenuEvaluationService { + getMenuEvaluationList( + params: MenuEvaluationParams, + ): Promise; + getMenuEvaluation(menuEvlSn: number): Promise; + createMenuEvaluation( + data: MenuEvaluationCreateRequestDto, + ): Promise; + updateMenuEvaluation( + menuEvlSn: number, + data: MenuEvaluationUpdateRequestDto, + ): Promise; + deleteMenuEvaluation(menuEvlSn: number): Promise; +} diff --git a/apps/common/features/src/pages/contents/model/menu-modification-demand-interface.ts b/apps/common/features/src/pages/contents/model/menu-modification-demand-interface.ts new file mode 100644 index 000000000..0d9c5b051 --- /dev/null +++ b/apps/common/features/src/pages/contents/model/menu-modification-demand-interface.ts @@ -0,0 +1,68 @@ +import type { PageableList, PageableParams } from "@/shared/model"; +import { z } from "zod"; + +export interface MenuModificationDemandParams extends PageableParams { + menuCd?: number; + siteId?: string; + mdfcnDmndSttusCd?: string; +} + +export interface MenuModificationDemand { + mdfcnRcptNo: number; + menuCd: number; + rcptDt: string; + aplcnt: string; + aplcnYn: string; +} + +export interface MenuModificationDemandDetail extends MenuModificationDemand { + mdfcnCn: string; + userUnqId: string; + aplcntEml: string; +} + +export const menuModificationDemandCreateRequestDtoSchema = z.object({ + menuCd: z.number(), + mdfcnCn: z.string(), + userUnqId: z.string().optional(), + aplcnt: z.string(), + aplcntEml: z.string().optional(), +}); + +export const menuModificationDemandUpdateRequestDtoSchema = z.object({ + mdfcnCn: z.string(), + aplcntEml: z.string().optional(), + aplcnYn: z.string(), +}); + +export type MenuModificationDemandCreateRequestDto = z.infer< + typeof menuModificationDemandCreateRequestDtoSchema +>; + +export type MenuModificationDemandUpdateRequestDto = z.infer< + typeof menuModificationDemandUpdateRequestDtoSchema +>; + +export type PageMenuModificationDemandListResponseDto = + PageableList; + +export interface MenuModificationDemandService { + getMenuModificationDemandList( + params: MenuModificationDemandParams, + ): Promise; + + getMenuModificationDemand( + mdfcnRcptNo: number, + ): Promise; + + createMenuModificationDemand( + data: MenuModificationDemandCreateRequestDto, + ): Promise; + + updateMenuModificationDemand( + mdfcnRcptNo: number, + data: MenuModificationDemandUpdateRequestDto, + ): Promise; + + deleteMenuModificationDemand(mdfcnRcptNo: number): Promise; +} diff --git a/apps/common/features/src/pages/contents/ui/contents-page.tsx b/apps/common/features/src/pages/contents/ui/contents-page.tsx index c0c8a4f74..46ab9669e 100644 --- a/apps/common/features/src/pages/contents/ui/contents-page.tsx +++ b/apps/common/features/src/pages/contents/ui/contents-page.tsx @@ -1,17 +1,71 @@ "use client"; -import { useEffect, useRef } from "react"; -import { useQuery } from "@tanstack/react-query"; +import dayjs from "dayjs"; +import Image from "next/image"; +import { toast } from "sonner"; +import { useEffect, useRef, useState } from "react"; +import { useMutation, useQuery } from "@tanstack/react-query"; +import { useClipboard } from "@frontend-opensource/use-react-hooks"; +import { + Button, + getCookie, + Label, + RadioGroup, + RadioGroupItem, +} from "@common/components"; +import { CodeApi } from "@/entities/code"; import { ContentsApi } from "@/entities/content"; -import { ENDPOINT } from "@/shared/config"; +import { AUTH_USER_ID, ENDPOINT } from "@/shared/config"; +import { useAlertStore, useMenuStore } from "@/shared/lib"; +import { MenuEvaluationApi } from "../api/menu-evaluation-service"; +import { MenuModificationDemandDialog } from "./menu-modification-demand-dialog"; function ContentsPage({ contsSn }: { contsSn: number }) { + const [evlScr, setEvlScr] = useState(5); const shadowRootRef = useRef(null); + const { copyText, copied } = useClipboard(); + const alert = useAlertStore((state) => state.setMessage); + const currentMenuInfo = useMenuStore((state) => state.currentMenuInfo); + const siteId = getCookie("site-id-key") ?? ""; + const userUnqId = getCookie(AUTH_USER_ID) ?? ""; const { data: contentsDetail } = useQuery({ queryKey: [ENDPOINT.CMS_SERVICE.CONTENTS_DETAILS, contsSn], - queryFn: () => ContentsApi.getLatestApprovedContentsInfo(contsSn), - enabled: !!contsSn, + queryFn: () => + ContentsApi.getLatestApprovedContentsInfo( + contsSn, + currentMenuInfo?.menuCd.toString() ?? "", + ), + enabled: !!contsSn && !!currentMenuInfo, + }); + + const { data: koglData } = useQuery({ + queryKey: [ENDPOINT.CMS_SERVICE.CODE, "KOGLCD"], + queryFn: () => CodeApi.getCode("KOGLCD"), + select: (data) => + data.find((code) => code.lwpoCd === contentsDetail?.koglCd), + enabled: !!contentsDetail, + }); + + const { data: cclData } = useQuery({ + queryKey: [ENDPOINT.CMS_SERVICE.CODE, "CCLCD"], + queryFn: () => CodeApi.getCode("CCLCD"), + select: (data) => + data.find((code) => code.lwpoCd === contentsDetail?.cclCd), + enabled: !!contentsDetail, + }); + + const { mutate: menuEvaluation } = useMutation({ + mutationFn: (evlScr: number) => + MenuEvaluationApi.createMenuEvaluation({ + menuCd: currentMenuInfo?.menuCd ?? 0, + siteId, + evlScr, + userUnqId, + }), + onSuccess: () => { + alert("게시물 평가가 완료되었습니다."); + }, }); useEffect(() => { @@ -28,10 +82,81 @@ function ContentsPage({ contsSn }: { contsSn: number }) { shadowRoot.appendChild(contentElement); }, [contentsDetail?.contsCn]); + useEffect(() => { + if (copied) toast.success("URL이 클립보드에 복사되었습니다."); + }, [copied]); + return ( -
-
-
+ <> +
+
+
+
+
+
+ setEvlScr(parseInt(value))} + className="flex items-center space-x-4" + > +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+
+
+ 최종수정일: {dayjs(contentsDetail?.aprvDt).format("YYYY-MM-DD")} | + 담당자: {contentsDetail?.menuPicUserUnqIdNm} +
+ CCL + CCL + + +
+
+ ); } diff --git a/apps/common/features/src/pages/contents/ui/menu-modification-demand-dialog.tsx b/apps/common/features/src/pages/contents/ui/menu-modification-demand-dialog.tsx new file mode 100644 index 000000000..b8fd2b68a --- /dev/null +++ b/apps/common/features/src/pages/contents/ui/menu-modification-demand-dialog.tsx @@ -0,0 +1,173 @@ +import { memo, useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import { useMutation } from "@tanstack/react-query"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useConfirm } from "@frontend-opensource/use-react-hooks"; +import { + Button, + Dialog, + DialogClose, + DialogContent, + DialogFooter, + DialogHeader, + DialogTitle, + Table, + TableBody, + TableCell, + TableHead, + TableRow, + Input, + DialogTrigger, + Textarea, +} from "@common/components"; +import { useAlertStore } from "@/shared/lib"; +import { Form, FormField, FormItem, FormControl } from "@/shared/ui/form"; +import { MenuModificationDemandApi } from "../api/menu-modification-demand-service"; +import { + menuModificationDemandCreateRequestDtoSchema, + type MenuModificationDemandCreateRequestDto, +} from "../model/menu-modification-demand-interface"; + +type MenuModificationDemandDialogProps = { + menuCd?: number; +}; + +const MenuModificationDemandDialog = memo( + ({ menuCd }: MenuModificationDemandDialogProps) => { + const [open, setOpen] = useState(false); + const { confirm } = useConfirm(); + const alert = useAlertStore((state) => state.setMessage); + + const { mutateAsync: createMenuModificationDemand } = useMutation({ + mutationFn: (data: MenuModificationDemandCreateRequestDto) => + MenuModificationDemandApi.createMenuModificationDemand(data), + onSuccess: () => { + alert("메뉴 수정 요청이 등록되었습니다.", () => setOpen(false)); + }, + }); + + const form = useForm({ + resolver: zodResolver(menuModificationDemandCreateRequestDtoSchema), + defaultValues: { + menuCd: menuCd ?? 0, + mdfcnCn: "", + aplcnt: "", + aplcntEml: "", + }, + }); + + const onSubmit = async (data: MenuModificationDemandCreateRequestDto) => { + if (await confirm("메뉴 수정 요청을 등록하시겠습니까?")) { + createMenuModificationDemand(data); + } + }; + + useEffect(() => { + if (!open) form.reset(); + }, [open]); + + return ( + + + + + + + 메뉴 수정 요청 + +
+ + + + + + + + + 신청자 + + ( + + + + + + )} + /> + + + + 이메일 + + ( + + + + + + )} + /> + + + + 요청 내용 + + ( + + +