feat: 콘텐츠 관리 목록 페이지 작업
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
import { ContentsPage } from "@/pages/contents";
|
||||
export default ContentsPage;
|
||||
@@ -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<ContentsList> {
|
||||
const response = await client.get<ContentsList>(
|
||||
`${ENDPOINT.CMS_SERVICE.CONTENTS}`,
|
||||
{ params },
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
public async getContentsInfo(contsSn: number): Promise<Contents> {
|
||||
const response = await client.get<Contents>(
|
||||
`${ENDPOINT.CMS_SERVICE.CONTENTS}/${contsSn}`,
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
public async createContents(data: ContentsFormData): Promise<void> {
|
||||
const response = await client.post<void>(
|
||||
`${ENDPOINT.CMS_SERVICE.CONTENTS}`,
|
||||
data,
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
public async updateContents(
|
||||
contsSn: number,
|
||||
data: ContentsFormData,
|
||||
): Promise<void> {
|
||||
const response = await client.put<void>(
|
||||
`${ENDPOINT.CMS_SERVICE.CONTENTS}/${contsSn}`,
|
||||
data,
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
public async deleteContents(contsSn: number): Promise<void> {
|
||||
await client.delete(`${ENDPOINT.CMS_SERVICE.CONTENTS}/${contsSn}`);
|
||||
}
|
||||
}
|
||||
|
||||
export const ContentsApi = new ContentsApiService();
|
||||
@@ -0,0 +1,2 @@
|
||||
export { ContentsApi } from "./api/contents-service";
|
||||
export type { Contents, ContentsParams } from "./model/contents-interface";
|
||||
@@ -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<typeof contentsSchema>;
|
||||
export type ContentsFormData = z.infer<typeof contentsFormSchema>;
|
||||
export type ContentsList = PageableList<Contents>;
|
||||
|
||||
export interface ContentsService {
|
||||
getContentsList(params: ContentsParams): Promise<ContentsList>;
|
||||
getContentsInfo(contsSn: number): Promise<Contents>;
|
||||
createContents(data: ContentsFormData): Promise<void>;
|
||||
updateContents(contsSn: number, data: ContentsFormData): Promise<void>;
|
||||
deleteContents(contsSn: number): Promise<void>;
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
export { GroupApi } from "./api/group-api";
|
||||
export {
|
||||
type GroupParams,
|
||||
type Group,
|
||||
type GroupFormData,
|
||||
} from "./model/types";
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { ContentsPage } from "./ui/contents-page";
|
||||
@@ -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<Contents>[] = [
|
||||
{
|
||||
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");
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -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<ContentsParams>({
|
||||
contsTtl: "",
|
||||
});
|
||||
const [pagination, setPagination] = useState<PaginationState>({
|
||||
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<HTMLFormElement>) => {
|
||||
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 (
|
||||
<div className="card card-border !mt-6">
|
||||
<div className="card card-border">
|
||||
<form onSubmit={handleSubmit} className="flex items-center gap-2 p-2">
|
||||
<Label htmlFor="" className="mr-2 text-label">
|
||||
콘텐츠명
|
||||
</Label>
|
||||
<Input
|
||||
className="h-[3.2rem] w-[24rem] rounded-sm !text-[1.3rem]"
|
||||
value={contentsQuery.contsTtl}
|
||||
onChange={(e) => setContentsQuery({ contsTtl: e.target.value })}
|
||||
/>
|
||||
<Button className="h-[3.2rem]" type="submit">
|
||||
조회
|
||||
</Button>
|
||||
</form>
|
||||
</div>
|
||||
<div className="card card-border">
|
||||
<div className="card-header">
|
||||
<h3 className="text-2xl font-medium text-label">콘텐츠 목록</h3>
|
||||
<Button onClick={() => router.push("/contents/register")}>
|
||||
등록
|
||||
</Button>
|
||||
</div>
|
||||
<div className="card !m-[1.2rem] h-[29.2rem] overflow-auto !p-0">
|
||||
<DataTable table={table} onRowClick={handleRowClick} viewPagination />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export { ContentsPage };
|
||||
@@ -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<ContentList> {
|
||||
const response = await client.get<ContentList>(
|
||||
`${ENDPOINT.CMS_SERVICE.CONTENTS}`,
|
||||
{ params },
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
}
|
||||
|
||||
export const ContentApi = new ContentApiService();
|
||||
@@ -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<Content>;
|
||||
|
||||
export interface ContentService {
|
||||
getContentList(params: ContentParams): Promise<ContentList>;
|
||||
}
|
||||
@@ -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<Content>[] = [
|
||||
export const columns: ColumnDef<Contents>[] = [
|
||||
{
|
||||
id: "no",
|
||||
header: "NO",
|
||||
@@ -13,7 +14,30 @@ export const columns: ColumnDef<Content>[] = [
|
||||
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: "선택",
|
||||
|
||||
@@ -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<typeof Dialog>;
|
||||
|
||||
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]);
|
||||
|
||||
Reference in New Issue
Block a user