Merge commit '63cc81cd3ffe7ef5c5bce7c7b8f208b72b3072a9'
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
<Switch
|
||||
defaultChecked={value as boolean}
|
||||
className="!h-[1.8rem] !w-[4.2rem] !bg-[length:2.4rem] !bg-[center_right_.1rem] data-[state=checked]:!bg-[center_left_.2rem] [&>span]:!size-[1.6rem] [&>span]:data-[state=checked]:!translate-x-[2.6rem]"
|
||||
/>,
|
||||
);
|
||||
}
|
||||
|
||||
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 };
|
||||
@@ -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<Site>();
|
||||
|
||||
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 };
|
||||
@@ -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 = () => {
|
||||
<h2 className="px-[1.8rem] pt-[2rem] text-[2.4rem] font-semibold text-[#4c5e6f]">
|
||||
사이트 관리
|
||||
</h2>
|
||||
<div className="card">
|
||||
<div className="card card-border">
|
||||
<div className="card card-border !border-[#dbe2e6] !bg-[#eef7ff]">
|
||||
<div className="flex items-center gap-2 p-2">
|
||||
<label htmlFor="" className="mr-2 text-[#657481]">
|
||||
@@ -46,20 +28,12 @@ const SitePage = () => {
|
||||
</div>
|
||||
</div>
|
||||
<div className="card card-border">
|
||||
<div className="flex items-center justify-between p-2">
|
||||
<h3>사이트 목록</h3>
|
||||
<div className="card-header">
|
||||
<h3 className="text-2xl font-medium text-[#666]">사이트 목록</h3>
|
||||
<Button className="">등록</Button>
|
||||
</div>
|
||||
<div className="card">
|
||||
<Grid
|
||||
data={data}
|
||||
columns={columns}
|
||||
header={header}
|
||||
rowHeight={25}
|
||||
bodyHeight={100}
|
||||
heightResizable={true}
|
||||
usageStatistics={false}
|
||||
/>
|
||||
<div className="card !m-[1.2rem] h-[44.5rem] overflow-auto !p-0">
|
||||
<Table data={data} columns={columns} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -15,4 +15,5 @@ export * from "./popover";
|
||||
export * from "./radio-group";
|
||||
export * from "./slider";
|
||||
export * from "./switch";
|
||||
export * from "./table";
|
||||
export * from "./tabs";
|
||||
|
||||
@@ -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<TData extends RowData, TValue> {
|
||||
align: "left" | "center" | "right";
|
||||
}
|
||||
}
|
||||
|
||||
function TableHead<T>({ table }: { table: Table<T> }) {
|
||||
return (
|
||||
<thead>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<tr key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => (
|
||||
<th key={header.id} className="sticky top-0">
|
||||
{header.isPlaceholder
|
||||
? null
|
||||
: flexRender(
|
||||
header.column.columnDef.header,
|
||||
header.getContext(),
|
||||
)}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</thead>
|
||||
);
|
||||
}
|
||||
|
||||
interface TableProps<T> {
|
||||
data: T[];
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
columns: ColumnDef<T, any>[];
|
||||
}
|
||||
|
||||
function Table<T>({ data, columns }: TableProps<T>) {
|
||||
function BasicTable<T>({ data, columns }: TableProps<T>) {
|
||||
const table = useReactTable({
|
||||
data,
|
||||
columns,
|
||||
@@ -21,36 +51,23 @@ function Table<T>({ data, columns }: TableProps<T>) {
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="p-2">
|
||||
<table>
|
||||
<thead>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<tr key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => (
|
||||
<th key={header.id}>
|
||||
{header.isPlaceholder
|
||||
? null
|
||||
: flexRender(
|
||||
header.column.columnDef.header,
|
||||
header.getContext(),
|
||||
)}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</thead>
|
||||
<tbody>
|
||||
{table.getRowModel().rows.map((row) => (
|
||||
<tr key={row.id}>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<td key={cell.id}>
|
||||
<table className="w-full">
|
||||
<TableHead table={table} />
|
||||
<tbody>
|
||||
{table.getRowModel().rows.map((row) => (
|
||||
<tr key={row.id}>
|
||||
{row.getVisibleCells().map((cell) => {
|
||||
const { meta } = cell.column.columnDef;
|
||||
return (
|
||||
<td key={cell.id} align="center" {...meta}>
|
||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
{/* <tfoot>
|
||||
);
|
||||
})}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
{/* <tfoot>
|
||||
{table.getFooterGroups().map((footerGroup) => (
|
||||
<tr key={footerGroup.id}>
|
||||
{footerGroup.headers.map((header) => (
|
||||
@@ -66,9 +83,8 @@ function Table<T>({ data, columns }: TableProps<T>) {
|
||||
</tr>
|
||||
))}
|
||||
</tfoot> */}
|
||||
</table>
|
||||
</div>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
|
||||
export { Table };
|
||||
export { BasicTable };
|
||||
|
||||
Generated
+11
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user