diff --git a/apps/common/features/app/(with-base-layout)/banner/page.tsx b/apps/common/features/app/(with-base-layout)/banner/page.tsx deleted file mode 100644 index 23f63defe..000000000 --- a/apps/common/features/app/(with-base-layout)/banner/page.tsx +++ /dev/null @@ -1,2 +0,0 @@ -import { BannerPage } from "@/pages/banner"; -export default BannerPage; diff --git a/apps/common/features/app/(with-base-layout)/banner/register/[sequential]/page.tsx b/apps/common/features/app/(with-base-layout)/banner/register/[sequential]/page.tsx deleted file mode 100644 index bc19150cb..000000000 --- a/apps/common/features/app/(with-base-layout)/banner/register/[sequential]/page.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { ModifyPage as VModifyPage } from "@/pages/banner"; - -export default async function ModifyPage({ - params, -}: { - params: Promise<{ sequential: string }>; -}) { - const { sequential } = await params; - return ; -} diff --git a/apps/common/features/app/(with-base-layout)/banner/register/page.tsx b/apps/common/features/app/(with-base-layout)/banner/register/page.tsx deleted file mode 100644 index 46eb12c1c..000000000 --- a/apps/common/features/app/(with-base-layout)/banner/register/page.tsx +++ /dev/null @@ -1,2 +0,0 @@ -import { CreatePage } from "@/pages/banner"; -export default CreatePage; diff --git a/apps/common/features/app/(with-base-layout)/bbs/page.tsx b/apps/common/features/app/(with-base-layout)/bbs/page.tsx deleted file mode 100644 index d9aab376a..000000000 --- a/apps/common/features/app/(with-base-layout)/bbs/page.tsx +++ /dev/null @@ -1,2 +0,0 @@ -import { BbsPage } from "@/pages/bbs"; -export default BbsPage; diff --git a/apps/common/features/app/(with-base-layout)/bbs/register/[id]/page.tsx b/apps/common/features/app/(with-base-layout)/bbs/register/[id]/page.tsx deleted file mode 100644 index 07cc78b6f..000000000 --- a/apps/common/features/app/(with-base-layout)/bbs/register/[id]/page.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { ModifyPage as VModifyPage } from "@/pages/bbs"; - -export default async function ModifyPage({ - params, -}: { - params: Promise<{ id: string }>; -}) { - const { id } = await params; - return ; -} diff --git a/apps/common/features/app/(with-base-layout)/bbs/register/page.tsx b/apps/common/features/app/(with-base-layout)/bbs/register/page.tsx deleted file mode 100644 index 3e74aef97..000000000 --- a/apps/common/features/app/(with-base-layout)/bbs/register/page.tsx +++ /dev/null @@ -1,2 +0,0 @@ -import { CreatePage } from "@/pages/bbs"; -export default CreatePage; diff --git a/apps/common/features/app/(with-base-layout)/contents/page.tsx b/apps/common/features/app/(with-base-layout)/contents/page.tsx deleted file mode 100644 index ba2a6a21d..000000000 --- a/apps/common/features/app/(with-base-layout)/contents/page.tsx +++ /dev/null @@ -1,2 +0,0 @@ -import { ContentsPage } from "@/pages/contents"; -export default ContentsPage; diff --git a/apps/common/features/app/(with-base-layout)/contents/register/[sequential]/page.tsx b/apps/common/features/app/(with-base-layout)/contents/register/[sequential]/page.tsx deleted file mode 100644 index aa9320b8f..000000000 --- a/apps/common/features/app/(with-base-layout)/contents/register/[sequential]/page.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { ModifyPage as VModifyPage } from "@/pages/contents"; - -export default async function ModifyPage({ - params, -}: { - params: Promise<{ sequential: string }>; -}) { - const { sequential } = await params; - return ; -} diff --git a/apps/common/features/app/(with-base-layout)/contents/register/page.tsx b/apps/common/features/app/(with-base-layout)/contents/register/page.tsx deleted file mode 100644 index fc33228e2..000000000 --- a/apps/common/features/app/(with-base-layout)/contents/register/page.tsx +++ /dev/null @@ -1,2 +0,0 @@ -import { CreatePage } from "@/pages/contents"; -export default CreatePage; diff --git a/apps/common/features/app/(with-base-layout)/dashboard/page.tsx b/apps/common/features/app/(with-base-layout)/dashboard/page.tsx deleted file mode 100644 index 53d564ac6..000000000 --- a/apps/common/features/app/(with-base-layout)/dashboard/page.tsx +++ /dev/null @@ -1,2 +0,0 @@ -import { DashboardPage } from "@/pages/dashboard"; -export default DashboardPage; diff --git a/apps/common/features/app/(with-base-layout)/file/page.tsx b/apps/common/features/app/(with-base-layout)/file/page.tsx deleted file mode 100644 index 6b03fe11d..000000000 --- a/apps/common/features/app/(with-base-layout)/file/page.tsx +++ /dev/null @@ -1,2 +0,0 @@ -import { FilePage } from "@/pages/file"; -export default FilePage; diff --git a/apps/common/features/app/(with-base-layout)/image/page.tsx b/apps/common/features/app/(with-base-layout)/image/page.tsx deleted file mode 100644 index 89ca9eeb1..000000000 --- a/apps/common/features/app/(with-base-layout)/image/page.tsx +++ /dev/null @@ -1,2 +0,0 @@ -import { ImagePage } from "@/pages/image"; -export default ImagePage; diff --git a/apps/common/features/app/(with-base-layout)/image/register/[sequential]/page.tsx b/apps/common/features/app/(with-base-layout)/image/register/[sequential]/page.tsx deleted file mode 100644 index 800f0b382..000000000 --- a/apps/common/features/app/(with-base-layout)/image/register/[sequential]/page.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { ModifyPage as VModifyPage } from "@/pages/image"; - -export default async function ModifyPage({ - params, -}: { - params: Promise<{ sequential: string }>; -}) { - const { sequential } = await params; - return ; -} diff --git a/apps/common/features/app/(with-base-layout)/image/register/page.tsx b/apps/common/features/app/(with-base-layout)/image/register/page.tsx deleted file mode 100644 index 3c29f2bfe..000000000 --- a/apps/common/features/app/(with-base-layout)/image/register/page.tsx +++ /dev/null @@ -1,2 +0,0 @@ -import { CreatePage } from "@/pages/image"; -export default CreatePage; diff --git a/apps/common/features/app/(with-base-layout)/layout.tsx b/apps/common/features/app/(with-base-layout)/layout.tsx index c3254a935..d6250a6e9 100644 --- a/apps/common/features/app/(with-base-layout)/layout.tsx +++ b/apps/common/features/app/(with-base-layout)/layout.tsx @@ -5,7 +5,6 @@ import { useQueryClient } from "@tanstack/react-query"; import type { MenuItem } from "@common/components/api"; import { BaseLayout } from "@common/components/ui"; import { useMenuStore } from "@/shared/lib"; -import { PageLayout } from "@/shared/ui/page-layout"; export default function WithBaseLayout({ children, @@ -29,7 +28,7 @@ export default function WithBaseLayout({ currentPath={pathname ?? ""} handleMenuClick={handleMenuClick} > - {children} + {children} ); } diff --git a/apps/common/features/app/(with-base-layout)/menu/page.tsx b/apps/common/features/app/(with-base-layout)/menu/page.tsx deleted file mode 100644 index bbac0670c..000000000 --- a/apps/common/features/app/(with-base-layout)/menu/page.tsx +++ /dev/null @@ -1,2 +0,0 @@ -import { MenuPage } from "@/pages/menu"; -export default MenuPage; diff --git a/apps/common/features/app/(with-base-layout)/module/page.tsx b/apps/common/features/app/(with-base-layout)/module/page.tsx deleted file mode 100644 index c395e9a79..000000000 --- a/apps/common/features/app/(with-base-layout)/module/page.tsx +++ /dev/null @@ -1,2 +0,0 @@ -import { ModulePage } from "@/pages/module"; -export default ModulePage; diff --git a/apps/common/features/app/(with-base-layout)/module/register/[sequential]/page.tsx b/apps/common/features/app/(with-base-layout)/module/register/[sequential]/page.tsx deleted file mode 100644 index 9f1c32ff5..000000000 --- a/apps/common/features/app/(with-base-layout)/module/register/[sequential]/page.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { ModifyPage as VModifyPage } from "@/pages/module"; - -export default async function ModifyPage({ - params, -}: { - params: Promise<{ sequential: string }>; -}) { - const { sequential } = await params; - return ; -} diff --git a/apps/common/features/app/(with-base-layout)/module/register/page.tsx b/apps/common/features/app/(with-base-layout)/module/register/page.tsx deleted file mode 100644 index 6354dc8ab..000000000 --- a/apps/common/features/app/(with-base-layout)/module/register/page.tsx +++ /dev/null @@ -1,2 +0,0 @@ -import { CreatePage } from "@/pages/module"; -export default CreatePage; diff --git a/apps/common/features/app/(with-base-layout)/organization/page.tsx b/apps/common/features/app/(with-base-layout)/organization/page.tsx deleted file mode 100644 index 39a15cf83..000000000 --- a/apps/common/features/app/(with-base-layout)/organization/page.tsx +++ /dev/null @@ -1,2 +0,0 @@ -import { OrganizationPage } from "@/pages/organization"; -export default OrganizationPage; diff --git a/apps/common/features/app/(with-base-layout)/pst/bbsnotice/[bbsId]/page.tsx b/apps/common/features/app/(with-base-layout)/pst/bbsnotice/[bbsId]/page.tsx new file mode 100644 index 000000000..df9ae763f --- /dev/null +++ b/apps/common/features/app/(with-base-layout)/pst/bbsnotice/[bbsId]/page.tsx @@ -0,0 +1,10 @@ +import { NoticePage } from "@/pages/notice"; + +export default async function PstBbsNoticePage({ + params, +}: { + params: Promise<{ bbsId: string }>; +}) { + const { bbsId } = await params; + return ; +} diff --git a/apps/common/features/app/(with-base-layout)/site/page.tsx b/apps/common/features/app/(with-base-layout)/site/page.tsx deleted file mode 100644 index 0cf120630..000000000 --- a/apps/common/features/app/(with-base-layout)/site/page.tsx +++ /dev/null @@ -1,2 +0,0 @@ -import { SitePage } from "@/pages/site"; -export default SitePage; diff --git a/apps/common/features/app/(with-base-layout)/site/register/[id]/page.tsx b/apps/common/features/app/(with-base-layout)/site/register/[id]/page.tsx deleted file mode 100644 index 685d77172..000000000 --- a/apps/common/features/app/(with-base-layout)/site/register/[id]/page.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { ModifyPage as VModifyPage } from "@/pages/site"; - -export default async function ModifyPage({ - params, -}: { - params: Promise<{ id: string }>; -}) { - const { id } = await params; - return ; -} diff --git a/apps/common/features/app/(with-base-layout)/site/register/page.tsx b/apps/common/features/app/(with-base-layout)/site/register/page.tsx deleted file mode 100644 index 01e25cc43..000000000 --- a/apps/common/features/app/(with-base-layout)/site/register/page.tsx +++ /dev/null @@ -1,2 +0,0 @@ -import { CreatePage } from "@/pages/site"; -export default CreatePage; diff --git a/apps/common/features/app/(with-base-layout)/template/page.tsx b/apps/common/features/app/(with-base-layout)/template/page.tsx deleted file mode 100644 index f5eab5953..000000000 --- a/apps/common/features/app/(with-base-layout)/template/page.tsx +++ /dev/null @@ -1,2 +0,0 @@ -import { TemplatePage } from "@/pages/template"; -export default TemplatePage; diff --git a/apps/common/features/app/(with-base-layout)/template/register/[sequential]/page.tsx b/apps/common/features/app/(with-base-layout)/template/register/[sequential]/page.tsx deleted file mode 100644 index 3878a4e41..000000000 --- a/apps/common/features/app/(with-base-layout)/template/register/[sequential]/page.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { ModifyPage as VModifyPage } from "@/pages/template"; - -export default async function ModifyPage({ - params, -}: { - params: Promise<{ sequential: string }>; -}) { - const { sequential } = await params; - return ; -} diff --git a/apps/common/features/app/(with-base-layout)/template/register/page.tsx b/apps/common/features/app/(with-base-layout)/template/register/page.tsx deleted file mode 100644 index 9be3f975a..000000000 --- a/apps/common/features/app/(with-base-layout)/template/register/page.tsx +++ /dev/null @@ -1,2 +0,0 @@ -import { CreatePage } from "@/pages/template"; -export default CreatePage; diff --git a/apps/common/features/app/(with-base-layout)/terms/page.tsx b/apps/common/features/app/(with-base-layout)/terms/page.tsx deleted file mode 100644 index 47f4c8e02..000000000 --- a/apps/common/features/app/(with-base-layout)/terms/page.tsx +++ /dev/null @@ -1,2 +0,0 @@ -import { TermsPage } from "@/pages/terms"; -export default TermsPage; diff --git a/apps/common/features/app/(with-base-layout)/terms/register/[sequential]/page.tsx b/apps/common/features/app/(with-base-layout)/terms/register/[sequential]/page.tsx deleted file mode 100644 index 8ff8b2010..000000000 --- a/apps/common/features/app/(with-base-layout)/terms/register/[sequential]/page.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { ModifyPage as VModifyPage } from "@/pages/terms"; - -export default async function ModifyPage({ - params, -}: { - params: Promise<{ sequential: string }>; -}) { - const { sequential } = await params; - return ; -} diff --git a/apps/common/features/app/(with-base-layout)/terms/register/page.tsx b/apps/common/features/app/(with-base-layout)/terms/register/page.tsx deleted file mode 100644 index 997fc119a..000000000 --- a/apps/common/features/app/(with-base-layout)/terms/register/page.tsx +++ /dev/null @@ -1,2 +0,0 @@ -import { CreatePage } from "@/pages/terms"; -export default CreatePage; diff --git a/apps/common/features/middleware.ts b/apps/common/features/middleware.ts index d56c1f8ab..018aab11a 100644 --- a/apps/common/features/middleware.ts +++ b/apps/common/features/middleware.ts @@ -2,7 +2,7 @@ import { NextRequest, NextResponse } from "next/server"; import { CLAIM_NAME, AUTH_USER_ID } from "./src/shared/config"; const serverApiUrl = - process.env.NEXT_PUBLIC_SERVER_API_URL ?? "http://localhost:3000"; + process.env.NEXT_PUBLIC_SERVER_API_URL ?? "http://localhost:13000"; export function middleware(req: NextRequest) { const pathname = req.nextUrl.pathname; diff --git a/apps/common/features/next.config.ts b/apps/common/features/next.config.ts index 7cbf8ee2e..cb651cdc0 100644 --- a/apps/common/features/next.config.ts +++ b/apps/common/features/next.config.ts @@ -1,32 +1,5 @@ import type { NextConfig } from "next"; -const subDomain = - process.env.NEXT_PUBLIC_SUB_DOMAIN || "http://localhost:13000"; - -const nextConfig: NextConfig = { - async rewrites() { - return { - beforeFiles: [ - { - source: "/_next/static/:path*", - destination: `${subDomain}/_next/static/:path*`, - has: [ - { - type: "header", - key: "x-rewrite-me", - }, - ], - }, - ], - afterFiles: [ - { - source: "/board/:path*", - destination: `${subDomain}/board/:path*`, - }, - ], - fallback: [], - }; - }, -}; +const nextConfig: NextConfig = {}; export default nextConfig; diff --git a/apps/common/features/package.json b/apps/common/features/package.json index b2b5f2355..8960c2cdb 100644 --- a/apps/common/features/package.json +++ b/apps/common/features/package.json @@ -3,9 +3,9 @@ "version": "0.0.0", "private": true, "scripts": { - "dev": "next dev", + "dev": "next dev -p 13000", "build": "next build", - "start": "next start", + "start": "next start -p 13000", "lint": "next lint" }, "dependencies": { diff --git a/apps/common/features/src/entities/post/api/post-service.ts b/apps/common/features/src/entities/post/api/post-service.ts new file mode 100644 index 000000000..b623af4fe --- /dev/null +++ b/apps/common/features/src/entities/post/api/post-service.ts @@ -0,0 +1,18 @@ +import { client } from "@/shared/api"; +import { ENDPOINT } from "@/shared/config"; +import type { + PostList, + PostParams, + PostService, +} from "../model/post-interface"; + +export class PostApiService implements PostService { + public async getPostList(params: PostParams): Promise { + const response = await client.get(ENDPOINT.BOARD_SERVICE.POSTS, { + params, + }); + return response.data; + } +} + +export const PostApi = new PostApiService(); diff --git a/apps/common/features/src/entities/post/index.ts b/apps/common/features/src/entities/post/index.ts new file mode 100644 index 000000000..933fb0a51 --- /dev/null +++ b/apps/common/features/src/entities/post/index.ts @@ -0,0 +1,7 @@ +export { PostApi } from "./api/post-service"; +export type { + Post, + PostList, + PostParams, + PostService, +} from "./model/post-interface"; diff --git a/apps/common/features/src/entities/post/model/post-interface.ts b/apps/common/features/src/entities/post/model/post-interface.ts new file mode 100644 index 000000000..5e0f394be --- /dev/null +++ b/apps/common/features/src/entities/post/model/post-interface.ts @@ -0,0 +1,38 @@ +import { + type PageableList, + type PageableParams, +} from "@/shared/model/api-interface"; + +export interface PostParams extends PageableParams { + keywordType?: string; + keyword?: string; + bbsId?: number; + delYn?: string; + frDateYmd?: string; + toDateYmd?: string; +} + +export interface Post { + bbsId: number; + pstNo: number; + pstTtl: string; + inqNocs: number; + rfrncUrl: string; + popupYn: string; + upndFixYn: string; + prdPstgYn: string; + rootPath: string; + pstPath: string; + ansPstn: number; + wrtrNm: string; + wrtrCplc: string; + atflCd: string; + regDt: string; + rgtrNm: string; +} + +export type PostList = PageableList; + +export interface PostService { + getPostList(params: PostParams): Promise; +} diff --git a/apps/common/features/src/pages/banner/api/banner-service.ts b/apps/common/features/src/pages/banner/api/banner-service.ts deleted file mode 100644 index 93646c770..000000000 --- a/apps/common/features/src/pages/banner/api/banner-service.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { client } from "@/shared/api"; -import { ENDPOINT } from "@/shared/config"; -import type { - BannerFormData, - BannerInfo, - BannerList, - BannerParams, - BannerService, -} from "../model/banner-interface"; - -export class BannerApiService implements BannerService { - public async getBannerList(params: BannerParams): Promise { - const response = await client.get( - `${ENDPOINT.CMS_SERVICE.BANNERS}`, - { params }, - ); - return response.data; - } - - public async getBannerInfo(bnrSn: string): Promise { - const response = await client.get( - `${ENDPOINT.CMS_SERVICE.BANNERS}/${bnrSn}`, - ); - return response.data; - } - - public async createBanner(data: BannerFormData): Promise { - const response = await client.post( - ENDPOINT.CMS_SERVICE.BANNERS, - data, - ); - return response.data; - } - - public async updateBanner( - bnrSn: string, - data: BannerFormData, - ): Promise { - const response = await client.put( - `${ENDPOINT.CMS_SERVICE.BANNERS}/${bnrSn}`, - data, - ); - return response.data; - } - - public async deleteBanner(bnrSn: string): Promise { - await client.delete(`${ENDPOINT.CMS_SERVICE.BANNERS}/${bnrSn}`); - } -} - -export const BannerApi = new BannerApiService(); diff --git a/apps/common/features/src/pages/banner/index.ts b/apps/common/features/src/pages/banner/index.ts deleted file mode 100644 index b2f0c6a55..000000000 --- a/apps/common/features/src/pages/banner/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { CreatePage } from "./ui/create-page"; -export { ModifyPage } from "./ui/modify-page"; -export { BannerPage } from "./ui/banner-page"; diff --git a/apps/common/features/src/pages/banner/lib/utils.ts b/apps/common/features/src/pages/banner/lib/utils.ts deleted file mode 100644 index c6c8ca101..000000000 --- a/apps/common/features/src/pages/banner/lib/utils.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { Upload, type HttpResponse } from "tus-js-client"; -import { getCookie, setCookie } from "@/shared/lib"; -import { AUTH_USER_ID, CLAIM_NAME, ENDPOINT } from "@/shared/config"; - -type UploadOptions = { - handleUploadStart?: (upload: Upload) => void; - handleProgress?: (percentage: string) => void; - handleSuccess?: (upload: Upload) => void; - handleResponse?: (res: HttpResponse) => void; -}; - -export const handleUpload = (files: FileList, options?: UploadOptions) => { - let metadata = {}; - - const file = files.item(0); - if (!file) return; - metadata = { - filename: file.name, - filetype: file.type, - }; - - const atflCd = getCookie("atfl-cd-key"); - if (atflCd) { - metadata = { - ...metadata, - atflCd, - }; - } - - const upload = new Upload(file, { - endpoint: `${ENDPOINT.ATTACH_SERVICE.TUS_UPLOAD}`, - chunkSize: 10 * 1024 * 1024, - headers: { - [CLAIM_NAME]: getCookie(CLAIM_NAME) ?? "", - [AUTH_USER_ID]: getCookie(AUTH_USER_ID) ?? "", - }, - metadata, - onError(error) { - console.error(`Failed because: ${error}`); - }, - onProgress(bytesUploaded, bytesTotal) { - const percentage = ((bytesUploaded / bytesTotal) * 100).toFixed(2); - options?.handleProgress?.(percentage); - }, - onAfterResponse(req, res) { - const fileInfo = res.getHeader("X-File-Info"); - if (fileInfo === undefined || fileInfo === null) return; - - setCookie("atfl-cd-key", JSON.parse(fileInfo)?.atflCd ?? ""); - options?.handleResponse?.(res); - }, - onSuccess() { - options?.handleSuccess?.(upload); - }, - }); - - upload.start(); - options?.handleUploadStart?.(upload); - - console.log("upload start >>>>>", upload); -}; diff --git a/apps/common/features/src/pages/banner/model/banner-interface.ts b/apps/common/features/src/pages/banner/model/banner-interface.ts deleted file mode 100644 index 1226f63e6..000000000 --- a/apps/common/features/src/pages/banner/model/banner-interface.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { z } from "zod"; -import type { PageableList, PageableParams } from "@/shared/model"; - -export const BANNER_TYPE = { - BNRMAIN: "BNRMAIN", - BNRFTER: "BNRFTER", -} as const; - -export type BannerType = (typeof BANNER_TYPE)[keyof typeof BANNER_TYPE]; - -export interface BannerParams extends PageableParams { - bnrTypeCd?: BannerType; - bnrTtl?: string; - useYn?: string; -} - -export interface Banner { - bnrSn: number; - bnrTypeCd: BannerType; - bnrTypeNm: string; - bnrTtl: string; - lnkgUrlAddr: string; - useYn: string; - atflCd: string; - mdfcnDt: string; - mdfr: string; - mdfrNm: string; -} - -export interface BannerInfo extends Omit { - bnrExpln: string; - newWndwYn: string; - atflCd: string; - regDt: string; - rgTrNm: string; -} - -export const bannerFormSchema = z.object({ - bnrTypeCd: z.string(), - bnrTtl: z.string(), - bnrExpln: z.string().nullish(), - lnkgUrlAddr: z.string().nullish(), - newWndwYn: z.string(), - atflCd: z.string().nullish(), - useYn: z.string(), -}); - -export type BannerFormData = z.infer; -export type BannerList = PageableList; - -export interface BannerService { - getBannerList: (params: BannerParams) => Promise; - getBannerInfo: (bnrSn: string) => Promise; - createBanner: (data: BannerFormData) => Promise; - updateBanner: (bnrSn: string, data: BannerFormData) => Promise; - deleteBanner: (bnrSn: string) => Promise; -} diff --git a/apps/common/features/src/pages/banner/model/banner-table-columns.tsx b/apps/common/features/src/pages/banner/model/banner-table-columns.tsx deleted file mode 100644 index 74da7c31c..000000000 --- a/apps/common/features/src/pages/banner/model/banner-table-columns.tsx +++ /dev/null @@ -1,100 +0,0 @@ -"use client"; - -import dayjs from "dayjs"; -import Image from "next/image"; -import { useQuery } from "@tanstack/react-query"; -import type { ColumnDef } from "@tanstack/react-table"; -import { Checkbox, Skeleton } from "@common/components/ui"; -import { AttachmentApi } from "@/entities/attachment"; -import { ENDPOINT } from "@/shared/config"; -import type { Banner } from "./banner-interface"; - -export const createColumns = ( - imageRefs: React.MutableRefObject>, -): ColumnDef[] => [ - { - id: "select", - header: ({ table }) => ( - e.stopPropagation()} - onCheckedChange={(checked) => - table.toggleAllPageRowsSelected(!!checked) - } - className="h-4 w-4 rounded border-gray-300" - /> - ), - cell: ({ row }) => ( - e.stopPropagation()} - onCheckedChange={(checked) => row.toggleSelected(!!checked)} - className="h-4 w-4 rounded border-gray-300" - /> - ), - }, - { - header: "배너 이미지", - cell: ({ row }) => ( - - ), - }, - { - accessorKey: "bnrTtl", - header: "배너명", - }, - { - accessorKey: "bnrTypeNm", - header: "배너구분", - }, - { - accessorKey: "useYn", - header: "사용여부", - }, - { - accessorKey: "mdfrNm", - header: "최종수정자", - }, - { - accessorKey: "mdfcnDt", - header: "최종수정일", - cell: ({ row }) => { - const date = row.getValue("mdfcnDt") as string; - return date ? dayjs(date).format("YYYY-MM-DD") : ""; - }, - }, -]; - -type BannerImageProps = { - atflCd: string; - bnrSn: number; - imageRefs: React.MutableRefObject>; -}; - -const BannerImage = ({ atflCd, bnrSn, imageRefs }: BannerImageProps) => { - const prevImage = imageRefs.current.get(bnrSn); - - const { data: imageUrl, isLoading } = useQuery({ - queryKey: [ENDPOINT.ATTACH_SERVICE.IMAGES, atflCd], - queryFn: () => AttachmentApi.getImageUrlByAtflCd(atflCd), - select: (url) => { - if (url) imageRefs.current.set(bnrSn, url); - return url; - }, - enabled: !prevImage && !!atflCd, - }); - - if (isLoading) return ; - - if (!imageUrl) return null; - - return ( - - - - ); -}; diff --git a/apps/common/features/src/pages/banner/model/posting-table-columns.tsx b/apps/common/features/src/pages/banner/model/posting-table-columns.tsx deleted file mode 100644 index a43cf8693..000000000 --- a/apps/common/features/src/pages/banner/model/posting-table-columns.tsx +++ /dev/null @@ -1,44 +0,0 @@ -"use client"; - -import dayjs from "dayjs"; -import type { ColumnDef } from "@tanstack/react-table"; -import { Checkbox } from "@common/components/ui"; -import type { Post } from "@/entities/posting"; - -export const columns: ColumnDef[] = [ - { - id: "select", - header: ({ table }) => ( - e.stopPropagation()} - onCheckedChange={(checked) => - table.toggleAllPageRowsSelected(!!checked) - } - className="h-4 w-4 rounded border-gray-300" - /> - ), - cell: ({ row }) => ( - e.stopPropagation()} - onCheckedChange={(checked) => row.toggleSelected(!!checked)} - className="h-4 w-4 rounded border-gray-300" - /> - ), - }, - { - accessorKey: "siteNm", - header: "사이트", - }, - { - accessorKey: "pstgPrdSeNm", - header: "게시기간", - cell: ({ row }) => { - const pstgPrdSeNm = row.original.pstgPrdSeNm; - const pstgBgngDt = row.original.pstgBgngDt; - const pstgEndDt = row.original.pstgEndDt; - return `${pstgPrdSeNm} (${dayjs(pstgBgngDt).format("YYYY-MM-DD")} ~ ${dayjs(pstgEndDt).format("YYYY-MM-DD")})`; - }, - }, -]; diff --git a/apps/common/features/src/pages/banner/ui/banner-form.tsx b/apps/common/features/src/pages/banner/ui/banner-form.tsx deleted file mode 100644 index f4af75ffe..000000000 --- a/apps/common/features/src/pages/banner/ui/banner-form.tsx +++ /dev/null @@ -1,296 +0,0 @@ -"use client"; - -import Image from "next/image"; -import { useEffect, useState } from "react"; -import { useQuery } from "@tanstack/react-query"; -import type { UseFormReturn } from "react-hook-form"; -import type { HttpResponse, Upload } from "tus-js-client"; -import { - FileUploader, - Input, - Label, - Progress, - RadioGroup, - RadioGroupItem, - Table, - TableBody, - TableCell, - TableHead, - TableRow, - Textarea, -} from "@common/components"; -import { FormWrapper } from "@/widgets/form-wrapper"; -import { CodeSelect } from "@/entities/code"; -import { AttachmentApi } from "@/entities/attachment"; -import { ENDPOINT } from "@/shared/config"; -import { setCookie } from "@/shared/lib/cookie"; -import { FormControl, FormField, FormItem } from "@/shared/ui/form"; -import type { BannerFormData } from "../model/banner-interface"; -import { handleUpload } from "../lib/utils"; - -type BannerFormProps = { - form: UseFormReturn; - bnrSn?: number; - handleSave: (data: BannerFormData) => Promise; - handleDelete?: () => Promise; -}; - -function BannerForm({ form, bnrSn, ...props }: BannerFormProps) { - const atflCd = form.watch("atflCd"); - - const [imageUrl, setImageUrl] = useState(""); - const [upload, setUpload] = useState(null); - const [progress, setProgress] = useState(0); - - const { data: attachmentData } = useQuery({ - queryKey: [ENDPOINT.ATTACH_SERVICE.ATTACHMENTS, atflCd], - queryFn: () => AttachmentApi.getAttachmentsInfo(atflCd!), - select: (data) => data?.[data.length - 1], - enabled: !!atflCd, - }); - - const handleProgress = (percentage: string) => { - setProgress(Number(percentage)); - }; - - const handleUploadResponse = async (res: HttpResponse) => { - const fileInfo = res.getHeader("X-File-Info"); - if (fileInfo === undefined || fileInfo === null) return; - - const { atflCd, atflId } = JSON.parse(fileInfo) ?? {}; - if (atflCd) form.setValue("atflCd", atflCd); - if (atflId) setImageUrl(await AttachmentApi.getImageUrl(atflId)); - if (attachmentData) - await AttachmentApi.deleteAttachmentByAtflCd(attachmentData.atflCd); - }; - - useEffect(() => { - if (!attachmentData) return; - - (async () => { - setImageUrl(await AttachmentApi.getImageUrl(attachmentData.atflId)); - })(); - }, [attachmentData]); - - return ( - - - - - - - - - - 배너명 - - ( - - - - - - )} - /> - - - - 배너구분 - - ( - - - - - - )} - /> - - - - 배너 이미지 - - ( - - - { - const files = e.target.files; - if (!files) return; - setCookie("atfl-cd-key", ""); - handleUpload(files, { - handleUploadStart: setUpload, - handleProgress, - handleSuccess: setUpload, - handleResponse: handleUploadResponse, - }); - }} - customPreview={ - <> - - - - {imageUrl && ( - - )} - - - {(upload?.file as File)?.name} - - - - - - - - 이미지 파일(jpg, jpeg, png, gif)만 업로드 - 가능합니다. - - - - > - } - /> - - - )} - /> - - - - 링크 URL - - ( - - - - - - )} - /> - - - - 링크 열기 - - ( - - - - - - 현재창 - - - - 새창 - - - - - )} - /> - - - - 배너 설명 - - ( - - - - - - )} - /> - - - - 사용여부 - - ( - - - - - - 사용 - - - - 미사용 - - - - - )} - /> - - - - - - - ); -} - -export { BannerForm }; diff --git a/apps/common/features/src/pages/banner/ui/banner-page.tsx b/apps/common/features/src/pages/banner/ui/banner-page.tsx deleted file mode 100644 index baa372b5f..000000000 --- a/apps/common/features/src/pages/banner/ui/banner-page.tsx +++ /dev/null @@ -1,144 +0,0 @@ -"use client"; - -import { useEffect, useState, useRef } from "react"; -import { useRouter } from "next/navigation"; -import { useQuery } from "@tanstack/react-query"; -import { - getCoreRowModel, - useReactTable, - type PaginationState, - type RowSelectionState, -} from "@tanstack/react-table"; -import { Button, DataTable, Input, Label } from "@common/components"; -import { POSTING_TYPE, PostingDialog } from "@/entities/posting"; -import { ENDPOINT } from "@/shared/config"; -import { BannerApi } from "../api/banner-service"; -import { createColumns } from "../model/banner-table-columns"; -import { type Banner, type BannerParams } from "../model/banner-interface"; -import { PreviewBannerDialog } from "./preview-banner-dialog"; - -type PostingTarget = { - pstgTrgtNm: string; - pstgTrgtNo: number; -}; - -function BannerPage() { - const router = useRouter(); - const imageRefs = useRef>(new Map()); - - const [rowSelection, setRowSelection] = useState({}); - const [postingTargets, setPostingTargets] = useState([]); - const [bannerQuery, setBannerQuery] = useState({ bnrTtl: "" }); - const [pagination, setPagination] = useState({ - pageIndex: 0, - pageSize: 5, - }); - - const { data: bannerList, refetch } = useQuery({ - queryKey: [ENDPOINT.CMS_SERVICE.BANNERS], - queryFn: () => - BannerApi.getBannerList({ - ...bannerQuery, - page: pagination.pageIndex, - size: pagination.pageSize, - }), - enabled: false, - }); - - const table = useReactTable({ - data: bannerList?.content ?? [], - columns: createColumns(imageRefs), - state: { - pagination, - rowSelection, - }, - rowCount: bannerList?.totalElements ?? 0, - manualPagination: true, - enableMultiRowSelection: true, - getCoreRowModel: getCoreRowModel(), - onPaginationChange: setPagination, - onRowSelectionChange: setRowSelection, - }); - - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault(); - refetch(); - }; - - const handleRowClick = (row: Banner) => { - router.push(`/banner/register/${row.bnrSn}`); - }; - - const getSelectedBannerImages = () => { - const selectedImages: string[] = []; - table.getFilteredSelectedRowModel().rows.forEach((row) => { - const imageUrl = imageRefs.current.get(row.original.bnrSn); - if (imageUrl) selectedImages.push(imageUrl); - }); - return selectedImages; - }; - - useEffect(() => { - const postingTargets = table - .getFilteredSelectedRowModel() - .rows.map((row) => ({ - pstgTrgtNm: row.original.bnrTtl, - pstgTrgtNo: row.original.bnrSn, - })); - - if (!postingTargets.length) { - setPostingTargets([]); - return; - } - - setPostingTargets(postingTargets); - }, [rowSelection]); - - useEffect(() => { - refetch(); - }, [pagination]); - - return ( - - - - - 배너명 - - - setBannerQuery({ ...bannerQuery, bnrTtl: e.target.value }) - } - /> - - 조회 - - - - - - - 배너 목록 - - - - router.push("/banner/register")}> - 등록 - - - - - - - - - - ); -} - -export { BannerPage }; diff --git a/apps/common/features/src/pages/banner/ui/create-page.tsx b/apps/common/features/src/pages/banner/ui/create-page.tsx deleted file mode 100644 index 23d082c1d..000000000 --- a/apps/common/features/src/pages/banner/ui/create-page.tsx +++ /dev/null @@ -1,46 +0,0 @@ -"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 { useAlertStore } from "@/shared/lib/use-alert-store"; -import { - bannerFormSchema, - type BannerFormData, -} from "../model/banner-interface"; -import { BannerApi } from "../api/banner-service"; -import { BannerForm } from "./banner-form"; - -function CreatePage() { - const router = useRouter(); - const { setMessage: alert } = useAlertStore((state) => state); - - const { mutateAsync: createBanner } = useMutation({ - mutationFn: (data: BannerFormData) => BannerApi.createBanner(data), - onSuccess: () => { - alert("등록되었습니다.", router.back); - }, - }); - - const form = useForm({ - resolver: zodResolver(bannerFormSchema), - defaultValues: { - newWndwYn: "N", - useYn: "Y", - }, - }); - - console.log(form.formState.errors); - - return ( - - - 배너 등록 - - - - ); -} - -export { CreatePage }; diff --git a/apps/common/features/src/pages/banner/ui/modify-page.tsx b/apps/common/features/src/pages/banner/ui/modify-page.tsx deleted file mode 100644 index f0f679529..000000000 --- a/apps/common/features/src/pages/banner/ui/modify-page.tsx +++ /dev/null @@ -1,132 +0,0 @@ -"use client"; - -import { useState } from "react"; -import { useRouter } from "next/navigation"; -import { useForm } from "react-hook-form"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; -import { - getCoreRowModel, - useReactTable, - type RowSelectionState, -} from "@tanstack/react-table"; -import { useConfirm } from "@frontend-opensource/use-react-hooks"; -import { Button, DataTable } from "@common/components/ui"; -import { PostingApi, type PostPayload } from "@/entities/posting"; -import { useAlertStore } from "@/shared/lib/use-alert-store"; -import { ENDPOINT } from "@/shared/config"; -import { - bannerFormSchema, - type BannerFormData, -} from "../model/banner-interface"; -import { columns } from "../model/posting-table-columns"; -import { BannerApi } from "../api/banner-service"; -import { BannerForm } from "./banner-form"; - -function ModifyPage({ sequential }: { sequential: string }) { - const router = useRouter(); - const { confirm } = useConfirm(); - const queryClient = useQueryClient(); - const { setMessage: alert } = useAlertStore((state) => state); - - const [rowSelection, setRowSelection] = useState({}); - - const { data: bannerDetail } = useQuery({ - queryKey: [ENDPOINT.CMS_SERVICE.BANNERS, sequential], - queryFn: () => BannerApi.getBannerInfo(sequential), - }); - - const { data: postHistory } = useQuery({ - queryKey: [ENDPOINT.CMS_SERVICE.POSTINGS, sequential], - queryFn: () => - PostingApi.getPostingHistory({ pstgTrgtNo: Number(sequential) }), - }); - - const { mutateAsync: updateBanner } = useMutation({ - mutationFn: (data: BannerFormData) => - BannerApi.updateBanner(sequential, data), - onSuccess: () => { - alert("수정되었습니다."); - }, - }); - - const { mutateAsync: deleteBanner } = useMutation({ - mutationFn: () => BannerApi.deleteBanner(sequential), - onSuccess: () => { - alert("삭제되었습니다.", router.back); - }, - }); - - const { mutateAsync: savePosting } = useMutation({ - mutationFn: (data: PostPayload[]) => PostingApi.savePosting(data), - onSuccess: () => { - queryClient.invalidateQueries({ - queryKey: [ENDPOINT.CMS_SERVICE.POSTINGS, sequential], - }); - alert("삭제되었습니다."); - }, - }); - - const form = useForm({ - resolver: zodResolver(bannerFormSchema), - values: bannerDetail, - }); - - const table = useReactTable({ - data: postHistory?.content ?? [], - columns, - state: { - rowSelection, - }, - enableMultiRowSelection: true, - getCoreRowModel: getCoreRowModel(), - onRowSelectionChange: setRowSelection, - }); - - const handleDeletePostingHistory = async () => { - const data = table.getFilteredSelectedRowModel().rows.map((row) => ({ - ...row.original, - mode: "D", - })); - - if (await confirm("삭제하시겠습니까?")) { - savePosting(data); - } - }; - - return ( - - - - 배너 수정 - - - - - - 사이트 게시 이력 - - - - - 이력삭제 - - - - - - - ); -} - -export { ModifyPage }; diff --git a/apps/common/features/src/pages/banner/ui/preview-banner-dialog.tsx b/apps/common/features/src/pages/banner/ui/preview-banner-dialog.tsx deleted file mode 100644 index 74be653dd..000000000 --- a/apps/common/features/src/pages/banner/ui/preview-banner-dialog.tsx +++ /dev/null @@ -1,60 +0,0 @@ -"use client"; - -import Image from "next/image"; -import { - Dialog, - DialogContent, - DialogHeader, - DialogTitle, - DialogTrigger, - Button, - Carousel, - CarouselContent, - CarouselItem, - CarouselNext, - CarouselPrevious, -} from "@common/components"; - -type PreviewBannerDialogProps = { - images: string[]; -}; - -function PreviewBannerDialog({ images }: PreviewBannerDialogProps) { - return ( - - - - - 미리보기 - - - - - 배너 이미지 미리보기 - - - - - {images.map((image, i) => ( - - - - - - ))} - - - - - - - - ); -} - -export { PreviewBannerDialog }; diff --git a/apps/common/features/src/pages/bbs/index.ts b/apps/common/features/src/pages/bbs/index.ts deleted file mode 100644 index bed839012..000000000 --- a/apps/common/features/src/pages/bbs/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { BbsPage } from "./ui/bbs-page"; -export { CreatePage } from "./ui/create-page"; -export { ModifyPage } from "./ui/modify-page"; diff --git a/apps/common/features/src/pages/bbs/model/table-columns.tsx b/apps/common/features/src/pages/bbs/model/table-columns.tsx deleted file mode 100644 index 697f953a7..000000000 --- a/apps/common/features/src/pages/bbs/model/table-columns.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import dayjs from "dayjs"; -import type { ColumnDef } from "@tanstack/react-table"; -import { Button } from "@common/components/ui"; -import type { Board } from "@/entities/board/model/board-interface"; - -export const columns: ColumnDef[] = [ - { - id: "no", - header: "NO", - cell: ({ row }) => row.index + 1, - }, - { - accessorKey: "bbsNm", - header: "게시판명", - }, - { - accessorKey: "bbsTypeNm", - header: "게시판유형", - }, - { - accessorKey: "mdfrNm", - header: "최종수정자", - }, - { - accessorKey: "mdfcnDt", - header: "최종수정일", - cell: ({ row }) => - row.original.mdfcnDt - ? dayjs(row.original.mdfcnDt).format("YYYY-MM-DD") - : "", - }, - { - accessorKey: "useYn", - header: "사용여부", - }, - { - id: "actions", - header: "관리", - cell: () => 관리, - }, -]; diff --git a/apps/common/features/src/pages/bbs/ui/bbs-form.tsx b/apps/common/features/src/pages/bbs/ui/bbs-form.tsx deleted file mode 100644 index ac2b5ebd5..000000000 --- a/apps/common/features/src/pages/bbs/ui/bbs-form.tsx +++ /dev/null @@ -1,447 +0,0 @@ -"use client"; - -import type { UseFormReturn } from "react-hook-form"; -import { - Input, - Label, - RadioGroup, - RadioGroupItem, - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, - Table, - TableBody, - TableCell, - TableHead, - TableRow, -} from "@common/components"; -import { FormWrapper } from "@/widgets/form-wrapper"; -import { CodeSelect } from "@/entities/code"; -import type { Board, BoardFormData } from "@/entities/board"; -import { FormControl, FormField, FormItem } from "@/shared/ui/form"; - -type BbsFormProps = { - form: UseFormReturn; - bbsId?: string; - handleSave: (data: BoardFormData) => Promise; -}; - -function BbsForm({ form, bbsId, ...props }: BbsFormProps) { - return ( - - - - - - - - - - 게시판명 - - ( - - - - - - )} - /> - - - - 게시판 설명 - - ( - - - - - - )} - /> - - - - 게시판 유형 - - ( - - - - - - )} - /> - - - - 참조 사용 여부 - - ( - - - - - - 사용 - - - - 사용안함 - - - - - )} - /> - - - - 첨부파일 사용 여부 - - ( - - - - - - 사용 - - - - 사용안함 - - - - - )} - /> - - - - 첨부파일 최대 개수 - - ( - - - { - const value = e.target.value; - if (isNaN(Number(value))) return; - field.onChange(value === "" ? null : Number(value)); - }} - className="w-[12rem] !text-[1.3rem]" - placeholder="최대 개수 입력" - disabled={form.watch("atflUseYn") === "N"} - /> - - - )} - /> - - - - 첨부파일 최대 크기 - - ( - - - { - const value = e.target.value; - if (isNaN(Number(value))) return; - field.onChange(value === "" ? null : Number(value)); - }} - className="w-[12rem] !text-[1.3rem]" - placeholder="최대 크기 입력" - disabled={form.watch("atflUseYn") === "N"} - /> - - - )} - /> - - - - 답글 사용 여부 - - ( - - - - - - 사용 - - - - 사용안함 - - - - - )} - /> - - - - 답글 사용 권한 - - ( - - - - - - - - 관리자 - 회원 - 일반 - - - - - )} - /> - - - - 댓글 사용 여부 - - ( - - - - - - 사용 - - - - 사용안함 - - - - - )} - /> - - - - 게시물평가 사용 여부 - - ( - - - - - - 사용 - - - - 사용안함 - - - - - )} - /> - - - - 팝업 사용 여부 - - ( - - - - - - 사용 - - - - 사용안함 - - - - - )} - /> - - - - SMS알림 사용 여부 - - ( - - - - - - 사용 - - - - 사용안함 - - - - - )} - /> - - - - 상단고정 사용 여부 - - ( - - - - - - 사용 - - - - 사용안함 - - - - - )} - /> - - - - 게시기간 사용 여부 - - ( - - - - - - 사용 - - - - 사용안함 - - - - - )} - /> - - - - 사용 여부 - - ( - - - - - - 사용 - - - - 사용안함 - - - - - )} - /> - - - - - - - ); -} - -export { BbsForm }; diff --git a/apps/common/features/src/pages/bbs/ui/bbs-page.tsx b/apps/common/features/src/pages/bbs/ui/bbs-page.tsx deleted file mode 100644 index 89562cb9c..000000000 --- a/apps/common/features/src/pages/bbs/ui/bbs-page.tsx +++ /dev/null @@ -1,145 +0,0 @@ -"use client"; - -import { useEffect, useState } from "react"; -import { useRouter } from "next/navigation"; -import { useQuery } from "@tanstack/react-query"; -import { - getCoreRowModel, - useReactTable, - type PaginationState, - type RowSelectionState, -} from "@tanstack/react-table"; -import { - Button, - DataTable, - Input, - Label, - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@common/components"; -import { CodeSelect } from "@/entities/code"; -import { BoardApi, type BoardParams } from "@/entities/board"; -import { ENDPOINT } from "@/shared/config"; -import { type Board } from "@/entities/board"; -import { columns } from "../model/table-columns"; - -function BbsPage() { - const router = useRouter(); - - const [rowSelection, setRowSelection] = useState({}); - const [boardParams, setBoardParams] = useState({ - bbsTypeCd: "", - bbsNm: "", - }); - const [pagination, setPagination] = useState({ - pageIndex: 0, - pageSize: 10, - }); - - const { data: boardList, refetch } = useQuery({ - queryKey: [ENDPOINT.BOARD_SERVICE.BBS], - queryFn: () => - BoardApi.getBoardList({ - ...boardParams, - page: pagination.pageIndex, - size: pagination.pageSize, - }), - enabled: false, - }); - - const table = useReactTable({ - data: boardList?.content ?? [], - columns, - state: { - pagination, - rowSelection, - }, - rowCount: boardList?.totalElements ?? 0, - manualPagination: true, - enableMultiRowSelection: true, - getCoreRowModel: getCoreRowModel(), - onPaginationChange: setPagination, - onRowSelectionChange: setRowSelection, - }); - - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault(); - refetch(); - }; - - const handleRowClick = (row: Board) => { - router.push(`/bbs/register/${row.bbsId}`); - }; - - useEffect(() => { - refetch(); - }, [pagination]); - - return ( - - - - - 게시판 유형 - - - setBoardParams({ ...boardParams, bbsTypeCd: value }) - } - /> - - 게시판명 - - - setBoardParams({ ...boardParams, bbsNm: e.target.value }) - } - /> - - 조회 - - - - - - - 게시판 목록 - - router.push("/bbs/register")}>등록 - { - table.setPageSize(Number(value)); - }} - > - - - - - {[10, 20, 30, 40, 50].map((pageSize) => ( - - {pageSize}개씩 보기 - - ))} - - - - - - - - - - - ); -} - -export { BbsPage }; diff --git a/apps/common/features/src/pages/bbs/ui/create-page.tsx b/apps/common/features/src/pages/bbs/ui/create-page.tsx deleted file mode 100644 index bb710c33b..000000000 --- a/apps/common/features/src/pages/bbs/ui/create-page.tsx +++ /dev/null @@ -1,55 +0,0 @@ -"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 { useAlertStore } from "@/shared/lib/use-alert-store"; -import { - BoardApi, - boardFormSchema, - type BoardFormData, -} from "@/entities/board"; -import { BbsForm } from "./bbs-form"; - -function CreatePage() { - const router = useRouter(); - const { setMessage: alert } = useAlertStore((state) => state); - - const { mutateAsync: createBoard } = useMutation({ - mutationFn: (data: BoardFormData) => BoardApi.createBoard(data), - onSuccess: () => { - alert("등록되었습니다.", router.back); - }, - }); - - const form = useForm({ - resolver: zodResolver(boardFormSchema), - defaultValues: { - bbsTypeCd: "", - bbsNm: "", - bbsExpln: "", - rfrncUseYn: "Y", - atflUseYn: "Y", - ansUseYn: "Y", - cmntUseYn: "Y", - dgstfnEvlUseYn: "Y", - smsUseYn: "Y", - upndFixUseYn: "Y", - pstgPrdUseYn: "Y", - popupUseYn: "Y", - useYn: "Y", - }, - }); - - return ( - - - 게시판 등록 - - - - ); -} - -export { CreatePage }; diff --git a/apps/common/features/src/pages/bbs/ui/modify-page.tsx b/apps/common/features/src/pages/bbs/ui/modify-page.tsx deleted file mode 100644 index f411cbd5c..000000000 --- a/apps/common/features/src/pages/bbs/ui/modify-page.tsx +++ /dev/null @@ -1,45 +0,0 @@ -"use client"; - -import { useForm } from "react-hook-form"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { useMutation, useQuery } from "@tanstack/react-query"; -import { useAlertStore } from "@/shared/lib/use-alert-store"; -import { ENDPOINT } from "@/shared/config"; -import { - BoardApi, - boardFormSchema, - type BoardFormData, -} from "@/entities/board"; -import { BbsForm } from "./bbs-form"; - -function ModifyPage({ bbsId }: { bbsId: string }) { - const { setMessage: alert } = useAlertStore((state) => state); - - const { data: boardInfo } = useQuery({ - queryKey: [ENDPOINT.BOARD_SERVICE.BBS, bbsId], - queryFn: () => BoardApi.getBoardInfo(bbsId), - }); - - const { mutateAsync: updateBoard } = useMutation({ - mutationFn: (data: BoardFormData) => BoardApi.updateBoard(bbsId, data), - onSuccess: () => { - alert("수정되었습니다."); - }, - }); - - const form = useForm({ - resolver: zodResolver(boardFormSchema), - values: boardInfo, - }); - - return ( - - - 게시판 수정 - - - - ); -} - -export { ModifyPage }; diff --git a/apps/common/features/src/pages/contents/index.ts b/apps/common/features/src/pages/contents/index.ts deleted file mode 100644 index 2bb0d4fb2..000000000 --- a/apps/common/features/src/pages/contents/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { ContentsPage } from "./ui/contents-page"; -export { CreatePage } from "./ui/create-page"; -export { ModifyPage } from "./ui/modify-page"; diff --git a/apps/common/features/src/pages/contents/model/table-columns.tsx b/apps/common/features/src/pages/contents/model/table-columns.tsx deleted file mode 100644 index 19d98a48b..000000000 --- a/apps/common/features/src/pages/contents/model/table-columns.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import dayjs from "dayjs"; -import { createColumnHelper } from "@tanstack/react-table"; -import type { Contents } from "@/entities/content"; - -const columnHelper = createColumnHelper(); - -export const columns = [ - columnHelper.display({ - id: "no", - header: "NO", - cell: ({ row }) => row.index + 1, - }), - columnHelper.accessor("contsTtl", { - id: "contsTtl", - header: "콘텐츠명", - }), - columnHelper.group({ - id: "workInfo", - header: "작성정보", - columns: [ - columnHelper.accessor("workContsVer", { - header: "작성버전", - cell: ({ row }) => - row.original.workContsVer ? `rev. ${row.original.workContsVer}` : "", - }), - columnHelper.accessor("workAprvNm", { - header: "작성상태", - }), - ], - }), - columnHelper.group({ - id: "aprvInfo", - header: "승인정보", - columns: [ - columnHelper.accessor("aprvContsVer", { - header: "승인버전", - cell: ({ row }) => - row.original.aprvContsVer ? `rev. ${row.original.aprvContsVer}` : "", - }), - columnHelper.accessor("autzrNm", { - header: "승인자", - }), - columnHelper.accessor("aprvDt", { - header: "승인일자", - cell: ({ row }) => - row.original.aprvDt - ? dayjs(row.original.aprvDt).format("YYYY-MM-DD") - : "", - }), - ], - }), - columnHelper.accessor("useYn", { - id: "useYn", - header: "사용여부", - }), - columnHelper.accessor("mdfrNm", { - id: "mdfrNm", - header: "최종수정자", - }), - columnHelper.accessor("mdfcnDt", { - id: "mdfcnDt", - header: "최종수정일", - cell: ({ row }) => - row.original.mdfcnDt - ? dayjs(row.original.mdfcnDt).format("YYYY-MM-DD") - : "", - }), -]; diff --git a/apps/common/features/src/pages/contents/ui/contents-form.tsx b/apps/common/features/src/pages/contents/ui/contents-form.tsx deleted file mode 100644 index 823d30173..000000000 --- a/apps/common/features/src/pages/contents/ui/contents-form.tsx +++ /dev/null @@ -1,268 +0,0 @@ -import dynamic from "next/dynamic"; -import { type UseFormReturn } from "react-hook-form"; -import { - Input, - Label, - RadioGroup, - RadioGroupItem, - Table, - TableBody, - TableCell, - TableHead, - TableRow, -} from "@common/components"; -import { FormWrapper } from "@/widgets/form-wrapper"; -import { FormItem, FormControl, FormField } from "@/shared/ui/form"; -import type { ContentsFormData } from "@/entities/content"; -import { useQuery } from "@tanstack/react-query"; -import { ENDPOINT } from "@/shared/config"; -import { CodeApi } from "@/entities/code/api/code-service"; -import Image from "next/image"; - -const Editor = dynamic( - () => import("@/entities/editor").then((module) => module.Editor), - { ssr: false }, -); - -interface ContentsFormProps { - form: UseFormReturn; - contsSn?: number; - isEditable?: boolean; - isContsTtlDisabled?: boolean; - handleSave: (data: ContentsFormData) => Promise; - handleDelete?: () => Promise; -} - -function ContentsForm({ - form, - contsSn, - isEditable, - isContsTtlDisabled, - ...props -}: ContentsFormProps) { - const { data: cclCode } = useQuery({ - queryKey: [ENDPOINT.CMS_SERVICE.CODE, "CCLCD"], - queryFn: () => CodeApi.getCode("CCLCD"), - }); - - const { data: koglCode } = useQuery({ - queryKey: [ENDPOINT.CMS_SERVICE.CODE, "KOGLCD"], - queryFn: () => CodeApi.getCode("KOGLCD"), - }); - - return ( - - - - - - - - - - 콘텐츠 제목 - - ( - - - - - - )} - /> - - - - 콘텐츠 내용 - - ( - - - - - - )} - /> - - - - CCL - - ( - - - - - - - 해당없음 - - - {cclCode?.map((code) => ( - - - - - - - ))} - - - - )} - /> - - - - 공공누리 - - ( - - - - - - - 해당없음 - - - {koglCode?.map((code) => ( - - - - - - - - - ))} - - - - )} - /> - - - - 태그 - - ( - - - - - - )} - /> - - - - 사용여부 - - ( - - - - - - 사용 - - - - 사용안함 - - - - - )} - /> - - - - - - - ); -} - -export { ContentsForm }; diff --git a/apps/common/features/src/pages/contents/ui/contents-page.tsx b/apps/common/features/src/pages/contents/ui/contents-page.tsx deleted file mode 100644 index dd47313e7..000000000 --- a/apps/common/features/src/pages/contents/ui/contents-page.tsx +++ /dev/null @@ -1,105 +0,0 @@ -"use client"; - -import { useRouter } from "next/navigation"; -import { useEffect, useState } from "react"; -import { keepPreviousData, useQuery } from "@tanstack/react-query"; -import { - getCoreRowModel, - useReactTable, - type PaginationState, -} from "@tanstack/react-table"; -import { Button, DataTable, Input, Label } from "@common/components"; -import { - ContentsApi, - type Contents, - type ContentsParams, -} from "@/entities/content"; -import { ENDPOINT } from "@/shared/config"; -import { columns } from "../model/table-columns"; - -function ContentsPage() { - const router = useRouter(); - - const [contentsQuery, setContentsQuery] = useState({ - contsTtl: "", - }); - const [pagination, setPagination] = useState({ - pageIndex: 0, - pageSize: 5, - }); - - const { data: contentsList, refetch } = useQuery({ - queryKey: [ENDPOINT.CMS_SERVICE.CONTENTS], - queryFn: () => - ContentsApi.getContentsList({ - ...contentsQuery, - page: pagination.pageIndex, - size: pagination.pageSize, - }), - placeholderData: keepPreviousData, - enabled: false, - }); - - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault(); - refetch(); - }; - - const handleRowClick = (row: Contents) => { - router.push(`/contents/register/${row.contsSn}`); - }; - - const table = useReactTable({ - data: contentsList?.content ?? [], - columns, - rowCount: contentsList?.totalElements ?? 0, - state: { - pagination, - }, - onPaginationChange: setPagination, - getCoreRowModel: getCoreRowModel(), - manualPagination: true, - }); - - useEffect(() => { - refetch(); - }, [pagination]); - - return ( - - - - - 콘텐츠명 - - setContentsQuery({ contsTtl: e.target.value })} - /> - - 조회 - - - - - - 콘텐츠 목록 - router.push("/contents/register")}> - 등록 - - - - - - - - ); -} - -export { ContentsPage }; diff --git a/apps/common/features/src/pages/contents/ui/create-page.tsx b/apps/common/features/src/pages/contents/ui/create-page.tsx deleted file mode 100644 index 8f7c662f3..000000000 --- a/apps/common/features/src/pages/contents/ui/create-page.tsx +++ /dev/null @@ -1,43 +0,0 @@ -"use client"; - -import { useForm } from "react-hook-form"; -import { useRouter } from "next/navigation"; -import { useMutation } from "@tanstack/react-query"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { - ContentsApi, - contentsFormSchema, - type ContentsFormData, -} from "@/entities/content"; -import { useAlertStore } from "@/shared/lib/use-alert-store"; -import { ContentsForm } from "./contents-form"; - -function CreatePage() { - const router = useRouter(); - const { setMessage: alert } = useAlertStore((state) => state); - - const { mutateAsync: createContents } = useMutation({ - mutationFn: (data: ContentsFormData) => ContentsApi.createContents(data), - onSuccess: () => { - alert("등록되었습니다.", router.back); - }, - }); - - const form = useForm({ - resolver: zodResolver(contentsFormSchema), - defaultValues: { - useYn: "Y", - }, - }); - - return ( - - - 콘텐츠 등록 - - - - ); -} - -export { CreatePage }; diff --git a/apps/common/features/src/pages/contents/ui/modify-page.tsx b/apps/common/features/src/pages/contents/ui/modify-page.tsx deleted file mode 100644 index e4b765f24..000000000 --- a/apps/common/features/src/pages/contents/ui/modify-page.tsx +++ /dev/null @@ -1,182 +0,0 @@ -"use client"; - -import { useEffect, useState } from "react"; -import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; -import { useConfirm } from "@frontend-opensource/use-react-hooks"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { useForm } from "react-hook-form"; -import { ENDPOINT } from "@/shared/config"; -import { useAlertStore } from "@/shared/lib/use-alert-store"; -import { - CONTENTS_APPROVE_STATUS, - ContentsApi, - contentsFormSchema, - type ContentsFormData, -} from "@/entities/content"; -import { ContentsForm } from "./contents-form"; -import { - Select, - SelectTrigger, - SelectValue, - SelectContent, - SelectItem, - Button, -} from "@common/components/ui"; - -function ModifyPage({ sequential }: { sequential: string }) { - const { confirm } = useConfirm(); - const queryClient = useQueryClient(); - const { setMessage: alert } = useAlertStore((state) => state); - - const contsSn = Number(sequential); - const [contsVer, setContsVer] = useState(0); - - const { data: contentsVersionList } = useQuery({ - queryKey: [ENDPOINT.CMS_SERVICE.CONTENTS_DETAILS, contsSn], - queryFn: () => - ContentsApi.getContentsVersionList({ - contsSn: contsSn, - page: 0, - size: 100, - }), - }); - - const { data: contentsDetail } = useQuery({ - queryKey: [ENDPOINT.CMS_SERVICE.CONTENTS_DETAILS, contsVer], - queryFn: () => ContentsApi.getLatestContentsInfo(contsSn, contsVer), - enabled: !!contsVer, - }); - - const { mutateAsync: createContents } = useMutation({ - mutationFn: (data: ContentsFormData) => ContentsApi.createContents(data), - onSuccess: () => { - setContsVer(0); - alert("수정되었습니다."); - queryClient.invalidateQueries({ - queryKey: [ENDPOINT.CMS_SERVICE.CONTENTS_DETAILS, contsSn], - }); - }, - }); - - const { mutateAsync: updateContents } = useMutation({ - mutationFn: (data: ContentsFormData) => - ContentsApi.updateContents(contsSn, contsVer, data), - onSuccess: () => { - alert("수정되었습니다."); - }, - }); - - const { mutateAsync: deleteContents } = useMutation({ - mutationFn: () => ContentsApi.deleteContents(contsSn, contsVer), - onSuccess: () => { - setContsVer(0); - alert("삭제되었습니다."); - queryClient.invalidateQueries({ - queryKey: [ENDPOINT.CMS_SERVICE.CONTENTS_DETAILS, contsSn], - }); - }, - }); - - const { mutateAsync: requestApprove } = useMutation({ - mutationFn: () => ContentsApi.requestApprove(contsSn, contsVer), - onSuccess: () => { - alert("승인 요청이 완료되었습니다."); - queryClient.invalidateQueries({ - queryKey: [ENDPOINT.CMS_SERVICE.CONTENTS_DETAILS], - }); - }, - }); - - const { mutateAsync: approveContents } = useMutation({ - mutationFn: () => ContentsApi.approveContents(contsSn, contsVer), - onSuccess: () => { - alert("승인되었습니다."); - queryClient.invalidateQueries({ - queryKey: [ENDPOINT.CMS_SERVICE.CONTENTS_DETAILS], - }); - }, - }); - - const form = useForm({ - resolver: zodResolver(contentsFormSchema), - values: contentsDetail, - }); - - const approveStatus = contentsDetail?.aprvCd; - const isContsTtlDisabled = - (contentsVersionList?.content?.[0]?.contsVer ?? 0) > 1; - - const onRequestApprove = async () => { - const message = - "승인 요청하면 더이상 내용을 수정할 수 없습니다.\n요청하시겠습니까?"; - if (await confirm(message)) requestApprove(); - }; - - const onApprove = async () => { - const message = "승인하시겠습니까?"; - if (await confirm(message)) approveContents(); - }; - - useEffect(() => { - if (contsVer) return; - if (!contentsVersionList?.content?.length) return; - - const latestContent = contentsVersionList?.content[0]; - if (!latestContent) return; - - setContsVer(latestContent?.contsVer ?? 0); - }, [contentsVersionList?.content]); - - return ( - - - 템플릿 수정 - - { - setContsVer(Number(value)); - }} - > - - - - - {contentsVersionList?.content?.map((item) => ( - - {item.contsCn} - - ))} - - - {approveStatus === CONTENTS_APPROVE_STATUS.DRAFT && ( - 승인요청 - )} - {approveStatus === CONTENTS_APPROVE_STATUS.REQUESTED && ( - <> - 승인 - 반려 - > - )} - - - - - ); -} - -export { ModifyPage }; diff --git a/apps/common/features/src/pages/dashboard/index.ts b/apps/common/features/src/pages/dashboard/index.ts deleted file mode 100644 index 5b787c2e2..000000000 --- a/apps/common/features/src/pages/dashboard/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { DashboardPage } from "./ui/dashboard-page"; diff --git a/apps/common/features/src/pages/dashboard/ui/dashboard-page.tsx b/apps/common/features/src/pages/dashboard/ui/dashboard-page.tsx deleted file mode 100644 index 4dc693361..000000000 --- a/apps/common/features/src/pages/dashboard/ui/dashboard-page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -function DashboardPage() { - return <>>; -} - -export { DashboardPage }; diff --git a/apps/common/features/src/pages/file/index.ts b/apps/common/features/src/pages/file/index.ts deleted file mode 100644 index 4c0093cfc..000000000 --- a/apps/common/features/src/pages/file/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { FilePage } from "./ui/file-page"; diff --git a/apps/common/features/src/pages/file/model/table-columns.tsx b/apps/common/features/src/pages/file/model/table-columns.tsx deleted file mode 100644 index 2606397c9..000000000 --- a/apps/common/features/src/pages/file/model/table-columns.tsx +++ /dev/null @@ -1,77 +0,0 @@ -"use client"; - -import dayjs from "dayjs"; -import { type ColumnDef } from "@tanstack/react-table"; -import { Button } from "@common/components"; -import type { Attachment } from "@/entities/attachment"; - -export const createColumns = ( - handleDeleteAttachment: (atflId: string) => void, -): ColumnDef[] => [ - { - id: "no", - header: "NO", - cell: (info) => info.row.index + 1, - }, - { - accessorKey: "atflId", - header: "파일ID", - }, - { - accessorKey: "atflSn", - header: "파일순번", - }, - { - accessorKey: "orgnlFileNm", - header: "파일명", - }, - { - accessorKey: "atflFlieSz", - header: "파일크기", - cell: ({ row }) => { - const size = row.getValue("atflFlieSz"); - return size ? formatFileSize(Number(size)) : "0 Bytes"; - }, - }, - - { - accessorKey: "dwnldNocs", - header: "다운로드수", - }, - { - accessorKey: "regDt", - header: "등록일시", - cell: ({ row }) => { - const date = row.getValue("regDt") as string; - return date ? dayjs(date).format("YYYY-MM-DD") : ""; - }, - }, - { - accessorKey: "delYn", - header: "삭제여부", - }, - { - id: "delete", - header: "삭제", - cell: ({ row }) => ( - handleDeleteAttachment(row.original.atflId)} - > - 삭제 - - ), - }, -]; - -const formatFileSize = (bytes: number): string => { - console.log("bytes: ", bytes); - if (bytes === 0) return "0 Bytes"; - - const k = 1024; - const sizes = ["Bytes", "KB", "MB", "GB", "TB"]; - const i = Math.floor(Math.log(bytes) / Math.log(k)); - - return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`; -}; diff --git a/apps/common/features/src/pages/file/ui/file-page.tsx b/apps/common/features/src/pages/file/ui/file-page.tsx deleted file mode 100644 index 4f6b79781..000000000 --- a/apps/common/features/src/pages/file/ui/file-page.tsx +++ /dev/null @@ -1,118 +0,0 @@ -"use client"; - -import { useEffect, useState } from "react"; -import { keepPreviousData, useMutation, useQuery } from "@tanstack/react-query"; -import { - getCoreRowModel, - useReactTable, - type PaginationState, -} from "@tanstack/react-table"; -import { useConfirm } from "@frontend-opensource/use-react-hooks"; -import { Button, DataTable, Input, Label } from "@common/components"; -import { AttachmentApi, type AttachmentParams } from "@/entities/attachment"; -import { useAlertStore } from "@/shared/lib/use-alert-store"; -import { ENDPOINT } from "@/shared/config"; -import { createColumns } from "../model/table-columns"; - -function FilePage() { - const { confirm } = useConfirm(); - const { setMessage: alert } = useAlertStore((state) => state); - - const [attachmentQuery, setAttachmentQuery] = useState({ - keyword: "", - }); - const [pagination, setPagination] = useState({ - pageIndex: 0, - pageSize: 10, - }); - - const { data: attachmentsList, refetch } = useQuery({ - queryKey: [ENDPOINT.ATTACH_SERVICE.ATTACHMENTS], - queryFn: () => - AttachmentApi.getAttachmentsList({ - ...attachmentQuery, - page: pagination.pageIndex, - size: pagination.pageSize, - }), - placeholderData: keepPreviousData, - enabled: false, - }); - - const { mutate: deleteAttachment } = useMutation({ - mutationFn: (atflId: string) => AttachmentApi.deleteAttachment(atflId), - onSuccess: () => { - refetch(); - alert("삭제되었습니다."); - }, - }); - - const handleDeleteAttachment = async (atflId: string) => { - if (await confirm("삭제하시겠습니까?")) { - deleteAttachment(atflId); - } - }; - - const table = useReactTable({ - data: attachmentsList?.content ?? [], - columns: createColumns(handleDeleteAttachment), - rowCount: attachmentsList?.totalElements ?? 0, - state: { - pagination, - }, - onPaginationChange: setPagination, - getCoreRowModel: getCoreRowModel(), - manualPagination: true, - }); - - const handleAttachmentQueryChange = - (key: keyof AttachmentParams) => (value: string) => { - setAttachmentQuery({ ...attachmentQuery, [key]: value }); - }; - - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault(); - refetch(); - }; - - useEffect(() => { - refetch(); - }, [pagination]); - - return ( - - - - - - - 파일명 - - - handleAttachmentQueryChange("keyword")(e.target.value) - } - placeholder="파일명을 입력하세요" - /> - - - - 조회 - - - - - - 파일 목록 - - - - - - - ); -} - -export { FilePage }; diff --git a/apps/common/features/src/pages/image/api/image-service.ts b/apps/common/features/src/pages/image/api/image-service.ts deleted file mode 100644 index af5d95bbe..000000000 --- a/apps/common/features/src/pages/image/api/image-service.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { client } from "@/shared/api"; -import { ENDPOINT } from "@/shared/config"; -import type { - Image, - ImageFormData, - ImageList, - ImageParams, - ImageService, -} from "../model/image-interface"; - -export class ImageApiService implements ImageService { - public async getImageList(params: ImageParams): Promise { - const response = await client.get( - `${ENDPOINT.CMS_SERVICE.IMAGES}`, - { - params, - }, - ); - return response.data; - } - - public async getImageInfo(imgSn: string): Promise { - const response = await client.get( - `${ENDPOINT.CMS_SERVICE.IMAGES}/${imgSn}`, - ); - return response.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: ImageFormData): Promise { - const response = await client.put( - `${ENDPOINT.CMS_SERVICE.IMAGES}/${imgSn}`, - data, - ); - return response.data; - } - - public async deleteImage(imgSn: string): Promise { - await client.delete(`${ENDPOINT.CMS_SERVICE.IMAGES}/${imgSn}`); - } -} - -export const ImageApi = new ImageApiService(); diff --git a/apps/common/features/src/pages/image/index.ts b/apps/common/features/src/pages/image/index.ts deleted file mode 100644 index 5c602ca7d..000000000 --- a/apps/common/features/src/pages/image/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { CreatePage } from "./ui/create-page"; -export { ModifyPage } from "./ui/modify-page"; -export { ImagePage } from "./ui/image-page"; diff --git a/apps/common/features/src/pages/image/model/image-interface.ts b/apps/common/features/src/pages/image/model/image-interface.ts deleted file mode 100644 index 2026ef9b5..000000000 --- a/apps/common/features/src/pages/image/model/image-interface.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { z } from "zod"; -import type { PageableList, PageableParams } from "@/shared/model"; - -export const IMAGE_TYPE = { - IMGSUB: "IMGSUB", - IMGMAIN: "IMGMAIN", - IMGLOGIN: "IMGLOGIN", -} as const; - -export type ImageType = (typeof IMAGE_TYPE)[keyof typeof IMAGE_TYPE]; - -export interface ImageParams extends PageableParams { - keyword: string; - imgTypeCd?: string; - useYn?: string; -} - -export interface Image { - imgSn: number; - imgTypeCd: ImageType; - imgTypeNm: string; - imgTtl: string; - lnkgUrlAddr: string; - atflYn: string; - sortSeq: number; - atflCd: string; - useYn: string; - mdfcnDt: string; - mdFr: string; - mdfrNm: string; -} - -export const imageFormSchema = z.object({ - imgTtl: z.string(), - sortSeq: z.number(), - imgTypeCd: z.string(), - atflYn: z.string(), - atflCd: z.string().nullish(), - lnkgUrlAddr: z.string().nullish(), - useYn: z.string(), -}); - -export type ImageFormData = z.infer; -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; - deleteImage: (imgSn: string) => Promise; -} diff --git a/apps/common/features/src/pages/image/model/image-table-columns.tsx b/apps/common/features/src/pages/image/model/image-table-columns.tsx deleted file mode 100644 index dd80ad9c7..000000000 --- a/apps/common/features/src/pages/image/model/image-table-columns.tsx +++ /dev/null @@ -1,73 +0,0 @@ -"use client"; - -import dayjs from "dayjs"; -import { type ColumnDef } from "@tanstack/react-table"; -import { Checkbox } from "@common/components/ui"; -import { type Image } from "./image-interface"; -import { IMAGE_TYPE } from "./image-interface"; - -export const columns: ColumnDef[] = [ - { - id: "select", - header: ({ table }) => ( - e.stopPropagation()} - onCheckedChange={(checked) => - table.toggleAllPageRowsSelected(!!checked) - } - className="h-4 w-4 rounded border-gray-300" - /> - ), - cell: ({ row }) => ( - e.stopPropagation()} - onCheckedChange={(checked) => row.toggleSelected(!!checked)} - className="h-4 w-4 rounded border-gray-300" - /> - ), - }, - { - accessorKey: "imgSn", - header: "NO", - cell: ({ row }) => row.index + 1, - }, - { - accessorKey: "imgTypeCd", - header: "이미지구분", - cell: ({ row }) => { - const type = row.getValue("imgTypeCd"); - switch (type) { - case IMAGE_TYPE.IMGSUB: - return "서브이미지"; - case IMAGE_TYPE.IMGMAIN: - return "메인이미지"; - case IMAGE_TYPE.IMGLOGIN: - return "로그인이미지"; - default: - return type; - } - }, - }, - { - accessorKey: "imgTtl", - header: "이미지명", - }, - { - accessorKey: "useYn", - header: "사용여부", - }, - { - accessorKey: "mdfrNm", - header: "최종수정자", - }, - { - accessorKey: "mdfcnDt", - header: "최종수정일", - cell: ({ row }) => { - const date = row.getValue("mdfcnDt") as string; - return date ? dayjs(date).format("YYYY-MM-DD") : ""; - }, - }, -]; diff --git a/apps/common/features/src/pages/image/model/posting-table-columns.tsx b/apps/common/features/src/pages/image/model/posting-table-columns.tsx deleted file mode 100644 index a43cf8693..000000000 --- a/apps/common/features/src/pages/image/model/posting-table-columns.tsx +++ /dev/null @@ -1,44 +0,0 @@ -"use client"; - -import dayjs from "dayjs"; -import type { ColumnDef } from "@tanstack/react-table"; -import { Checkbox } from "@common/components/ui"; -import type { Post } from "@/entities/posting"; - -export const columns: ColumnDef[] = [ - { - id: "select", - header: ({ table }) => ( - e.stopPropagation()} - onCheckedChange={(checked) => - table.toggleAllPageRowsSelected(!!checked) - } - className="h-4 w-4 rounded border-gray-300" - /> - ), - cell: ({ row }) => ( - e.stopPropagation()} - onCheckedChange={(checked) => row.toggleSelected(!!checked)} - className="h-4 w-4 rounded border-gray-300" - /> - ), - }, - { - accessorKey: "siteNm", - header: "사이트", - }, - { - accessorKey: "pstgPrdSeNm", - header: "게시기간", - cell: ({ row }) => { - const pstgPrdSeNm = row.original.pstgPrdSeNm; - const pstgBgngDt = row.original.pstgBgngDt; - const pstgEndDt = row.original.pstgEndDt; - return `${pstgPrdSeNm} (${dayjs(pstgBgngDt).format("YYYY-MM-DD")} ~ ${dayjs(pstgEndDt).format("YYYY-MM-DD")})`; - }, - }, -]; diff --git a/apps/common/features/src/pages/image/ui/create-page.tsx b/apps/common/features/src/pages/image/ui/create-page.tsx deleted file mode 100644 index 5b3ea3924..000000000 --- a/apps/common/features/src/pages/image/ui/create-page.tsx +++ /dev/null @@ -1,40 +0,0 @@ -"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 { useAlertStore } from "@/shared/lib/use-alert-store"; -import { imageFormSchema, type ImageFormData } from "../model/image-interface"; -import { ImageApi } from "../api/image-service"; -import { ImageForm } from "./image-form"; - -function CreatePage() { - const router = useRouter(); - const { setMessage: alert } = useAlertStore((state) => state); - - const { mutateAsync: createImage } = useMutation({ - mutationFn: (data: ImageFormData) => ImageApi.createImage(data), - onSuccess: () => { - alert("등록되었습니다.", router.back); - }, - }); - - const form = useForm({ - resolver: zodResolver(imageFormSchema), - defaultValues: { - atflYn: "Y", - }, - }); - - return ( - - - 이미지 등록 - - - - ); -} - -export { CreatePage }; diff --git a/apps/common/features/src/pages/image/ui/image-form.tsx b/apps/common/features/src/pages/image/ui/image-form.tsx deleted file mode 100644 index b334f1858..000000000 --- a/apps/common/features/src/pages/image/ui/image-form.tsx +++ /dev/null @@ -1,307 +0,0 @@ -"use client"; - -import Image from "next/image"; -import { useEffect, useState } from "react"; -import { useQuery } from "@tanstack/react-query"; -import type { UseFormReturn } from "react-hook-form"; -import type { HttpResponse } from "tus-js-client"; -import { - FileUploader, - Input, - Label, - Progress, - RadioGroup, - RadioGroupItem, - Table, - TableBody, - TableCell, - TableHead, - TableRow, -} from "@common/components"; -import { FormWrapper } from "@/widgets/form-wrapper"; -import { CodeSelect } from "@/entities/code"; -import { AttachmentApi } from "@/entities/attachment"; -import { ENDPOINT } from "@/shared/config"; -import { useTus } from "@/shared/lib/use-tus"; -import { FormControl, FormField, FormItem } from "@/shared/ui/form"; -import { useAttachmentStore } from "@/shared/lib/use-attachment-store"; -import type { ImageFormData } from "../model/image-interface"; - -type ImageFormProps = { - form: UseFormReturn; - imgSn?: number; - handleSave: (data: ImageFormData) => Promise; - handleDelete?: () => Promise; -}; - -function ImageForm({ form, imgSn, ...props }: ImageFormProps) { - const atflYn = form.watch("atflYn"); - const atflCd = form.watch("atflCd"); - - const { - file: uploadFile, - response: uploadResponse, - progress, - setFile, - } = useTus(); - const { setAttachmentFileCode } = useAttachmentStore((state) => state); - const [imageUrl, setImageUrl] = useState(""); - - const { data: attachmentData } = useQuery({ - queryKey: [ENDPOINT.ATTACH_SERVICE.ATTACHMENTS, atflCd], - queryFn: () => AttachmentApi.getAttachmentsInfo(atflCd!), - select: (data) => data?.[data.length - 1], - enabled: !!atflCd, - }); - - const handleUploadResponse = async (res: HttpResponse) => { - const fileInfo = res.getHeader("X-File-Info"); - if (fileInfo === undefined || fileInfo === null) return; - - const { atflCd, atflId } = JSON.parse(fileInfo) ?? {}; - if (atflCd) form.setValue("atflCd", atflCd); - if (atflId) setImageUrl(await AttachmentApi.getImageUrl(atflId)); - if (attachmentData) - await AttachmentApi.deleteAttachmentByAtflCd(attachmentData.atflCd); - }; - - useEffect(() => { - if (!uploadResponse) return; - handleUploadResponse(uploadResponse); - }, [uploadResponse]); - - useEffect(() => { - (async () => { - setImageUrl( - await AttachmentApi.getImageUrlByAtflCd(attachmentData?.atflCd ?? ""), - ); - })(); - }, [attachmentData]); - - return ( - - - - - - - - - - 이미지명 - - ( - - - - - - )} - /> - - - - 정렬순서 - - ( - - - { - const value = e.target.value; - if (isNaN(Number(value))) return; - field.onChange(Number(value)); - }} - className="w-[12rem] !text-[1.3rem]" - placeholder="정렬순서 입력" - /> - - - )} - /> - - - - 이미지유형 - - ( - - - - - - )} - /> - - - - 첨부파일 - - ( - - - - - - 이미지파일 - - - - 외부링크 - - - - - )} - /> - - - {atflYn === "Y" && ( - - 이미지파일 - - ( - - - { - const files = e.target.files; - if (!files) return; - setAttachmentFileCode(""); - setFile(files[0] ?? null); - }} - customPreview={ - <> - - - - {imageUrl && ( - - )} - - - {uploadFile?.name} - - - - - - - 권장 사이즈: 1920x500 - 파일 최대 크기: 2MB - 확장자: jpg, jpeg, png 가능 - - - > - } - /> - - - )} - /> - - - )} - {atflYn === "N" && ( - - 외부링크 - - ( - - - - - - )} - /> - - - )} - - 사용여부 - - ( - - - - - - 사용 - - - - 미사용 - - - - - )} - /> - - - - - - - ); -} - -export { ImageForm }; diff --git a/apps/common/features/src/pages/image/ui/image-page.tsx b/apps/common/features/src/pages/image/ui/image-page.tsx deleted file mode 100644 index 3571a4a1f..000000000 --- a/apps/common/features/src/pages/image/ui/image-page.tsx +++ /dev/null @@ -1,132 +0,0 @@ -"use client"; - -import { useEffect, useState } from "react"; -import { useRouter } from "next/navigation"; -import { useQuery } from "@tanstack/react-query"; -import { - getCoreRowModel, - useReactTable, - type PaginationState, - type RowSelectionState, -} from "@tanstack/react-table"; -import { Button, DataTable, Input, Label } from "@common/components"; -import { POSTING_TYPE, PostingDialog } from "@/entities/posting"; -import { ENDPOINT } from "@/shared/config"; -import { ImageApi } from "../api/image-service"; -import { columns } from "../model/image-table-columns"; -import { type Image, type ImageParams } from "../model/image-interface"; - -type PostingTarget = { - pstgTrgtNm: string; - pstgTrgtNo: number; -}; - -function ImagePage() { - const router = useRouter(); - - const [rowSelection, setRowSelection] = useState({}); - const [postingTargets, setPostingTargets] = useState([]); - const [imageQuery, setImageQuery] = useState({ keyword: "" }); - const [pagination, setPagination] = useState({ - pageIndex: 0, - pageSize: 5, - }); - - const { data: imageList, refetch } = useQuery({ - queryKey: [ENDPOINT.CMS_SERVICE.IMAGES], - queryFn: () => - ImageApi.getImageList({ - ...imageQuery, - page: pagination.pageIndex, - size: pagination.pageSize, - }), - enabled: false, - }); - - const table = useReactTable({ - data: imageList?.content ?? [], - columns, - state: { - pagination, - rowSelection, - }, - rowCount: imageList?.totalElements ?? 0, - manualPagination: true, - enableMultiRowSelection: true, - getCoreRowModel: getCoreRowModel(), - onPaginationChange: setPagination, - onRowSelectionChange: setRowSelection, - }); - - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault(); - refetch(); - }; - - const handleRowClick = (row: Image) => { - router.push(`/image/register/${row.imgSn}`); - }; - - useEffect(() => { - const postingTargets = table - .getFilteredSelectedRowModel() - .rows.map((row) => ({ - pstgTrgtNm: row.original.imgTtl, - pstgTrgtNo: row.original.imgSn, - })); - - if (!postingTargets.length) { - setPostingTargets([]); - return; - } - - setPostingTargets(postingTargets); - }, [rowSelection]); - - useEffect(() => { - refetch(); - }, [pagination]); - - return ( - - - - - 이미지명 - - - setImageQuery({ ...imageQuery, keyword: e.target.value }) - } - /> - - 조회 - - - - - - - 이미지 목록 - - - router.push("/image/register")}> - 등록 - - - - - - - - - - ); -} - -export { ImagePage }; diff --git a/apps/common/features/src/pages/image/ui/modify-page.tsx b/apps/common/features/src/pages/image/ui/modify-page.tsx deleted file mode 100644 index ec6030531..000000000 --- a/apps/common/features/src/pages/image/ui/modify-page.tsx +++ /dev/null @@ -1,128 +0,0 @@ -"use client"; - -import { useState } from "react"; -import { useRouter } from "next/navigation"; -import { useForm } from "react-hook-form"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; -import { - getCoreRowModel, - useReactTable, - type RowSelectionState, -} from "@tanstack/react-table"; -import { useConfirm } from "@frontend-opensource/use-react-hooks"; -import { Button, DataTable } from "@common/components/ui"; -import { PostingApi, type PostPayload } from "@/entities/posting"; -import { useAlertStore } from "@/shared/lib/use-alert-store"; -import { ENDPOINT } from "@/shared/config"; -import { imageFormSchema, type ImageFormData } from "../model/image-interface"; -import { columns } from "../model/posting-table-columns"; -import { ImageApi } from "../api/image-service"; -import { ImageForm } from "./image-form"; - -function ModifyPage({ sequential }: { sequential: string }) { - const router = useRouter(); - const { confirm } = useConfirm(); - const queryClient = useQueryClient(); - const { setMessage: alert } = useAlertStore((state) => state); - - const [rowSelection, setRowSelection] = useState({}); - - const { data: imageDetail } = useQuery({ - queryKey: [ENDPOINT.CMS_SERVICE.IMAGES, sequential], - queryFn: () => ImageApi.getImageInfo(sequential), - }); - - const { data: postHistory } = useQuery({ - queryKey: [ENDPOINT.CMS_SERVICE.POSTINGS, sequential], - queryFn: () => - PostingApi.getPostingHistory({ pstgTrgtNo: Number(sequential) }), - }); - - const { mutateAsync: updateImage } = useMutation({ - mutationFn: (data: ImageFormData) => ImageApi.updateImage(sequential, data), - onSuccess: () => { - alert("수정되었습니다."); - }, - }); - - const { mutateAsync: deleteImage } = useMutation({ - mutationFn: () => ImageApi.deleteImage(sequential), - onSuccess: () => { - alert("삭제되었습니다.", router.back); - }, - }); - - const { mutateAsync: savePosting } = useMutation({ - mutationFn: (data: PostPayload[]) => PostingApi.savePosting(data), - onSuccess: () => { - queryClient.invalidateQueries({ - queryKey: [ENDPOINT.CMS_SERVICE.POSTINGS, sequential], - }); - alert("삭제되었습니다."); - }, - }); - - const form = useForm({ - resolver: zodResolver(imageFormSchema), - values: imageDetail, - }); - - const table = useReactTable({ - data: postHistory?.content ?? [], - columns, - state: { - rowSelection, - }, - enableMultiRowSelection: true, - getCoreRowModel: getCoreRowModel(), - onRowSelectionChange: setRowSelection, - }); - - const handleDeletePostingHistory = async () => { - const data = table.getFilteredSelectedRowModel().rows.map((row) => ({ - ...row.original, - mode: "D", - })); - - if (await confirm("삭제하시겠습니까?")) { - await savePosting(data); - } - }; - - return ( - - - - 이미지 수정 - - - - - - 사이트 게시 이력 - - - - - 이력삭제 - - - - - - - ); -} - -export { ModifyPage }; diff --git a/apps/common/features/src/pages/menu/api/menu-service.ts b/apps/common/features/src/pages/menu/api/menu-service.ts deleted file mode 100644 index dbb67cb3e..000000000 --- a/apps/common/features/src/pages/menu/api/menu-service.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { client } from "@/shared/api"; -import { ENDPOINT } from "@/shared/config"; -import type { - Menu, - MenuFormData, - MenuService, - TreeMenu, - TreeNode, -} from "../model/menu-interface"; - -export class MenuApiService implements MenuService { - public async getMenuInfo(menuCd: number): Promise { - const response = await client.get( - `${ENDPOINT.CMS_SERVICE.MENUS}/${menuCd}`, - ); - return response.data; - } - - public async createMenu(data: MenuFormData): Promise { - const response = await client.post( - `${ENDPOINT.CMS_SERVICE.MENUS}`, - data, - ); - return response.data; - } - - public async updateMenu( - menuCd: number, - data: Omit, - ): Promise { - const response = await client.put( - `${ENDPOINT.CMS_SERVICE.MENUS}/${menuCd}`, - data, - ); - return response.data; - } - - public async deleteMenu(menuCd: number): Promise { - await client.delete(`${ENDPOINT.CMS_SERVICE.MENUS}/${menuCd}`); - } - - public async getTreeMenuList(siteId: string): Promise { - const response = await client.get( - `${ENDPOINT.CMS_SERVICE.MENUS}/${siteId}/tree`, - ); - return response.data; - } - - public async updateTreeMenu(siteId: string, data: TreeNode[]): Promise { - const response = await client.put( - `${ENDPOINT.CMS_SERVICE.MENUS}/${siteId}/tree`, - data, - ); - return response.data; - } - - public async updateMenuName(menuCd: number, name: string): Promise { - const response = await client.put( - `${ENDPOINT.CMS_SERVICE.MENUS}/${menuCd}/${name}`, - ); - return response.data; - } -} - -export const MenuApi = new MenuApiService(); diff --git a/apps/common/features/src/pages/menu/index.ts b/apps/common/features/src/pages/menu/index.ts deleted file mode 100644 index fa0eda54f..000000000 --- a/apps/common/features/src/pages/menu/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { MenuPage } from "./ui/menu-page"; diff --git a/apps/common/features/src/pages/menu/lib/utils.ts b/apps/common/features/src/pages/menu/lib/utils.ts deleted file mode 100644 index f6d13be33..000000000 --- a/apps/common/features/src/pages/menu/lib/utils.ts +++ /dev/null @@ -1,171 +0,0 @@ -import type { DropOptions, NodeModel } from "@minoru/react-dnd-treeview"; -import type { - TreeUserOrganization, - TreeUser, -} from "@/entities/user-organization"; -import type { TreeMenu, TreeNode } from "../model/menu-interface"; - -export const generateMenuTreeData = ( - data: TreeMenu[], -): NodeModel[] => { - return data.reduce[]>((acc, menu) => { - const node: NodeModel = { - id: menu.menuCd ?? 0, - parent: menu.parentId ?? 0, - text: menu.menuKornNm, - droppable: true, - data: menu, - }; - - acc.push(node); - - if (menu.children?.length) { - acc.push(...generateMenuTreeData(menu.children)); - } - - return acc; - }, []); -}; - -export const generateNewMenuTreeData = ( - treeData: NodeModel[], - options: DropOptions, -): TreeNode | null => { - const { dragSourceId, dragSource, dropTargetId, dropTarget } = options; - - if (!dragSource?.data) return null; - - const siblings = treeData.filter((node) => node.parent === dropTargetId); - - const dragSourceIndex = siblings.findIndex( - (node) => node.id === dragSourceId, - ); - - const prevSibling = siblings[dragSourceIndex - 1]; - - const newSortSeq = - dragSourceIndex === 0 ? 1 : (prevSibling?.data?.sortSeq ?? 0) + 1; - - const childNodes = treeData.filter( - (node) => node.parent === dragSourceId && node.data, - ); - - const baseNode = (node: NodeModel): TreeNode => { - if (!node.data) return {}; - return { - menuCd: node.data?.menuCd, - name: node.data?.menuKornNm, - icon: node.data?.iconNm, - }; - }; - - const treeNode: TreeNode = { - ...baseNode(dragSource), - parentId: dropTargetId === 0 ? null : (dropTargetId as number), - sortSeq: newSortSeq, - levelNo: (dropTarget?.data?.levelNo ?? 0) + 1, - }; - - const result: TreeNode = { - ...treeNode, - children: childNodes.map((child) => ({ - ...baseNode(child), - parentId: child.data?.parentId === 0 ? null : (dropTargetId as number), - sortSeq: child.data?.sortSeq, - levelNo: (treeNode?.levelNo ?? 0) + 1, - })), - }; - - return result; -}; - -export const handleSelectNode = - (callback: (node: NodeModel) => void) => - (node: NodeModel) => { - callback(node); - }; - -export const handleDropNode = - (callback: (newTree: TreeNode) => void) => - (newTree: NodeModel[], options: DropOptions) => { - const newTreeData = generateNewMenuTreeData(newTree, options); - if (!newTreeData) return; - callback(newTreeData); - }; - -export const handleNodeTextChange = - (treeData: NodeModel[]) => - (callback: (newTree: NodeModel[]) => void) => - (id: NodeModel["id"], value: string) => { - const newTree = treeData.map((node) => { - if (node.id === id) { - return { - ...node, - text: value, - }; - } - - return node; - }); - callback(newTree); - }; - -export const generateUserOrganizationTreeData = ( - organizations: TreeUserOrganization[], -): NodeModel[] => { - return organizations.reduce[]>( - (acc, org) => { - const orgNode: NodeModel = { - id: org.ognzSn, - parent: org.upOgnzSn ?? 0, - text: org.ognzNm, - data: org, - droppable: false, - }; - - acc.push(orgNode); - - if (org.children?.length) { - acc.push(...generateUserOrganizationTreeData(org.children)); - } - - if (org.users?.length) { - acc.push( - ...org.users.map((user) => ({ - id: user.userUnqId, - parent: org.ognzSn, - text: user.userNm, - data: user, - droppable: false, - })), - ); - } - - return acc; - }, - [], - ); -}; - -export const filterUserOrganizationTreeDataByKeyword = ( - treeData: NodeModel[], - keyword: string, -): NodeModel[] => { - return Array.from( - treeData.reduce((matchedNodes, node) => { - if (!node.text.includes(keyword)) return matchedNodes; - - matchedNodes.add(node); - - let parentId = node.parent; - while (parentId) { - const parentNode = treeData.find((n) => n.id === parentId); - if (!parentNode) break; - matchedNodes.add(parentNode); - parentId = parentNode.parent; - } - - return matchedNodes; - }, new Set>()), - ); -}; diff --git a/apps/common/features/src/pages/menu/model/board-table-columns.tsx b/apps/common/features/src/pages/menu/model/board-table-columns.tsx deleted file mode 100644 index 4cdf29498..000000000 --- a/apps/common/features/src/pages/menu/model/board-table-columns.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import type { ColumnDef } from "@tanstack/react-table"; -import { Checkbox } from "@common/components/ui"; -import type { BoardPopup } from "@/entities/board"; - -export const columns: ColumnDef[] = [ - { - id: "no", - header: "NO", - cell: ({ row }) => row.index + 1, - }, - { - accessorKey: "bbsNm", - header: "게시판명", - }, - { - accessorKey: "bbsTypeNm", - header: "게시판유형", - }, - { - accessorKey: "regDt", - header: "최초생성일", - }, - { - accessorKey: "pstCnt", - header: "게시물건수", - }, - { - id: "select", - header: "선택", - cell: ({ row }) => { - return ( - - ); - }, - }, -]; diff --git a/apps/common/features/src/pages/menu/model/contents-table-colums.tsx b/apps/common/features/src/pages/menu/model/contents-table-colums.tsx deleted file mode 100644 index a96434c39..000000000 --- a/apps/common/features/src/pages/menu/model/contents-table-colums.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import dayjs from "dayjs"; -import type { ColumnDef } from "@tanstack/react-table"; -import { Checkbox } from "@common/components/ui"; -import type { Contents } from "@/entities/content"; - -export const columns: ColumnDef[] = [ - { - id: "no", - header: "NO", - cell: ({ row }) => row.index + 1, - }, - { - id: "contsTtl", - header: "콘텐츠명", - cell: ({ row }) => row.original.contsTtl, - }, - { - accessorKey: "rgTrNm", - header: "승인자", - }, - { - accessorKey: "aprvDt", - header: "승인일자", - cell: ({ row }) => { - const date = dayjs(row.original.aprvDt); - return date.format("YYYY-MM-DD"); - }, - }, - { - accessorKey: "mdfrNm", - header: "최종수정자", - }, - { - accessorKey: "mdfcnDt", - header: "최종수정일", - cell: ({ row }) => { - const date = dayjs(row.original.mdfcnDt); - return date.format("YYYY-MM-DD"); - }, - }, - { - id: "select", - header: "선택", - cell: ({ row }) => { - return ( - - ); - }, - }, -]; diff --git a/apps/common/features/src/pages/menu/model/menu-interface.ts b/apps/common/features/src/pages/menu/model/menu-interface.ts deleted file mode 100644 index f9650e0e2..000000000 --- a/apps/common/features/src/pages/menu/model/menu-interface.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { z } from "zod"; - -export const MENU_TYPE = { - EMPTY: "EMPTY", - CONTENTS: "CONTENTS", - BOARD: "BOARD", - INSIDE: "INSIDE", - OUTSIDE: "OUTSIDE", - BOTTOM: "BOTTOM", -} as const; - -export type MenuType = (typeof MENU_TYPE)[keyof typeof MENU_TYPE]; - -export interface TreeNode { - menuCd?: number; - name?: string; - sortSeq?: number; - parentId?: number | null; - levelNo?: number; - icon?: string; - children?: TreeNode[]; -} - -export interface TreeMenu extends Omit { - menuKornNm: string; - menuEngNm?: string; - iconNm?: string; - children?: TreeMenu[]; - urlPath?: string; - menuLnkgTypeCd: string; - menuLnkgSn?: number; - qrcd?: string; - useYn: string; - otptYn: string; - requiredUrlPath?: boolean; -} - -export const menuFormSchema = z.object({ - siteId: z.string(), - menuKornNm: z.string(), - menuSubKornNm: z.string().nullish(), - menuEngNm: z.string().nullish(), - menuSubEngNm: z.string().nullish(), - levelNo: z.number(), - urlPath: z.string().nullish(), - iconNm: z.string().nullish(), - menuLnkgTypeCd: z.enum([ - MENU_TYPE.EMPTY, - MENU_TYPE.CONTENTS, - MENU_TYPE.BOARD, - MENU_TYPE.INSIDE, - MENU_TYPE.OUTSIDE, - MENU_TYPE.BOTTOM, - ]), - menuLnkgSn: z.number().nullish(), - otptYn: z.string().default("Y"), - prntMenuCd: z.number().nullish(), - sortSeq: z.number(), - evlUseYn: z.string().default("Y"), - qrcd: z.string().nullish(), - useYn: z.string().default("Y"), - newWndwYn: z.string().nullish(), - menuLnkgSnNm: z.string().nullish(), - menuPicUserUnqId: z.string().nullish(), - menuPicUserUnqIdNm: z.string().nullish(), - menuPicDsgnYmd: z.string().nullish(), -}); - -export type MenuFormData = z.infer; -export type Menu = Omit & { - menuCd: number; - iconName?: string; -}; - -export interface MenuService { - getMenuInfo: (menuCd: number) => Promise; - createMenu: (data: MenuFormData) => Promise; - updateMenu: ( - menuCd: number, - data: Omit, - ) => Promise; - deleteMenu: (menuCd: number) => Promise; - getTreeMenuList: (siteId: string) => Promise; - updateTreeMenu: (siteId: string, data: TreeNode[]) => Promise; - updateMenuName: (menuCd: number, name: string) => Promise; -} diff --git a/apps/common/features/src/pages/menu/ui/base-menu-form.tsx b/apps/common/features/src/pages/menu/ui/base-menu-form.tsx deleted file mode 100644 index 26ead2bb1..000000000 --- a/apps/common/features/src/pages/menu/ui/base-menu-form.tsx +++ /dev/null @@ -1,124 +0,0 @@ -import { type UseFormReturn } from "react-hook-form"; -import { - Input, - Label, - RadioGroup, - RadioGroupItem, - Table, - TableBody, - TableCell, - TableHead, - TableRow, -} from "@common/components"; -import { FormItem, FormControl, FormField } from "@/shared/ui/form"; -import { MENU_TYPE, type MenuFormData } from "../model/menu-interface"; - -interface BaseMenuFormProps { - form: UseFormReturn; - menuCd?: number; -} - -function BaseMenuForm({ form, menuCd }: BaseMenuFormProps) { - return ( - - - - - - - {menuCd && ( - - 메뉴ID - - - {menuCd} - - - - )} - - 메뉴명 - - ( - - - - - - )} - /> - - - - 메뉴 영문명 - - ( - - - - - - )} - /> - - - - 메뉴 유형 - - ( - - - - - {Object.entries(MENU_TYPE).map(([key, value]) => ( - - - - {key === "EMPTY" && "빈메뉴"} - {key === "CONTENTS" && "콘텐츠"} - {key === "BOARD" && "게시판"} - {key === "INSIDE" && "내부링크"} - {key === "OUTSIDE" && "외부링크"} - {key === "BOTTOM" && "하단메뉴"} - - - ))} - - - - - )} - /> - - - - - ); -} - -export { BaseMenuForm }; diff --git a/apps/common/features/src/pages/menu/ui/board-search-dialog.tsx b/apps/common/features/src/pages/menu/ui/board-search-dialog.tsx deleted file mode 100644 index 531e17c86..000000000 --- a/apps/common/features/src/pages/menu/ui/board-search-dialog.tsx +++ /dev/null @@ -1,95 +0,0 @@ -import { useEffect, useState } from "react"; -import { useQuery } from "@tanstack/react-query"; -import { - getCoreRowModel, - useReactTable, - type RowSelectionState, -} from "@tanstack/react-table"; -import { - Button, - DataTable, - Dialog, - DialogTrigger, - DialogContent, - DialogHeader, - DialogTitle, - Input, -} from "@common/components/ui"; -import { ENDPOINT } from "@/shared/config"; -import { BoardApi, type BoardPopup } from "@/entities/board"; -import { columns } from "../model/board-table-columns"; - -type BoardSearchDialogProps = { - handleBoardSelect: (board: BoardPopup) => void; -} & React.ComponentProps; - -function BoardSearchDialog({ - handleBoardSelect, - ...props -}: BoardSearchDialogProps) { - const [rowSelection, setRowSelection] = useState({}); - const [bbsNm, setBbsNm] = useState(""); - - const { data: boardList, refetch } = useQuery({ - queryKey: [ENDPOINT.BOARD_SERVICE.BBS], - queryFn: () => BoardApi.getBoardPopupList({ bbsNm }), - enabled: false, - }); - - const table = useReactTable({ - data: boardList?.content ?? [], - columns, - state: { - rowSelection, - }, - enableMultiRowSelection: false, - getCoreRowModel: getCoreRowModel(), - onRowSelectionChange: setRowSelection, - }); - - useEffect(() => { - const boardData = table.getSelectedRowModel().rows[0] - ?.original as BoardPopup; - if (!boardData) return; - handleBoardSelect(boardData); - }, [table.getSelectedRowModel().rows]); - - useEffect(() => { - refetch(); - }, []); - - return ( - - - - - 조회 - - - - - 게시판 조회 - - - - setBbsNm(e.target.value)} - /> - refetch()}> - - 조회 - - - - - - - - - ); -} - -export { BoardSearchDialog }; diff --git a/apps/common/features/src/pages/menu/ui/content-search-dialog.tsx b/apps/common/features/src/pages/menu/ui/content-search-dialog.tsx deleted file mode 100644 index 73e9b7263..000000000 --- a/apps/common/features/src/pages/menu/ui/content-search-dialog.tsx +++ /dev/null @@ -1,95 +0,0 @@ -import { useEffect, useState } from "react"; -import { useQuery } from "@tanstack/react-query"; -import { - getCoreRowModel, - useReactTable, - type RowSelectionState, -} from "@tanstack/react-table"; -import { - Button, - DataTable, - Dialog, - DialogTrigger, - DialogContent, - DialogHeader, - DialogTitle, - Input, -} from "@common/components/ui"; -import { ENDPOINT } from "@/shared/config"; -import { ContentsApi, type Contents } from "@/entities/content"; -import { columns } from "../model/contents-table-colums"; - -type ContentSearchDialogProps = { - handleContentSelect: (content: Contents) => void; -} & React.ComponentProps; - -function ContentSearchDialog({ - handleContentSelect, - ...props -}: ContentSearchDialogProps) { - const [rowSelection, setRowSelection] = useState({}); - const [contsTtl, setContsTtl] = useState(""); - - const { data: contentList, refetch } = useQuery({ - queryKey: [ENDPOINT.CMS_SERVICE.CONTENTS], - queryFn: () => ContentsApi.getContentsList({ contsTtl }), - enabled: false, - }); - - const table = useReactTable({ - data: contentList?.content ?? [], - columns, - state: { - rowSelection, - }, - enableMultiRowSelection: false, - getCoreRowModel: getCoreRowModel(), - onRowSelectionChange: setRowSelection, - }); - - useEffect(() => { - const contentData = table.getSelectedRowModel().rows[0] - ?.original as Contents; - if (!contentData) return; - handleContentSelect(contentData); - }, [table.getSelectedRowModel().rows]); - - useEffect(() => { - refetch(); - }, []); - - return ( - - - - - 조회 - - - - - 콘텐츠 조회 - - - - setContsTtl(e.target.value)} - /> - refetch()}> - - 조회 - - - - - - - - - ); -} - -export { ContentSearchDialog }; diff --git a/apps/common/features/src/pages/menu/ui/editable-node.tsx b/apps/common/features/src/pages/menu/ui/editable-node.tsx deleted file mode 100644 index 9ada835e2..000000000 --- a/apps/common/features/src/pages/menu/ui/editable-node.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import type { ComponentProps } from "react"; -import { Button, Input } from "@common/components/ui"; - -type EditableNodeProps = { - depth?: number; - onSave: () => void; - onCancel: () => void; -} & ComponentProps; - -function EditableNode({ - depth, - onSave, - onCancel, - ...props -}: EditableNodeProps) { - return ( - - - - - - - - - - - - ); -} - -export { EditableNode }; diff --git a/apps/common/features/src/pages/menu/ui/menu-form.tsx b/apps/common/features/src/pages/menu/ui/menu-form.tsx deleted file mode 100644 index 0a45214eb..000000000 --- a/apps/common/features/src/pages/menu/ui/menu-form.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import type { UseFormReturn } from "react-hook-form"; -import { useConfirm } from "@frontend-opensource/use-react-hooks"; -import { Button } from "@common/components"; -import { Form } from "@/shared/ui/form"; -import { ManagementAuthorityDialog } from "@/entities/menu-authority"; -import type { MenuFormData } from "../model/menu-interface"; -import { BaseMenuForm } from "./base-menu-form"; -import { TypeMenuForm } from "./type-menu-form"; - -interface MenuFormProps { - form: UseFormReturn; - menuCd?: number; - handleCreate: (data: MenuFormData) => void; - handleUpdate: ({ - menuCd, - data, - }: { - menuCd: number; - data: Omit; - }) => void; - handleDelete?: (menuCd: number) => void; -} - -function MenuForm({ - form, - menuCd, - handleCreate, - handleUpdate, - handleDelete, -}: MenuFormProps) { - const isModifyMode = !!menuCd; - - const { confirm } = useConfirm(); - - const onSubmit = async (data: MenuFormData) => { - if (isModifyMode) { - await onUpdate(data); - } else { - await onCreate(data); - } - }; - - const onCreate = async (data: MenuFormData) => { - if (await confirm("저장하시겠습니까?")) { - handleCreate(data); - } - }; - - const onUpdate = async (data: MenuFormData) => { - if (await confirm("수정하시겠습니까?")) { - handleUpdate({ menuCd: menuCd!, data }); - } - }; - - const onDelete = async () => { - if (await confirm("삭제하시겠습니까?")) { - handleDelete?.(menuCd!); - } - }; - - const menuType = form.watch("menuLnkgTypeCd"); - const isSubmitDisabled = !form.watch("siteId"); - - return ( - - - - - - {isModifyMode ? "메뉴 수정" : "메뉴 등록"} - - - - - - - - - - - 저장 - - {isModifyMode && ( - - 삭제 - - )} - - - - ); -} - -export { MenuForm }; diff --git a/apps/common/features/src/pages/menu/ui/menu-node.tsx b/apps/common/features/src/pages/menu/ui/menu-node.tsx deleted file mode 100644 index 1cde10b99..000000000 --- a/apps/common/features/src/pages/menu/ui/menu-node.tsx +++ /dev/null @@ -1,115 +0,0 @@ -import { useEffect, useState } from "react"; -import { useMutation, useQueryClient } from "@tanstack/react-query"; -import type { NodeModel, TreeMethods } from "@minoru/react-dnd-treeview"; -import { Button } from "@common/components/ui"; -import { cn } from "@common/components/lib"; -import { ENDPOINT } from "@/shared/config"; -import { MenuApi } from "../api/menu-service"; -import type { TreeMenu } from "../model/menu-interface"; -import { EditableNode } from "./editable-node"; - -type MenuNodeProps = { - node: NodeModel; - depth: number; - isOpen: boolean; - isSelected: boolean; - isAdditionalParent: boolean; - treeMethods: TreeMethods | null; - onToggle: (id: NodeModel["id"]) => void; - onSelect: (node: NodeModel) => void; -}; - -function MenuNode({ - node, - depth, - isOpen, - isSelected, - isAdditionalParent, - treeMethods, - onToggle, - onSelect, -}: MenuNodeProps) { - const { id, text, data } = node; - const indent = depth * 10 + 5; - - const queryClient = useQueryClient(); - - const { mutateAsync: updateMenuName } = useMutation({ - mutationFn: (nodeName: string) => { - const encodedName = encodeURIComponent(nodeName); - return MenuApi.updateMenuName(id as number, encodedName); - }, - onSuccess: () => { - queryClient.invalidateQueries({ - queryKey: [ENDPOINT.CMS_SERVICE.MENUS], - }); - }, - }); - - const [editMode, setEditMode] = useState(false); - const [nodeName, setNodeName] = useState(text); - - const handleToggle = () => onToggle(id); - const handleSelect = () => onSelect(node); - - const handleSave = async () => { - await updateMenuName(nodeName); - setEditMode(false); - }; - - const handleCancel = () => { - setEditMode(false); - setNodeName(text); - }; - - useEffect(() => { - if (isAdditionalParent) treeMethods?.open(id); - }, [isAdditionalParent]); - - return ( - - {editMode ? ( - setNodeName(e.target.value)} - onSave={handleSave} - onCancel={handleCancel} - /> - ) : ( - <> - - {data?.children?.length && ( - { - e.stopPropagation(); - handleToggle(); - }} - /> - )} - {text} - - setEditMode(true)} - > - - - > - )} - - ); -} - -export { MenuNode }; diff --git a/apps/common/features/src/pages/menu/ui/menu-page.tsx b/apps/common/features/src/pages/menu/ui/menu-page.tsx deleted file mode 100644 index a54a42563..000000000 --- a/apps/common/features/src/pages/menu/ui/menu-page.tsx +++ /dev/null @@ -1,256 +0,0 @@ -"use client"; - -import { useState } from "react"; -import { useForm } from "react-hook-form"; -import { zodResolver } from "@hookform/resolvers/zod"; -import type { NodeModel } from "@minoru/react-dnd-treeview"; -import { useConfirm } from "@frontend-opensource/use-react-hooks"; -import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; -import { - Button, - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@common/components/ui"; -import { SiteApi } from "@/entities/site"; -import { ENDPOINT } from "@/shared/config"; -import { useAlertStore } from "@/shared/lib/use-alert-store"; -import type { TreeMenu } from "../model/menu-interface"; -import { MenuApi } from "../api/menu-service"; -import { - MENU_TYPE, - menuFormSchema, - type MenuFormData, -} from "../model/menu-interface"; -import { MenuTree } from "./menu-tree"; -import { MenuForm } from "./menu-form"; -import { generateMenuTreeData } from "../lib/utils"; - -function MenuPage() { - const queryClient = useQueryClient(); - const { setMessage: alert } = useAlertStore((state) => state); - const { confirm } = useConfirm(); - - const [siteId, setSiteId] = useState(""); - const [selectedMenu, setSelectedMenu] = useState(null); - const [additionalNode, setAdditionalNode] = - useState | null>(null); - - const { data: menuInfo } = useQuery({ - queryKey: [ENDPOINT.CMS_SERVICE.MENUS, selectedMenu?.menuCd], - queryFn: () => MenuApi.getMenuInfo(selectedMenu?.menuCd ?? 0), - enabled: !!selectedMenu?.menuCd, - }); - - const { data: menuTreeData } = useQuery({ - queryKey: [ENDPOINT.CMS_SERVICE.MENUS, siteId], - queryFn: () => MenuApi.getTreeMenuList(siteId), - select: (data) => { - const treeData = generateMenuTreeData(data); - const defaultSortSeq = - treeData.filter((node) => node.data?.levelNo === 1).length + 1; - form.setValue("sortSeq", defaultSortSeq); - - return treeData; - }, - enabled: !!siteId, - }); - - const { data: options } = useQuery({ - queryKey: [ENDPOINT.CMS_SERVICE.SITES], - queryFn: () => SiteApi.getSiteList(), - select: (data) => - data.content.map((site) => ({ - label: site.siteNm, - value: site.siteId, - })), - }); - - const { mutateAsync: createMenu } = useMutation({ - mutationFn: (data: MenuFormData) => MenuApi.createMenu(data), - onSuccess: () => { - alert("저장되었습니다."); - queryClient.invalidateQueries({ - queryKey: [ENDPOINT.CMS_SERVICE.MENUS, siteId], - }); - }, - }); - - const { mutate: updateMenu } = useMutation({ - mutationFn: ({ - menuCd, - data, - }: { - menuCd: number; - data: Omit; - }) => MenuApi.updateMenu(menuCd, data), - onSuccess: () => { - alert("수정되었습니다."); - queryClient.invalidateQueries({ - queryKey: [ENDPOINT.CMS_SERVICE.MENUS, siteId], - }); - }, - }); - - const { mutate: deleteMenu } = useMutation({ - mutationFn: (menuCd: number) => MenuApi.deleteMenu(menuCd), - onSuccess: () => { - alert("삭제되었습니다."); - queryClient.invalidateQueries({ - queryKey: [ENDPOINT.CMS_SERVICE.MENUS, siteId], - }); - }, - }); - - const form = useForm({ - resolver: zodResolver(menuFormSchema), - defaultValues: { - menuLnkgTypeCd: MENU_TYPE.EMPTY, - }, - values: { - ...menuInfo, - siteId, - levelNo: menuInfo?.levelNo ?? 1, - sortSeq: menuInfo?.sortSeq ?? 1, - menuKornNm: menuInfo?.menuKornNm ?? "", - menuLnkgTypeCd: menuInfo?.menuLnkgTypeCd ?? MENU_TYPE.EMPTY, - iconNm: menuInfo?.iconName, - otptYn: menuInfo?.otptYn ?? "Y", - evlUseYn: menuInfo?.evlUseYn ?? "Y", - useYn: menuInfo?.useYn ?? "Y", - }, - }); - - const additionalNodeForm = useForm({ - resolver: zodResolver(menuFormSchema), - defaultValues: { - menuLnkgTypeCd: MENU_TYPE.EMPTY, - levelNo: 1, - sortSeq: 1, - otptYn: "Y", - evlUseYn: "Y", - useYn: "Y", - }, - }); - - const handleAdditionalNodeSave = async () => { - const prntMenuCd = !additionalNode?.parent - ? null - : (additionalNode?.parent as number); - - await createMenu({ - ...additionalNodeForm.getValues(), - prntMenuCd, - }); - resetAdditionalNode(); - }; - - const resetAdditionalNode = () => { - additionalNodeForm.setValue("menuKornNm", ""); - setAdditionalNode(null); - }; - - const handleSiteIdChange = (value: string) => { - form.setValue("siteId", value); - additionalNodeForm.setValue("siteId", value); - - setSiteId(value); - setSelectedMenu(null); - }; - - const handleNodeSelect = (node: NodeModel) => { - if (!node.data) return; - if (node.data.menuCd === selectedMenu?.menuCd) { - setSelectedMenu(null); - additionalNodeForm.setValue("levelNo", 1); - return; - } - setSelectedMenu(node.data); - additionalNodeForm.setValue("levelNo", (node.data.levelNo ?? 0) + 1); - }; - - const handleNodeDelete = async () => { - if (await confirm("삭제하시겠습니까?")) { - deleteMenu(selectedMenu!.menuCd!); - } - }; - - const handleAddMenu = () => { - if (additionalNode) return; - setAdditionalNode({ - id: -1, - parent: selectedMenu?.menuCd ?? 0, - text: "", - }); - }; - - const tree = additionalNode - ? [...(additionalNode ? [additionalNode] : []), ...(menuTreeData ?? [])] - : (menuTreeData ?? []); - - return ( - - - - - - - - {options?.map((option) => ( - - {option.label} - - ))} - - - {siteId && ( - <> - - - 메뉴 추가 - - - 메뉴 삭제 - - - - - - > - )} - - - - - - ); -} - -export { MenuPage }; diff --git a/apps/common/features/src/pages/menu/ui/menu-tree.tsx b/apps/common/features/src/pages/menu/ui/menu-tree.tsx deleted file mode 100644 index 45a4cbb37..000000000 --- a/apps/common/features/src/pages/menu/ui/menu-tree.tsx +++ /dev/null @@ -1,151 +0,0 @@ -"use client"; - -import { useRef } from "react"; -import { DndProvider } from "react-dnd"; -import type { UseFormReturn } from "react-hook-form"; -import { useMutation, useQueryClient } from "@tanstack/react-query"; -import { - Tree, - MultiBackend, - getBackendOptions, - type NodeModel, - type TreeMethods, -} from "@minoru/react-dnd-treeview"; -import { Button } from "@common/components/ui"; -import { ENDPOINT } from "@/shared/config"; -import { MenuApi } from "../api/menu-service"; -import type { MenuFormData, TreeMenu, TreeNode } from "../model/menu-interface"; -import { handleDropNode, handleSelectNode } from "../lib/utils"; -import { MenuNode } from "./menu-node"; -import { EditableNode } from "./editable-node"; - -type MenuTreeProps = { - tree: NodeModel[]; - siteId: string; - selectedMenu: TreeMenu | null; - additionalNodeForm: UseFormReturn; - onSelectNode: (node: NodeModel) => void; - onAdditionalNodeSave: () => void; - onAdditionalNodeCancel: () => void; -}; - -function MenuTree({ - tree, - siteId, - selectedMenu, - additionalNodeForm, - onSelectNode, - onAdditionalNodeSave, - onAdditionalNodeCancel, -}: MenuTreeProps) { - const queryClient = useQueryClient(); - const treeMethods = useRef(null); - - const { mutate: updateTreeMenu } = useMutation({ - mutationFn: (newTree: TreeNode) => - MenuApi.updateTreeMenu(siteId, [newTree]), - onSuccess: () => { - queryClient.invalidateQueries({ - queryKey: [ENDPOINT.CMS_SERVICE.MENUS], - }); - }, - }); - - const additionalNode = tree.find((n) => n.id === -1); - - const isAdditionalParent = (node: NodeModel): boolean => { - if (!additionalNode) return false; - return additionalNode?.parent === node.id; - }; - - return ( - <> - - - , - { depth, isOpen, onToggle }, - ) => { - if (node.id === -1) { - return ( - - additionalNodeForm.setValue("menuKornNm", e.target.value) - } - depth={depth} - onSave={onAdditionalNodeSave} - onCancel={onAdditionalNodeCancel} - /> - ); - } - return ( - - ); - }} - onDrop={handleDropNode(updateTreeMenu)} - classes={{ - root: "p-4", - container: "flex flex-col", - dropTarget: "!bg-[rgba(32,148,250,0.5)] rounded-sm", - placeholder: "relative", - }} - sort={false} - dropTargetOffset={5} - insertDroppableFirst={false} - canDrop={(_, { dragSource, dropTargetId }) => { - if (dragSource?.parent === dropTargetId) { - return true; - } - }} - placeholderRender={(_, { depth }) => { - return ( - - ); - }} - /> - - - - { - treeMethods.current?.openAll(); - }} - > - - 전체펼침 - - { - treeMethods.current?.closeAll(); - }} - > - - 전체접기 - - - > - ); -} - -export { MenuTree }; diff --git a/apps/common/features/src/pages/menu/ui/organization-search-dialog.tsx b/apps/common/features/src/pages/menu/ui/organization-search-dialog.tsx deleted file mode 100644 index 14eccbf98..000000000 --- a/apps/common/features/src/pages/menu/ui/organization-search-dialog.tsx +++ /dev/null @@ -1,187 +0,0 @@ -"use client"; - -import { useEffect, useState } from "react"; -import { useQuery } from "@tanstack/react-query"; -import { DndProvider } from "react-dnd"; -import { - Tree, - MultiBackend, - getBackendOptions, - type NodeModel, -} from "@minoru/react-dnd-treeview"; -import { - Button, - Dialog, - DialogContent, - DialogHeader, - DialogTitle, - DialogTrigger, - Input, -} from "@common/components/ui"; -import { cn } from "@common/components/lib"; -import { - UserOrganizationApi, - type TreeUserOrganization, - type TreeUser, -} from "@/entities/user-organization"; -import { ENDPOINT } from "@/shared/config"; -import { - filterUserOrganizationTreeDataByKeyword, - generateUserOrganizationTreeData, -} from "../lib/utils"; - -type TreeModel = TreeUserOrganization | TreeUser; - -interface OrganizationSearchDialogProps { - handleUserSelect: (user: TreeUser) => void; -} - -function OrganizationSearchDialog({ - handleUserSelect, -}: OrganizationSearchDialogProps) { - const [keyword, setKeyword] = useState(""); - const [tree, setTree] = useState[]>([]); - const [selectedNode, setSelectedNode] = useState | null>( - null, - ); - - const { data: treeData } = useQuery({ - queryKey: [ENDPOINT.CMS_SERVICE.USER_ORGANIZATION], - queryFn: () => UserOrganizationApi.getUserOrganizationTreeList(), - select: (data) => generateUserOrganizationTreeData(data), - }); - - const handleSelectNode = (node: NodeModel) => { - setSelectedNode(node); - handleUserSelect(node.data as TreeUser); - }; - - useEffect(() => { - setTree(treeData ?? []); - }, [treeData]); - - return ( - - - - - 조회 - - - - - 담당자 조회 - - - - setKeyword(e.target.value)} - /> - - setTree(filterUserOrganizationTreeDataByKeyword(tree, keyword)) - } - > - - 조회 - - - - - , - { depth, isOpen, onToggle }, - ) => ( - - )} - classes={{ - container: "flex flex-col", - }} - sort={false} - onDrop={() => {}} - /> - - - - - - ); -} - -interface TreeNodeProps { - node: NodeModel; - depth: number; - isOpen: boolean; - selectedNode: NodeModel | null; - onToggle: (id: NodeModel["id"]) => void; - onSelect: (node: NodeModel) => void; -} - -const ORGANIZATION_PREFIX = "OGNZ"; - -function TreeNode({ - node, - depth, - isOpen, - selectedNode, - onToggle, - onSelect, -}: TreeNodeProps) { - const { id, text, data } = node; - const indent = depth * 15 + 5; - - const handleToggle = () => onToggle(id); - const handleSelect = () => { - if (id.toString().startsWith(ORGANIZATION_PREFIX)) return; - onSelect(node); - }; - - const isSelected = selectedNode?.id === id; - - return ( - - - - {data && ("children" in data || "users" in data) && ( - { - e.stopPropagation(); - handleToggle(); - }} - > - - - )} - {text} - - {isSelected && } - - - ); -} - -export { OrganizationSearchDialog }; diff --git a/apps/common/features/src/pages/menu/ui/type-menu-form.tsx b/apps/common/features/src/pages/menu/ui/type-menu-form.tsx deleted file mode 100644 index 1f0483d1b..000000000 --- a/apps/common/features/src/pages/menu/ui/type-menu-form.tsx +++ /dev/null @@ -1,368 +0,0 @@ -import dayjs from "dayjs"; -import { type UseFormReturn } from "react-hook-form"; -import { - DatePicker, - IconFinder, - Input, - Label, - RadioGroup, - RadioGroupItem, - Table, - TableBody, - TableCell, - TableHead, - TableRow, -} from "@common/components"; -import type { BoardPopup } from "@/entities/board"; -import type { Contents } from "@/entities/content"; -import type { TreeUser } from "@/entities/user-organization"; -import { FormItem, FormControl, FormField } from "@/shared/ui/form"; -import type { MenuFormData, MenuType } from "../model/menu-interface"; -import { OrganizationSearchDialog } from "./organization-search-dialog"; -import { ContentSearchDialog } from "./content-search-dialog"; -import { BoardSearchDialog } from "./board-search-dialog"; - -type TypeMenuFormProps = { - form: UseFormReturn; - menuType: MenuType; -}; - -function TypeMenuForm({ form, menuType }: TypeMenuFormProps) { - const handleIconClick = (icon: string) => { - form.setValue("iconNm", icon); - }; - - const handleContentSelect = (content: Contents) => { - form.setValue("menuLnkgSn", content.contsSn); - form.setValue("menuLnkgSnNm", content.contsTtl); - }; - - const handleUserSelect = (user: TreeUser) => { - form.setValue("menuPicUserUnqId", user.userUnqId); - form.setValue("menuPicUserUnqIdNm", user.userNm); - }; - - const handleBoardSelect = (board: BoardPopup) => { - form.setValue("menuLnkgSn", board.bbsId); - form.setValue("menuLnkgSnNm", board.bbsNm); - }; - - const handleDateSelect = (date: Date | undefined) => { - if (!date) return; - form.setValue("menuPicDsgnYmd", dayjs(date).format("YYYYMMDD")); - }; - - return ( - - - - - - - {menuType === "CONTENTS" && ( - - 콘텐츠 - - - - {!form.watch("menuLnkgSnNm") && - "메뉴에 표시될 콘텐츠를 조회 후 선택하십시오."} - {form.watch("menuLnkgSnNm")} - - - - - - )} - {menuType === "BOARD" && ( - - 게시판 - - - - {!form.watch("menuLnkgSnNm") && - "메뉴에 표시될 게시판을 조회 후 선택하십시오."} - {form.watch("menuLnkgSnNm")} - - - - - - )} - {(menuType === "INSIDE" || menuType === "OUTSIDE") && ( - - 연결 URL - - ( - - - - - - )} - /> - - - )} - {(menuType === "CONTENTS" || - menuType === "BOARD" || - menuType === "INSIDE" || - menuType === "OUTSIDE") && ( - - 연결 형태 - - ( - - - - - - - 새창 - - - - 현재창 - - - - - - )} - /> - - - )} - {(menuType === "CONTENTS" || menuType === "BOARD") && ( - - 메뉴 평가 사용 여부 - - ( - - - - - - - 사용 - - - - 사용안함 - - - - - - )} - /> - - - )} - - {/* 메뉴 유형 공통 폼 S */} - - 메뉴 서브명 - - ( - - - - - - )} - /> - - - - 메뉴 아이콘 - - - - {!form.watch("iconNm") && "아이콘을 조회 후 선택하십시오."} - {form.watch("iconNm")} - - - - - - - 출력 여부 - - ( - - - - - - - 출력 - - - - 출력안함 - - - - - - )} - /> - - - - 사용 여부 - - ( - - - - - - - 사용 - - - - 사용안함 - - - - - - )} - /> - - - {/* 메뉴 유형 공통 폼 E */} - - {(menuType === "CONTENTS" || menuType === "BOARD") && ( - <> - - QR코드 - - ( - - - - - - - 사용 - - - - 사용안함 - - - - - - )} - /> - - - - 담당자 - - - - - {!form.watch("menuPicUserUnqIdNm") && - "메뉴 담당자를 조회 후 선택하십시오."} - {form.watch("menuPicUserUnqIdNm")} - - - - - 지정일: - ( - - - { - field.onChange(date); - handleDateSelect(date); - }} - /> - - - )} - /> - - - - - > - )} - - - ); -} - -export { TypeMenuForm }; diff --git a/apps/common/features/src/pages/module/api/authorization-service.ts b/apps/common/features/src/pages/module/api/authorization-service.ts deleted file mode 100644 index d68c6920a..000000000 --- a/apps/common/features/src/pages/module/api/authorization-service.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { client } from "@/shared/api"; -import { ENDPOINT } from "@/shared/config"; -import type { - AuthorizationsService, - AuthorizationsParams, - AuthorizationsList, - AuthorizationFormData, - WithRoleAuthorizations, -} from "../model/authorizations-interface"; - -export class AuthorizationsApiService implements AuthorizationsService { - public async getAuthorizationsList( - params: AuthorizationsParams, - ): Promise { - const response = await client.get( - `${ENDPOINT.USER_SERVICE.AUTHORIZATIONS}`, - { params }, - ); - return response.data; - } - - public async getAuthorizationInfo( - authorizationNo: string, - ): Promise { - const response = await client.get( - `${ENDPOINT.USER_SERVICE.AUTHORIZATIONS}/${authorizationNo}`, - ); - return response.data; - } - - public async createAuthorization( - data: AuthorizationFormData, - ): Promise { - const response = await client.post( - `${ENDPOINT.USER_SERVICE.AUTHORIZATIONS}`, - data, - ); - return response.data; - } - - public async updateAuthorization( - authorizationNo: string, - data: AuthorizationFormData, - ): Promise { - const response = await client.put( - `${ENDPOINT.USER_SERVICE.AUTHORIZATIONS}/${authorizationNo}`, - data, - ); - return response.data; - } -} - -export const AuthorizationsApi = new AuthorizationsApiService(); diff --git a/apps/common/features/src/pages/module/api/role-authorization-service.ts b/apps/common/features/src/pages/module/api/role-authorization-service.ts deleted file mode 100644 index 92f2c8eb9..000000000 --- a/apps/common/features/src/pages/module/api/role-authorization-service.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { client } from "@/shared/api"; -import { ENDPOINT } from "@/shared/config"; -import type { - RoleAuthorizationsService, - RoleAuthorizationsParams, - RoleAuthorizationsList, - RoleAuthorizationsData, - RoleAuthorizations, -} from "../model/role-authorizations-interface"; - -export class RoleAuthorizationsApiService implements RoleAuthorizationsService { - public async getRoleAuthorizationsList( - params: RoleAuthorizationsParams, - ): Promise { - const response = await client.get( - `${ENDPOINT.USER_SERVICE.ROLE_AUTHORIZATIONS}/api`, - { params }, - ); - return response.data; - } - - public async createRoleAuthorizations( - data: RoleAuthorizationsData, - ): Promise { - const response = await client.post( - `${ENDPOINT.USER_SERVICE.ROLE_AUTHORIZATIONS}`, - data, - ); - return response.data; - } - - public async updateRoleAuthorizations( - data: RoleAuthorizationsData, - ): Promise { - const response = await client.put( - `${ENDPOINT.USER_SERVICE.ROLE_AUTHORIZATIONS}`, - data, - ); - return response.data; - } -} - -export const RoleAuthorizationsApi = new RoleAuthorizationsApiService(); diff --git a/apps/common/features/src/pages/module/index.ts b/apps/common/features/src/pages/module/index.ts deleted file mode 100644 index fffbd617b..000000000 --- a/apps/common/features/src/pages/module/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { ModulePage } from "./ui/module-page"; -export { CreatePage } from "./ui/create-page"; -export { ModifyPage } from "./ui/modify-page"; diff --git a/apps/common/features/src/pages/module/model/authorizations-interface.ts b/apps/common/features/src/pages/module/model/authorizations-interface.ts deleted file mode 100644 index e34d150e0..000000000 --- a/apps/common/features/src/pages/module/model/authorizations-interface.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { z } from "zod"; -import type { PageableList, PageableParams } from "@/shared/model"; - -export interface AuthorizationsParams extends PageableParams { - keywordType?: string; - keyword?: string; - authorizationName?: string; - httpMethodCode?: string; - useYn?: string; -} - -export interface Authorizations { - authorizationNo: number; - authorizationName: string; - urlPatternValue: string; - httpMethodCode: string; - sortSeq: number; - useYn: string; - mdfr: string; - mdfcnDt: string; -} - -export interface RoleAuthorizations { - roleId: string; - authorizationNo: number; - authorizationName: string; - urlPatternValue: string; - httpMethodCode: string; - sortSeq: number; - createdAt: string; -} - -export interface WithRoleAuthorizations extends Authorizations { - roleAuthorizations: RoleAuthorizations[]; -} - -export const authorizationFormSchema = z.object({ - authorizationName: z.string(), - urlPatternValue: z.string(), - httpMethodCode: z.string(), - sortSeq: z.number(), - useYn: z.string(), -}); - -export type AuthorizationsList = PageableList; -export type AuthorizationFormData = z.infer; - -export interface AuthorizationsService { - getAuthorizationsList: ( - params: AuthorizationsParams, - ) => Promise; - getAuthorizationInfo: ( - authorizationNo: string, - ) => Promise; - createAuthorization: ( - data: AuthorizationFormData, - ) => Promise; - updateAuthorization: ( - authorizationNo: string, - data: AuthorizationFormData, - ) => Promise; -} diff --git a/apps/common/features/src/pages/module/model/authorizations-table-columns.tsx b/apps/common/features/src/pages/module/model/authorizations-table-columns.tsx deleted file mode 100644 index a2ccb2a88..000000000 --- a/apps/common/features/src/pages/module/model/authorizations-table-columns.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import dayjs from "dayjs"; -import { type ColumnDef } from "@tanstack/react-table"; -import type { Authorizations } from "./authorizations-interface"; -import { ManagementAuthorityDialog } from "../ui/management-authority-dialog"; - -export const columns: ColumnDef[] = [ - { - id: "no", - header: "NO", - cell: ({ row }) => row.index + 1, - }, - { - accessorKey: "authorizationName", - header: "API명", - }, - { - accessorKey: "urlPatternValue", - header: "API URL", - }, - { - accessorKey: "httpMethodCode", - header: "요청 메소드", - }, - { - accessorKey: "useYn", - header: "사용여부", - }, - { - accessorKey: "mdfr", - header: "최종수정자", - }, - { - accessorKey: "mdfcnDt", - header: "최종수정일", - cell: ({ row }) => { - const date = dayjs(row.original.mdfcnDt); - return date.format("YYYY-MM-DD"); - }, - }, - { - id: "action", - header: "권한관리", - cell: ({ row }) => { - return ( - { - e.stopPropagation(); - }} - > - - - ); - }, - }, -]; diff --git a/apps/common/features/src/pages/module/model/role-authorizations-interface.ts b/apps/common/features/src/pages/module/model/role-authorizations-interface.ts deleted file mode 100644 index a2352346e..000000000 --- a/apps/common/features/src/pages/module/model/role-authorizations-interface.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { z } from "zod"; -import type { PageableList, PageableParams } from "@/shared/model"; - -export interface RoleAuthorizationsParams extends PageableParams { - keywordType?: string; - keyword?: string; - authorizationNo?: string; -} - -export interface RoleAuthorizations { - roleId: string; - roleName: string; - roleContent: string; - useYn: string; - authorizationNo: number; - authYn: string; -} - -export const roleAuthorizationsSchema = z.object({ - roleId: z.string(), - authorizationNo: z.number(), - authYn: z.string(), -}); - -export const roleAuthorizationsFormSchema = z.object({ - roleAuthorizations: z.array(roleAuthorizationsSchema), -}); - -export type RoleAuthorizationsList = PageableList; -export type RoleAuthorizationsData = z.infer[]; -export type RoleAuthorizationsFormData = z.infer< - typeof roleAuthorizationsFormSchema ->; - -export interface RoleAuthorizationsService { - getRoleAuthorizationsList: ( - params: RoleAuthorizationsParams, - ) => Promise; - createRoleAuthorizations: ( - data: RoleAuthorizationsData, - ) => Promise; - updateRoleAuthorizations: ( - data: RoleAuthorizationsData, - ) => Promise; -} diff --git a/apps/common/features/src/pages/module/model/role-authorizations-table-columns.tsx b/apps/common/features/src/pages/module/model/role-authorizations-table-columns.tsx deleted file mode 100644 index c5517824b..000000000 --- a/apps/common/features/src/pages/module/model/role-authorizations-table-columns.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import type { ColumnDef } from "@tanstack/react-table"; -import type { UseFormReturn } from "react-hook-form"; -import { Checkbox } from "@common/components/ui"; -import type { RoleAuthorizations } from "./role-authorizations-interface"; -import type { RoleAuthorizationsFormData } from "./role-authorizations-interface"; - -export const createColumns = ( - form: UseFormReturn, -): ColumnDef[] => [ - { - id: "select", - header: ({ table }) => ( - role.authYn === "Y")} - onCheckedChange={(checked) => { - table.getRowModel().rows.forEach((row, index) => { - form.setValue( - `roleAuthorizations.${index}.authYn`, - checked ? "Y" : "N", - ); - }); - }} - className="h-4 w-4 rounded border-gray-300" - /> - ), - cell: ({ row }) => ( - { - form.setValue( - `roleAuthorizations.${row.index}.authYn`, - checked ? "Y" : "N", - ); - }} - className="h-4 w-4 rounded border-gray-300" - /> - ), - }, - { - accessorKey: "roleId", - header: "권한ID", - }, - { - accessorKey: "roleName", - header: "권한명", - }, - { - accessorKey: "roleContent", - header: "권한설명", - }, -]; diff --git a/apps/common/features/src/pages/module/ui/create-page.tsx b/apps/common/features/src/pages/module/ui/create-page.tsx deleted file mode 100644 index 50a78fc4c..000000000 --- a/apps/common/features/src/pages/module/ui/create-page.tsx +++ /dev/null @@ -1,45 +0,0 @@ -"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 { useAlertStore } from "@/shared/lib/use-alert-store"; -import { - authorizationFormSchema, - type AuthorizationFormData, -} from "../model/authorizations-interface"; -import { AuthorizationsApi } from "../api/authorization-service"; -import { ModuleForm } from "./module-form"; - -function CreatePage() { - const router = useRouter(); - const { setMessage: alert } = useAlertStore((state) => state); - - const { mutateAsync: createAuthorization } = useMutation({ - mutationFn: (data: AuthorizationFormData) => - AuthorizationsApi.createAuthorization(data), - onSuccess: () => { - alert("등록되었습니다.", router.back); - }, - }); - - const form = useForm({ - resolver: zodResolver(authorizationFormSchema), - defaultValues: { - sortSeq: 0, - useYn: "Y", - }, - }); - - return ( - - - 모듈API 등록 - - - - ); -} - -export { CreatePage }; diff --git a/apps/common/features/src/pages/module/ui/management-authority-dialog.tsx b/apps/common/features/src/pages/module/ui/management-authority-dialog.tsx deleted file mode 100644 index 66384f79a..000000000 --- a/apps/common/features/src/pages/module/ui/management-authority-dialog.tsx +++ /dev/null @@ -1,148 +0,0 @@ -import { useState } from "react"; -import { useForm } from "react-hook-form"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { useMutation, useQuery } from "@tanstack/react-query"; -import { - getCoreRowModel, - useReactTable, - type RowSelectionState, -} from "@tanstack/react-table"; -import { useConfirm } from "@frontend-opensource/use-react-hooks"; -import { - Button, - DataTable, - Dialog, - DialogClose, - DialogContent, - DialogFooter, - DialogHeader, - DialogTitle, - DialogTrigger, - Table, - TableBody, - TableCell, - TableHead, - TableHeader, - TableRow, -} from "@common/components/ui"; -import { ENDPOINT } from "@/shared/config"; -import { useAlertStore } from "@/shared/lib/use-alert-store"; -import { - roleAuthorizationsFormSchema, - type RoleAuthorizationsFormData, - type RoleAuthorizationsData, -} from "../model/role-authorizations-interface"; -import { createColumns } from "../model/role-authorizations-table-columns"; -import type { Authorizations } from "../model/authorizations-interface"; -import { RoleAuthorizationsApi } from "../api/role-authorization-service"; - -type ManagementAuthorityDialogProps = { - authorizationNo?: string; - rowData?: Authorizations; -} & React.ComponentProps; - -function ManagementAuthorityDialog({ - authorizationNo, - rowData, - ...props -}: ManagementAuthorityDialogProps) { - const { confirm } = useConfirm(); - const { setMessage: alert } = useAlertStore((state) => state); - - const [isOpened, setIsOpened] = useState(false); - const [rowSelection, setRowSelection] = useState({}); - - const { data: roleAuthorizationList } = useQuery({ - queryKey: [ENDPOINT.USER_SERVICE.ROLE_AUTHORIZATIONS, authorizationNo], - queryFn: () => - RoleAuthorizationsApi.getRoleAuthorizationsList({ authorizationNo }), - enabled: !!authorizationNo && isOpened, - }); - - const { mutateAsync: saveRoleAuthorization } = useMutation({ - mutationFn: (data: RoleAuthorizationsData) => - RoleAuthorizationsApi.createRoleAuthorizations(data), - onSuccess: () => { - alert("저장되었습니다."); - }, - }); - - const form = useForm({ - resolver: zodResolver(roleAuthorizationsFormSchema), - values: { - roleAuthorizations: - roleAuthorizationList?.content.map( - ({ roleId, authorizationNo, useYn }) => ({ - roleId, - authorizationNo, - authYn: useYn, - }), - ) ?? [], - }, - }); - - const table = useReactTable({ - data: roleAuthorizationList?.content ?? [], - columns: createColumns(form), - state: { - rowSelection, - }, - getCoreRowModel: getCoreRowModel(), - onRowSelectionChange: setRowSelection, - }); - - const handleSave = async () => { - if (await confirm("저장하시겠습니까?")) { - const data = form.getValues("roleAuthorizations"); - await saveRoleAuthorization(data); - } - }; - - return ( - - - setIsOpened(true)}> - 관리 - - - - - 모듈API 권한관리 - - - - - - API명 - 요청 메소드 - API URL - - - - - - {rowData?.authorizationName} - - {rowData?.httpMethodCode} - {rowData?.urlPatternValue} - - - - - - - - 저장 - - - - 취소 - - - - - - ); -} - -export { ManagementAuthorityDialog }; diff --git a/apps/common/features/src/pages/module/ui/modify-page.tsx b/apps/common/features/src/pages/module/ui/modify-page.tsx deleted file mode 100644 index 8eff7f0a8..000000000 --- a/apps/common/features/src/pages/module/ui/modify-page.tsx +++ /dev/null @@ -1,50 +0,0 @@ -"use client"; - -import { useForm } from "react-hook-form"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { useMutation, useQuery } from "@tanstack/react-query"; -import { useAlertStore } from "@/shared/lib/use-alert-store"; -import { ENDPOINT } from "@/shared/config"; -import { - authorizationFormSchema, - type AuthorizationFormData, -} from "../model/authorizations-interface"; -import { AuthorizationsApi } from "../api/authorization-service"; -import { ModuleForm } from "./module-form"; - -function ModifyPage({ sequential }: { sequential: string }) { - const { setMessage: alert } = useAlertStore((state) => state); - - const { data: authorizationInfo } = useQuery({ - queryKey: [ENDPOINT.USER_SERVICE.AUTHORIZATIONS, sequential], - queryFn: () => AuthorizationsApi.getAuthorizationInfo(sequential), - }); - - const { mutateAsync: updateAuthorization } = useMutation({ - mutationFn: (data: AuthorizationFormData) => - AuthorizationsApi.updateAuthorization(sequential, data), - onSuccess: () => { - alert("수정되었습니다."); - }, - }); - - const form = useForm({ - resolver: zodResolver(authorizationFormSchema), - values: authorizationInfo, - }); - - return ( - - - 모듈API 수정 - - - - ); -} - -export { ModifyPage }; diff --git a/apps/common/features/src/pages/module/ui/module-form.tsx b/apps/common/features/src/pages/module/ui/module-form.tsx deleted file mode 100644 index 4c0fdafbc..000000000 --- a/apps/common/features/src/pages/module/ui/module-form.tsx +++ /dev/null @@ -1,166 +0,0 @@ -"use client"; - -import type { UseFormReturn } from "react-hook-form"; -import { - Input, - Label, - RadioGroup, - RadioGroupItem, - Table, - TableBody, - TableCell, - TableHead, - TableRow, -} from "@common/components"; -import { FormWrapper } from "@/widgets/form-wrapper"; -import { CodeSelect } from "@/entities/code"; -import { FormControl, FormField, FormItem } from "@/shared/ui/form"; -import type { - AuthorizationFormData, - WithRoleAuthorizations, -} from "../model/authorizations-interface"; - -type BannerFormProps = { - form: UseFormReturn; - authorizationNo?: number; - handleSave: (data: AuthorizationFormData) => Promise; - handleDelete?: () => Promise; -}; - -function ModuleForm({ form, authorizationNo, ...props }: BannerFormProps) { - return ( - - - - - - - - - - API명 - - ( - - - - - - )} - /> - - - - API URL - - ( - - - - - - )} - /> - - - - 요청 메소드 - - ( - - - - - - )} - /> - - - - 정렬순서 - - ( - - - { - const value = e.target.value; - if (isNaN(Number(value))) return; - field.onChange(Number(value)); - }} - className="w-[12rem] !text-[1.3rem]" - placeholder="정렬순서 입력" - /> - - - )} - /> - - - - 사용여부 - - ( - - - - - - 사용 - - - - 사용안함 - - - - - )} - /> - - - - - - - ); -} - -export { ModuleForm }; diff --git a/apps/common/features/src/pages/module/ui/module-page.tsx b/apps/common/features/src/pages/module/ui/module-page.tsx deleted file mode 100644 index b6c98cb14..000000000 --- a/apps/common/features/src/pages/module/ui/module-page.tsx +++ /dev/null @@ -1,124 +0,0 @@ -"use client"; - -import { useRouter } from "next/navigation"; -import { useEffect, useState } from "react"; -import { keepPreviousData, useQuery } from "@tanstack/react-query"; -import { - getCoreRowModel, - useReactTable, - type PaginationState, -} from "@tanstack/react-table"; -import { Button, DataTable, Input, Label } from "@common/components"; -import { CodeSelect } from "@/entities/code"; -import { ENDPOINT } from "@/shared/config"; -import type { - Authorizations, - AuthorizationsParams, -} from "../model/authorizations-interface"; -import { columns } from "../model/authorizations-table-columns"; -import { AuthorizationsApi } from "../api/authorization-service"; - -function ModulePage() { - const router = useRouter(); - - const [authorizationQuery, setAuthorizationQuery] = - useState({ - authorizationName: "", - httpMethodCode: "", - }); - const [pagination, setPagination] = useState({ - pageIndex: 0, - pageSize: 10, - }); - - const { data: authorizationsList, refetch } = useQuery({ - queryKey: [ENDPOINT.USER_SERVICE.AUTHORIZATIONS], - queryFn: () => - AuthorizationsApi.getAuthorizationsList({ - ...authorizationQuery, - page: pagination.pageIndex, - size: pagination.pageSize, - }), - placeholderData: keepPreviousData, - enabled: false, - }); - - const table = useReactTable({ - data: authorizationsList?.content ?? [], - columns, - rowCount: authorizationsList?.totalElements ?? 0, - state: { - pagination, - }, - onPaginationChange: setPagination, - getCoreRowModel: getCoreRowModel(), - manualPagination: true, - }); - - const handleAuthorizationQueryChange = - (key: keyof AuthorizationsParams) => (value: string) => { - setAuthorizationQuery({ ...authorizationQuery, [key]: value }); - }; - - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault(); - refetch(); - }; - - const handleRowClick = (row: Authorizations) => { - router.push(`/module/register/${row.authorizationNo}`); - }; - - useEffect(() => { - refetch(); - }, [pagination]); - - return ( - - - - - - - 요청 메소드 - - - - - - API명 - - - handleAuthorizationQueryChange("authorizationName")( - e.target.value, - ) - } - /> - - - - 조회 - - - - - - 모듈API 목록 - router.push("/module/register")}>등록 - - - - - - - ); -} - -export { ModulePage }; diff --git a/apps/common/features/src/pages/notice/index.ts b/apps/common/features/src/pages/notice/index.ts new file mode 100644 index 000000000..1b5281e57 --- /dev/null +++ b/apps/common/features/src/pages/notice/index.ts @@ -0,0 +1 @@ +export { NoticePage } from "./ui/notice-page"; diff --git a/apps/common/features/src/pages/notice/model/notice-table-columns.tsx b/apps/common/features/src/pages/notice/model/notice-table-columns.tsx new file mode 100644 index 000000000..623479a05 --- /dev/null +++ b/apps/common/features/src/pages/notice/model/notice-table-columns.tsx @@ -0,0 +1,32 @@ +import dayjs from "dayjs"; +import type { ColumnDef } from "@tanstack/react-table"; +import type { Post } from "@/entities/post"; + +export const columns: ColumnDef[] = [ + { + id: "no", + header: "NO", + cell: ({ row }) => row.index + 1, + }, + { + accessorKey: "pstTtl", + header: "제목", + }, + { + accessorKey: "inqNocs", + header: "조회수", + }, + { + accessorKey: "atflCd", + header: "첨부파일", + }, + { + accessorKey: "rgtrNm", + header: "작성자", + }, + { + accessorKey: "regDt", + header: "등록일", + cell: ({ row }) => dayjs(row.original.regDt).format("YYYY-MM-DD"), + }, +]; diff --git a/apps/common/features/src/pages/notice/ui/notice-page.tsx b/apps/common/features/src/pages/notice/ui/notice-page.tsx new file mode 100644 index 000000000..8f1cf2128 --- /dev/null +++ b/apps/common/features/src/pages/notice/ui/notice-page.tsx @@ -0,0 +1,172 @@ +"use client"; + +import { PostApi, type Post, type PostParams } from "@/entities/post"; +import { ENDPOINT } from "@/shared/config"; +import { PageLayout } from "@/shared/ui/page-layout"; +import { + Button, + DataTable, + Input, + Label, + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@common/components/ui"; +import { useQuery } from "@tanstack/react-query"; +import { + getCoreRowModel, + useReactTable, + type PaginationState, + type RowSelectionState, +} from "@tanstack/react-table"; +import { useRouter } from "next/navigation"; +import { useEffect, useState } from "react"; +import { columns } from "../model/notice-table-columns"; + +function NoticePage({ bbsId }: { bbsId: string }) { + const router = useRouter(); + + const [rowSelection, setRowSelection] = useState({}); + const [postParams, setPostParams] = useState({ + keywordType: "", + keyword: "", + delYn: "", + }); + const [pagination, setPagination] = useState({ + pageIndex: 0, + pageSize: 10, + }); + + const { data: postList, refetch } = useQuery({ + queryKey: [ENDPOINT.BOARD_SERVICE.POSTS], + queryFn: () => + PostApi.getPostList({ + ...postParams, + bbsId: Number(bbsId), + page: pagination.pageIndex, + size: pagination.pageSize, + }), + enabled: false, + }); + + const table = useReactTable({ + data: postList?.content ?? [], + columns, + state: { + pagination, + rowSelection, + }, + rowCount: postList?.totalElements ?? 0, + manualPagination: true, + enableMultiRowSelection: true, + getCoreRowModel: getCoreRowModel(), + onPaginationChange: setPagination, + onRowSelectionChange: setRowSelection, + }); + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + refetch(); + }; + + const handleRowClick = (row: Post) => { + router.push(`/bbs/register/${row.bbsId}`); + }; + + useEffect(() => { + refetch(); + }, [pagination]); + + return ( + + + + + 상태 + { + setPostParams({ ...postParams, delYn }); + }} + > + + + + + 정상 + 삭제 + + + + 검색 + { + setPostParams({ ...postParams, keywordType }); + }} + > + + + + + 제목 + 제목+내용 + 작성자 + + + + setPostParams({ ...postParams, keyword: e.target.value }) + } + className="h-[3.2rem] w-[24rem] rounded-sm !text-[1.3rem]" + /> + + 조회 + + + + + + + 공지사항 목록 + + router.push("/bbs/register")}> + 등록 + + { + table.setPageSize(Number(value)); + }} + > + + + + + {[10, 20, 30, 40, 50].map((pageSize) => ( + + {pageSize}개씩 보기 + + ))} + + + + + + + + + + + + ); +} + +export { NoticePage }; diff --git a/apps/common/features/src/pages/organization/index.ts b/apps/common/features/src/pages/organization/index.ts deleted file mode 100644 index cf047f517..000000000 --- a/apps/common/features/src/pages/organization/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { OrganizationPage } from "./ui/organization-page"; diff --git a/apps/common/features/src/pages/organization/model/additional-user-table-columns.tsx b/apps/common/features/src/pages/organization/model/additional-user-table-columns.tsx deleted file mode 100644 index 0b6e1e1e3..000000000 --- a/apps/common/features/src/pages/organization/model/additional-user-table-columns.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import dayjs from "dayjs"; -import type { ColumnDef } from "@tanstack/react-table"; -import { Checkbox } from "@common/components/ui"; -import type { SearchUser } from "@/entities/user-organization"; - -export const columns: ColumnDef[] = [ - { - id: "select", - header: ({ table }) => ( - - table.toggleAllPageRowsSelected(!!checked) - } - className="h-4 w-4 rounded border-gray-300" - /> - ), - cell: ({ row }) => ( - row.toggleSelected(!!checked)} - className="h-4 w-4 rounded border-gray-300" - /> - ), - }, - { - accessorKey: "userNm", - header: "이름", - }, - { - accessorKey: "ogdpSeNm", - header: "소속", - }, - { - accessorKey: "regDt", - header: "등록일", - cell: ({ row }) => dayjs(row.original.regDt).format("YYYY-MM-DD HH:mm:ss"), - }, -]; diff --git a/apps/common/features/src/pages/organization/ui/additional-user-dialog.tsx b/apps/common/features/src/pages/organization/ui/additional-user-dialog.tsx deleted file mode 100644 index 9350b0897..000000000 --- a/apps/common/features/src/pages/organization/ui/additional-user-dialog.tsx +++ /dev/null @@ -1,114 +0,0 @@ -import { useEffect, useState } from "react"; -import { useQuery } from "@tanstack/react-query"; -import { - getCoreRowModel, - useReactTable, - type RowSelectionState, -} from "@tanstack/react-table"; -import { - Button, - DataTable, - Dialog, - DialogTrigger, - DialogContent, - DialogHeader, - DialogTitle, - Input, - DialogFooter, - DialogClose, -} from "@common/components/ui"; -import { - UserOrganizationApi, - type SearchUser, -} from "@/entities/user-organization"; -import { ENDPOINT } from "@/shared/config"; -import { columns } from "../model/additional-user-table-columns"; - -type AdditionalUserDialogProps = { - onAddUsers: (user: SearchUser[]) => void; -} & React.ComponentProps; - -function AdditionalUserDialog({ - onAddUsers, - ...props -}: AdditionalUserDialogProps) { - const [open, setOpen] = useState(false); - const [userNm, setUserNm] = useState(""); - const [rowSelection, setRowSelection] = useState({}); - - const { data: userList, refetch } = useQuery({ - queryKey: [ENDPOINT.CMS_SERVICE.USER_ORGANIZATION], - queryFn: () => UserOrganizationApi.searchUser({ userNm }), - enabled: false, - }); - - const table = useReactTable({ - data: userList?.content ?? [], - columns, - state: { rowSelection }, - getCoreRowModel: getCoreRowModel(), - onRowSelectionChange: setRowSelection, - }); - - const handleAddUsers = () => { - const selectedRows = table.getSelectedRowModel().rows; - const userData = selectedRows.map((row) => row.original); - if (!userData) return; - - onAddUsers(userData); - table.setRowSelection({}); - }; - - const handleCancel = () => { - setRowSelection({}); - setUserNm(""); - }; - - useEffect(() => { - if (open) refetch(); - }, [open]); - - return ( - - - - 사용자 추가 - - - - - 콘텐츠 조회 - - - - setUserNm(e.target.value)} - /> - refetch()}> - - 조회 - - - - - - - - - - 취소 - - - - 추가 - - - - - ); -} - -export { AdditionalUserDialog }; diff --git a/apps/common/features/src/pages/organization/ui/move-organization-dialog.tsx b/apps/common/features/src/pages/organization/ui/move-organization-dialog.tsx deleted file mode 100644 index 7951aa302..000000000 --- a/apps/common/features/src/pages/organization/ui/move-organization-dialog.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import { useState } from "react"; -import { useQueryClient } from "@tanstack/react-query"; -import type { NodeModel } from "@minoru/react-dnd-treeview"; -import { useConfirm } from "@frontend-opensource/use-react-hooks"; -import { - Button, - Dialog, - DialogClose, - DialogContent, - DialogFooter, - DialogHeader, - DialogTitle, - DialogTrigger, -} from "@common/components/ui"; -import { DndTree } from "@/widgets/dnd-tree"; -import { - generateOrganizationTreeData, - type OrganizationTree, -} from "@/entities/organization"; -import { ENDPOINT } from "@/shared/config"; - -type MoveOrganizationDialogProps = { - buttonDisabled?: boolean; - handleMoveOrganization: (organization: OrganizationTree) => Promise; -}; - -function MoveOrganizationDialog({ - buttonDisabled, - handleMoveOrganization, -}: MoveOrganizationDialogProps) { - const { confirm } = useConfirm(); - const queryClient = useQueryClient(); - const queryData = queryClient.getQueryData([ - ENDPOINT.CMS_SERVICE.ORGANIZATIONS, - ]); - const organizationTreeData = generateOrganizationTreeData(queryData ?? []); - - const [open, setOpen] = useState(false); - const [selectedOrganization, setSelectedOrganization] = - useState(null); - - const handleNodeSelect = (node: NodeModel) => { - if (!node.data) return; - setSelectedOrganization(node.data); - }; - - const onMoveOrganization = async () => { - if (await confirm("이동하시겠습니까?")) { - await handleMoveOrganization(selectedOrganization as OrganizationTree); - setOpen(false); - } - }; - - return ( - - - - 조직 이동 - - - - - 조직 이동 - - - - - 취소 - - 저장 - - - - ); -} - -export { MoveOrganizationDialog }; diff --git a/apps/common/features/src/pages/organization/ui/organization-form.tsx b/apps/common/features/src/pages/organization/ui/organization-form.tsx deleted file mode 100644 index 4ddb94a57..000000000 --- a/apps/common/features/src/pages/organization/ui/organization-form.tsx +++ /dev/null @@ -1,207 +0,0 @@ -"use client"; - -import type { UseFormReturn } from "react-hook-form"; -import { useConfirm } from "@frontend-opensource/use-react-hooks"; -import { - Button, - Input, - Label, - RadioGroup, - RadioGroupItem, - Table, - TableBody, - TableCell, - TableHead, - TableRow, -} from "@common/components"; -import type { OrganizationFormData } from "@/entities/organization"; -import { Form, FormControl, FormField, FormItem } from "@/shared/ui/form"; - -type OrganizationFormProps = { - form: UseFormReturn; - ognzSn?: number; - handleCreate: (data: OrganizationFormData) => Promise; - handleUpdate: ({ - ognzSn, - data, - }: { - ognzSn: number; - data: OrganizationFormData; - }) => Promise; - handleDelete?: (ognzSn: number) => Promise; -}; - -function OrganizationForm({ - form, - ognzSn, - handleCreate, - handleUpdate, - handleDelete, -}: OrganizationFormProps) { - const isModifyMode = !!ognzSn; - - const { confirm } = useConfirm(); - - const onSubmit = async (data: OrganizationFormData) => { - if (isModifyMode) { - await onUpdate(data); - } else { - await onCreate(data); - } - }; - - const onCreate = async (data: OrganizationFormData) => { - if (await confirm("저장하시겠습니까?")) { - handleCreate(data); - } - }; - - const onUpdate = async (data: OrganizationFormData) => { - if (await confirm("수정하시겠습니까?")) { - handleUpdate({ ognzSn: ognzSn!, data }); - } - }; - - const onDelete = async () => { - if (await confirm("삭제하시겠습니까?")) { - handleDelete?.(ognzSn!); - } - }; - - return ( - - - - - - {isModifyMode ? "조직도 수정" : "조직도 등록"} - - - - - - - - - - - - - 조직명 - - ( - - - - - - )} - /> - - 정렬순서 - - ( - - - - field.onChange(Number(e.target.value)) - } - className="!text-[1.3rem]" - placeholder="정렬순서를 입력해주세요" - /> - - - )} - /> - - - - 조직설명 - - ( - - - - - - )} - /> - - 사용여부 - - ( - - - - - - 사용 - - - - 사용안함 - - - - - )} - /> - - - - - - - - - 저장 - - {isModifyMode && ( - - 삭제 - - )} - - - - ); -} - -export { OrganizationForm }; diff --git a/apps/common/features/src/pages/organization/ui/organization-page.tsx b/apps/common/features/src/pages/organization/ui/organization-page.tsx deleted file mode 100644 index 545488305..000000000 --- a/apps/common/features/src/pages/organization/ui/organization-page.tsx +++ /dev/null @@ -1,238 +0,0 @@ -"use client"; - -import { useState } from "react"; -import { useForm } from "react-hook-form"; -import { zodResolver } from "@hookform/resolvers/zod"; -import type { NodeModel } from "@minoru/react-dnd-treeview"; -import { useConfirm } from "@frontend-opensource/use-react-hooks"; -import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; -import { Button } from "@common/components/ui"; -import { DndTree, EditableNode } from "@/widgets/dnd-tree"; -import { - OrganizationApi, - organizationFormSchema, - generateOrganizationTreeData, - handleDropNode, - type OrganizationFormData, - type OrganizationTree, - type OrganizationTreeForDnd, -} from "@/entities/organization"; -import { ENDPOINT } from "@/shared/config"; -import { useAlertStore } from "@/shared/lib/use-alert-store"; -import { OrganizationForm } from "./organization-form"; -import { UserOrganizationSection } from "./user-organization-section"; - -function OrganizationPage() { - const queryClient = useQueryClient(); - const { setMessage: alert } = useAlertStore((state) => state); - const { confirm } = useConfirm(); - - const [additionalNode, setAdditionalNode] = - useState | null>(null); - const [selectedOrganization, setSelectedOrganization] = - useState(null); - - const { data: organizationTreeData } = useQuery({ - queryKey: [ENDPOINT.CMS_SERVICE.ORGANIZATIONS], - queryFn: () => OrganizationApi.getOrganizationsTreeList(), - select: (data) => generateOrganizationTreeData(data), - }); - - const { data: organizationInfo } = useQuery({ - queryKey: [ - ENDPOINT.CMS_SERVICE.ORGANIZATIONS, - selectedOrganization?.ognzSn, - ], - queryFn: () => - OrganizationApi.getOrganizationInfo(selectedOrganization!.ognzSn!), - enabled: !!selectedOrganization?.ognzSn, - }); - - const { mutateAsync: createOrganization } = useMutation({ - mutationFn: (form: OrganizationFormData) => - OrganizationApi.createOrganization(form), - onSuccess: () => { - alert("저장되었습니다."); - queryClient.invalidateQueries({ - queryKey: [ENDPOINT.CMS_SERVICE.ORGANIZATIONS], - }); - }, - }); - - const { mutateAsync: deleteOrganization } = useMutation({ - mutationFn: (ognzSn: number) => OrganizationApi.deleteOrganization(ognzSn), - onSuccess: () => { - alert("삭제되었습니다."); - setSelectedOrganization(null); - queryClient.invalidateQueries({ - queryKey: [ENDPOINT.CMS_SERVICE.ORGANIZATIONS], - }); - }, - }); - - const { mutateAsync: updateOrganizationName } = useMutation({ - mutationFn: ({ ognzSn, name }: { ognzSn: number; name: string }) => - OrganizationApi.updateOrganizationName(ognzSn, name), - onSuccess: () => { - alert("저장되었습니다."); - queryClient.invalidateQueries({ - queryKey: [ENDPOINT.CMS_SERVICE.ORGANIZATIONS], - }); - }, - }); - - const { mutateAsync: updateOrganization } = useMutation({ - mutationFn: ({ - ognzSn, - data, - }: { - ognzSn: number; - data: OrganizationFormData; - }) => OrganizationApi.updateOrganization(ognzSn, data), - onSuccess: () => { - alert("수정되었습니다."); - queryClient.invalidateQueries({ - queryKey: [ENDPOINT.CMS_SERVICE.ORGANIZATIONS], - }); - }, - }); - - const { mutateAsync: updateOrganizationByDnd } = useMutation({ - mutationFn: (data: OrganizationTreeForDnd) => - OrganizationApi.updateOrganizationByDnd([data]), - onSuccess: () => { - queryClient.invalidateQueries({ - queryKey: [ENDPOINT.CMS_SERVICE.ORGANIZATIONS], - }); - }, - }); - - const form = useForm({ - resolver: zodResolver(organizationFormSchema), - defaultValues: { - ognzNm: "", - sortSeq: 1, - useYn: "Y", - }, - values: { - ognzNm: organizationInfo?.ognzNm ?? "", - ognzExpln: organizationInfo?.ognzExpln ?? "", - sortSeq: organizationInfo?.sortSeq ?? 1, - useYn: organizationInfo?.useYn ?? "Y", - }, - }); - - const additionalNodeForm = useForm({ - resolver: zodResolver(organizationFormSchema), - defaultValues: { - ognzNm: "", - sortSeq: 1, - }, - }); - - const tree = additionalNode - ? [ - ...(additionalNode ? [additionalNode] : []), - ...(organizationTreeData ?? []), - ] - : (organizationTreeData ?? []); - - const onSaveAdditionalNode = async () => { - await createOrganization({ ...additionalNodeForm.getValues() }); - resetAdditionalNode(); - }; - - const onDeleteOrganization = async () => { - if (await confirm("삭제하시겠습니까?")) { - deleteOrganization(selectedOrganization!.ognzSn!); - } - }; - - const resetAdditionalNode = () => { - additionalNodeForm.setValue("ognzNm", ""); - setAdditionalNode(null); - }; - - const handleNodeSelect = (node: NodeModel) => { - if (!node.data) return; - if (node.data.ognzSn === selectedOrganization?.ognzSn) { - setSelectedOrganization(null); - additionalNodeForm.setValue("upOgnzSn", null); - return; - } - setSelectedOrganization(node.data); - additionalNodeForm.setValue("upOgnzSn", (node.id as number) ?? 0); - }; - - const handleAddOrganization = () => { - if (additionalNode) return; - setAdditionalNode({ - id: -1, - parent: selectedOrganization?.ognzSn ?? 0, - text: "", - }); - }; - - return ( - - - - - 조직 추가 - - - 조직 삭제 - - - - ( - - additionalNodeForm.setValue("ognzNm", e.target.value) - } - onSave={onSaveAdditionalNode} - onCancel={resetAdditionalNode} - {...props} - /> - )} - handleSelectNode={handleNodeSelect} - handleDropNode={handleDropNode(updateOrganizationByDnd)} - updateNodeName={async (id, name) => { - await updateOrganizationName({ ognzSn: id as number, name }); - }} - classes={{ wrapper: "max-h-[calc(100vh-29rem)]" }} - /> - - - - - {selectedOrganization?.ognzSn && ( - - )} - - - ); -} - -export { OrganizationPage }; diff --git a/apps/common/features/src/pages/organization/ui/user-organization-section.tsx b/apps/common/features/src/pages/organization/ui/user-organization-section.tsx deleted file mode 100644 index 74a367750..000000000 --- a/apps/common/features/src/pages/organization/ui/user-organization-section.tsx +++ /dev/null @@ -1,385 +0,0 @@ -import dayjs from "dayjs"; -import React, { useEffect, useState, useCallback, memo } from "react"; -import { useFieldArray, useForm, type UseFormReturn } from "react-hook-form"; -import { useConfirm } from "@frontend-opensource/use-react-hooks"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { - useQuery, - useMutation, - useQueryClient, - keepPreviousData, -} from "@tanstack/react-query"; -import { - Button, - Checkbox, - Input, - Table, - TableBody, - TableCell, - TableHead, - TableHeader, - TableRow, -} from "@common/components/ui"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@common/components/ui"; -import { - UserOrganizationApi, - userOrganizationFormSchema, - type UserOrganizationFormData, - type UserOrganizationPayload, - type User, - type SearchUser, - type MoveUserOrganizationPayload, -} from "@/entities/user-organization"; -import { CodeApi, type ICode } from "@/entities/code"; -import type { OrganizationTree } from "@/entities/organization"; -import { ENDPOINT } from "@/shared/config"; -import { useAlertStore } from "@/shared/lib/use-alert-store"; -import { AdditionalUserDialog } from "./additional-user-dialog"; -import { MoveOrganizationDialog } from "./move-organization-dialog"; - -type UserOrganizationSectionProps = { - ognzSn: number; -}; - -function UserOrganizationSection({ ognzSn }: UserOrganizationSectionProps) { - const queryClient = useQueryClient(); - const { confirm } = useConfirm(); - const { setMessage: alert } = useAlertStore((state) => state); - - const [userList, setUserList] = useState([]); - const [selectedUsers, setSelectedUsers] = useState([]); - - const isAllSelected = - userList.length > 0 && selectedUsers.length === userList.length; - - const { data: userOrganizationList } = useQuery({ - queryKey: [ENDPOINT.CMS_SERVICE.USER_ORGANIZATION, ognzSn], - queryFn: () => UserOrganizationApi.getUserOrganizationList({ ognzSn }), - enabled: !!ognzSn, - placeholderData: keepPreviousData, - }); - - const { data: jobPositionCodes } = useQuery({ - queryKey: [ENDPOINT.CMS_SERVICE.CODE, "JPBSCLCD"], - queryFn: () => CodeApi.getCode("JPBSCLCD"), - }); - - const { data: jobGradeCodes } = useQuery({ - queryKey: [ENDPOINT.CMS_SERVICE.CODE, "JDGDCLCD"], - queryFn: () => CodeApi.getCode("JDGDCLCD"), - }); - - const { mutate: saveUserOrganization } = useMutation({ - mutationFn: (data: UserOrganizationPayload[]) => - UserOrganizationApi.saveUserOrganization(data), - onSuccess: () => { - alert("저장되었습니다."); - queryClient.invalidateQueries({ - queryKey: [ENDPOINT.CMS_SERVICE.USER_ORGANIZATION, ognzSn], - }); - }, - }); - - const { mutate: moveUserOrganization } = useMutation({ - mutationFn: (data: MoveUserOrganizationPayload[]) => - UserOrganizationApi.moveUserOrganization(data), - onSuccess: () => { - alert("이동되었습니다."); - queryClient.invalidateQueries({ - queryKey: [ENDPOINT.CMS_SERVICE.USER_ORGANIZATION, ognzSn], - }); - }, - }); - - const form = useForm({ - resolver: zodResolver(userOrganizationFormSchema), - values: { - userOrganization: - userOrganizationList?.content.map((user) => ({ - mode: user.mode, - ognzSn: user.ognzSn, - userUnqId: user.userUnqId, - jbpsSeCd: user.jbpsSeCd, - jbgdSeCd: user.jbgdSeCd, - sortSeq: user.sortSeq, - })) ?? [], - }, - }); - - const { append } = useFieldArray({ - name: "userOrganization", - control: form.control, - }); - - const handleAddUsers = (users: SearchUser[]) => { - const generateUserData = (user: SearchUser) => ({ - mode: "C", - ognzSn, - userUnqId: user.userUnqId, - jbpsSeCd: "", - jbgdSeCd: "", - sortSeq: 0, - }); - - const newFormData = users.map(generateUserData); - append(newFormData); - - const newUserList = users.map((user) => ({ - ...generateUserData(user), - userUnqNm: user.userNm, - })); - const existingUserIds = new Set(newUserList.map((user) => user.userUnqId)); - const filteredUserList = userList.filter( - (user) => !existingUserIds.has(user.userUnqId), - ); - setUserList([...filteredUserList, ...newUserList]); - }; - - const handleToggleSelectUser = useCallback((user: User) => { - setSelectedUsers((prevSelected) => { - const isSelected = prevSelected.some( - (item) => item.userUnqId === user.userUnqId, - ); - - if (isSelected) { - return prevSelected.filter((item) => item.userUnqId !== user.userUnqId); - } else { - return [...prevSelected, user]; - } - }); - }, []); - - const handleToggleSelectAll = () => { - if (isAllSelected) { - setSelectedUsers([]); - } else { - setSelectedUsers([...userList]); - } - }; - - const handleDeleteUsers = () => { - const selectedUserIds = selectedUsers.map((user) => user.userUnqId); - const updatedFormData = form.getValues("userOrganization").map((item) => { - if (selectedUserIds.includes(item.userUnqId)) { - return { ...item, mode: "D" }; - } - return item; - }); - form.setValue("userOrganization", updatedFormData); - setUserList((prevUsers) => - prevUsers.filter((user) => !selectedUserIds.includes(user.userUnqId)), - ); - setSelectedUsers([]); - }; - - const handleSaveUsers = async () => { - if (await confirm("저장하시겠습니까?")) { - const filteredData = form - .watch("userOrganization") - .filter((item) => item.mode !== "R"); - saveUserOrganization(filteredData); - } - }; - - const handleMoveOrganization = async (organization: OrganizationTree) => { - const payloadData: MoveUserOrganizationPayload[] = selectedUsers.map( - (user) => ({ - ognzSn: user.ognzSn, - toOgnzSn: organization.ognzSn!, - userUnqId: user.userUnqId, - }), - ); - moveUserOrganization(payloadData); - }; - - const isUserSelected = useCallback( - (userUnqId: string) => - selectedUsers.some((item) => item.userUnqId === userUnqId), - [selectedUsers], - ); - - useEffect(() => { - if (userOrganizationList?.content) { - setUserList(userOrganizationList.content); - setSelectedUsers([]); - } - }, [userOrganizationList]); - - return ( - - - 소속원 목록 - - - - - 선택삭제 - - - 저장 - - - - - - - - - - - NO - 이름 - 직위 - 직급 - 정렬순서 - 최종수정자 - 최종수정일 - - - - {userList.map((user, index) => ( - - - handleToggleSelectUser(user)} - /> - - - - ))} - - - - - ); -} - -type UserCellsProps = { - form: UseFormReturn; - user: User; - index: number; - jobPositionCodeData?: ICode[]; - jobGradeCodeData?: ICode[]; -}; - -const UserCells = memo( - ({ - form, - user, - index, - jobPositionCodeData, - jobGradeCodeData, - }: UserCellsProps) => { - const updateMode = (index: number) => { - const mode = form.watch(`userOrganization.${index}.mode`); - if (mode === "R") { - form.setValue(`userOrganization.${index}.mode`, "U"); - } - }; - - const updateFormValue = - (index: number, key: keyof UserOrganizationPayload) => - (value: string | number) => { - updateMode(index); - form.setValue(`userOrganization.${index}.${key}`, value); - }; - - return ( - <> - {index + 1} - {user.userUnqNm} - - - - - - - - - updateFormValue(index, "sortSeq")(Number(e.target.value)) - } - className="w-[6rem] !text-[1.3rem]" - /> - - {user.mdfrNm} - - {dayjs(user.mdfcnDt).format("YYYY-MM-DD")} - - > - ); - }, -); - -UserCells.displayName = "UserCells"; - -type CodeSelectProps = React.ComponentPropsWithoutRef & { - codeData?: ICode[]; -}; - -const CodeSelect = memo(({ codeData, ...props }: CodeSelectProps) => { - if (!codeData) return null; - - const placeholderText = - codeData.find((item) => item.lwpoCd === props.defaultValue)?.lwpoCdNm ?? - "선택"; - - return ( - - - - - - {codeData.map((item) => ( - - {item.lwpoCdNm} - - ))} - - - ); -}); - -CodeSelect.displayName = "CodeSelect"; - -export { UserOrganizationSection }; diff --git a/apps/common/features/src/pages/site/api/domain-service.ts b/apps/common/features/src/pages/site/api/domain-service.ts deleted file mode 100644 index a8d2acfab..000000000 --- a/apps/common/features/src/pages/site/api/domain-service.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { client } from "@/shared/api"; -import { ENDPOINT } from "@/shared/config"; -import type { - Domain, - DomainService, - DomainList, - DomainParams, -} from "../model/domain-interface"; - -export class DomainApiService implements DomainService { - public async getDomainList(params: DomainParams): Promise { - const response = await client.get( - `${ENDPOINT.CMS_SERVICE.DOMAINS}`, - { params }, - ); - return response.data; - } - - public async saveDomainList(body: Domain[]): Promise { - const response = await client.post( - `${ENDPOINT.CMS_SERVICE.DOMAINS}`, - body, - ); - return response.data; - } -} - -export const DomainApi = new DomainApiService(); diff --git a/apps/common/features/src/pages/site/api/ip-service.ts b/apps/common/features/src/pages/site/api/ip-service.ts deleted file mode 100644 index 903f0407e..000000000 --- a/apps/common/features/src/pages/site/api/ip-service.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { client } from "@/shared/api"; -import { ENDPOINT } from "@/shared/config"; -import type { IpParams, IP, IpList, IpService } from "../model/ip-interface"; - -export class IpApiService implements IpService { - public async getIpList(params: IpParams): Promise { - const response = await client.get(`${ENDPOINT.CMS_SERVICE.IPS}`, { - params, - }); - return response.data; - } - - public async saveIpList(body: IP[]): Promise { - const response = await client.post( - `${ENDPOINT.CMS_SERVICE.IPS}`, - body, - ); - return response.data; - } -} - -export const IpApi = new IpApiService(); diff --git a/apps/common/features/src/pages/site/index.ts b/apps/common/features/src/pages/site/index.ts deleted file mode 100644 index 40c65cf04..000000000 --- a/apps/common/features/src/pages/site/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { CreatePage } from "./ui/create-page"; -export { ModifyPage } from "./ui/modify-page"; -export { SitePage } from "./ui/site-page"; diff --git a/apps/common/features/src/pages/site/model/domain-interface.ts b/apps/common/features/src/pages/site/model/domain-interface.ts deleted file mode 100644 index e93f86bc1..000000000 --- a/apps/common/features/src/pages/site/model/domain-interface.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { z } from "zod"; -import type { PageableParams, PageableList } from "@/shared/model"; - -export interface DomainParams extends PageableParams { - keywordType?: string; - keyword?: string; - siteId?: string; - useYn?: string; -} - -export const domainSchema = z.object({ - siteDmnNo: z.number().nullish(), - siteId: z.string(), - dmnAddr: z.string().nonempty(), - useYn: z.string(), - rprsDmnYn: z.string(), - mode: z.string(), -}); - -export const domainFormSchema = z.object({ - domains: z.array(domainSchema), -}); - -export type Domain = z.infer; -export type DomainFormData = z.infer; -export type DomainList = PageableList; - -export interface DomainService { - getDomainList(params: DomainParams): Promise; - saveDomainList(body: Domain[]): Promise; -} diff --git a/apps/common/features/src/pages/site/model/ip-interface.ts b/apps/common/features/src/pages/site/model/ip-interface.ts deleted file mode 100644 index 427fbfe57..000000000 --- a/apps/common/features/src/pages/site/model/ip-interface.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { z } from "zod"; -import type { PageableParams, PageableList } from "@/shared/model"; - -export interface IpParams extends PageableParams { - siteId: string; - ipAddr?: string; - prmYn?: string; -} - -export const ipSchema = z.object({ - mngNo: z.number().nullish(), - siteId: z.string(), - ipAddr: z.string().nonempty(), - prmYn: z.string(), - mode: z.string(), -}); - -export const ipFormSchema = z.object({ - ips: z.array(ipSchema), -}); - -export type IP = z.infer; -export type IPFormData = z.infer; -export type IpList = PageableList
- {(upload?.file as File)?.name} -
- {uploadFile?.name} -