diff --git a/apps/cs/app/(with-base-layout)/contents/page.tsx b/apps/cs/app/(with-base-layout)/contents/page.tsx new file mode 100644 index 000000000..ba2a6a21d --- /dev/null +++ b/apps/cs/app/(with-base-layout)/contents/page.tsx @@ -0,0 +1,2 @@ +import { ContentsPage } from "@/pages/contents"; +export default ContentsPage; diff --git a/apps/cs/src/entities/contents/api/contents-service.ts b/apps/cs/src/entities/contents/api/contents-service.ts new file mode 100644 index 000000000..8d73d2bf0 --- /dev/null +++ b/apps/cs/src/entities/contents/api/contents-service.ts @@ -0,0 +1,51 @@ +import { client } from "@/shared/api"; +import { ENDPOINT } from "@/shared/config"; +import type { + ContentsService, + ContentsParams, + ContentsList, + Contents, + ContentsFormData, +} from "../model/contents-interface"; + +export class ContentsApiService implements ContentsService { + public async getContentsList(params: ContentsParams): Promise { + const response = await client.get( + `${ENDPOINT.CMS_SERVICE.CONTENTS}`, + { params }, + ); + return response.data; + } + + public async getContentsInfo(contsSn: number): Promise { + const response = await client.get( + `${ENDPOINT.CMS_SERVICE.CONTENTS}/${contsSn}`, + ); + return response.data; + } + + public async createContents(data: ContentsFormData): Promise { + const response = await client.post( + `${ENDPOINT.CMS_SERVICE.CONTENTS}`, + data, + ); + return response.data; + } + + public async updateContents( + contsSn: number, + data: ContentsFormData, + ): Promise { + const response = await client.put( + `${ENDPOINT.CMS_SERVICE.CONTENTS}/${contsSn}`, + data, + ); + return response.data; + } + + public async deleteContents(contsSn: number): Promise { + await client.delete(`${ENDPOINT.CMS_SERVICE.CONTENTS}/${contsSn}`); + } +} + +export const ContentsApi = new ContentsApiService(); diff --git a/apps/cs/src/entities/contents/index.ts b/apps/cs/src/entities/contents/index.ts new file mode 100644 index 000000000..6c612996c --- /dev/null +++ b/apps/cs/src/entities/contents/index.ts @@ -0,0 +1,2 @@ +export { ContentsApi } from "./api/contents-service"; +export type { Contents, ContentsParams } from "./model/contents-interface"; diff --git a/apps/cs/src/entities/contents/model/contents-interface.ts b/apps/cs/src/entities/contents/model/contents-interface.ts new file mode 100644 index 000000000..d5a9f833d --- /dev/null +++ b/apps/cs/src/entities/contents/model/contents-interface.ts @@ -0,0 +1,37 @@ +import { z } from "zod"; +import type { PageableList, PageableParams } from "@/shared/model"; + +export interface ContentsParams extends PageableParams { + contsTtl?: string; + aprvContsVer?: number; + useYn?: string; +} + +export const contentsSchema = z.object({ + contsSn: z.number(), + contsTtl: z.string(), + aprvContsVer: z.number(), + useYn: z.string(), + regDt: z.string(), + mdfcnDt: z.string(), + rgTrNm: z.string(), + mdfrNm: z.string(), +}); + +export const contentsFormSchema = z.object({ + contsTtl: z.string(), + useYn: z.string(), + rgtr: z.string().nullish(), +}); + +export type Contents = z.infer; +export type ContentsFormData = z.infer; +export type ContentsList = PageableList; + +export interface ContentsService { + getContentsList(params: ContentsParams): Promise; + getContentsInfo(contsSn: number): Promise; + createContents(data: ContentsFormData): Promise; + updateContents(contsSn: number, data: ContentsFormData): Promise; + deleteContents(contsSn: number): Promise; +} diff --git a/apps/cs/src/entities/group/index.ts b/apps/cs/src/entities/group/index.ts deleted file mode 100644 index 6bfb42bf1..000000000 --- a/apps/cs/src/entities/group/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export { GroupApi } from "./api/group-api"; -export { - type GroupParams, - type Group, - type GroupFormData, -} from "./model/types"; diff --git a/apps/cs/src/entities/group/model/types.ts b/apps/cs/src/entities/group/model/types.ts deleted file mode 100644 index 79ba1211b..000000000 --- a/apps/cs/src/entities/group/model/types.ts +++ /dev/null @@ -1,29 +0,0 @@ -export interface Group { - groupId: string; - groupNm: string; - groupSeCd: string; - groupExpln: string; - delYn: string; - regDt: string; - rgtr: string; - mdfcnDt: string | null; - mdfr: string | null; - authrtId: string; - siteId: string | null; -} - -export interface GroupParams { - groupNm?: string; - groupSeCd?: string; - siteId?: string; -} - -export interface GroupFormData { - groupId: string; - groupNm: string; - groupSeCd: string; - groupExpln: string; - delYn: string; - authrtId: string; - siteId: string; -} \ No newline at end of file diff --git a/apps/cs/src/pages/contents/index.ts b/apps/cs/src/pages/contents/index.ts new file mode 100644 index 000000000..c14f174e2 --- /dev/null +++ b/apps/cs/src/pages/contents/index.ts @@ -0,0 +1 @@ +export { ContentsPage } from "./ui/contents-page"; diff --git a/apps/cs/src/pages/contents/model/table-columns.tsx b/apps/cs/src/pages/contents/model/table-columns.tsx new file mode 100644 index 000000000..8790c2176 --- /dev/null +++ b/apps/cs/src/pages/contents/model/table-columns.tsx @@ -0,0 +1,47 @@ +import dayjs from "dayjs"; +import { type ColumnDef } from "@tanstack/react-table"; +import type { Contents } from "@/entities/contents"; + +export const columns: ColumnDef[] = [ + { + id: "no", + header: "NO", + cell: ({ row }) => row.index + 1, + }, + { + accessorKey: "contsTtl", + header: "콘텐츠명", + }, + { + accessorKey: "aprvContsVer", + header: "승인여부", + }, + { + accessorKey: "rgTrNm", + header: "승인자", + }, + { + accessorKey: "regDt", + header: "승인일자", + cell: ({ row }) => { + const date = dayjs(row.original.regDt); + return date.format("YYYY-MM-DD"); + }, + }, + { + accessorKey: "useYn", + header: "사용여부", + }, + { + accessorKey: "mdfrNm", + header: "최종수정자", + }, + { + accessorKey: "mdfcnDt", + header: "최종수정일", + cell: ({ row }) => { + const date = dayjs(row.original.mdfcnDt); + return date.format("YYYY-MM-DD"); + }, + }, +]; diff --git a/apps/cs/src/pages/contents/ui/contents-page.tsx b/apps/cs/src/pages/contents/ui/contents-page.tsx new file mode 100644 index 000000000..2687b2606 --- /dev/null +++ b/apps/cs/src/pages/contents/ui/contents-page.tsx @@ -0,0 +1,100 @@ +"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/contents"; +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 })} + /> + +
+
+
+
+

