From 8c84ef2be8fd600bc3b2020c1ea9441403184b28 Mon Sep 17 00:00:00 2001 From: bicochan Date: Thu, 15 May 2025 16:32:30 +0900 Subject: [PATCH 1/8] =?UTF-8?q?feat:=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=EB=B0=8F=20=EB=8F=99=EC=98=81=EC=83=81=20=EA=B2=8C=EC=8B=9C?= =?UTF-8?q?=EA=B8=80=20=EC=9E=91=EC=97=85(cs,=20features)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../attachment/api/attachment-service.ts | 18 +- .../attachment/model/attachment-interface.ts | 4 +- .../src/entities/post/api/post-service.ts | 12 +- .../src/entities/post/model/post-interface.ts | 14 +- .../src/pages/(pst)/image/ui/modify-page.tsx | 4 +- .../src/pages/(pst)/normal/ui/modify-page.tsx | 4 +- .../src/pages/(pst)/notice/ui/modify-page.tsx | 4 +- apps/common/features/src/shared/config/api.ts | 1 + .../features/src/shared/lib/use-prompt.tsx | 6 +- .../src/shared/providers/prompt-provider.tsx | 15 +- .../src/shared/ui/grid-view-data-table.tsx | 2 +- .../post/model/grid-view-table-columns.tsx | 166 ++++++++++++------ .../src/widgets/post/ui/post-action.tsx | 4 +- .../src/widgets/post/ui/post-comment.tsx | 3 +- .../src/widgets/post/ui/post-page.tsx | 52 ++---- .../features/src/widgets/post/ui/post.tsx | 8 +- .../post/ui/with-preview-file-uploader.tsx | 2 +- apps/cs/src/app/providers/app-provider.tsx | 31 ++-- apps/cs/src/entities/post/api/post-service.ts | 29 ++- .../src/entities/post/model/post-interface.ts | 14 +- .../src/pages/(post)/image/ui/modify-page.tsx | 6 +- .../src/pages/(post)/movie/ui/modify-page.tsx | 6 +- .../pages/(post)/normal/ui/modify-page.tsx | 6 +- .../pages/(post)/notice/ui/modify-page.tsx | 6 +- apps/cs/src/shared/config/api.ts | 2 +- apps/cs/src/shared/lib/use-prompt.tsx | 6 +- apps/cs/src/shared/providers/index.ts | 1 + .../src/shared/providers/prompt-provider.tsx | 15 +- .../cs/src/shared/ui/grid-view-data-table.tsx | 2 +- apps/cs/src/shared/ui/prompt-dialog.tsx | 45 +++++ .../post/model/grid-view-table-columns.tsx | 163 +++++++++++------ .../widgets/post/model/post-table-columns.tsx | 2 +- apps/cs/src/widgets/post/ui/post-action.tsx | 14 +- apps/cs/src/widgets/post/ui/post-comment.tsx | 3 +- apps/cs/src/widgets/post/ui/post-page.tsx | 34 ++-- apps/cs/src/widgets/post/ui/post.tsx | 30 +--- 36 files changed, 456 insertions(+), 278 deletions(-) create mode 100644 apps/cs/src/shared/ui/prompt-dialog.tsx diff --git a/apps/common/features/src/entities/attachment/api/attachment-service.ts b/apps/common/features/src/entities/attachment/api/attachment-service.ts index a5352c712..fc2dcab05 100644 --- a/apps/common/features/src/entities/attachment/api/attachment-service.ts +++ b/apps/common/features/src/entities/attachment/api/attachment-service.ts @@ -52,7 +52,15 @@ export class AttachmentApiService implements AttachmentService { return URL.createObjectURL(blob); } - public async getMovieThumbnailUrl(atflId: string): Promise { + public async getStreamVideoUrlByAtflId(atflId: string): Promise { + const response = await fetch( + `${ENDPOINT.ATTACH_SERVICE.STREAM_VIDEO}/${atflId}`, + ); + const blob = await response.blob(); + return URL.createObjectURL(blob); + } + + public async getMovieThumbnailUrlByAtflId(atflId: string): Promise { const response = await fetch( `${ENDPOINT.ATTACH_SERVICE.MOVIE_THUMBNAIL}/${atflId}`, ); @@ -60,6 +68,14 @@ export class AttachmentApiService implements AttachmentService { return URL.createObjectURL(blob); } + public async getMovieThumbnailUrlByAtflCd(atflCd: string): Promise { + const response = await fetch( + `${ENDPOINT.ATTACH_SERVICE.MOVIE_THUMBNAIL}/atflCd/${atflCd}`, + ); + const blob = await response.blob(); + return URL.createObjectURL(blob); + } + public async downloadAttachmentByAtflCd( atflCd: string, ): Promise { diff --git a/apps/common/features/src/entities/attachment/model/attachment-interface.ts b/apps/common/features/src/entities/attachment/model/attachment-interface.ts index 5ad0fc3ea..0f1b5d1ee 100644 --- a/apps/common/features/src/entities/attachment/model/attachment-interface.ts +++ b/apps/common/features/src/entities/attachment/model/attachment-interface.ts @@ -40,6 +40,8 @@ export interface AttachmentService { deleteAttachmentByAtflCd: (atflCd: string) => Promise; getImageUrlByAtflId: (atflId: string) => Promise; getImageUrlByAtflCd: (atflCd: string) => Promise; - getMovieThumbnailUrl: (atflId: string) => Promise; + getStreamVideoUrlByAtflId: (atflId: string) => Promise; + getMovieThumbnailUrlByAtflId: (atflId: string) => Promise; + getMovieThumbnailUrlByAtflCd: (atflCd: string) => Promise; downloadAttachmentByAtflCd: (atflCd: string) => Promise; } diff --git a/apps/common/features/src/entities/post/api/post-service.ts b/apps/common/features/src/entities/post/api/post-service.ts index a07fc4d2a..1e5ac5146 100644 --- a/apps/common/features/src/entities/post/api/post-service.ts +++ b/apps/common/features/src/entities/post/api/post-service.ts @@ -14,7 +14,7 @@ import { export class PostApiService implements PostService { public async getPostList( - bbsId: string, + bbsId: number, params: PostParams, ): Promise { const response = await client.get( @@ -24,7 +24,7 @@ export class PostApiService implements PostService { return response.data; } - public async getPostInfo(bbsId: string, pstNo: string): Promise { + public async getPostInfo(bbsId: number, pstNo: number): Promise { const response = await client.get( `${ENDPOINT.BOARD_SERVICE.POSTS_USER}/${bbsId}/${pstNo}`, ); @@ -41,8 +41,8 @@ export class PostApiService implements PostService { } public async updatePost( - bbsId: string, - pstNo: string, + bbsId: number, + pstNo: number, data: PostUpdateFormData, ): Promise { const validatedData = postUpdateFormSchema.parse(data); @@ -54,8 +54,8 @@ export class PostApiService implements PostService { } public async deletePost( - bbsId: string, - pstNo: string, + bbsId: number, + pstNo: number, pstPswd?: string, ): Promise { await client.delete( diff --git a/apps/common/features/src/entities/post/model/post-interface.ts b/apps/common/features/src/entities/post/model/post-interface.ts index 9cb97ef92..11778002f 100644 --- a/apps/common/features/src/entities/post/model/post-interface.ts +++ b/apps/common/features/src/entities/post/model/post-interface.ts @@ -8,13 +8,15 @@ export interface PostParams extends PageableParams { upndFixYn?: string; prdPstgYn?: string; siteId?: string; + bbsTypeCd?: string; } export interface Post { - bbsId: number; rowNo: number; + bbsId: number; pstNo: number; pstTtl: string; + pstCn: string; inqNocs: number; rfrncUrl: string; popupYn: string; @@ -73,13 +75,13 @@ export type PostFormData = z.infer; export type PostUpdateFormData = z.infer; export interface PostService { - getPostList(bbsId: string, params: PostParams): Promise; - getPostInfo(bbsId: string, pstNo: string): Promise; + getPostList(bbsId: number, params: PostParams): Promise; + getPostInfo(bbsId: number, pstNo: number): Promise; createPost(data: PostFormData): Promise; updatePost( - bbsId: string, - pstNo: string, + bbsId: number, + pstNo: number, data: PostUpdateFormData, ): Promise; - deletePost(bbsId: string, pstNo: string, pstPswd?: string): Promise; + deletePost(bbsId: number, pstNo: number, pstPswd?: string): Promise; } diff --git a/apps/common/features/src/pages/(pst)/image/ui/modify-page.tsx b/apps/common/features/src/pages/(pst)/image/ui/modify-page.tsx index c721286e4..8b09be8c3 100644 --- a/apps/common/features/src/pages/(pst)/image/ui/modify-page.tsx +++ b/apps/common/features/src/pages/(pst)/image/ui/modify-page.tsx @@ -20,12 +20,12 @@ function ModifyPage({ bbsId, pstNo }: { bbsId: string; pstNo: string }) { const { data: postInfo } = useQuery({ queryKey: [ENDPOINT.BOARD_SERVICE.POSTS_USER, bbsId, pstNo], - queryFn: () => PostApi.getPostInfo(bbsId, pstNo), + queryFn: () => PostApi.getPostInfo(parseInt(bbsId), parseInt(pstNo)), }); const { mutateAsync: updatePost } = useMutation({ mutationFn: (data: PostUpdateFormData) => - PostApi.updatePost(bbsId, pstNo, data), + PostApi.updatePost(parseInt(bbsId), parseInt(pstNo), data), onSuccess: () => { alert("수정되었습니다.", router.back); }, diff --git a/apps/common/features/src/pages/(pst)/normal/ui/modify-page.tsx b/apps/common/features/src/pages/(pst)/normal/ui/modify-page.tsx index d6114ab44..e0d1834d4 100644 --- a/apps/common/features/src/pages/(pst)/normal/ui/modify-page.tsx +++ b/apps/common/features/src/pages/(pst)/normal/ui/modify-page.tsx @@ -20,12 +20,12 @@ function ModifyPage({ bbsId, pstNo }: { bbsId: string; pstNo: string }) { const { data: postInfo } = useQuery({ queryKey: [ENDPOINT.BOARD_SERVICE.POSTS_USER, bbsId, pstNo], - queryFn: () => PostApi.getPostInfo(bbsId, pstNo), + queryFn: () => PostApi.getPostInfo(parseInt(bbsId), parseInt(pstNo)), }); const { mutateAsync: updatePost } = useMutation({ mutationFn: (data: PostUpdateFormData) => - PostApi.updatePost(bbsId, pstNo, data), + PostApi.updatePost(parseInt(bbsId), parseInt(pstNo), data), onSuccess: () => { alert("수정되었습니다.", router.back); }, diff --git a/apps/common/features/src/pages/(pst)/notice/ui/modify-page.tsx b/apps/common/features/src/pages/(pst)/notice/ui/modify-page.tsx index bd014917f..9fe65782d 100644 --- a/apps/common/features/src/pages/(pst)/notice/ui/modify-page.tsx +++ b/apps/common/features/src/pages/(pst)/notice/ui/modify-page.tsx @@ -20,12 +20,12 @@ function ModifyPage({ bbsId, pstNo }: { bbsId: string; pstNo: string }) { const { data: postInfo } = useQuery({ queryKey: [ENDPOINT.BOARD_SERVICE.POSTS_USER, bbsId, pstNo], - queryFn: () => PostApi.getPostInfo(bbsId, pstNo), + queryFn: () => PostApi.getPostInfo(parseInt(bbsId), parseInt(pstNo)), }); const { mutateAsync: updatePost } = useMutation({ mutationFn: (data: PostUpdateFormData) => - PostApi.updatePost(bbsId, pstNo, data), + PostApi.updatePost(parseInt(bbsId), parseInt(pstNo), data), onSuccess: () => { alert("수정되었습니다.", router.back); }, diff --git a/apps/common/features/src/shared/config/api.ts b/apps/common/features/src/shared/config/api.ts index 243afca6d..b6191979e 100644 --- a/apps/common/features/src/shared/config/api.ts +++ b/apps/common/features/src/shared/config/api.ts @@ -10,6 +10,7 @@ export const ENDPOINT = { MOVIE_THUMBNAIL: "/attach-service/api/v1/movie/thumbnail", ATTACHMENTS: "/attach-service/api/v1/atfl/attachments", DOWNLOAD: "/attach-service/api/v1/atflcd/download", + STREAM_VIDEO: "/attach-service/api/v1/stream/video", }, USER_SERVICE: { LOGIN: "/user-service/login", diff --git a/apps/common/features/src/shared/lib/use-prompt.tsx b/apps/common/features/src/shared/lib/use-prompt.tsx index da9c4b386..f36858eb0 100644 --- a/apps/common/features/src/shared/lib/use-prompt.tsx +++ b/apps/common/features/src/shared/lib/use-prompt.tsx @@ -7,18 +7,18 @@ export const usePrompt = () => { const prompt = (message: string) => { setMessage(message); - return new Promise((resolve) => { + return new Promise<{ resolved: boolean; value?: string }>((resolve) => { setResolve(() => resolve); }); }; const onSubmit = (value: string) => { - resolve?.(value); + resolve?.({ resolved: true, value }); setMessage(""); }; const onCancel = () => { - resolve?.(""); + resolve?.({ resolved: false }); setMessage(""); }; diff --git a/apps/common/features/src/shared/providers/prompt-provider.tsx b/apps/common/features/src/shared/providers/prompt-provider.tsx index 267b7aafc..10338c338 100644 --- a/apps/common/features/src/shared/providers/prompt-provider.tsx +++ b/apps/common/features/src/shared/providers/prompt-provider.tsx @@ -2,9 +2,15 @@ import { createContext, useState } from "react"; interface PromptContext { message?: string; - resolve?: (value: string) => void; + resolve?: ({ + resolved, + value, + }: { + resolved: boolean; + value?: string; + }) => void; setMessage: (message: string) => void; - setResolve: (resolve: (value: string) => void) => void; + setResolve: (resolve: () => void) => void; } export const PromptContext = createContext({ @@ -16,7 +22,10 @@ export const PromptContext = createContext({ export const PromptProvider = ({ children }: { children: React.ReactNode }) => { const [message, setMessage] = useState(); - const [resolve, setResolve] = useState<(value: string) => void>(); + const [resolve, setResolve] = + useState< + ({ resolved, value }: { resolved: boolean; value?: string }) => void + >(); return ( ({ key={row.id} onClick={() => onRowClick?.(row.original)} className={cn( - "rounded-md border border-border bg-background p-4 shadow-md", + "overflow-hidden rounded-md border border-border bg-background p-4 shadow-md", onRowClick && "cursor-pointer hover:bg-accent", )} > diff --git a/apps/common/features/src/widgets/post/model/grid-view-table-columns.tsx b/apps/common/features/src/widgets/post/model/grid-view-table-columns.tsx index 242c7ee2f..34f2fc237 100644 --- a/apps/common/features/src/widgets/post/model/grid-view-table-columns.tsx +++ b/apps/common/features/src/widgets/post/model/grid-view-table-columns.tsx @@ -1,11 +1,19 @@ import dayjs from "dayjs"; import Image from "next/image"; import type { ColumnDef } from "@tanstack/react-table"; -import { Badge, Checkbox, Skeleton } from "@common/components"; +import { Badge, Checkbox, Skeleton, cn } from "@common/components"; +import { AttachmentApi } from "@/entities/attachment"; import type { Post } from "@/entities/post"; import { useQuery } from "@tanstack/react-query"; import { ENDPOINT } from "@/shared/config"; -import { AttachmentApi } from "@/entities/attachment"; +import type { BbsType } from "@/shared/store/menu-store"; + +declare module "@tanstack/react-table" { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + interface TableMeta { + bbsTypeCd?: BbsType; + } +} export const createColumns = ( thumbRefs: React.MutableRefObject>, @@ -22,68 +30,120 @@ export const createColumns = ( className="h-4 w-4 rounded border-gray-300" /> ), - cell: ({ row }) => { - const { pstTtl, inqNocs } = row.original; - return ( -
-
- e.stopPropagation()} - onCheckedChange={(checked) => row.toggleSelected(!!checked)} - className="h-4 w-4 rounded border-gray-300" + cell: ({ row, table }) => { + const { atflCd, pstNo, pstTtl, inqNocs, rgtrNm, regDt } = row.original; + const bbsTypeCd = table.options.meta?.bbsTypeCd; + + if (bbsTypeCd === "BBSIMAGE") { + return ( +
+
+ e.stopPropagation()} + onCheckedChange={(checked) => row.toggleSelected(!!checked)} + className="h-4 w-4 rounded border-gray-300" + /> + {inqNocs >= 100 && HOT} + {dayjs().diff(dayjs(row.original.regDt), "hour") < 24 && ( + NEW + )} + + {pstTtl} + +
+ - {inqNocs >= 100 && HOT} - {dayjs().diff(dayjs(row.original.regDt), "hour") < 24 && ( - NEW - )} +
+ + {rgtrNm} + + + {dayjs(regDt).format("YYYY-MM-DD")} + +
- - {pstTtl} - -
- ); + ); + } + + if (bbsTypeCd === "BBSMOVIE") { + return ( +
+ +
+
+
+ e.stopPropagation()} + onCheckedChange={(checked) => row.toggleSelected(!!checked)} + className="h-4 w-4 rounded border-gray-300" + /> + {inqNocs >= 100 && HOT} + {dayjs().diff(dayjs(regDt), "hour") < 24 && ( + NEW + )} + + {pstTtl} + +
+
+ + {rgtrNm} + + + {dayjs(regDt).format("YYYY-MM-DD")} + +
+
+
+
+ ); + } }, }, - { - id: "atflCd", - cell: ({ row }) => { - const { atflCd, pstNo } = row.original; - return ; - }, - }, - { - id: "rgtrNm", - cell: ({ row }) => { - return ( - - {row.original.rgtrNm} - - ); - }, - }, - { - id: "regDt", - cell: ({ row }) => ( - - {dayjs(row.original.regDt).format("YYYY-MM-DD")} - - ), - }, ]; type ThumbImageProps = { atflCd: string; pstNo: number; thumbRefs: React.MutableRefObject>; + bbsTypeCd?: string; + classes?: { + image?: string; + }; }; -const ThumbImage = ({ atflCd, pstNo, thumbRefs }: ThumbImageProps) => { +const ThumbImage = ({ + atflCd, + pstNo, + thumbRefs, + bbsTypeCd, + classes, +}: ThumbImageProps) => { const prevImage = thumbRefs.current.get(pstNo); const { data: imageUrl, isLoading } = useQuery({ queryKey: [ENDPOINT.ATTACH_SERVICE.IMAGES, atflCd], - queryFn: () => AttachmentApi.getImageUrlByAtflCd(atflCd), + queryFn: () => { + if (bbsTypeCd === "BBSIMAGE") { + return AttachmentApi.getImageUrlByAtflCd(atflCd); + } else if (bbsTypeCd === "BBSMOVIE") { + return AttachmentApi.getMovieThumbnailUrlByAtflCd(atflCd); + } + }, select: (url) => { if (url) thumbRefs.current.set(pstNo, url); return url; @@ -93,17 +153,17 @@ const ThumbImage = ({ atflCd, pstNo, thumbRefs }: ThumbImageProps) => { if (isLoading) return ( -
- +
+
); if (imageUrl) return ( -
+
썸네일 이미지 @@ -111,7 +171,7 @@ const ThumbImage = ({ atflCd, pstNo, thumbRefs }: ThumbImageProps) => { ); return ( -
+
); diff --git a/apps/common/features/src/widgets/post/ui/post-action.tsx b/apps/common/features/src/widgets/post/ui/post-action.tsx index c74c76482..0169e313c 100644 --- a/apps/common/features/src/widgets/post/ui/post-action.tsx +++ b/apps/common/features/src/widgets/post/ui/post-action.tsx @@ -35,7 +35,7 @@ function PostAction({ children, handleDelete }: PostActionProps) { }; return ( -
+ <> {children}
-
+ ); } diff --git a/apps/common/features/src/widgets/post/ui/post-comment.tsx b/apps/common/features/src/widgets/post/ui/post-comment.tsx index f87a90fca..1800b2581 100644 --- a/apps/common/features/src/widgets/post/ui/post-comment.tsx +++ b/apps/common/features/src/widgets/post/ui/post-comment.tsx @@ -89,7 +89,8 @@ function PostComment({ bbsId, pstNo }: { bbsId: number; pstNo: number }) { if (userInfo?.userUnqId) { cmntPswd = userInfo?.userUnqId; } else { - cmntPswd = await prompt("비밀번호를 입력해주세요."); + const { resolved, value } = await prompt("비밀번호를 입력해주세요."); + if (resolved) cmntPswd = value ?? ""; } return cmntPswd; }; diff --git a/apps/common/features/src/widgets/post/ui/post-page.tsx b/apps/common/features/src/widgets/post/ui/post-page.tsx index 262226a48..c4bf33576 100644 --- a/apps/common/features/src/widgets/post/ui/post-page.tsx +++ b/apps/common/features/src/widgets/post/ui/post-page.tsx @@ -24,11 +24,10 @@ import { } from "@common/components"; import { PostApi, type Post, type PostParams } from "@/entities/post"; import { ENDPOINT } from "@/shared/config"; -import type { BbsType } from "@/shared/store/menu-store"; import { useMenuStore, useUserStore } from "@/shared/lib"; import { GridViewDataTable } from "@/shared/ui/grid-view-data-table"; -import { columns } from "../model/post-table-columns"; -import { createColumns } from "../model/grid-view-table-columns"; +import { columns as tableColumns } from "../model/post-table-columns"; +import { createColumns as createGridViewColumns } from "../model/grid-view-table-columns"; function PostPage({ bbsId }: { bbsId: string }) { const router = useRouter(); @@ -36,7 +35,6 @@ function PostPage({ bbsId }: { bbsId: string }) { const { confirm } = useConfirm(); const { userInfo } = useUserStore((state) => state); const { bbsInfo, pageMenuAuthority } = useMenuStore((state) => state); - const bbsType = bbsInfo?.bbsTypeCd as BbsType; const [postParams, setPostParams] = useState({ keywordType: "title", @@ -53,7 +51,7 @@ function PostPage({ bbsId }: { bbsId: string }) { const { data: upndFixPostList } = useQuery({ queryKey: [ENDPOINT.BOARD_SERVICE.POSTS_USER, bbsId, bbsInfo], queryFn: () => - PostApi.getPostList(bbsId, { + PostApi.getPostList(parseInt(bbsId), { ...postParams, upndFixYn: bbsInfo?.upndFixUseYn, }), @@ -77,7 +75,7 @@ function PostPage({ bbsId }: { bbsId: string }) { } = useQuery({ queryKey: [ENDPOINT.BOARD_SERVICE.POSTS_USER, bbsId], queryFn: () => - PostApi.getPostList(bbsId, { + PostApi.getPostList(parseInt(bbsId), { ...postParams, page: pagination.pageIndex, size: pagination.pageSize, @@ -85,9 +83,14 @@ function PostPage({ bbsId }: { bbsId: string }) { enabled: false, }); + const isGridViewPage = + bbsInfo?.bbsTypeCd === "BBSIMAGE" || bbsInfo?.bbsTypeCd === "BBSMOVIE"; + const table = useReactTable({ - data: [...(upndFixPostList?.content ?? []), ...(postList?.content ?? [])], - columns, + data: isGridViewPage + ? (postList?.content ?? []) + : [...(upndFixPostList?.content ?? []), ...(postList?.content ?? [])], + columns: isGridViewPage ? createGridViewColumns(thumbRefs) : tableColumns, state: { pagination, rowSelection }, rowCount: postList?.totalElements ?? 0, manualPagination: true, @@ -95,19 +98,10 @@ function PostPage({ bbsId }: { bbsId: string }) { getCoreRowModel: getCoreRowModel(), onPaginationChange: setPagination, onRowSelectionChange: setRowSelection, + meta: { bbsTypeCd: bbsInfo?.bbsTypeCd }, }); - const gridViewTable = useReactTable({ - data: postList?.content ?? [], - columns: createColumns(thumbRefs), - state: { pagination, rowSelection }, - rowCount: postList?.totalElements ?? 0, - manualPagination: true, - enableMultiRowSelection: true, - getCoreRowModel: getCoreRowModel(), - onPaginationChange: setPagination, - onRowSelectionChange: setRowSelection, - }); + const DataTableComponent = isGridViewPage ? GridViewDataTable : DataTable; const onPostDelete = async () => { const selectedPosts = table @@ -118,7 +112,7 @@ function PostPage({ bbsId }: { bbsId: string }) { setRowSelection({}); await Promise.all( selectedPosts.map((pstNo) => - PostApi.deletePost(bbsId, pstNo.toString(), userInfo?.userUnqId), + PostApi.deletePost(parseInt(bbsId), pstNo, userInfo?.userUnqId), ), ); refetch(); @@ -211,19 +205,11 @@ function PostPage({ bbsId }: { bbsId: string }) {
{isFetched ? ( - bbsType === "BBSIMAGE" ? ( - - ) : ( - - ) + ) : ( )} diff --git a/apps/common/features/src/widgets/post/ui/post.tsx b/apps/common/features/src/widgets/post/ui/post.tsx index 379bcdd38..19e271315 100644 --- a/apps/common/features/src/widgets/post/ui/post.tsx +++ b/apps/common/features/src/widgets/post/ui/post.tsx @@ -29,7 +29,7 @@ function Post({ bbsId, pstNo }: { bbsId: string; pstNo: string }) { const { data: postData } = useQuery({ queryKey: [ENDPOINT.BOARD_SERVICE.POSTS_USER, bbsId, pstNo], - queryFn: () => PostApi.getPostInfo(bbsId, pstNo), + queryFn: () => PostApi.getPostInfo(parseInt(bbsId), parseInt(pstNo)), }); const { data: downloadResponse, isFetching } = useQuery({ @@ -47,7 +47,8 @@ function Post({ bbsId, pstNo }: { bbsId: string; pstNo: string }) { }); const { mutate: deletePost } = useMutation({ - mutationFn: (pstPswd: string) => PostApi.deletePost(bbsId, pstNo, pstPswd), + mutationFn: (pstPswd: string) => + PostApi.deletePost(parseInt(bbsId), parseInt(pstNo), pstPswd), onSuccess: () => { alert("삭제되었습니다.", router.back); }, @@ -58,7 +59,8 @@ function Post({ bbsId, pstNo }: { bbsId: string; pstNo: string }) { if (userInfo?.userUnqId) { pstPswd = userInfo?.userUnqId; } else { - pstPswd = await prompt("비밀번호를 입력해주세요."); + const { resolved, value } = await prompt("비밀번호를 입력해주세요."); + if (resolved) pstPswd = value ?? ""; } deletePost(pstPswd); }; diff --git a/apps/common/features/src/widgets/post/ui/with-preview-file-uploader.tsx b/apps/common/features/src/widgets/post/ui/with-preview-file-uploader.tsx index 618108017..9496280e6 100644 --- a/apps/common/features/src/widgets/post/ui/with-preview-file-uploader.tsx +++ b/apps/common/features/src/widgets/post/ui/with-preview-file-uploader.tsx @@ -50,7 +50,7 @@ function WithPreviewFileUploader({ imageUrl = await AttachmentApi.getImageUrlByAtflId(atflId); } if (bbsInfo?.bbsTypeCd === "BBSMOVIE") { - imageUrl = await AttachmentApi.getMovieThumbnailUrl(atflId); + imageUrl = await AttachmentApi.getMovieThumbnailUrlByAtflId(atflId); } setThumbnailUrl(imageUrl); } diff --git a/apps/cs/src/app/providers/app-provider.tsx b/apps/cs/src/app/providers/app-provider.tsx index 713bc172c..7732d34ba 100644 --- a/apps/cs/src/app/providers/app-provider.tsx +++ b/apps/cs/src/app/providers/app-provider.tsx @@ -6,8 +6,9 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { ConfirmProvider } from "@frontend-opensource/use-react-hooks"; import { AuthWrapper } from "@/entities/auth"; import { MenuWrapper } from "@/entities/menu"; -import { StoreProvider } from "@/shared/providers"; +import { PromptProvider, StoreProvider } from "@/shared/providers"; import { AlertDialog } from "@/shared/ui/alert-dialog"; +import { PromptDialog } from "@/shared/ui/prompt-dialog"; import { ConfirmDialog } from "@/shared/ui/confirm-dialog"; import { initializeNextClientConfig } from "@common/config"; @@ -19,17 +20,23 @@ const AppProvider = ({ children }: Readonly<{ children: React.ReactNode }>) => { return ( - - - - - {children} - - - - - - + + + + + + {children} + + + + + + + + ); diff --git a/apps/cs/src/entities/post/api/post-service.ts b/apps/cs/src/entities/post/api/post-service.ts index a07fc4d2a..7820af709 100644 --- a/apps/cs/src/entities/post/api/post-service.ts +++ b/apps/cs/src/entities/post/api/post-service.ts @@ -14,19 +14,19 @@ import { export class PostApiService implements PostService { public async getPostList( - bbsId: string, + bbsId: number, params: PostParams, ): Promise { const response = await client.get( - `${ENDPOINT.BOARD_SERVICE.POSTS_USER}/${bbsId}`, + `${ENDPOINT.BOARD_SERVICE.POSTS}/${bbsId}`, { params }, ); return response.data; } - public async getPostInfo(bbsId: string, pstNo: string): Promise { + public async getPostInfo(bbsId: number, pstNo: number): Promise { const response = await client.get( - `${ENDPOINT.BOARD_SERVICE.POSTS_USER}/${bbsId}/${pstNo}`, + `${ENDPOINT.BOARD_SERVICE.POSTS}/${bbsId}/${pstNo}`, ); return response.data; } @@ -34,34 +34,33 @@ export class PostApiService implements PostService { public async createPost(data: PostFormData): Promise { const validatedData = postFormSchema.parse(data); const response = await client.post( - `${ENDPOINT.BOARD_SERVICE.POSTS_USER}`, + `${ENDPOINT.BOARD_SERVICE.POSTS}`, validatedData, ); return response.data; } public async updatePost( - bbsId: string, - pstNo: string, + bbsId: number, + pstNo: number, data: PostUpdateFormData, ): Promise { const validatedData = postUpdateFormSchema.parse(data); const response = await client.put( - `${ENDPOINT.BOARD_SERVICE.POSTS_USER}/${bbsId}/${pstNo}`, + `${ENDPOINT.BOARD_SERVICE.POSTS}/${bbsId}/${pstNo}`, validatedData, ); return response.data; } public async deletePost( - bbsId: string, - pstNo: string, - pstPswd?: string, + bbsId: number, + pstNo: number, + delRsn?: string, ): Promise { - await client.delete( - `${ENDPOINT.BOARD_SERVICE.POSTS_USER}/${bbsId}/${pstNo}`, - { params: { pstPswd } }, - ); + await client.delete(`${ENDPOINT.BOARD_SERVICE.POSTS}/${bbsId}/${pstNo}`, { + params: { delRsn }, + }); } } diff --git a/apps/cs/src/entities/post/model/post-interface.ts b/apps/cs/src/entities/post/model/post-interface.ts index 9cb97ef92..b848ccc6b 100644 --- a/apps/cs/src/entities/post/model/post-interface.ts +++ b/apps/cs/src/entities/post/model/post-interface.ts @@ -8,13 +8,15 @@ export interface PostParams extends PageableParams { upndFixYn?: string; prdPstgYn?: string; siteId?: string; + bbsTypeCd?: string; } export interface Post { - bbsId: number; rowNo: number; + bbsId: number; pstNo: number; pstTtl: string; + pstCn: string; inqNocs: number; rfrncUrl: string; popupYn: string; @@ -73,13 +75,13 @@ export type PostFormData = z.infer; export type PostUpdateFormData = z.infer; export interface PostService { - getPostList(bbsId: string, params: PostParams): Promise; - getPostInfo(bbsId: string, pstNo: string): Promise; + getPostList(bbsId: number, params: PostParams): Promise; + getPostInfo(bbsId: number, pstNo: number): Promise; createPost(data: PostFormData): Promise; updatePost( - bbsId: string, - pstNo: string, + bbsId: number, + pstNo: number, data: PostUpdateFormData, ): Promise; - deletePost(bbsId: string, pstNo: string, pstPswd?: string): Promise; + deletePost(bbsId: number, pstNo: number, delRsn?: string): Promise; } diff --git a/apps/cs/src/pages/(post)/image/ui/modify-page.tsx b/apps/cs/src/pages/(post)/image/ui/modify-page.tsx index c721286e4..c2426c134 100644 --- a/apps/cs/src/pages/(post)/image/ui/modify-page.tsx +++ b/apps/cs/src/pages/(post)/image/ui/modify-page.tsx @@ -19,13 +19,13 @@ function ModifyPage({ bbsId, pstNo }: { bbsId: string; pstNo: string }) { const { userInfo } = useUserStore((state) => state); const { data: postInfo } = useQuery({ - queryKey: [ENDPOINT.BOARD_SERVICE.POSTS_USER, bbsId, pstNo], - queryFn: () => PostApi.getPostInfo(bbsId, pstNo), + queryKey: [ENDPOINT.BOARD_SERVICE.POSTS, bbsId, pstNo], + queryFn: () => PostApi.getPostInfo(parseInt(bbsId), parseInt(pstNo)), }); const { mutateAsync: updatePost } = useMutation({ mutationFn: (data: PostUpdateFormData) => - PostApi.updatePost(bbsId, pstNo, data), + PostApi.updatePost(parseInt(bbsId), parseInt(pstNo), data), onSuccess: () => { alert("수정되었습니다.", router.back); }, diff --git a/apps/cs/src/pages/(post)/movie/ui/modify-page.tsx b/apps/cs/src/pages/(post)/movie/ui/modify-page.tsx index 2f67e313c..bee473938 100644 --- a/apps/cs/src/pages/(post)/movie/ui/modify-page.tsx +++ b/apps/cs/src/pages/(post)/movie/ui/modify-page.tsx @@ -19,13 +19,13 @@ function ModifyPage({ bbsId, pstNo }: { bbsId: string; pstNo: string }) { const { userInfo } = useUserStore((state) => state); const { data: postInfo } = useQuery({ - queryKey: [ENDPOINT.BOARD_SERVICE.POSTS_USER, bbsId, pstNo], - queryFn: () => PostApi.getPostInfo(bbsId, pstNo), + queryKey: [ENDPOINT.BOARD_SERVICE.POSTS, bbsId, pstNo], + queryFn: () => PostApi.getPostInfo(parseInt(bbsId), parseInt(pstNo)), }); const { mutateAsync: updatePost } = useMutation({ mutationFn: (data: PostUpdateFormData) => - PostApi.updatePost(bbsId, pstNo, data), + PostApi.updatePost(parseInt(bbsId), parseInt(pstNo), data), onSuccess: () => { alert("수정되었습니다.", router.back); }, diff --git a/apps/cs/src/pages/(post)/normal/ui/modify-page.tsx b/apps/cs/src/pages/(post)/normal/ui/modify-page.tsx index d6114ab44..2e23f62a0 100644 --- a/apps/cs/src/pages/(post)/normal/ui/modify-page.tsx +++ b/apps/cs/src/pages/(post)/normal/ui/modify-page.tsx @@ -19,13 +19,13 @@ function ModifyPage({ bbsId, pstNo }: { bbsId: string; pstNo: string }) { const { userInfo } = useUserStore((state) => state); const { data: postInfo } = useQuery({ - queryKey: [ENDPOINT.BOARD_SERVICE.POSTS_USER, bbsId, pstNo], - queryFn: () => PostApi.getPostInfo(bbsId, pstNo), + queryKey: [ENDPOINT.BOARD_SERVICE.POSTS, bbsId, pstNo], + queryFn: () => PostApi.getPostInfo(parseInt(bbsId), parseInt(pstNo)), }); const { mutateAsync: updatePost } = useMutation({ mutationFn: (data: PostUpdateFormData) => - PostApi.updatePost(bbsId, pstNo, data), + PostApi.updatePost(parseInt(bbsId), parseInt(pstNo), data), onSuccess: () => { alert("수정되었습니다.", router.back); }, diff --git a/apps/cs/src/pages/(post)/notice/ui/modify-page.tsx b/apps/cs/src/pages/(post)/notice/ui/modify-page.tsx index bd014917f..fa12dbcce 100644 --- a/apps/cs/src/pages/(post)/notice/ui/modify-page.tsx +++ b/apps/cs/src/pages/(post)/notice/ui/modify-page.tsx @@ -19,13 +19,13 @@ function ModifyPage({ bbsId, pstNo }: { bbsId: string; pstNo: string }) { const { userInfo } = useUserStore((state) => state); const { data: postInfo } = useQuery({ - queryKey: [ENDPOINT.BOARD_SERVICE.POSTS_USER, bbsId, pstNo], - queryFn: () => PostApi.getPostInfo(bbsId, pstNo), + queryKey: [ENDPOINT.BOARD_SERVICE.POSTS, bbsId, pstNo], + queryFn: () => PostApi.getPostInfo(parseInt(bbsId), parseInt(pstNo)), }); const { mutateAsync: updatePost } = useMutation({ mutationFn: (data: PostUpdateFormData) => - PostApi.updatePost(bbsId, pstNo, data), + PostApi.updatePost(parseInt(bbsId), parseInt(pstNo), data), onSuccess: () => { alert("수정되었습니다.", router.back); }, diff --git a/apps/cs/src/shared/config/api.ts b/apps/cs/src/shared/config/api.ts index 0ff7efe42..4238dec5f 100644 --- a/apps/cs/src/shared/config/api.ts +++ b/apps/cs/src/shared/config/api.ts @@ -40,7 +40,7 @@ export const ENDPOINT = { BOARD_SERVICE: { BBS: "/board-service/api/v1/bbs", BBS_TEMPLATES: "/board-service/api/v1/bbsTmplt", - POSTS_USER: "/board-service/api/v1/posts/user", + POSTS: "/board-service/api/v1/posts", POSTS_COMMENTS: "/board-service/api/v1/posts-cmnts", }, }; diff --git a/apps/cs/src/shared/lib/use-prompt.tsx b/apps/cs/src/shared/lib/use-prompt.tsx index da9c4b386..f36858eb0 100644 --- a/apps/cs/src/shared/lib/use-prompt.tsx +++ b/apps/cs/src/shared/lib/use-prompt.tsx @@ -7,18 +7,18 @@ export const usePrompt = () => { const prompt = (message: string) => { setMessage(message); - return new Promise((resolve) => { + return new Promise<{ resolved: boolean; value?: string }>((resolve) => { setResolve(() => resolve); }); }; const onSubmit = (value: string) => { - resolve?.(value); + resolve?.({ resolved: true, value }); setMessage(""); }; const onCancel = () => { - resolve?.(""); + resolve?.({ resolved: false }); setMessage(""); }; diff --git a/apps/cs/src/shared/providers/index.ts b/apps/cs/src/shared/providers/index.ts index f2e092170..fa8320fe7 100644 --- a/apps/cs/src/shared/providers/index.ts +++ b/apps/cs/src/shared/providers/index.ts @@ -1 +1,2 @@ export { StoreProvider } from "./store-provider"; +export { PromptProvider } from "./prompt-provider"; diff --git a/apps/cs/src/shared/providers/prompt-provider.tsx b/apps/cs/src/shared/providers/prompt-provider.tsx index 267b7aafc..10338c338 100644 --- a/apps/cs/src/shared/providers/prompt-provider.tsx +++ b/apps/cs/src/shared/providers/prompt-provider.tsx @@ -2,9 +2,15 @@ import { createContext, useState } from "react"; interface PromptContext { message?: string; - resolve?: (value: string) => void; + resolve?: ({ + resolved, + value, + }: { + resolved: boolean; + value?: string; + }) => void; setMessage: (message: string) => void; - setResolve: (resolve: (value: string) => void) => void; + setResolve: (resolve: () => void) => void; } export const PromptContext = createContext({ @@ -16,7 +22,10 @@ export const PromptContext = createContext({ export const PromptProvider = ({ children }: { children: React.ReactNode }) => { const [message, setMessage] = useState(); - const [resolve, setResolve] = useState<(value: string) => void>(); + const [resolve, setResolve] = + useState< + ({ resolved, value }: { resolved: boolean; value?: string }) => void + >(); return ( ({ key={row.id} onClick={() => onRowClick?.(row.original)} className={cn( - "rounded-md border border-border bg-background p-4 shadow-md", + "overflow-hidden rounded-md border border-border bg-background p-4 shadow-md", onRowClick && "cursor-pointer hover:bg-accent", )} > diff --git a/apps/cs/src/shared/ui/prompt-dialog.tsx b/apps/cs/src/shared/ui/prompt-dialog.tsx new file mode 100644 index 000000000..54c2f3fcc --- /dev/null +++ b/apps/cs/src/shared/ui/prompt-dialog.tsx @@ -0,0 +1,45 @@ +import { useState } from "react"; +import { + Button, + Dialog, + DialogContent, + DialogFooter, + DialogClose, + Input, +} from "@common/components"; +import { usePrompt } from "@/shared/lib/use-prompt"; + +function PromptDialog() { + const { message, isOpen, onSubmit, onCancel } = usePrompt(); + const [value, setValue] = useState(""); + + return ( + + +
+

+ {message} +

+ setValue(e.target.value)} + /> +
+ + + + + + + + +
+
+ ); +} + +export { PromptDialog }; diff --git a/apps/cs/src/widgets/post/model/grid-view-table-columns.tsx b/apps/cs/src/widgets/post/model/grid-view-table-columns.tsx index 96f7866db..34f2fc237 100644 --- a/apps/cs/src/widgets/post/model/grid-view-table-columns.tsx +++ b/apps/cs/src/widgets/post/model/grid-view-table-columns.tsx @@ -1,12 +1,19 @@ import dayjs from "dayjs"; import Image from "next/image"; import type { ColumnDef } from "@tanstack/react-table"; -import { Badge, Checkbox, Skeleton } from "@common/components"; +import { Badge, Checkbox, Skeleton, cn } from "@common/components"; import { AttachmentApi } from "@/entities/attachment"; import type { Post } from "@/entities/post"; import { useQuery } from "@tanstack/react-query"; import { ENDPOINT } from "@/shared/config"; -import { useMenuStore } from "@/shared/lib"; +import type { BbsType } from "@/shared/store/menu-store"; + +declare module "@tanstack/react-table" { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + interface TableMeta { + bbsTypeCd?: BbsType; + } +} export const createColumns = ( thumbRefs: React.MutableRefObject>, @@ -23,73 +30,117 @@ export const createColumns = ( className="h-4 w-4 rounded border-gray-300" /> ), - cell: ({ row }) => { - const { pstTtl, inqNocs } = row.original; - return ( -
-
- e.stopPropagation()} - onCheckedChange={(checked) => row.toggleSelected(!!checked)} - className="h-4 w-4 rounded border-gray-300" + cell: ({ row, table }) => { + const { atflCd, pstNo, pstTtl, inqNocs, rgtrNm, regDt } = row.original; + const bbsTypeCd = table.options.meta?.bbsTypeCd; + + if (bbsTypeCd === "BBSIMAGE") { + return ( +
+
+ e.stopPropagation()} + onCheckedChange={(checked) => row.toggleSelected(!!checked)} + className="h-4 w-4 rounded border-gray-300" + /> + {inqNocs >= 100 && HOT} + {dayjs().diff(dayjs(row.original.regDt), "hour") < 24 && ( + NEW + )} + + {pstTtl} + +
+ - {inqNocs >= 100 && HOT} - {dayjs().diff(dayjs(row.original.regDt), "hour") < 24 && ( - NEW - )} +
+ + {rgtrNm} + + + {dayjs(regDt).format("YYYY-MM-DD")} + +
- - {pstTtl} - -
- ); + ); + } + + if (bbsTypeCd === "BBSMOVIE") { + return ( +
+ +
+
+
+ e.stopPropagation()} + onCheckedChange={(checked) => row.toggleSelected(!!checked)} + className="h-4 w-4 rounded border-gray-300" + /> + {inqNocs >= 100 && HOT} + {dayjs().diff(dayjs(regDt), "hour") < 24 && ( + NEW + )} + + {pstTtl} + +
+
+ + {rgtrNm} + + + {dayjs(regDt).format("YYYY-MM-DD")} + +
+
+
+
+ ); + } }, }, - { - id: "atflCd", - cell: ({ row }) => { - const { atflCd, pstNo } = row.original; - return ; - }, - }, - { - id: "rgtrNm", - cell: ({ row }) => { - return ( - - {row.original.rgtrNm} - - ); - }, - }, - { - id: "regDt", - cell: ({ row }) => ( - - {dayjs(row.original.regDt).format("YYYY-MM-DD")} - - ), - }, ]; type ThumbImageProps = { atflCd: string; pstNo: number; thumbRefs: React.MutableRefObject>; + bbsTypeCd?: string; + classes?: { + image?: string; + }; }; -const ThumbImage = ({ atflCd, pstNo, thumbRefs }: ThumbImageProps) => { +const ThumbImage = ({ + atflCd, + pstNo, + thumbRefs, + bbsTypeCd, + classes, +}: ThumbImageProps) => { const prevImage = thumbRefs.current.get(pstNo); - const { bbsInfo } = useMenuStore((state) => state); - const { data: imageUrl, isLoading } = useQuery({ queryKey: [ENDPOINT.ATTACH_SERVICE.IMAGES, atflCd], queryFn: () => { - if (bbsInfo?.bbsTypeCd === "BBSIMAGE") { + if (bbsTypeCd === "BBSIMAGE") { return AttachmentApi.getImageUrlByAtflCd(atflCd); - } else if (bbsInfo?.bbsTypeCd === "BBSMOVIE") { + } else if (bbsTypeCd === "BBSMOVIE") { return AttachmentApi.getMovieThumbnailUrlByAtflCd(atflCd); } }, @@ -102,17 +153,17 @@ const ThumbImage = ({ atflCd, pstNo, thumbRefs }: ThumbImageProps) => { if (isLoading) return ( -
- +
+
); if (imageUrl) return ( -
+
썸네일 이미지 @@ -120,7 +171,7 @@ const ThumbImage = ({ atflCd, pstNo, thumbRefs }: ThumbImageProps) => { ); return ( -
+
); diff --git a/apps/cs/src/widgets/post/model/post-table-columns.tsx b/apps/cs/src/widgets/post/model/post-table-columns.tsx index 862538896..973ce9315 100644 --- a/apps/cs/src/widgets/post/model/post-table-columns.tsx +++ b/apps/cs/src/widgets/post/model/post-table-columns.tsx @@ -34,7 +34,7 @@ export const columns: ColumnDef[] = [ return ( diff --git a/apps/cs/src/widgets/post/ui/post-action.tsx b/apps/cs/src/widgets/post/ui/post-action.tsx index c74c76482..acaa60bf5 100644 --- a/apps/cs/src/widgets/post/ui/post-action.tsx +++ b/apps/cs/src/widgets/post/ui/post-action.tsx @@ -1,24 +1,24 @@ import { useRouter, usePathname } from "next/navigation"; -import { useConfirm } from "@frontend-opensource/use-react-hooks"; import { Button } from "@common/components"; -import { useMenuStore, useUserStore } from "@/shared/lib"; +import { useMenuStore, usePrompt, useUserStore } from "@/shared/lib"; type PostActionProps = { bbsId: string; pstNo: string; children: React.ReactNode; - handleDelete: () => Promise; + handleDelete: (delRsn?: string) => Promise; }; function PostAction({ children, handleDelete }: PostActionProps) { const router = useRouter(); const pathname = usePathname(); - const { confirm } = useConfirm(); + const { prompt } = usePrompt(); const { bbsInfo } = useMenuStore((state) => state); const { userInfo } = useUserStore((state) => state); const onDelete = async () => { - if (await confirm("삭제하시겠습니까?")) handleDelete(); + const { resolved, value } = await prompt("삭제 사유를 입력해주세요."); + if (resolved) handleDelete(value); }; const checkAnsUseAuthrt = () => { @@ -35,7 +35,7 @@ function PostAction({ children, handleDelete }: PostActionProps) { }; return ( -
+ <> {children}
-
+ ); } diff --git a/apps/cs/src/widgets/post/ui/post-comment.tsx b/apps/cs/src/widgets/post/ui/post-comment.tsx index f87a90fca..1800b2581 100644 --- a/apps/cs/src/widgets/post/ui/post-comment.tsx +++ b/apps/cs/src/widgets/post/ui/post-comment.tsx @@ -89,7 +89,8 @@ function PostComment({ bbsId, pstNo }: { bbsId: number; pstNo: number }) { if (userInfo?.userUnqId) { cmntPswd = userInfo?.userUnqId; } else { - cmntPswd = await prompt("비밀번호를 입력해주세요."); + const { resolved, value } = await prompt("비밀번호를 입력해주세요."); + if (resolved) cmntPswd = value ?? ""; } return cmntPswd; }; diff --git a/apps/cs/src/widgets/post/ui/post-page.tsx b/apps/cs/src/widgets/post/ui/post-page.tsx index 6389bc799..d419f19f6 100644 --- a/apps/cs/src/widgets/post/ui/post-page.tsx +++ b/apps/cs/src/widgets/post/ui/post-page.tsx @@ -2,7 +2,6 @@ import { useEffect, useRef, useState } from "react"; import { usePathname, useRouter } from "next/navigation"; -import { useConfirm } from "@frontend-opensource/use-react-hooks"; import { useQuery } from "@tanstack/react-query"; import { getCoreRowModel, @@ -24,7 +23,7 @@ import { } from "@common/components"; import { PostApi, type Post, type PostParams } from "@/entities/post"; import { ENDPOINT } from "@/shared/config"; -import { useMenuStore, useUserStore } from "@/shared/lib"; +import { useMenuStore, usePrompt } from "@/shared/lib"; import { GridViewDataTable } from "@/shared/ui/grid-view-data-table"; import { columns as tableColumns } from "../model/post-table-columns"; import { createColumns as createGridViewColumns } from "../model/grid-view-table-columns"; @@ -32,8 +31,7 @@ import { createColumns as createGridViewColumns } from "../model/grid-view-table function PostPage({ bbsId }: { bbsId: string }) { const router = useRouter(); const pathname = usePathname(); - const { confirm } = useConfirm(); - const { userInfo } = useUserStore((state) => state); + const { prompt } = usePrompt(); const { bbsInfo, pageMenuAuthority } = useMenuStore((state) => state); const thumbRefs = useRef>(new Map()); @@ -48,9 +46,9 @@ function PostPage({ bbsId }: { bbsId: string }) { const [rowSelection, setRowSelection] = useState({}); const { data: upndFixPostList } = useQuery({ - queryKey: [ENDPOINT.BOARD_SERVICE.POSTS_USER, bbsId, bbsInfo], + queryKey: [ENDPOINT.BOARD_SERVICE.POSTS, bbsId, bbsInfo], queryFn: () => - PostApi.getPostList(bbsId, { + PostApi.getPostList(parseInt(bbsId), { ...postParams, upndFixYn: bbsInfo?.upndFixUseYn, }), @@ -69,9 +67,9 @@ function PostPage({ bbsId }: { bbsId: string }) { isFetched, refetch, } = useQuery({ - queryKey: [ENDPOINT.BOARD_SERVICE.POSTS_USER, bbsId], + queryKey: [ENDPOINT.BOARD_SERVICE.POSTS, bbsId], queryFn: () => - PostApi.getPostList(bbsId, { + PostApi.getPostList(parseInt(bbsId), { ...postParams, page: pagination.pageIndex, size: pagination.pageSize, @@ -94,6 +92,7 @@ function PostPage({ bbsId }: { bbsId: string }) { getCoreRowModel: getCoreRowModel(), onPaginationChange: setPagination, onRowSelectionChange: setRowSelection, + meta: { bbsTypeCd: bbsInfo?.bbsTypeCd }, }); const DataTableComponent = isGridViewPage ? GridViewDataTable : DataTable; @@ -103,15 +102,16 @@ function PostPage({ bbsId }: { bbsId: string }) { .getFilteredSelectedRowModel() .rows.map((row) => row.original.pstNo); - if (await confirm("삭제하시겠습니까?")) { - setRowSelection({}); - await Promise.all( - selectedPosts.map((pstNo) => - PostApi.deletePost(bbsId, pstNo.toString(), userInfo?.userUnqId), - ), - ); - refetch(); - } + const { resolved, value: delRsn } = + await prompt("삭제 사유를 입력해주세요."); + if (!resolved) return; + setRowSelection({}); + await Promise.all( + selectedPosts.map((pstNo) => + PostApi.deletePost(parseInt(bbsId), pstNo, delRsn), + ), + ); + refetch(); }; const handleSubmit = (e: React.FormEvent) => { diff --git a/apps/cs/src/widgets/post/ui/post.tsx b/apps/cs/src/widgets/post/ui/post.tsx index 3cac19401..370c9cc1d 100644 --- a/apps/cs/src/widgets/post/ui/post.tsx +++ b/apps/cs/src/widgets/post/ui/post.tsx @@ -8,28 +8,21 @@ import { useMutation, useQuery } from "@tanstack/react-query"; import { Button, Skeleton } from "@common/components"; import { AttachmentApi } from "@/entities/attachment"; import { PostApi } from "@/entities/post"; -import { - useAlertStore, - useUserStore, - useMenuStore, - usePrompt, -} from "@/shared/lib"; +import { useAlertStore, useMenuStore } from "@/shared/lib"; import { ENDPOINT } from "@/shared/config"; import { PostAction } from "./post-action"; import { PostComment } from "./post-comment"; function Post({ bbsId, pstNo }: { bbsId: string; pstNo: string }) { const router = useRouter(); - const { prompt } = usePrompt(); const { setMessage: alert } = useAlertStore((state) => state); - const { userInfo } = useUserStore((state) => state); const { bbsInfo } = useMenuStore((state) => state); const [filename, setFilename] = useState(""); const { data: postData } = useQuery({ - queryKey: [ENDPOINT.BOARD_SERVICE.POSTS_USER, bbsId, pstNo], - queryFn: () => PostApi.getPostInfo(bbsId, pstNo), + queryKey: [ENDPOINT.BOARD_SERVICE.POSTS, bbsId, pstNo], + queryFn: () => PostApi.getPostInfo(parseInt(bbsId), parseInt(pstNo)), }); const { data: downloadResponse, isFetching } = useQuery({ @@ -65,23 +58,14 @@ function Post({ bbsId, pstNo }: { bbsId: string; pstNo: string }) { enabled: !!videoAttachment?.atflId, }); - const { mutate: deletePost } = useMutation({ - mutationFn: (pstPswd: string) => PostApi.deletePost(bbsId, pstNo, pstPswd), + const { mutateAsync: deletePost } = useMutation({ + mutationFn: (delRsn?: string) => + PostApi.deletePost(parseInt(bbsId), parseInt(pstNo), delRsn), onSuccess: () => { alert("삭제되었습니다.", router.back); }, }); - const handleDelete = async () => { - let pstPswd = ""; - if (userInfo?.userUnqId) { - pstPswd = userInfo?.userUnqId; - } else { - pstPswd = await prompt("비밀번호를 입력해주세요."); - } - deletePost(pstPswd); - }; - const handleDownload = async () => { if (!downloadResponse) return; try { @@ -123,7 +107,7 @@ function Post({ bbsId, pstNo }: { bbsId: string; pstNo: string }) { return ( <> - +

{postData?.pstTtl} From 8b4cf7c2b015cb575e8dcc6fe3bf27f67f9c997c Mon Sep 17 00:00:00 2001 From: bicochan Date: Thu, 15 May 2025 16:40:20 +0900 Subject: [PATCH 2/8] =?UTF-8?q?design:=20=EA=B3=A0=EC=A0=95=20=EA=B2=8C?= =?UTF-8?q?=EC=8B=9C=EA=B8=80=20=EC=95=84=EC=9D=B4=EC=BD=98=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../features/src/widgets/post/model/post-table-columns.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/common/features/src/widgets/post/model/post-table-columns.tsx b/apps/common/features/src/widgets/post/model/post-table-columns.tsx index 862538896..973ce9315 100644 --- a/apps/common/features/src/widgets/post/model/post-table-columns.tsx +++ b/apps/common/features/src/widgets/post/model/post-table-columns.tsx @@ -34,7 +34,7 @@ export const columns: ColumnDef[] = [ return ( From 629ae450484f5e18073d84c1fbe5120cf0e298e8 Mon Sep 17 00:00:00 2001 From: bicochan Date: Thu, 15 May 2025 16:53:05 +0900 Subject: [PATCH 3/8] =?UTF-8?q?feat:=20=EB=8F=99=EC=98=81=EC=83=81=20?= =?UTF-8?q?=EA=B2=8C=EC=8B=9C=ED=8C=90=20=EC=B6=94=EA=B0=80(features)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bbsmovie/[bbsId]/[pstNo]/modify/page.tsx | 10 ++++ .../pst/bbsmovie/[bbsId]/[pstNo]/page.tsx | 10 ++++ .../pst/bbsmovie/[bbsId]/page.tsx | 10 ++++ .../pst/bbsmovie/[bbsId]/register/page.tsx | 10 ++++ .../features/src/pages/(pst)/movie/index.ts | 4 ++ .../src/pages/(pst)/movie/ui/create-page.tsx | 43 +++++++++++++++ .../src/pages/(pst)/movie/ui/modify-page.tsx | 53 +++++++++++++++++++ .../src/pages/(pst)/movie/ui/movie-page.tsx | 3 ++ .../src/pages/(pst)/movie/ui/movie-post.tsx | 3 ++ .../features/src/widgets/post/ui/post.tsx | 26 ++++++++- apps/cs/src/pages/bbs/model/table-columns.tsx | 4 +- 11 files changed, 174 insertions(+), 2 deletions(-) create mode 100644 apps/common/features/app/(with-base-layout)/pst/bbsmovie/[bbsId]/[pstNo]/modify/page.tsx create mode 100644 apps/common/features/app/(with-base-layout)/pst/bbsmovie/[bbsId]/[pstNo]/page.tsx create mode 100644 apps/common/features/app/(with-base-layout)/pst/bbsmovie/[bbsId]/page.tsx create mode 100644 apps/common/features/app/(with-base-layout)/pst/bbsmovie/[bbsId]/register/page.tsx create mode 100644 apps/common/features/src/pages/(pst)/movie/index.ts create mode 100644 apps/common/features/src/pages/(pst)/movie/ui/create-page.tsx create mode 100644 apps/common/features/src/pages/(pst)/movie/ui/modify-page.tsx create mode 100644 apps/common/features/src/pages/(pst)/movie/ui/movie-page.tsx create mode 100644 apps/common/features/src/pages/(pst)/movie/ui/movie-post.tsx diff --git a/apps/common/features/app/(with-base-layout)/pst/bbsmovie/[bbsId]/[pstNo]/modify/page.tsx b/apps/common/features/app/(with-base-layout)/pst/bbsmovie/[bbsId]/[pstNo]/modify/page.tsx new file mode 100644 index 000000000..76f2d06c3 --- /dev/null +++ b/apps/common/features/app/(with-base-layout)/pst/bbsmovie/[bbsId]/[pstNo]/modify/page.tsx @@ -0,0 +1,10 @@ +import { ModifyPage } from "@/pages/(pst)/movie"; + +export default async function PstBbsMoviePostModifyPage({ + params, +}: { + params: Promise<{ bbsId: string; pstNo: string }>; +}) { + const { bbsId, pstNo } = await params; + return ; +} diff --git a/apps/common/features/app/(with-base-layout)/pst/bbsmovie/[bbsId]/[pstNo]/page.tsx b/apps/common/features/app/(with-base-layout)/pst/bbsmovie/[bbsId]/[pstNo]/page.tsx new file mode 100644 index 000000000..e0983bd61 --- /dev/null +++ b/apps/common/features/app/(with-base-layout)/pst/bbsmovie/[bbsId]/[pstNo]/page.tsx @@ -0,0 +1,10 @@ +import { MoviePost } from "@/pages/(pst)/movie"; + +export default async function PstBbsMoviePostPage({ + params, +}: { + params: Promise<{ bbsId: string; pstNo: string }>; +}) { + const { bbsId, pstNo } = await params; + return ; +} diff --git a/apps/common/features/app/(with-base-layout)/pst/bbsmovie/[bbsId]/page.tsx b/apps/common/features/app/(with-base-layout)/pst/bbsmovie/[bbsId]/page.tsx new file mode 100644 index 000000000..bbd6b2619 --- /dev/null +++ b/apps/common/features/app/(with-base-layout)/pst/bbsmovie/[bbsId]/page.tsx @@ -0,0 +1,10 @@ +import { MoviePage } from "@/pages/(pst)/movie"; + +export default async function PstBbsMoviePage({ + params, +}: { + params: Promise<{ bbsId: string }>; +}) { + const { bbsId } = await params; + return ; +} diff --git a/apps/common/features/app/(with-base-layout)/pst/bbsmovie/[bbsId]/register/page.tsx b/apps/common/features/app/(with-base-layout)/pst/bbsmovie/[bbsId]/register/page.tsx new file mode 100644 index 000000000..5cfffb031 --- /dev/null +++ b/apps/common/features/app/(with-base-layout)/pst/bbsmovie/[bbsId]/register/page.tsx @@ -0,0 +1,10 @@ +import { CreatePage } from "@/pages/(pst)/movie"; + +export default async function PstBbsMovieRegisterPage({ + params, +}: { + params: Promise<{ bbsId: string }>; +}) { + const { bbsId } = await params; + return ; +} diff --git a/apps/common/features/src/pages/(pst)/movie/index.ts b/apps/common/features/src/pages/(pst)/movie/index.ts new file mode 100644 index 000000000..c746cff6c --- /dev/null +++ b/apps/common/features/src/pages/(pst)/movie/index.ts @@ -0,0 +1,4 @@ +export { CreatePage } from "./ui/create-page"; +export { ModifyPage } from "./ui/modify-page"; +export { MoviePage } from "./ui/movie-page"; +export { MoviePost } from "./ui/movie-post"; diff --git a/apps/common/features/src/pages/(pst)/movie/ui/create-page.tsx b/apps/common/features/src/pages/(pst)/movie/ui/create-page.tsx new file mode 100644 index 000000000..f7e2e837f --- /dev/null +++ b/apps/common/features/src/pages/(pst)/movie/ui/create-page.tsx @@ -0,0 +1,43 @@ +"use client"; + +import { useRouter } from "next/navigation"; +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useMutation } from "@tanstack/react-query"; +import { PostForm } from "@/widgets/post"; +import { PostApi, postFormSchema, type PostFormData } from "@/entities/post"; +import { useAlertStore, useUserStore } from "@/shared/lib"; + +function CreatePage({ bbsId }: { bbsId: string }) { + const router = useRouter(); + const { userInfo } = useUserStore((state) => state); + const { setMessage: alert } = useAlertStore((state) => state); + + const { mutateAsync: createPost } = useMutation({ + mutationFn: (data: PostFormData) => PostApi.createPost(data), + onSuccess: () => { + alert("등록되었습니다.", router.back); + }, + }); + + const form = useForm({ + resolver: zodResolver(postFormSchema), + values: { + bbsId: parseInt(bbsId), + pstTtl: "", + wrtrNm: userInfo?.userName ?? "", + pstPswd: userInfo?.userUnqId ?? "", + }, + }); + + return ( +
+
+

동영상 게시글 등록

+
+ +
+ ); +} + +export { CreatePage }; diff --git a/apps/common/features/src/pages/(pst)/movie/ui/modify-page.tsx b/apps/common/features/src/pages/(pst)/movie/ui/modify-page.tsx new file mode 100644 index 000000000..6bbdd8d94 --- /dev/null +++ b/apps/common/features/src/pages/(pst)/movie/ui/modify-page.tsx @@ -0,0 +1,53 @@ +"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 { PostForm } from "@/widgets/post"; +import { + PostApi, + postUpdateFormSchema, + type PostUpdateFormData, +} from "@/entities/post"; +import { ENDPOINT } from "@/shared/config"; +import { useAlertStore, useUserStore } from "@/shared/lib"; + +function ModifyPage({ bbsId, pstNo }: { bbsId: string; pstNo: string }) { + const router = useRouter(); + const { setMessage: alert } = useAlertStore((state) => state); + const { userInfo } = useUserStore((state) => state); + + const { data: postInfo } = useQuery({ + queryKey: [ENDPOINT.BOARD_SERVICE.POSTS_USER, bbsId, pstNo], + queryFn: () => PostApi.getPostInfo(parseInt(bbsId), parseInt(pstNo)), + }); + + const { mutateAsync: updatePost } = useMutation({ + mutationFn: (data: PostUpdateFormData) => + PostApi.updatePost(parseInt(bbsId), parseInt(pstNo), data), + onSuccess: () => { + alert("수정되었습니다.", router.back); + }, + }); + + const form = useForm({ + resolver: zodResolver(postUpdateFormSchema), + values: { + ...postInfo, + pstTtl: postInfo?.pstTtl ?? "", + pstPswd: userInfo?.userUnqId ?? "", + }, + }); + + return ( +
+
+

동영상 게시글 수정

+
+ +
+ ); +} + +export { ModifyPage }; diff --git a/apps/common/features/src/pages/(pst)/movie/ui/movie-page.tsx b/apps/common/features/src/pages/(pst)/movie/ui/movie-page.tsx new file mode 100644 index 000000000..5ba24e94b --- /dev/null +++ b/apps/common/features/src/pages/(pst)/movie/ui/movie-page.tsx @@ -0,0 +1,3 @@ +"use client"; + +export { PostPage as MoviePage } from "@/widgets/post"; diff --git a/apps/common/features/src/pages/(pst)/movie/ui/movie-post.tsx b/apps/common/features/src/pages/(pst)/movie/ui/movie-post.tsx new file mode 100644 index 000000000..8a5bf6996 --- /dev/null +++ b/apps/common/features/src/pages/(pst)/movie/ui/movie-post.tsx @@ -0,0 +1,3 @@ +"use client"; + +export { Post as MoviePost } from "@/widgets/post"; diff --git a/apps/common/features/src/widgets/post/ui/post.tsx b/apps/common/features/src/widgets/post/ui/post.tsx index 19e271315..44e549a70 100644 --- a/apps/common/features/src/widgets/post/ui/post.tsx +++ b/apps/common/features/src/widgets/post/ui/post.tsx @@ -36,7 +36,10 @@ function Post({ bbsId, pstNo }: { bbsId: string; pstNo: string }) { queryKey: [ENDPOINT.ATTACH_SERVICE.DOWNLOAD, postData?.atflCd], queryFn: () => AttachmentApi.downloadAttachmentByAtflCd(postData?.atflCd as string), - enabled: !!postData?.atflCd && bbsInfo?.bbsTypeCd !== "BBSIMAGE", + enabled: + !!postData?.atflCd && + bbsInfo?.bbsTypeCd !== "BBSIMAGE" && + bbsInfo?.bbsTypeCd !== "BBSMOVIE", }); const { data: imageUrl, isLoading } = useQuery({ @@ -46,6 +49,22 @@ function Post({ bbsId, pstNo }: { bbsId: string; pstNo: string }) { enabled: !!postData?.atflCd, }); + const { data: videoAttachment } = useQuery({ + queryKey: [ENDPOINT.ATTACH_SERVICE.ATTACHMENTS, postData?.atflCd], + queryFn: () => AttachmentApi.getAttachmentsInfo(postData?.atflCd as string), + select: (data) => data[0], + enabled: !!postData?.atflCd && bbsInfo?.bbsTypeCd === "BBSMOVIE", + }); + + const { data: videoUrl } = useQuery({ + queryKey: [ENDPOINT.ATTACH_SERVICE.STREAM_VIDEO, videoAttachment?.atflId], + queryFn: () => + AttachmentApi.getStreamVideoUrlByAtflId( + videoAttachment?.atflId as string, + ), + enabled: !!videoAttachment?.atflId, + }); + const { mutate: deletePost } = useMutation({ mutationFn: (pstPswd: string) => PostApi.deletePost(parseInt(bbsId), parseInt(pstNo), pstPswd), @@ -144,6 +163,11 @@ function Post({ bbsId, pstNo }: { bbsId: string; pstNo: string }) { )} )} + {bbsInfo?.bbsTypeCd === "BBSMOVIE" && ( +
+
+ )}
             {postData?.pstCn ?? ""}
           
diff --git a/apps/cs/src/pages/bbs/model/table-columns.tsx b/apps/cs/src/pages/bbs/model/table-columns.tsx index f3975aca2..12b241a8a 100644 --- a/apps/cs/src/pages/bbs/model/table-columns.tsx +++ b/apps/cs/src/pages/bbs/model/table-columns.tsx @@ -45,6 +45,7 @@ export const columns: ColumnDef[] = [ const ManageButton = ({ row }: { row: Row }) => { const router = useRouter(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars const pathname = usePathname(); const [, setBbsInfo] = useLocalStorage("bbs-info", ""); const { setBbsInfo: setStoreBbsInfo } = useMenuStore((state) => state); @@ -55,7 +56,8 @@ const ManageButton = ({ row }: { row: Row }) => { size="sm" onClick={(e) => { e.stopPropagation(); - router.push(`${pathname}/${bbsTypeCd.toLowerCase()}/${bbsId}`); + // router.push(`${pathname}/${bbsTypeCd.toLowerCase()}/${bbsId}`); + router.push(`/pst/${bbsTypeCd.toLowerCase()}/${bbsId}`); setBbsInfo(JSON.stringify(row.original)); setStoreBbsInfo(row.original); }} From 464e61f0c7ef33caa8a32bc8b65889efe6b6efdc Mon Sep 17 00:00:00 2001 From: bicochan Date: Thu, 15 May 2025 17:43:14 +0900 Subject: [PATCH 4/8] =?UTF-8?q?refactor:=20=EA=B2=8C=EC=8B=9C=EA=B8=80=20?= =?UTF-8?q?=ED=8F=BC=20=ED=86=B5=ED=95=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/pages/(post)/image/ui/modify-page.tsx | 46 +- .../src/pages/(post)/movie/ui/modify-page.tsx | 46 +- .../pages/(post)/normal/ui/modify-page.tsx | 46 +- .../pages/(post)/notice/ui/modify-page.tsx | 46 +- apps/cs/src/widgets/post/ui/post-form.tsx | 743 +++++++++--------- .../post/ui/with-preview-file-uploader.tsx | 4 +- 6 files changed, 397 insertions(+), 534 deletions(-) diff --git a/apps/cs/src/pages/(post)/image/ui/modify-page.tsx b/apps/cs/src/pages/(post)/image/ui/modify-page.tsx index c2426c134..6b92afbb4 100644 --- a/apps/cs/src/pages/(post)/image/ui/modify-page.tsx +++ b/apps/cs/src/pages/(post)/image/ui/modify-page.tsx @@ -1,53 +1,9 @@ "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 { PostForm } from "@/widgets/post"; -import { - PostApi, - postUpdateFormSchema, - type PostUpdateFormData, -} from "@/entities/post"; -import { ENDPOINT } from "@/shared/config"; -import { useAlertStore, useUserStore } from "@/shared/lib"; function ModifyPage({ bbsId, pstNo }: { bbsId: string; pstNo: string }) { - const router = useRouter(); - const { setMessage: alert } = useAlertStore((state) => state); - const { userInfo } = useUserStore((state) => state); - - const { data: postInfo } = useQuery({ - queryKey: [ENDPOINT.BOARD_SERVICE.POSTS, bbsId, pstNo], - queryFn: () => PostApi.getPostInfo(parseInt(bbsId), parseInt(pstNo)), - }); - - const { mutateAsync: updatePost } = useMutation({ - mutationFn: (data: PostUpdateFormData) => - PostApi.updatePost(parseInt(bbsId), parseInt(pstNo), data), - onSuccess: () => { - alert("수정되었습니다.", router.back); - }, - }); - - const form = useForm({ - resolver: zodResolver(postUpdateFormSchema), - values: { - ...postInfo, - pstTtl: postInfo?.pstTtl ?? "", - pstPswd: userInfo?.userUnqId ?? "", - }, - }); - - return ( -
-
-

이미지 게시글 수정

-
- -
- ); + return ; } export { ModifyPage }; diff --git a/apps/cs/src/pages/(post)/movie/ui/modify-page.tsx b/apps/cs/src/pages/(post)/movie/ui/modify-page.tsx index bee473938..6b92afbb4 100644 --- a/apps/cs/src/pages/(post)/movie/ui/modify-page.tsx +++ b/apps/cs/src/pages/(post)/movie/ui/modify-page.tsx @@ -1,53 +1,9 @@ "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 { PostForm } from "@/widgets/post"; -import { - PostApi, - postUpdateFormSchema, - type PostUpdateFormData, -} from "@/entities/post"; -import { ENDPOINT } from "@/shared/config"; -import { useAlertStore, useUserStore } from "@/shared/lib"; function ModifyPage({ bbsId, pstNo }: { bbsId: string; pstNo: string }) { - const router = useRouter(); - const { setMessage: alert } = useAlertStore((state) => state); - const { userInfo } = useUserStore((state) => state); - - const { data: postInfo } = useQuery({ - queryKey: [ENDPOINT.BOARD_SERVICE.POSTS, bbsId, pstNo], - queryFn: () => PostApi.getPostInfo(parseInt(bbsId), parseInt(pstNo)), - }); - - const { mutateAsync: updatePost } = useMutation({ - mutationFn: (data: PostUpdateFormData) => - PostApi.updatePost(parseInt(bbsId), parseInt(pstNo), data), - onSuccess: () => { - alert("수정되었습니다.", router.back); - }, - }); - - const form = useForm({ - resolver: zodResolver(postUpdateFormSchema), - values: { - ...postInfo, - pstTtl: postInfo?.pstTtl ?? "", - pstPswd: userInfo?.userUnqId ?? "", - }, - }); - - return ( -
-
-

동영상 게시글 수정

-
- -
- ); + return ; } export { ModifyPage }; diff --git a/apps/cs/src/pages/(post)/normal/ui/modify-page.tsx b/apps/cs/src/pages/(post)/normal/ui/modify-page.tsx index 2e23f62a0..6b92afbb4 100644 --- a/apps/cs/src/pages/(post)/normal/ui/modify-page.tsx +++ b/apps/cs/src/pages/(post)/normal/ui/modify-page.tsx @@ -1,53 +1,9 @@ "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 { PostForm } from "@/widgets/post"; -import { - PostApi, - postUpdateFormSchema, - type PostUpdateFormData, -} from "@/entities/post"; -import { ENDPOINT } from "@/shared/config"; -import { useAlertStore, useUserStore } from "@/shared/lib"; function ModifyPage({ bbsId, pstNo }: { bbsId: string; pstNo: string }) { - const router = useRouter(); - const { setMessage: alert } = useAlertStore((state) => state); - const { userInfo } = useUserStore((state) => state); - - const { data: postInfo } = useQuery({ - queryKey: [ENDPOINT.BOARD_SERVICE.POSTS, bbsId, pstNo], - queryFn: () => PostApi.getPostInfo(parseInt(bbsId), parseInt(pstNo)), - }); - - const { mutateAsync: updatePost } = useMutation({ - mutationFn: (data: PostUpdateFormData) => - PostApi.updatePost(parseInt(bbsId), parseInt(pstNo), data), - onSuccess: () => { - alert("수정되었습니다.", router.back); - }, - }); - - const form = useForm({ - resolver: zodResolver(postUpdateFormSchema), - values: { - ...postInfo, - pstTtl: postInfo?.pstTtl ?? "", - pstPswd: userInfo?.userUnqId ?? "", - }, - }); - - return ( -
-
-

게시글 수정

-
- -
- ); + return ; } export { ModifyPage }; diff --git a/apps/cs/src/pages/(post)/notice/ui/modify-page.tsx b/apps/cs/src/pages/(post)/notice/ui/modify-page.tsx index fa12dbcce..6b92afbb4 100644 --- a/apps/cs/src/pages/(post)/notice/ui/modify-page.tsx +++ b/apps/cs/src/pages/(post)/notice/ui/modify-page.tsx @@ -1,53 +1,9 @@ "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 { PostForm } from "@/widgets/post"; -import { - PostApi, - postUpdateFormSchema, - type PostUpdateFormData, -} from "@/entities/post"; -import { ENDPOINT } from "@/shared/config"; -import { useAlertStore, useUserStore } from "@/shared/lib"; function ModifyPage({ bbsId, pstNo }: { bbsId: string; pstNo: string }) { - const router = useRouter(); - const { setMessage: alert } = useAlertStore((state) => state); - const { userInfo } = useUserStore((state) => state); - - const { data: postInfo } = useQuery({ - queryKey: [ENDPOINT.BOARD_SERVICE.POSTS, bbsId, pstNo], - queryFn: () => PostApi.getPostInfo(parseInt(bbsId), parseInt(pstNo)), - }); - - const { mutateAsync: updatePost } = useMutation({ - mutationFn: (data: PostUpdateFormData) => - PostApi.updatePost(parseInt(bbsId), parseInt(pstNo), data), - onSuccess: () => { - alert("수정되었습니다.", router.back); - }, - }); - - const form = useForm({ - resolver: zodResolver(postUpdateFormSchema), - values: { - ...postInfo, - pstTtl: postInfo?.pstTtl ?? "", - pstPswd: userInfo?.userUnqId ?? "", - }, - }); - - return ( -
-
-

공지 게시글 수정

-
- -
- ); + return ; } export { ModifyPage }; diff --git a/apps/cs/src/widgets/post/ui/post-form.tsx b/apps/cs/src/widgets/post/ui/post-form.tsx index cf3c14aab..dcd41de64 100644 --- a/apps/cs/src/widgets/post/ui/post-form.tsx +++ b/apps/cs/src/widgets/post/ui/post-form.tsx @@ -1,7 +1,8 @@ import { useRouter } from "next/navigation"; -import { useEffect, useState } from "react"; -import { useQuery } from "@tanstack/react-query"; -import type { UseFormReturn, FieldPath } from "react-hook-form"; +import { useState } from "react"; +import { useMutation, useQuery } from "@tanstack/react-query"; +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; import { useConfirm } from "@frontend-opensource/use-react-hooks"; import { Input, @@ -18,159 +19,114 @@ import { } from "@common/components"; import { BOARD_POSTING_CODE } from "@/entities/board"; import { AttachmentApi } from "@/entities/attachment"; -import type { Post, PostFormData, PostUpdateFormData } from "@/entities/post"; +import { + PostApi, + postUpdateFormSchema, + type PostFormData, + type PostUpdateFormData, +} from "@/entities/post"; import { ENDPOINT } from "@/shared/config"; -import { useUserStore, setCookie, useMenuStore } from "@/shared/lib"; +import type { BbsType } from "@/shared/store/menu-store"; +import { useUserStore, useMenuStore, useAlertStore } from "@/shared/lib"; import { Form, FormControl, FormField, FormItem } from "@/shared/ui/form"; import { PostingPeriodSelector } from "./posting-period-selector"; import { GeneralFileUploader } from "./general-file-uploader"; import { WithPreviewFileUploader } from "./with-preview-file-uploader"; -type PostFormProps = { - form: UseFormReturn; - handleSave: (data: T) => Promise; +type PostFormProps = { + bbsId: number; + pstNo: number; }; -function PostForm({ - form, - handleSave, -}: PostFormProps) { - const atflCd = form.getValues("atflCd" as FieldPath); +const getBbsTitle = (bbsTypeCd?: BbsType) => { + switch (bbsTypeCd) { + case "BBSNOTICE": + return "공지"; + case "BBSNORMAL": + return "일반"; + case "BBSFAQ": + return "FAQ"; + case "BBSIMAGE": + return "이미지"; + case "BBSMOVIE": + return "동영상"; + default: + return "게시판"; + } +}; +function PostForm({ bbsId, pstNo }: PostFormProps) { const router = useRouter(); const { confirm } = useConfirm(); const { bbsInfo } = useMenuStore((state) => state); const { userInfo } = useUserStore((state) => state); + const { setMessage: alert } = useAlertStore((state) => state); const [deleteAttachmentIds, setDeleteAttachmentIds] = useState([]); + const { data: postInfo } = useQuery({ + queryKey: [ENDPOINT.BOARD_SERVICE.POSTS, bbsId, pstNo], + queryFn: () => PostApi.getPostInfo(bbsId, pstNo), + }); + + const { mutateAsync: updatePost } = useMutation({ + mutationFn: (data: PostUpdateFormData) => + PostApi.updatePost(bbsId, pstNo, data), + onSuccess: () => { + alert("수정되었습니다.", router.back); + }, + }); + + const form = useForm({ + resolver: zodResolver(postUpdateFormSchema), + values: { + ...postInfo, + pstTtl: postInfo?.pstTtl ?? "", + pstPswd: userInfo?.userUnqId ?? "", + }, + }); + + const atflCd = form.getValues("atflCd"); const { data: attachmentData } = useQuery({ queryKey: [ENDPOINT.ATTACH_SERVICE.ATTACHMENTS, atflCd], queryFn: () => AttachmentApi.getAttachmentsInfo(atflCd as string), enabled: !!atflCd, }); - const onSubmit = async (data: T) => { + const onSubmit = async (data: PostFormData | PostUpdateFormData) => { if (await confirm("저장하시겠습니까?")) { if (deleteAttachmentIds.length > 0) { await Promise.all( deleteAttachmentIds.map(AttachmentApi.deleteAttachmentByAtflId), ); } - await handleSave(data); + await updatePost(data); } }; - useEffect(() => { - setCookie("atfl-cd-key", ""); - }, []); - return ( -
- -
- - - - - - - - 제목 - - } - render={({ field }) => ( - - - - - - )} - /> - - - - 작성자 - - } - render={({ field }) => ( - - - - - - )} - /> - - - {!userInfo?.userUnqId && ( +
+
+

+ {getBbsTitle(bbsInfo?.bbsTypeCd)} 게시글 수정 +

+
+ + +
+
+ + + + + - 비밀번호 + 제목 } - render={({ field }) => ( - - - - - - )} - /> - - - )} - - 내용 - - } - render={({ field }) => ( - - -