From ee2301470a0149336a39566a84c285b1878a316c Mon Sep 17 00:00:00 2001 From: bicochan Date: Tue, 17 Mar 2026 14:51:30 +0900 Subject: [PATCH] =?UTF-8?q?[st]=20feat:=20=ED=86=B5=EA=B3=84=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=20=EC=83=81=EC=84=B8=20=ED=85=8C=EC=9D=B4=EB=B8=94=20?= =?UTF-8?q?=EC=B2=B4=ED=81=AC=EB=B0=95=EC=8A=A4=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../detail/HbVerificationDetail.tsx | 3 + .../verification/detail/RegionalGrid.tsx | 86 +------------- .../verification/detail/VerificationList.tsx | 86 +++----------- .../statistics/model/regional-grid-columns.ts | 83 ++++++++++++++ .../model/verification-list-columns.tsx | 108 ++++++++++++++++++ 5 files changed, 210 insertions(+), 156 deletions(-) create mode 100644 apps/st/src/entities/statistics/model/regional-grid-columns.ts create mode 100644 apps/st/src/entities/statistics/model/verification-list-columns.tsx diff --git a/apps/st/src/components/statistics/verification/detail/HbVerificationDetail.tsx b/apps/st/src/components/statistics/verification/detail/HbVerificationDetail.tsx index a9e267506..c95b771aa 100644 --- a/apps/st/src/components/statistics/verification/detail/HbVerificationDetail.tsx +++ b/apps/st/src/components/statistics/verification/detail/HbVerificationDetail.tsx @@ -26,6 +26,7 @@ const HbVerificationDetail: React.FC = ({ const [selectedVrfcTrgtSn, setSelectedVrfcTrgtSn] = useState( null, ); + const [checkedSns, setCheckedSns] = useState([]); const [appliedFilter, setAppliedFilter] = useState({ sido: "", gugun: "", @@ -75,6 +76,8 @@ const HbVerificationDetail: React.FC = ({ anlsSn={anlsSn} selectedVrfcTrgtSn={selectedVrfcTrgtSn} onSelect={handleTargetSelect} + checkedSns={checkedSns} + onCheck={setCheckedSns} /> diff --git a/apps/st/src/components/statistics/verification/detail/RegionalGrid.tsx b/apps/st/src/components/statistics/verification/detail/RegionalGrid.tsx index 1a691283d..1850ac456 100644 --- a/apps/st/src/components/statistics/verification/detail/RegionalGrid.tsx +++ b/apps/st/src/components/statistics/verification/detail/RegionalGrid.tsx @@ -1,12 +1,12 @@ import React, { useEffect, useMemo, useState } from "react"; import { useQuery } from "@tanstack/react-query"; -import { ValueType } from "realgrid"; import { ENDPOINT } from "@/shared/config"; import { CodeApi } from "@/entities/code/api/code-service"; import { useStAnlsRsltList } from "@/entities/statistics/api/queries"; import { stAnlsRsltApi } from "@/entities/statistics/api/statistics-analysis-result-api"; +import { regionalGridColumnInfo as columnInfo } from "@/entities/statistics/model/regional-grid-columns"; import type { StAnlsRsltResponse } from "@/entities/statistics/model/statistics-analysis-result-interface"; import { Button, @@ -21,7 +21,6 @@ import { import type { AppliedFilter } from "./HbVerificationDetail"; import DataGrid from "@/components/module/DataGrid"; -import type { ColumnInfo } from "@/components/type/DataGrid.type"; interface RegionalGridProps { anlsSn: number; @@ -148,89 +147,6 @@ const RegionalGrid: React.FC = ({ gugunOptions, ]); - const columnInfo: ColumnInfo = useMemo( - () => ({ - columns: [ - { - name: "sido", - fieldName: "sido", - header: { text: "시도" }, - width: 80, - }, - { - name: "gugun", - fieldName: "gugun", - header: { text: "구군" }, - width: 80, - }, - { - name: "admdstNm", - fieldName: "admdstNm", - header: { text: "행정구역" }, - width: 100, - }, - { - name: "prvyrVl", - fieldName: "prvyrVl", - header: { text: "이전년도" }, - width: 80, - valueType: ValueType.NUMBER, - dataFormat: "#,##0", - }, - { - name: "cryrVl", - fieldName: "cryrVl", - header: { text: "당해년도" }, - width: 80, - valueType: ValueType.NUMBER, - dataFormat: "#,##0", - }, - { - name: "chgRt", - fieldName: "chgRt", - header: { text: "R" }, - width: 60, - valueType: ValueType.NUMBER, - dataFormat: "#,##0.00", - }, - { - name: "trsfRt", - fieldName: "trsfRt", - header: { text: "S" }, - width: 60, - valueType: ValueType.NUMBER, - dataFormat: "#,##0.00", - }, - { - name: "otlrTrsfRt", - fieldName: "otlrTrsfRt", - header: { text: "E" }, - width: 60, - valueType: ValueType.NUMBER, - dataFormat: "#,##0.00", - }, - { - name: "otlrYn", - fieldName: "otlrYn", - header: { text: "상태" }, - width: 60, - }, - ], - columnLayout: [ - "sido", - "gugun", - "admdstNm", - "prvyrVl", - "cryrVl", - "chgRt", - "trsfRt", - "otlrTrsfRt", - "otlrYn", - ], - }), - [], - ); - const handleSidoChange = (val: string) => { setSelectedSido(val); setSelectedGugun(""); diff --git a/apps/st/src/components/statistics/verification/detail/VerificationList.tsx b/apps/st/src/components/statistics/verification/detail/VerificationList.tsx index 5c2f89065..84f3b17ba 100644 --- a/apps/st/src/components/statistics/verification/detail/VerificationList.tsx +++ b/apps/st/src/components/statistics/verification/detail/VerificationList.tsx @@ -2,44 +2,31 @@ import React, { useEffect, useMemo } from "react"; import { useQuery } from "@tanstack/react-query"; import { - type ColumnDef, getCoreRowModel, useReactTable, } from "@tanstack/react-table"; import { ENDPOINT } from "@/shared/config"; import { stAnlsDtlApi } from "@/entities/statistics/api/statistics-analysis-detail-api"; -import { verificationTargetManagementApi } from "@/entities/statistics/api/verification-target-management-service"; import type { StAnlsDtlResponse } from "@/entities/statistics/model/statistics-analysis-detail-interface"; +import { getVerificationListColumns } from "@/entities/statistics/model/verification-list-columns"; import { DataTable } from "@common/components"; interface VerificationListProps { anlsSn: number; selectedVrfcTrgtSn: string | null; onSelect: (row: StAnlsDtlResponse) => void; + checkedSns: string[]; + onCheck: (checkedSns: string[], rows: StAnlsDtlResponse[]) => void; } const VerificationList: React.FC = ({ anlsSn, selectedVrfcTrgtSn, onSelect, + checkedSns, + onCheck, }) => { - const { data: verificationTargetCodes } = useQuery({ - queryKey: [ENDPOINT.STATVERIFY_SERVICE.VRFCTGT_MNG, "codes"], - queryFn: () => - verificationTargetManagementApi.getVerificationTargetCodes(), - }); - - const targetCodeMap = useMemo(() => { - const map = new Map(); - if (verificationTargetCodes) { - verificationTargetCodes.forEach((item) => { - map.set(item.vrfcTrgtSn, item.vrfcTrgtNm); - }); - } - return map; - }, [verificationTargetCodes]); - const { data: detailData, isLoading } = useQuery({ queryKey: [ENDPOINT.STATVERIFY_SERVICE.ST_ANLS_DTL, "list", anlsSn], queryFn: () => @@ -51,50 +38,9 @@ const VerificationList: React.FC = ({ return (detailData?.content ?? []) as StAnlsDtlResponse[]; }, [detailData]); - const columns: ColumnDef[] = useMemo( - () => [ - { - header: "통계 년도", - accessorKey: "statsYr", - }, - { - header: "통계 항목", - accessorKey: "statsNm", - }, - { - header: "검증 항목", - accessorKey: "vrfcTrgtNm", - }, - { - header: "μ", - accessorKey: "ipcrCntVl", - }, - { - header: "Percentile", - accessorKey: "pctlCrtrVl", - }, - { - header: "A", - accessorKey: "scopAjmtCntVl", - }, - { - header: "C", - accessorKey: "thrsCntVl", - }, - { - header: "정상건수", - accessorKey: "nmlDataNocs", - }, - { - header: "제외건수", - accessorKey: "exclDataNocs", - }, - { - header: "이상건수", - accessorKey: "otlrDataNocs", - }, - ], - [targetCodeMap], + const columns = useMemo( + () => getVerificationListColumns({ rowData, checkedSns, onCheck }), + [rowData, checkedSns, onCheck], ); const table = useReactTable({ @@ -134,15 +80,13 @@ const VerificationList: React.FC = ({ )} {!isLoading && rowData.length > 0 && ( -
- -
+ )} ); diff --git a/apps/st/src/entities/statistics/model/regional-grid-columns.ts b/apps/st/src/entities/statistics/model/regional-grid-columns.ts new file mode 100644 index 000000000..5c324b398 --- /dev/null +++ b/apps/st/src/entities/statistics/model/regional-grid-columns.ts @@ -0,0 +1,83 @@ +import { ValueType } from "realgrid"; + +import type { ColumnInfo } from "@/components/type/DataGrid.type"; + +export const regionalGridColumnInfo: ColumnInfo = { + columns: [ + { + name: "sido", + fieldName: "sido", + header: { text: "시도" }, + width: 80, + }, + { + name: "gugun", + fieldName: "gugun", + header: { text: "구군" }, + width: 80, + }, + { + name: "admdstNm", + fieldName: "admdstNm", + header: { text: "행정구역" }, + width: 100, + }, + { + name: "prvyrVl", + fieldName: "prvyrVl", + header: { text: "이전년도" }, + width: 80, + valueType: ValueType.NUMBER, + dataFormat: "#,##0", + }, + { + name: "cryrVl", + fieldName: "cryrVl", + header: { text: "당해년도" }, + width: 80, + valueType: ValueType.NUMBER, + dataFormat: "#,##0", + }, + { + name: "chgRt", + fieldName: "chgRt", + header: { text: "R" }, + width: 60, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.00", + }, + { + name: "trsfRt", + fieldName: "trsfRt", + header: { text: "S" }, + width: 60, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.00", + }, + { + name: "otlrTrsfRt", + fieldName: "otlrTrsfRt", + header: { text: "E" }, + width: 60, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.00", + }, + { + name: "otlrYn", + fieldName: "otlrYn", + header: { text: "상태" }, + width: 60, + }, + ], + columnLayout: [ + "sido", + "gugun", + "admdstNm", + "prvyrVl", + "cryrVl", + "chgRt", + "trsfRt", + "otlrTrsfRt", + "otlrYn", + ], +}; diff --git a/apps/st/src/entities/statistics/model/verification-list-columns.tsx b/apps/st/src/entities/statistics/model/verification-list-columns.tsx new file mode 100644 index 000000000..d2c503add --- /dev/null +++ b/apps/st/src/entities/statistics/model/verification-list-columns.tsx @@ -0,0 +1,108 @@ +import React from "react"; + +import type { ColumnDef } from "@tanstack/react-table"; + +import type { StAnlsDtlResponse } from "@/entities/statistics/model/statistics-analysis-detail-interface"; +import { Checkbox } from "@common/components"; + +interface GetVerificationListColumnsParams { + rowData: StAnlsDtlResponse[]; + checkedSns: string[]; + onCheck: (checkedSns: string[], rows: StAnlsDtlResponse[]) => void; +} + +export const getVerificationListColumns = ({ + rowData, + checkedSns, + onCheck, +}: GetVerificationListColumnsParams): ColumnDef[] => [ + { + id: "checkbox", + header: () => { + const isAllChecked = + rowData.length > 0 && checkedSns.length === rowData.length; + return ( +
+ { + if (value) { + onCheck( + rowData.map((r) => String(r.vrfcTrgtSn)), + rowData, + ); + } else { + onCheck([], []); + } + }} + aria-label="Select all rows" + /> +
+ ); + }, + cell: ({ row }) => { + const rowId = String(row.original.vrfcTrgtSn); + const isChecked = checkedSns.includes(rowId); + return ( +
+ { + const newCheckedSns = value + ? [...checkedSns, rowId] + : checkedSns.filter((id) => id !== rowId); + const newCheckedRows = rowData.filter((r) => + newCheckedSns.includes(String(r.vrfcTrgtSn)), + ); + onCheck(newCheckedSns, newCheckedRows); + }} + onClick={(e: React.MouseEvent) => e.stopPropagation()} + aria-label={`Select row ${rowId}`} + /> +
+ ); + }, + enableSorting: false, + enableHiding: false, + }, + { + header: "통계 년도", + accessorKey: "statsYr", + }, + { + header: "통계 항목", + accessorKey: "statsNm", + }, + { + header: "검증 항목", + accessorKey: "vrfcTrgtNm", + }, + { + header: "μ", + accessorKey: "ipcrCntVl", + }, + { + header: "Percentile", + accessorKey: "pctlCrtrVl", + }, + { + header: "A", + accessorKey: "scopAjmtCntVl", + }, + { + header: "C", + accessorKey: "thrsCntVl", + }, + { + header: "정상건수", + accessorKey: "nmlDataNocs", + }, + { + header: "제외건수", + accessorKey: "exclDataNocs", + }, + { + header: "이상건수", + accessorKey: "otlrDataNocs", + }, +];