From 8c384a51ce9178b86b69dd6dbc914553424b235a Mon Sep 17 00:00:00 2001 From: bicochan Date: Wed, 11 Mar 2026 14:35:57 +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=99=94=EB=A9=B4=20=EC=9E=91?= =?UTF-8?q?=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../detail/HbVerificationDetail.tsx | 22 +- .../verification/detail/Indicators.tsx | 9 +- .../verification/detail/RegionalGrid.tsx | 220 +++++++++++------- .../verification/detail/ScatterChart.tsx | 9 +- .../verification/detail/VerificationList.tsx | 5 + .../statistics-analysis-detail-interface.ts | 4 + 6 files changed, 162 insertions(+), 107 deletions(-) diff --git a/apps/st/src/components/statistics/verification/detail/HbVerificationDetail.tsx b/apps/st/src/components/statistics/verification/detail/HbVerificationDetail.tsx index 90ffd5350..359b25a67 100644 --- a/apps/st/src/components/statistics/verification/detail/HbVerificationDetail.tsx +++ b/apps/st/src/components/statistics/verification/detail/HbVerificationDetail.tsx @@ -62,20 +62,16 @@ const HbVerificationDetail: React.FC = ({ />
-
- -
+ -
- -
+
diff --git a/apps/st/src/components/statistics/verification/detail/Indicators.tsx b/apps/st/src/components/statistics/verification/detail/Indicators.tsx index eaabf7849..f311e1667 100644 --- a/apps/st/src/components/statistics/verification/detail/Indicators.tsx +++ b/apps/st/src/components/statistics/verification/detail/Indicators.tsx @@ -4,6 +4,7 @@ import { useQuery } from "@tanstack/react-query"; import { ENDPOINT } from "@/shared/config"; import { stAnlsRsltApi } from "@/entities/statistics/api/statistics-analysis-result-api"; +import { LoadingWithAnimation } from "@common/components"; interface IndicatorsProps { anlsSn: number; @@ -34,8 +35,10 @@ const Indicators: React.FC = ({ anlsSn, vrfcTrgtSn }) => { if (isLoading) { return ( -
- 지표 데이터를 불러오는 중... +
+
+ +
); } @@ -61,7 +64,7 @@ const Indicators: React.FC = ({ anlsSn, vrfcTrgtSn }) => { ); return ( -
+
= ({ status: "", }); + const [gridRowData, setGridRowData] = useState< + Record[] + >([]); + const { data: regionCodes } = useQuery({ queryKey: [ENDPOINT.CMS_SERVICE.CODE, "LAESECD"], queryFn: () => CodeApi.getCode("LAESECD"), @@ -99,34 +100,45 @@ const RegionalGrid: React.FC = ({ !appliedFilter.gugun && !appliedFilter.status ) { - return showAllOnEmptyFilter ? data : []; - } + data = showAllOnEmptyFilter ? data : []; + } else { + if (appliedFilter.sido) { + const sidoName = sidoOptions.find( + (opt) => opt.lwpoCd === appliedFilter.sido, + )?.lwpoCdNm; + if (sidoName) { + data = data.filter((item) => item.sido === sidoName); + } + } - if (appliedFilter.sido) { - const sidoName = sidoOptions.find( - (opt) => opt.lwpoCd === appliedFilter.sido, - )?.lwpoCdNm; - if (sidoName) { - data = data.filter((item) => item.sido === sidoName); + if (appliedFilter.gugun) { + const gugunName = gugunOptions.find( + (opt) => opt.lwpoCd === appliedFilter.gugun, + )?.lwpoCdNm; + if (gugunName) { + data = data.filter((item) => + gugunName.includes(item.gugun), + ); + } + } + + if (appliedFilter.status) { + data = data.filter( + (item) => + item.otlrYn.toLowerCase() === appliedFilter.status, + ); } } - if (appliedFilter.gugun) { - const gugunName = gugunOptions.find( - (opt) => opt.lwpoCd === appliedFilter.gugun, - )?.lwpoCdNm; - if (gugunName) { - data = data.filter((item) => gugunName.includes(item.gugun)); - } - } + const mappedData = data.map((item) => ({ + ...item, + otlrYn: item.otlrYn === "0" ? "정상" : "이상치", + })); - if (appliedFilter.status) { - data = data.filter( - (item) => item.otlrYn.toLowerCase() === appliedFilter.status, - ); - } - - return data; + setGridRowData( + mappedData as unknown as Record[], + ); + return mappedData; }, [ resultData, appliedFilter, @@ -135,60 +147,89 @@ const RegionalGrid: React.FC = ({ gugunOptions, ]); - const columns: ColumnDef[] = useMemo( - () => [ - { header: "시도", accessorKey: "sido", size: 80 }, - { header: "구군", accessorKey: "gugun", size: 80 }, - { header: "행정구역", accessorKey: "admdstNm", size: 100 }, - { header: "이전년도", accessorKey: "prvyrVl", size: 80 }, - { header: "당해년도", accessorKey: "cryrVl", size: 80 }, - { - header: "R", - accessorKey: "chgRt", - size: 60, - cell: (info) => Number(info.getValue() ?? 0).toFixed(2), - }, - { - header: "S", - accessorKey: "trsfRt", - size: 60, - cell: (info) => Number(info.getValue() ?? 0).toFixed(2), - }, - { - header: "E", - accessorKey: "otlrTrsfRt", - size: 60, - cell: (info) => Number(info.getValue() ?? 0).toFixed(2), - }, - { - header: "상태", - accessorKey: "otlrYn", - size: 60, - cell: (info) => { - const val = info.getValue(); - return ( - - {val === "0" ? "정상" : "이상치"} - - ); + 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 table = useReactTable({ - data: rowData, - columns, - getCoreRowModel: getCoreRowModel(), - getPaginationRowModel: getPaginationRowModel(), - initialState: { - pagination: { pageSize: 10 }, - }, - }); - const handleSidoChange = (val: string) => { onSidoSelect(!val ? null : val); setSelectedGugun(""); @@ -258,8 +299,8 @@ const RegionalGrid: React.FC = ({ - 정상 - 이상치 + 정상 + 이상치
@@ -269,7 +310,9 @@ const RegionalGrid: React.FC = ({ 초기화 - +
@@ -289,11 +332,12 @@ const RegionalGrid: React.FC = ({ )} {!isLoading && rowData.length > 0 && ( - )} diff --git a/apps/st/src/components/statistics/verification/detail/ScatterChart.tsx b/apps/st/src/components/statistics/verification/detail/ScatterChart.tsx index 97eef64c3..be23841b1 100644 --- a/apps/st/src/components/statistics/verification/detail/ScatterChart.tsx +++ b/apps/st/src/components/statistics/verification/detail/ScatterChart.tsx @@ -17,6 +17,7 @@ import { ENDPOINT } from "@/shared/config"; import { CodeApi } from "@/entities/code/api/code-service"; import { stAnlsRsltApi } from "@/entities/statistics/api/statistics-analysis-result-api"; import type { StAnlsRsltResponse } from "@/entities/statistics/model/statistics-analysis-result-interface"; +import { LoadingWithAnimation } from "@common/components"; ChartJS.register( LinearScale, @@ -231,11 +232,13 @@ const ScatterChart: React.FC = ({ 항목을 선택해주세요 ) : isLoading ? ( -
- 차트를 불러오는 중... +
+
+ +
) : chartConfig ? ( -
+
= ({ const table = useReactTable({ data: rowData, columns, + state: { + rowSelection: selectedVrfcTrgtSn ? { [selectedVrfcTrgtSn]: true } : {}, + }, + enableMultiRowSelection: false, + getRowId: (row) => String(row.vrfcTrgtSn), getCoreRowModel: getCoreRowModel(), }); diff --git a/apps/st/src/entities/statistics/model/statistics-analysis-detail-interface.ts b/apps/st/src/entities/statistics/model/statistics-analysis-detail-interface.ts index 3f6eb9003..af0919b87 100644 --- a/apps/st/src/entities/statistics/model/statistics-analysis-detail-interface.ts +++ b/apps/st/src/entities/statistics/model/statistics-analysis-detail-interface.ts @@ -18,6 +18,10 @@ export interface StAnlsDtlResponse { nmlDataNocs: number; // 정상 데이터 건수 exclDataNocs: number; // 제외 데이터 건수 otlrDataNocs: number; // 이상치 데이터 건수 + statsCd: string; // 통계 코드 + statsNm: string; // 통계 항목 명 + vrfcTrgtNm: string; // 검증대상명 + statsYr: string; // 통계년도 } export interface StAnlsDtlSearchParams extends PageableParams {