From 1d132a86de145abe7836835708fda49c94fe0be5 Mon Sep 17 00:00:00 2001 From: bicochan Date: Wed, 2 Apr 2025 14:07:36 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=97=85?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=8A=B8=20=EB=B0=8F=20=EC=82=AD=EC=A0=9C=20?= =?UTF-8?q?=EC=9E=91=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../image/register/[serial]/page.tsx | 10 + apps/cs/src/entities/site/api/site-service.ts | 2 +- .../src/entities/site/model/site-interface.ts | 2 +- apps/cs/src/pages/image/api/image-service.ts | 14 +- apps/cs/src/pages/image/index.ts | 1 + .../src/pages/image/model/image-interface.ts | 4 +- apps/cs/src/pages/image/ui/image-form.tsx | 452 +++++++++--------- apps/cs/src/pages/image/ui/modify-page.tsx | 56 +++ apps/cs/src/pages/menu/api/menu-service.ts | 6 +- .../cs/src/pages/menu/model/menu-interface.ts | 6 +- apps/cs/src/pages/menu/ui/menu-form.tsx | 14 +- apps/cs/src/pages/menu/ui/menu-node.tsx | 6 +- apps/cs/src/pages/menu/ui/menu-page.tsx | 6 +- apps/cs/src/pages/menu/ui/menu-tree.tsx | 6 +- apps/cs/src/pages/site/ui/modify-page.tsx | 10 +- apps/cs/src/pages/site/ui/site-form.tsx | 28 +- 16 files changed, 356 insertions(+), 267 deletions(-) create mode 100644 apps/cs/app/(with-base-layout)/image/register/[serial]/page.tsx create mode 100644 apps/cs/src/pages/image/ui/modify-page.tsx diff --git a/apps/cs/app/(with-base-layout)/image/register/[serial]/page.tsx b/apps/cs/app/(with-base-layout)/image/register/[serial]/page.tsx new file mode 100644 index 000000000..8df89b17a --- /dev/null +++ b/apps/cs/app/(with-base-layout)/image/register/[serial]/page.tsx @@ -0,0 +1,10 @@ +import { ModifyPage as VModifyPage } from "@/pages/image"; + +export default async function ModifyPage({ + params, +}: { + params: Promise<{ serial: string }>; +}) { + const { serial } = await params; + return ; +} diff --git a/apps/cs/src/entities/site/api/site-service.ts b/apps/cs/src/entities/site/api/site-service.ts index 65f6a95a0..37fd7e49a 100644 --- a/apps/cs/src/entities/site/api/site-service.ts +++ b/apps/cs/src/entities/site/api/site-service.ts @@ -34,7 +34,7 @@ export class SiteApiService implements SiteService { return response.data; } - public async modifySite(siteId: string, data: SiteFormData): Promise { + public async updateSite(siteId: string, data: SiteFormData): Promise { const validatedData = siteFormSchema.parse(data); const response = await client.put( `${ENDPOINT.CMS_SERVICE.SITES}/${siteId}`, diff --git a/apps/cs/src/entities/site/model/site-interface.ts b/apps/cs/src/entities/site/model/site-interface.ts index a8fb2796f..85b1f802f 100644 --- a/apps/cs/src/entities/site/model/site-interface.ts +++ b/apps/cs/src/entities/site/model/site-interface.ts @@ -54,6 +54,6 @@ export type SiteList = PageableList; export interface SiteService { getSiteList(params: SiteParams): Promise; siteDetail(siteId: string): Promise; - modifySite(siteId: string, data: SiteFormData): Promise; + updateSite(siteId: string, data: SiteFormData): Promise; createSite(data: SiteFormData): Promise; } diff --git a/apps/cs/src/pages/image/api/image-service.ts b/apps/cs/src/pages/image/api/image-service.ts index f16f2f322..c7fb56240 100644 --- a/apps/cs/src/pages/image/api/image-service.ts +++ b/apps/cs/src/pages/image/api/image-service.ts @@ -26,19 +26,13 @@ export class ImageApiService implements ImageService { return response.data; } - public async createImage(data: ImageFormData): Promise { - const response = await client.post( - ENDPOINT.CMS_SERVICE.IMAGES, - data, - ); + public async createImage(data: ImageFormData): Promise { + const response = await client.post(ENDPOINT.CMS_SERVICE.IMAGES, data); return response.data; } - public async updateImage( - imgSn: string, - data: Partial, - ): Promise { - const response = await client.put( + public async updateImage(imgSn: string, data: Partial): Promise { + const response = await client.put( `${ENDPOINT.CMS_SERVICE.IMAGES}/${imgSn}`, data, ); diff --git a/apps/cs/src/pages/image/index.ts b/apps/cs/src/pages/image/index.ts index 6b02dedec..5c602ca7d 100644 --- a/apps/cs/src/pages/image/index.ts +++ b/apps/cs/src/pages/image/index.ts @@ -1,2 +1,3 @@ export { CreatePage } from "./ui/create-page"; +export { ModifyPage } from "./ui/modify-page"; export { ImagePage } from "./ui/image-page"; diff --git a/apps/cs/src/pages/image/model/image-interface.ts b/apps/cs/src/pages/image/model/image-interface.ts index cdeff74a1..4933bc860 100644 --- a/apps/cs/src/pages/image/model/image-interface.ts +++ b/apps/cs/src/pages/image/model/image-interface.ts @@ -46,7 +46,7 @@ export type ImageList = PageableList; export interface ImageService { getImageList: (params: ImageParams) => Promise; getImageInfo: (imgSn: string) => Promise; - createImage: (data: ImageFormData) => Promise; - updateImage: (imgSn: string, data: ImageFormData) => Promise; + createImage: (data: ImageFormData) => Promise; + updateImage: (imgSn: string, data: ImageFormData) => Promise; deleteImage: (imgSn: string) => Promise; } diff --git a/apps/cs/src/pages/image/ui/image-form.tsx b/apps/cs/src/pages/image/ui/image-form.tsx index 22fa52714..19a63eea9 100644 --- a/apps/cs/src/pages/image/ui/image-form.tsx +++ b/apps/cs/src/pages/image/ui/image-form.tsx @@ -3,6 +3,8 @@ import { useState } from "react"; import { useRouter } from "next/navigation"; import type { UseFormReturn } from "react-hook-form"; +import type { HttpResponse, Upload } from "tus-js-client"; +import { useConfirm } from "@frontend-opensource/use-react-hooks"; import { Button, FileUploader, @@ -18,15 +20,15 @@ import { TableRow, } from "@common/components"; import { CodeSelect } from "@/entities/code"; +import { ConfirmDialog } from "@/shared/ui/confirm-dialog"; import { Form, FormControl, FormField, FormItem } from "@/shared/ui/form"; -import type { ImageFormData } from "../model/image-interface"; import { handleUpload } from "../lib/utils"; -import type { HttpResponse, Upload } from "tus-js-client"; +import type { ImageFormData } from "../model/image-interface"; type ImageFormProps = { form: UseFormReturn; imgSn?: string; - handleSave?: (data: ImageFormData) => Promise; + handleSave: (data: ImageFormData) => Promise; handleDelete?: () => Promise; }; @@ -35,15 +37,35 @@ function ImageForm({ form, imgSn, handleSave, handleDelete }: ImageFormProps) { const atflYn = form.watch("atflYn"); const router = useRouter(); + const { confirm } = useConfirm(); + const [onConfirm, setOnConfirm] = useState(false); const [upload, setUpload] = useState(null); const [progress, setProgress] = useState(0); + const handleConfirm = async (callback: () => Promise) => { + setOnConfirm(true); + await callback(); + setOnConfirm(false); + }; + const onSubmit = async (data: ImageFormData) => { - await handleSave?.(data); + const confirmMessage = isModifyMode + ? "수정하시겠습니까?" + : "저장하시겠습니까?"; + + await handleConfirm(async () => { + if (await confirm(confirmMessage)) { + handleSave(data); + } + }); }; const onDelete = async () => { - await handleDelete?.(); + await handleConfirm(async () => { + if (await confirm("삭제하시겠습니까?")) { + await handleDelete?.(); + } + }); }; const handleProgress = (percentage: string) => { @@ -58,165 +80,22 @@ function ImageForm({ form, imgSn, handleSave, handleDelete }: ImageFormProps) { }; return ( -
- -
- - - - - - - - 이미지명 - - ( - - - - - - )} - /> - - - - 정렬순서 - - ( - - - { - const value = e.target.value; - if (isNaN(Number(value))) return; - field.onChange(Number(value)); - }} - className="!text-[1.3rem]" - placeholder="정렬순서를 입력해주세요" - /> - - - )} - /> - - - - 이미지유형 - - ( - - - - - - )} - /> - - - - 첨부파일 - - ( - - - -
- - -
-
- - -
-
-
-
- )} - /> -
-
- {atflYn === "Y" && ( + <> + + +
+
+ + + + + - 이미지파일 + 이미지명 ( - - - { - const files = e.target.files; - if (!files) return; - handleUpload(files, { - handleUploadStart: setUpload, - handleProgress, - handleSuccess: setUpload, - handleResponse: handleUploadResponse, - }); - }} - customPreview={ - - } - /> - - - )} - /> - - - )} - {atflYn === "N" && ( - - 외부링크 - - ( @@ -224,7 +103,7 @@ function ImageForm({ form, imgSn, handleSave, handleDelete }: ImageFormProps) { {...field} value={field.value ?? ""} className="!text-[1.3rem]" - placeholder="외부링크를 입력해주세요" + placeholder="이미지명을 입력해주세요" /> @@ -232,59 +111,206 @@ function ImageForm({ form, imgSn, handleSave, handleDelete }: ImageFormProps) { /> - )} - - 사용여부 - - ( - - - -
- - -
-
- - -
-
-
-
- )} - /> -
-
-
-
-
-
- - - {isModifyMode && ( -
+
+ - )} -
-
- + + {isModifyMode && ( + + )} + + + + + ); } diff --git a/apps/cs/src/pages/image/ui/modify-page.tsx b/apps/cs/src/pages/image/ui/modify-page.tsx new file mode 100644 index 000000000..c440c56f9 --- /dev/null +++ b/apps/cs/src/pages/image/ui/modify-page.tsx @@ -0,0 +1,56 @@ +"use client"; + +import { useRouter } from "next/navigation"; +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useMutation, useQuery } from "@tanstack/react-query"; +import { ENDPOINT } from "@/shared/config"; +import { imageFormSchema, type ImageFormData } from "../model/image-interface"; +import { ImageApi } from "../api/image-service"; +import { ImageForm } from "./image-form"; +import { useAlertStore } from "@/shared/lib/use-alert-store"; + +function ModifyPage({ serial }: { serial: string }) { + const router = useRouter(); + const { setMessage: alert } = useAlertStore((state) => state); + + const { data: imageDetail } = useQuery({ + queryKey: [ENDPOINT.CMS_SERVICE.IMAGES, serial], + queryFn: () => ImageApi.getImageInfo(serial), + }); + + const { mutateAsync: updateImage } = useMutation({ + mutationFn: (data: ImageFormData) => ImageApi.updateImage(serial, data), + onSuccess: () => { + alert("수정되었습니다."); + }, + }); + + const { mutateAsync: deleteImage } = useMutation({ + mutationFn: () => ImageApi.deleteImage(serial), + onSuccess: () => { + alert("삭제되었습니다.", router.back); + }, + }); + + const form = useForm({ + resolver: zodResolver(imageFormSchema), + values: imageDetail, + }); + + return ( +
+
+

이미지 수정

+
+ +
+ ); +} + +export { ModifyPage }; diff --git a/apps/cs/src/pages/menu/api/menu-service.ts b/apps/cs/src/pages/menu/api/menu-service.ts index 4e03f6cdf..7598c2abb 100644 --- a/apps/cs/src/pages/menu/api/menu-service.ts +++ b/apps/cs/src/pages/menu/api/menu-service.ts @@ -24,7 +24,7 @@ export class MenuApiService implements MenuService { return response.data; } - public async modifyMenu( + public async updateMenu( menuCd: number, data: Omit, ): Promise { @@ -49,7 +49,7 @@ export class MenuApiService implements MenuService { return response.data; } - public async modifyTreeMenu(siteId: string, data: TreeNode[]): Promise { + public async updateTreeMenu(siteId: string, data: TreeNode[]): Promise { const response = await client.put( `${ENDPOINT.CMS_SERVICE.MENUS}/${siteId}/tree`, data, @@ -57,7 +57,7 @@ export class MenuApiService implements MenuService { return response.data; } - public async modifyMenuName(menuCd: number, name: string): Promise { + public async updateMenuName(menuCd: number, name: string): Promise { const response = await client.put( `${ENDPOINT.CMS_SERVICE.MENUS}/${menuCd}/${name}`, ); diff --git a/apps/cs/src/pages/menu/model/menu-interface.ts b/apps/cs/src/pages/menu/model/menu-interface.ts index 51258fc49..f9650e0e2 100644 --- a/apps/cs/src/pages/menu/model/menu-interface.ts +++ b/apps/cs/src/pages/menu/model/menu-interface.ts @@ -75,12 +75,12 @@ export type Menu = Omit & { export interface MenuService { getMenuInfo: (menuCd: number) => Promise; createMenu: (data: MenuFormData) => Promise; - modifyMenu: ( + updateMenu: ( 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; + updateTreeMenu: (siteId: string, data: TreeNode[]) => Promise; + updateMenuName: (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 48e7589be..a4f21128b 100644 --- a/apps/cs/src/pages/menu/ui/menu-form.tsx +++ b/apps/cs/src/pages/menu/ui/menu-form.tsx @@ -13,7 +13,7 @@ interface MenuFormProps { form: UseFormReturn; menuCd?: number; handleCreate: (data: MenuFormData) => void; - handleModify: ({ + handleUpdate: ({ menuCd, data, }: { @@ -27,7 +27,7 @@ function MenuForm({ form, menuCd, handleCreate, - handleModify, + handleUpdate, handleDelete, }: MenuFormProps) { const isModifyMode = !!menuCd; @@ -35,15 +35,15 @@ function MenuForm({ const { confirm } = useConfirm(); const [onConfirm, setOnConfirm] = useState(false); - const handleConfirm = async (fn: () => Promise) => { + const handleConfirm = async (callback: () => Promise) => { setOnConfirm(true); - await fn(); + await callback(); setOnConfirm(false); }; const onSubmit = async (data: MenuFormData) => { if (isModifyMode) { - await onModify(data); + await onUpdate(data); } else { await onCreate(data); } @@ -57,10 +57,10 @@ function MenuForm({ }); }; - const onModify = async (data: MenuFormData) => { + const onUpdate = async (data: MenuFormData) => { await handleConfirm(async () => { if (await confirm("수정하시겠습니까?")) { - handleModify({ menuCd: menuCd!, data }); + handleUpdate({ 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 b2e595923..1cde10b99 100644 --- a/apps/cs/src/pages/menu/ui/menu-node.tsx +++ b/apps/cs/src/pages/menu/ui/menu-node.tsx @@ -34,10 +34,10 @@ function MenuNode({ const queryClient = useQueryClient(); - const { mutateAsync: modifyMenuName } = useMutation({ + const { mutateAsync: updateMenuName } = useMutation({ mutationFn: (nodeName: string) => { const encodedName = encodeURIComponent(nodeName); - return MenuApi.modifyMenuName(id as number, encodedName); + return MenuApi.updateMenuName(id as number, encodedName); }, onSuccess: () => { queryClient.invalidateQueries({ @@ -53,7 +53,7 @@ function MenuNode({ const handleSelect = () => onSelect(node); const handleSave = async () => { - await modifyMenuName(nodeName); + await updateMenuName(nodeName); setEditMode(false); }; diff --git a/apps/cs/src/pages/menu/ui/menu-page.tsx b/apps/cs/src/pages/menu/ui/menu-page.tsx index c3a186eb6..71ad0042b 100644 --- a/apps/cs/src/pages/menu/ui/menu-page.tsx +++ b/apps/cs/src/pages/menu/ui/menu-page.tsx @@ -80,14 +80,14 @@ function MenuPage() { }, }); - const { mutate: modifyMenu } = useMutation({ + const { mutate: updateMenu } = useMutation({ mutationFn: ({ menuCd, data, }: { menuCd: number; data: Omit; - }) => MenuApi.modifyMenu(menuCd, data), + }) => MenuApi.updateMenu(menuCd, data), onSuccess: () => { alert("수정되었습니다."); queryClient.invalidateQueries({ @@ -250,7 +250,7 @@ function MenuPage() { form={form} menuCd={selectedMenu?.menuCd} handleCreate={createMenu} - handleModify={modifyMenu} + handleUpdate={updateMenu} handleDelete={deleteMenu} /> diff --git a/apps/cs/src/pages/menu/ui/menu-tree.tsx b/apps/cs/src/pages/menu/ui/menu-tree.tsx index 76dc02610..45a4cbb37 100644 --- a/apps/cs/src/pages/menu/ui/menu-tree.tsx +++ b/apps/cs/src/pages/menu/ui/menu-tree.tsx @@ -41,9 +41,9 @@ function MenuTree({ const queryClient = useQueryClient(); const treeMethods = useRef(null); - const { mutate: modifyTreeMenu } = useMutation({ + const { mutate: updateTreeMenu } = useMutation({ mutationFn: (newTree: TreeNode) => - MenuApi.modifyTreeMenu(siteId, [newTree]), + MenuApi.updateTreeMenu(siteId, [newTree]), onSuccess: () => { queryClient.invalidateQueries({ queryKey: [ENDPOINT.CMS_SERVICE.MENUS], @@ -96,7 +96,7 @@ function MenuTree({ /> ); }} - onDrop={handleDropNode(modifyTreeMenu)} + onDrop={handleDropNode(updateTreeMenu)} classes={{ root: "p-4", container: "flex flex-col", diff --git a/apps/cs/src/pages/site/ui/modify-page.tsx b/apps/cs/src/pages/site/ui/modify-page.tsx index b09276996..6b6c43c1f 100644 --- a/apps/cs/src/pages/site/ui/modify-page.tsx +++ b/apps/cs/src/pages/site/ui/modify-page.tsx @@ -10,8 +10,6 @@ import { useAlertStore } from "@/shared/lib/use-alert-store"; import { SiteForm } from "./site-form"; function ModifyPage({ siteId }: { siteId: string }) { - const isModifyMode = !!siteId; - const router = useRouter(); const queryClient = useQueryClient(); const { setMessage: alert } = useAlertStore((state) => state); @@ -30,11 +28,11 @@ function ModifyPage({ siteId }: { siteId: string }) { bscSiteYn: data.basicSiteYn, }; }, - enabled: isModifyMode, + enabled: !!siteId, }); - const { mutateAsync: modifySite } = useMutation({ - mutationFn: (data: SiteFormData) => SiteApi.modifySite(siteId, data), + const { mutateAsync: updateSite } = useMutation({ + mutationFn: (data: SiteFormData) => SiteApi.updateSite(siteId, data), onSuccess: () => { alert("수정되었습니다.", () => { queryClient.invalidateQueries({ @@ -69,7 +67,7 @@ function ModifyPage({ siteId }: { siteId: string }) { diff --git a/apps/cs/src/pages/site/ui/site-form.tsx b/apps/cs/src/pages/site/ui/site-form.tsx index d8eac7343..cbf90c95b 100644 --- a/apps/cs/src/pages/site/ui/site-form.tsx +++ b/apps/cs/src/pages/site/ui/site-form.tsx @@ -38,6 +38,12 @@ function SiteForm({ form, siteId, handleSave, handleDelete }: SiteFormProps) { const [isIpManagementOpen, setIsIpManagementOpen] = useState(false); const [isDomainManagementOpen, setIsDomainManagementOpen] = useState(false); + const handleConfirm = async (callback: () => Promise) => { + setOnConfirm(true); + await callback(); + setOnConfirm(false); + }; + const handleSaveDomain = (rows: Domain[]) => { form.setValue( "dmnAddr", @@ -50,21 +56,19 @@ function SiteForm({ form, siteId, handleSave, handleDelete }: SiteFormProps) { ? "수정하시겠습니까?" : "저장하시겠습니까?"; - setOnConfirm(true); - if (await confirm(confirmMessage)) { - setOnConfirm(false); - handleSave(data); - } - setOnConfirm(false); + await handleConfirm(async () => { + if (await confirm(confirmMessage)) { + handleSave(data); + } + }); }; const onDelete = async () => { - setOnConfirm(true); - if (await confirm("삭제하시겠습니까?")) { - setOnConfirm(false); - handleDelete?.(); - } - setOnConfirm(false); + await handleConfirm(async () => { + if (await confirm("삭제하시겠습니까?")) { + handleDelete?.(); + } + }); }; return (