diff --git a/apps/cs/package.json b/apps/cs/package.json index f39701d7d..479772234 100644 --- a/apps/cs/package.json +++ b/apps/cs/package.json @@ -10,12 +10,12 @@ }, "dependencies": { "@common/components": "workspace:*", - "@toast-ui/react-grid": "^4.21.22", - "next": "15.1.7", - "tui-grid": "^4.21.22" + "@tanstack/react-table": "^8.21.2", + "next": "15.1.7" }, "devDependencies": { "@eslint/eslintrc": "^3", + "@faker-js/faker": "^9.5.0", "@types/node": "^22.13.1", "eslint": "^9", "eslint-config-next": "15.1.7", diff --git a/apps/cs/src/app/styles/components.css b/apps/cs/src/app/styles/components.css index 298c5585c..4d5caaf96 100644 --- a/apps/cs/src/app/styles/components.css +++ b/apps/cs/src/app/styles/components.css @@ -7,4 +7,10 @@ .card-border { @apply shadow-blur border border-[#dbe2e6] bg-[rgba(255,255,255,0.6)]; } + .card-header { + @apply -mx-[0.6rem] flex items-center justify-between border-b border-b-[#e1e7eb] p-4; + } + .card-footer { + @apply -mx-[0.6rem] mt-2 flex items-center justify-center border-t border-t-[#e1e7eb] py-4; + } } diff --git a/apps/cs/src/pages/site/model/__mock__.tsx b/apps/cs/src/pages/site/model/__mock__.tsx deleted file mode 100644 index 8d44dbb71..000000000 --- a/apps/cs/src/pages/site/model/__mock__.tsx +++ /dev/null @@ -1,90 +0,0 @@ -"use client"; - -import { createRoot } from "react-dom/client"; -import type { AlignType } from "tui-grid/types/store/column"; -import type { CellRendererProps } from "tui-grid/types/renderer"; - -import { Switch } from "@common/components"; - -class SwitchComponent { - private el: HTMLDivElement; - - constructor(props: CellRendererProps) { - this.el = document.createElement("div"); - - const { value } = props; - - createRoot(this.el).render( - , - ); - } - - getElement() { - return this.el; - } -} - -const data = [ - { - id: 1, - siteCode: "WR", - siteName: "물재이용", - siteUrl: "https://www.hasudoon.kr/wr/", - enabled: true, - active: true, - registrant: "홍길동", - createdAt: "2025-01-01", - }, - { - id: 2, - siteCode: "SN", - siteName: "하수관로", - siteUrl: "https://www.hasudoon.kr/sn/", - enabled: false, - active: true, - registrant: "홍길동", - createdAt: "2025-01-01", - }, - { - id: 3, - siteCode: "CS", - siteName: "통합관리시스템", - siteUrl: "https://www.hasudoon.kr/cs/", - enabled: false, - active: true, - registrant: "홍길동", - createdAt: "2025-01-01", - }, -]; -const columns = [ - { name: "id", header: "NO", align: "center" as AlignType }, - { name: "siteCode", header: "사이트코드", align: "center" as AlignType }, - { name: "siteName", header: "사이트명", align: "center" as AlignType }, - { name: "siteUrl", header: "대표 URL", align: "center" as AlignType }, - { - name: "enabled", - header: "기본 사이트 여부", - align: "center" as AlignType, - renderer: { - type: SwitchComponent, - }, - }, - { - name: "active", - header: "사용 여부", - align: "center" as AlignType, - renderer: { - type: SwitchComponent, - }, - }, - { name: "registrant", header: "등록자", align: "center" as AlignType }, - { name: "createdAt", header: "등록일", align: "center" as AlignType }, -]; -const header = { - align: "center" as AlignType, -}; - -export { data, columns, header }; diff --git a/apps/cs/src/pages/site/model/__mocks__.tsx b/apps/cs/src/pages/site/model/__mocks__.tsx new file mode 100644 index 000000000..1d11afa9a --- /dev/null +++ b/apps/cs/src/pages/site/model/__mocks__.tsx @@ -0,0 +1,59 @@ +import { createColumnHelper } from "@tanstack/react-table"; +import { fakerKO as faker } from "@faker-js/faker"; + +type Site = { + id: number; + 사이트코드: string; + 사이트명: string; + 대표_URL: string; + 기본_사이트_여부: boolean; + 사용_여부: boolean; + 등록자: string; + 등록일: string; +}; + +faker.seed(123); + +const data: Site[] = Array.from({ length: 100 }, (_, i) => ({ + id: i + 1, + 사이트코드: faker.string.alpha(2).toUpperCase(), + 사이트명: faker.lorem.word(), + 대표_URL: faker.internet.url(), + 기본_사이트_여부: faker.datatype.boolean(), + 사용_여부: faker.datatype.boolean(), + 등록자: faker.person.fullName(), + 등록일: faker.date.recent().toISOString().split("T")[0] as string, +})); + +const columnHelper = createColumnHelper(); + +const columns = [ + columnHelper.accessor("id", { + header: "NO", + }), + columnHelper.accessor("사이트코드", { + header: "사이트코드", + }), + columnHelper.accessor("사이트명", { + header: "사이트명", + }), + columnHelper.accessor("대표_URL", { + header: "대표_URL", + }), + columnHelper.accessor("기본_사이트_여부", { + header: "기본 사이트 여부", + cell: (info) => (info.getValue() ? "Y" : "N"), + }), + columnHelper.accessor("사용_여부", { + header: "사용 여부", + cell: (info) => (info.getValue() ? "Y" : "N"), + }), + columnHelper.accessor("등록자", { + header: "등록자", + }), + columnHelper.accessor("등록일", { + header: "등록일", + }), +]; + +export { data, columns }; diff --git a/apps/cs/src/pages/site/ui/index.tsx b/apps/cs/src/pages/site/ui/index.tsx index 413ae9398..edf9a5605 100644 --- a/apps/cs/src/pages/site/ui/index.tsx +++ b/apps/cs/src/pages/site/ui/index.tsx @@ -2,31 +2,13 @@ import "@common/assets/styles/grid.css"; -import TuiGrid from "tui-grid"; -import Grid from "@toast-ui/react-grid"; -import { BaseLayout, Button, Input } from "@common/components"; -import { data, columns, header } from "../model/__mock__"; - -TuiGrid.applyTheme("clean", { - row: { - hover: { - background: "#f5f5f5", - }, - }, - cell: { - normal: { - background: "#fff", - border: "#e0e6ea", - text: "#666", - showHorizontalBorder: true, - }, - header: { - background: "#c6ccd1", - text: "#fff", - showVerticalBorder: false, - }, - }, -}); +import { + BaseLayout, + Button, + Input, + BasicTable as Table, +} from "@common/components"; +import { data, columns } from "../model/__mocks__"; const SitePage = () => { return ( @@ -35,7 +17,7 @@ const SitePage = () => {

사이트 관리

-
+
-
-

사이트 목록

+
+

사이트 목록

-
- +
+ diff --git a/packages/common/components/src/ui/index.ts b/packages/common/components/src/ui/index.ts index 403cd158e..8b8ec807e 100644 --- a/packages/common/components/src/ui/index.ts +++ b/packages/common/components/src/ui/index.ts @@ -15,4 +15,5 @@ export * from "./popover"; export * from "./radio-group"; export * from "./slider"; export * from "./switch"; +export * from "./table"; export * from "./tabs"; diff --git a/packages/common/components/src/ui/table/index.tsx b/packages/common/components/src/ui/table/index.tsx index 100b16c8f..72c2bd18b 100644 --- a/packages/common/components/src/ui/table/index.tsx +++ b/packages/common/components/src/ui/table/index.tsx @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ import "./table.css"; import { @@ -6,14 +5,45 @@ import { getCoreRowModel, useReactTable, type ColumnDef, + type RowData, + type Table, } from "@tanstack/react-table"; +declare module "@tanstack/react-table" { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + interface ColumnMeta { + align: "left" | "center" | "right"; + } +} + +function TableHead({ table }: { table: Table }) { + return ( + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + + ))} + + ))} + + ); +} + interface TableProps { data: T[]; + // eslint-disable-next-line @typescript-eslint/no-explicit-any columns: ColumnDef[]; } -function Table({ data, columns }: TableProps) { +function BasicTable({ data, columns }: TableProps) { const table = useReactTable({ data, columns, @@ -21,36 +51,23 @@ function Table({ data, columns }: TableProps) { }); return ( -
-
+ {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext(), + )} +
- - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => ( - - ))} - - ))} - - - {table.getRowModel().rows.map((row) => ( - - {row.getVisibleCells().map((cell) => ( -
- {header.isPlaceholder - ? null - : flexRender( - header.column.columnDef.header, - header.getContext(), - )} -
+ + + + {table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => { + const { meta } = cell.column.columnDef; + return ( + - ))} - - ))} - - {/* + ); + })} + + ))} + + {/* {table.getFooterGroups().map((footerGroup) => ( {footerGroup.headers.map((header) => ( @@ -66,9 +83,8 @@ function Table({ data, columns }: TableProps) { ))} */} -
{flexRender(cell.column.columnDef.cell, cell.getContext())}
- +
); } -export { Table }; +export { BasicTable }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8533bb2cb..3d7c23e28 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -36,6 +36,9 @@ importers: '@common/components': specifier: workspace:* version: link:../../packages/common/components + '@tanstack/react-table': + specifier: ^8.21.2 + version: 8.21.2(react-dom@18.3.1)(react@18.3.1) next: specifier: 15.1.7 version: 15.1.7(react-dom@18.3.1)(react@18.3.1) @@ -43,6 +46,9 @@ importers: '@eslint/eslintrc': specifier: ^3 version: 3.3.0 + '@faker-js/faker': + specifier: ^9.5.0 + version: 9.5.0 '@types/node': specifier: ^22.13.1 version: 22.13.5 @@ -976,6 +982,11 @@ packages: levn: 0.4.1 dev: true + /@faker-js/faker@9.5.0: + resolution: {integrity: sha512-3qbjLv+fzuuCg3umxc9/7YjrEXNaKwHgmig949nfyaTx8eL4FAsvFbu+1JcFUj1YAXofhaDn6JdEUBTYuk0Ssw==} + engines: {node: '>=18.0.0', npm: '>=9.0.0'} + dev: true + /@feature-sliced/filesystem@2.4.0: resolution: {integrity: sha512-aOFdPACNvkFXYb+gNHId8UCRD5fm7Z9/RV6JgBPlgEvm1QtLS4idg2XeHMDGnHrNRCS4ki+0RauxVZxm+g9pvw==} dependencies: