From d19b74207bd8e0360de99a4c855f96c4631d9c7f Mon Sep 17 00:00:00 2001 From: zio Date: Mon, 13 Jul 2026 05:38:57 +0900 Subject: [PATCH] feat(web): apply KxDataTable to common-code admin (4th screen, per-column control demo) Co-Authored-By: Claude Fable 5 --- .../src/screens/admin/CommonCodeAdminPage.tsx | 103 +++++++++--------- .../src/screens/admin/admin-modules.css | 3 + 2 files changed, 54 insertions(+), 52 deletions(-) diff --git a/src/frontend/src/screens/admin/CommonCodeAdminPage.tsx b/src/frontend/src/screens/admin/CommonCodeAdminPage.tsx index feef8a6..336737f 100644 --- a/src/frontend/src/screens/admin/CommonCodeAdminPage.tsx +++ b/src/frontend/src/screens/admin/CommonCodeAdminPage.tsx @@ -4,11 +4,12 @@ * DELETE /api/admin/codes/groups/{grp} · DELETE /api/admin/codes/{grp}/{code} (ADMIN). * UI: 좌 그룹 목록(선택·추가), 우 코드 값 테이블(추가/수정/삭제 모달). 저장은 upsert(코드 존재 시 갱신). */ -import { useEffect, useState } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { useTranslation } from 'react-i18next'; import { EmptyState, ErrorState, Skeleton } from '../../components/ui/States'; import { Button } from '../../components/ui/Button'; +import { KxDataTable, type KxColumn } from '../../components/KxDataTable'; import { IconPlus } from '../../components/ui/icons'; import { errMessage, useToast } from '../work/workShared'; import { codeApi, type Code, type CodeGroup } from './adminCrudApi'; @@ -77,6 +78,44 @@ export function CommonCodeAdminPage() { const codes = codesQ.data ?? []; + // 코드 값 테이블 컬럼 — 검색·정렬·페이지·CSV 는 KxDataTable 담당(관리 열은 정렬/검색/CSV 제외). + const codeColumns = useMemo[]>( + () => [ + { key: 'code', header: t('code.code'), value: (c) => c.code, + render: (c) => {c.code} }, + { key: 'name', header: t('code.name'), value: (c) => c.codeName, + render: (c) => {c.codeName} }, + { key: 'value', header: t('code.value'), value: (c) => c.codeValue ?? '', + render: (c) => {c.codeValue ?? '—'} }, + { key: 'sort', header: t('code.sort'), numeric: true, value: (c) => c.sortOrder }, + { key: 'use', header: t('code.use'), value: (c) => (c.useYn === 'N' ? t('code.unused') : t('code.used')), + render: (c) => ( + + {c.useYn === 'N' ? t('code.unused') : t('code.used')} + + ) }, + { key: 'manage', header: t('code.manage'), csv: false, sortable: false, searchable: false, + thClassName: 'kx-adm-col-manage', tdClassName: 'kx-adm-col-manage', + render: (c) => ( +
+ + +
+ ) }, + ], + [t, delCodeM.isPending, delCodeM], + ); + return (
@@ -153,57 +192,17 @@ export function CommonCodeAdminPage() {
)} {!codesQ.isLoading && !codesQ.isError && ( - codes.length === 0 ? ( -
- -
- ) : ( -
- - - - - - - - - - - - - {codes.map((c) => ( - - - - - - - - - ))} - -
{t('code.code')}{t('code.name')}{t('code.value')}{t('code.sort')}{t('code.use')}{t('code.manage')}
{c.code}{c.codeName}{c.codeValue ?? '—'}{c.sortOrder} - - {c.useYn === 'N' ? t('code.unused') : t('code.used')} - - -
- - -
-
-
- ) +
+ c.code} + csvFileName={`codes_${selected ?? 'group'}`} + ariaLabel={t('code.codeValues')} + emptyTitle={t('code.noCodesTitle')} + emptyDescription={t('code.noCodesDesc')} + /> +
)} diff --git a/src/frontend/src/screens/admin/admin-modules.css b/src/frontend/src/screens/admin/admin-modules.css index 45350d8..15caa13 100644 --- a/src/frontend/src/screens/admin/admin-modules.css +++ b/src/frontend/src/screens/admin/admin-modules.css @@ -551,6 +551,9 @@ border-bottom: var(--border-card); } .kx-adm-codes__head h2 { font-size: var(--fs-h3); font-weight: var(--fw-bold); color: var(--color-neutral-900); } +/* 코드 값 KxDataTable 래퍼 — 카드 padding:0 을 상쇄(툴바·표·페이저 여백) */ +.kx-adm-codes__tbl { padding: var(--space-3) var(--space-4) var(--space-4); } +.kx-adm-col-manage { text-align: right; } .kx-adm-glist { list-style: none; margin: 0; padding: var(--space-2); display: flex; flex-direction: column; gap: 4px; } .kx-adm-gitem { width: 100%;