콘텐츠 목록

+ +
+
+ +
+
+
+ ); +} + +export { ContentsPage }; diff --git a/apps/cs/src/pages/menu/api/content-service.ts b/apps/cs/src/pages/menu/api/content-service.ts deleted file mode 100644 index 2f76baec8..000000000 --- a/apps/cs/src/pages/menu/api/content-service.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { client } from "@/shared/api"; -import { ENDPOINT } from "@/shared/config"; -import type { - ContentService, - ContentParams, - ContentList, -} from "../model/content-interface"; - -export class ContentApiService implements ContentService { - public async getContentList(params: ContentParams): Promise { - const response = await client.get( - `${ENDPOINT.CMS_SERVICE.CONTENTS}`, - { params }, - ); - return response.data; - } -} - -export const ContentApi = new ContentApiService(); diff --git a/apps/cs/src/pages/menu/model/content-interface.ts b/apps/cs/src/pages/menu/model/content-interface.ts deleted file mode 100644 index 8d59b1516..000000000 --- a/apps/cs/src/pages/menu/model/content-interface.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type { PageableList, PageableParams } from "@/shared/model"; - -export interface ContentParams extends PageableParams { - contsTtl?: string; - aprvContsVer?: number; - useYn?: string; -} - -export interface Content { - contsSn: number; - contsTtl: string; - aprvContsVer: number; - useYn: string; - rgtr: string; - regDt: string; -} - -export type ContentList = PageableList; - -export interface ContentService { - getContentList(params: ContentParams): Promise; -} diff --git a/apps/cs/src/pages/menu/model/content-table-colums.tsx b/apps/cs/src/pages/menu/model/content-table-colums.tsx index 844055b73..8670058bc 100644 --- a/apps/cs/src/pages/menu/model/content-table-colums.tsx +++ b/apps/cs/src/pages/menu/model/content-table-colums.tsx @@ -1,8 +1,9 @@ +import dayjs from "dayjs"; import type { ColumnDef } from "@tanstack/react-table"; import { Checkbox } from "@common/components/ui"; -import type { Content } from "./content-interface"; +import type { Contents } from "@/entities/contents"; -export const columns: ColumnDef[] = [ +export const columns: ColumnDef[] = [ { id: "no", header: "NO", @@ -13,7 +14,30 @@ export const columns: ColumnDef[] = [ header: "콘텐츠명", cell: ({ row }) => row.original.contsTtl, }, - // TODO: 최종수정자, 최종수정일, 승인일시 추가 + { + accessorKey: "rgTrNm", + header: "승인자", + }, + { + accessorKey: "regDt", + header: "승인일자", + cell: ({ row }) => { + const date = dayjs(row.original.regDt); + 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: "선택", diff --git a/apps/cs/src/pages/menu/ui/content-search-dialog.tsx b/apps/cs/src/pages/menu/ui/content-search-dialog.tsx index 1cfa122a2..a3910eda9 100644 --- a/apps/cs/src/pages/menu/ui/content-search-dialog.tsx +++ b/apps/cs/src/pages/menu/ui/content-search-dialog.tsx @@ -16,12 +16,11 @@ import { Input, } from "@common/components/ui"; import { ENDPOINT } from "@/shared/config"; -import { ContentApi } from "../api/content-service"; -import type { Content } from "../model/content-interface"; +import { ContentsApi, type Contents } from "@/entities/contents"; import { columns } from "../model/content-table-colums"; type ContentSearchDialogProps = { - handleContentSelect: (content: Content) => void; + handleContentSelect: (content: Contents) => void; } & React.ComponentProps; function ContentSearchDialog({ @@ -33,7 +32,7 @@ function ContentSearchDialog({ const { data: contentList, refetch } = useQuery({ queryKey: [ENDPOINT.CMS_SERVICE.CONTENTS], - queryFn: () => ContentApi.getContentList({ contsTtl }), + queryFn: () => ContentsApi.getContentsList({ contsTtl }), enabled: false, }); @@ -50,7 +49,7 @@ function ContentSearchDialog({ useEffect(() => { const contentData = table.getSelectedRowModel().rows[0] - ?.original as Content; + ?.original as Contents; if (!contentData) return; handleContentSelect(contentData); }, [table.getSelectedRowModel().rows]);