From 9a24e4aa234c28663fee0dfe34d27834de582ea4 Mon Sep 17 00:00:00 2001 From: rsone001 Date: Thu, 22 Oct 2026 16:48:34 +0900 Subject: [PATCH] =?UTF-8?q?[ST]=20-=20=EC=9E=85=EB=A0=A5=20verificationRes?= =?UTF-8?q?ult=20-=20cmprCn,=20nowCn=20=EA=B0=92=20null=20=EC=9D=BC?= =?UTF-8?q?=EC=8B=9C=20=EC=B9=98=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../input/ExcretaTreatmentFacilityInp.tsx | 26 +++---- .../input/PrivateSewageFacilityInp.tsx | 23 +++--- .../input/PublicSewageFacilityInp.tsx | 30 ++++---- .../statistics/input/PumpingStationInp.tsx | 26 +++---- .../input/SewageExcretaRelatedInp.tsx | 22 ++---- .../input/SewageExcretaSlurgeInp.tsx | 22 ++---- .../statistics/input/SewagePipeInp.tsx | 76 ++++++++++--------- .../input/SewagePipeMaintainInp.tsx | 35 ++++----- .../input/SewageReservoirPumpInp.tsx | 24 ++---- .../statistics/input/SewerageFinanceInp.tsx | 23 +++--- .../input/SeweragePenetrationRateInp.tsx | 23 +++--- .../input/SewerageServiceLevelInp.tsx | 23 +++--- .../statistics/input/WaterReuseInp.tsx | 35 ++++----- .../util/input/verification-failed-flag.ts | 10 ++- .../util/input/verification-result-display.ts | 47 +++++++++++- 15 files changed, 224 insertions(+), 221 deletions(-) diff --git a/apps/st/src/components/statistics/input/ExcretaTreatmentFacilityInp.tsx b/apps/st/src/components/statistics/input/ExcretaTreatmentFacilityInp.tsx index 35ea779f2..87df51eca 100644 --- a/apps/st/src/components/statistics/input/ExcretaTreatmentFacilityInp.tsx +++ b/apps/st/src/components/statistics/input/ExcretaTreatmentFacilityInp.tsx @@ -75,7 +75,11 @@ import { toGridRows } from "@/components/util/grid/gridFunction"; import { formatCompactYmdForGrid, toCompactYmdOrNull } from "@/components/util/grid/ymdFormat"; import { toExcelExportSearchContextFromSearchForm } from "@/components/util/gridExcelExport"; import { isVerificationFailedFromCommonInfo } from "@/components/util/input/verification-failed-flag"; -import type { VerificationResultLikeMap } from "@/components/util/input/verification-result-display"; +import { + formatVerificationTooltip, + normalizeVerificationResultMap, + type VerificationResultLikeMap, +} from "@/components/util/input/verification-result-display"; import { useAdminStore } from "@/store/admin-store"; import { useTabStore } from "@/store/tab-store"; @@ -207,7 +211,7 @@ function mapExcretaTreatmentFacilityListToGridRows( }, ])[0] as GridRowWithVerification; if (item.verificationResult) { - row.verificationResult = item.verificationResult; + row.verificationResult = normalizeVerificationResultMap(item.verificationResult); } return row; }); @@ -502,7 +506,9 @@ const ExcretaTreatmentFacilityInp = () => { setStatusInfo(nextStatusInfo); setStatsCd(commonInfo.statsCd); setIsVerificationFailed(isVerificationFailedFromCommonInfo(commonInfo)); - setFooterVerificationResult(commonInfo.verificationResult ?? null); + setFooterVerificationResult( + normalizeVerificationResultMap(commonInfo.verificationResult) ?? null, + ); if (overrides?.mode !== "PRE") { setIsNotApplicable(gridRows.length === 0); } else if (gridRows.length > 0) { @@ -952,19 +958,7 @@ const ExcretaTreatmentFacilityInp = () => { if (!verification) return; const errors = verification[fieldName]; if (Array.isArray(errors) && errors.length > 0) { - return errors - .map((e) => { - const prefix = - e.vrfcCd === "IMR" - ? "시계열 검증 - " - : e.vrfcCd === "NV" - ? "일반검증 - " - : "모집단 검증 -"; - return `${prefix}${e.rsnExpln} -(검증 기준값: ${e.cmprCn} / 현재 입력값: ${e.nowCn})`; - }) - .filter(Boolean) - .join("\n"); + return formatVerificationTooltip(errors); } }; diff --git a/apps/st/src/components/statistics/input/PrivateSewageFacilityInp.tsx b/apps/st/src/components/statistics/input/PrivateSewageFacilityInp.tsx index d9198d218..9b37b277c 100644 --- a/apps/st/src/components/statistics/input/PrivateSewageFacilityInp.tsx +++ b/apps/st/src/components/statistics/input/PrivateSewageFacilityInp.tsx @@ -68,7 +68,11 @@ import { patchFooterVerificationByTab, patchVerificationFailedByTab, } from "@/components/util/input/verification-failed-flag"; -import type { VerificationResultLikeMap } from "@/components/util/input/verification-result-display"; +import { + formatVerificationTooltip, + normalizeVerificationResultMap, + type VerificationResultLikeMap, +} from "@/components/util/input/verification-result-display"; import { nullableTrimmedString } from "@/components/util/input/nullableString"; import { useAdminStore } from "@/store/admin-store"; import { useTabStore } from "@/store/tab-store"; @@ -175,7 +179,9 @@ function mapRegionalListToGridRows< return filterBySelectedAdmdstCd(list, sidoListRef.current, values).map((item) => { const { verificationResult, ...rest } = item; const row = toGridRows([{ ctpvNm, ...rest }])[0] as GridRowWithVerification; - if (verificationResult) row.verificationResult = verificationResult; + if (verificationResult) { + row.verificationResult = normalizeVerificationResultMap(verificationResult); + } return row; }); } @@ -1144,18 +1150,7 @@ const PrivateSewageFacilityInp = () => { if (!verification) return; const errors = verification[fieldName]; if (Array.isArray(errors) && errors.length > 0) { - return errors - .map((e) => { - const prefix = - e.vrfcCd === "IMR" - ? "시계열 검증 - " - : e.vrfcCd === "NV" - ? "일반검증 - " - : "모집단 검증 - "; - return `${prefix}${e.rsnExpln}(검증 기준값: ${e.cmprCn} / 현재 입력값: ${e.nowCn})`; - }) - .filter(Boolean) - .join("\n"); + return formatVerificationTooltip(errors); } }; diff --git a/apps/st/src/components/statistics/input/PublicSewageFacilityInp.tsx b/apps/st/src/components/statistics/input/PublicSewageFacilityInp.tsx index bcc35197e..48b9ca3b0 100644 --- a/apps/st/src/components/statistics/input/PublicSewageFacilityInp.tsx +++ b/apps/st/src/components/statistics/input/PublicSewageFacilityInp.tsx @@ -52,7 +52,11 @@ import { fetchColumnGuidesRecord } from '@/components/util/grid/columnGuides'; import { toGridRows } from '@/components/util/grid/gridFunction'; import { toExcelExportSearchContextFromSearchForm } from "@/components/util/gridExcelExport"; import { patchFooterVerificationByTab,patchVerificationFailedByTab } from '@/components/util/input/verification-failed-flag'; -import type { VerificationResultLikeMap } from '@/components/util/input/verification-result-display'; +import { + formatVerificationTooltip, + withNormalizedVerificationResult, + type VerificationResultLikeMap, +} from '@/components/util/input/verification-result-display'; import { nullableTrimmedString } from '@/components/util/input/nullableString'; import { useAdminStore } from '@/store/admin-store'; import { useTabStore } from '@/store/tab-store'; @@ -789,11 +793,13 @@ const PublicSewageFacilityInp = () => { sidoListRef.current, searchContext, ); - const convertedData = list.map((item) => ({ - ctpvNm: commonInfo.ctpvNm, - ...item, - rmrk: item.rmrk ?? null, - })); + const convertedData = list.map((item) => + withNormalizedVerificationResult({ + ctpvNm: commonInfo.ctpvNm, + ...item, + rmrk: item.rmrk ?? null, + }), + ); if (list.length === 0) { setShowIsNotApplicable(true); @@ -1575,17 +1581,7 @@ const PublicSewageFacilityInp = () => { if (!verification) return; const errors = verification[fieldName]; if (Array.isArray(errors) && errors.length > 0) { - return errors - .map((e) => { - const prefix = - e.vrfcCd === "IMR" ? "시계열 검증 - " : (e.vrfcCd === "NV" ? "일반검증 - " : "모집단 검증 - "); - const comp = e.cmprCn ?? ""; - const now = e.nowCn ?? ""; - return `${prefix}${e.rsnExpln} - (검증 기준값: ${comp} / 현재 입력값: ${now})`; - }) - .filter(Boolean) - .join("\n"); + return formatVerificationTooltip(errors); } }} /> diff --git a/apps/st/src/components/statistics/input/PumpingStationInp.tsx b/apps/st/src/components/statistics/input/PumpingStationInp.tsx index 0f00b1c01..752528b27 100644 --- a/apps/st/src/components/statistics/input/PumpingStationInp.tsx +++ b/apps/st/src/components/statistics/input/PumpingStationInp.tsx @@ -75,7 +75,11 @@ import { toGridRows } from "@/components/util/grid/gridFunction"; import { formatCompactYmdForGrid, toCompactYmdOrNull } from "@/components/util/grid/ymdFormat"; import { toExcelExportSearchContextFromSearchForm } from "@/components/util/gridExcelExport"; import { isVerificationFailedFromCommonInfo } from "@/components/util/input/verification-failed-flag"; -import type { VerificationResultLikeMap } from "@/components/util/input/verification-result-display"; +import { + formatVerificationTooltip, + normalizeVerificationResultMap, + type VerificationResultLikeMap, +} from "@/components/util/input/verification-result-display"; import { useAdminStore } from "@/store/admin-store"; import { useTabStore } from "@/store/tab-store"; @@ -206,7 +210,7 @@ function mapPumpingStationListToGridRows( }, ])[0] as GridRowWithVerification; if (item.verificationResult) { - row.verificationResult = item.verificationResult; + row.verificationResult = normalizeVerificationResultMap(item.verificationResult); } return row; }); @@ -493,7 +497,9 @@ const PumpingStationInp = () => { setStatusInfo(nextStatusInfo); setStatsCd(commonInfo.statsCd); setIsVerificationFailed(isVerificationFailedFromCommonInfo(commonInfo)); - setFooterVerificationResult(commonInfo.verificationResult ?? null); + setFooterVerificationResult( + normalizeVerificationResultMap(commonInfo.verificationResult) ?? null, + ); if (overrides?.mode !== "PRE") { setIsNotApplicable(gridRows.length === 0); } else if (gridRows.length > 0) { @@ -935,19 +941,7 @@ const PumpingStationInp = () => { if (!verification) return; const errors = verification[fieldName]; if (Array.isArray(errors) && errors.length > 0) { - return errors - .map((e) => { - const prefix = - e.vrfcCd === "IMR" - ? "시계열 검증 - " - : e.vrfcCd === "NV" - ? "일반검증 - " - : "모집단 검증 -"; - return `${prefix}${e.rsnExpln} -(검증 기준값: ${e.cmprCn} / 현재 입력값: ${e.nowCn})`; - }) - .filter(Boolean) - .join("\n"); + return formatVerificationTooltip(errors); } }; diff --git a/apps/st/src/components/statistics/input/SewageExcretaRelatedInp.tsx b/apps/st/src/components/statistics/input/SewageExcretaRelatedInp.tsx index 18b9e78b4..8ab561ba6 100644 --- a/apps/st/src/components/statistics/input/SewageExcretaRelatedInp.tsx +++ b/apps/st/src/components/statistics/input/SewageExcretaRelatedInp.tsx @@ -84,7 +84,11 @@ import { patchFooterVerificationByTab, patchVerificationFailedByTab, } from "@/components/util/input/verification-failed-flag"; -import type { VerificationResultLikeMap } from "@/components/util/input/verification-result-display"; +import { + formatVerificationTooltip, + normalizeVerificationResultMap, + type VerificationResultLikeMap, +} from "@/components/util/input/verification-result-display"; import { useAdminStore } from "@/store/admin-store"; import { useTabStore } from "@/store/tab-store"; @@ -552,19 +556,7 @@ function verificationCellTooltip({ if (!verification) return; const errors = verification[fieldName]; if (Array.isArray(errors) && errors.length > 0) { - return errors - .map((e) => { - const prefix = - e.vrfcCd === "IMR" - ? "시계열 검증 - " - : e.vrfcCd === "NV" - ? "일반검증 - " - : "모집단 검증 - "; - return `${prefix}${e.rsnExpln} -(검증 기준값: ${e.cmprCn} / 현재 입력값: ${e.nowCn})`; - }) - .filter(Boolean) - .join("\n"); + return formatVerificationTooltip(errors); } } @@ -588,7 +580,7 @@ function mapListToGridRows { const row = toGridRows([{ ctpvNm, ...item }])[0] as GridRowWithVerification; if (item.verificationResult) { - row.verificationResult = item.verificationResult; + row.verificationResult = normalizeVerificationResultMap(item.verificationResult); } return row; }); diff --git a/apps/st/src/components/statistics/input/SewageExcretaSlurgeInp.tsx b/apps/st/src/components/statistics/input/SewageExcretaSlurgeInp.tsx index 32c414156..c40e5018a 100644 --- a/apps/st/src/components/statistics/input/SewageExcretaSlurgeInp.tsx +++ b/apps/st/src/components/statistics/input/SewageExcretaSlurgeInp.tsx @@ -90,7 +90,11 @@ import { patchFooterVerificationByTab, patchVerificationFailedByTab, } from "@/components/util/input/verification-failed-flag"; -import type { VerificationResultLikeMap } from "@/components/util/input/verification-result-display"; +import { + formatVerificationTooltip, + normalizeVerificationResultMap, + type VerificationResultLikeMap, +} from "@/components/util/input/verification-result-display"; import { useAdminStore } from "@/store/admin-store"; import { useTabStore } from "@/store/tab-store"; @@ -148,19 +152,7 @@ function verificationCellTooltip({ if (!verification) return; const errors = verification[fieldName]; if (Array.isArray(errors) && errors.length > 0) { - return errors - .map((e) => { - const prefix = - e.vrfcCd === "IMR" - ? "시계열 검증 - " - : e.vrfcCd === "NV" - ? "일반검증 - " - : "모집단 검증 - "; - return `${prefix}${e.rsnExpln} -(검증 기준값: ${e.cmprCn} / 현재 입력값: ${e.nowCn})`; - }) - .filter(Boolean) - .join("\n"); + return formatVerificationTooltip(errors); } } @@ -185,7 +177,7 @@ function mapListToGridRows { const row = toGridRows([{ ctpvNm, ...item }])[0] as GridRowWithVerification; if (item.verificationResult) { - row.verificationResult = item.verificationResult; + row.verificationResult = normalizeVerificationResultMap(item.verificationResult); } return row; }); diff --git a/apps/st/src/components/statistics/input/SewagePipeInp.tsx b/apps/st/src/components/statistics/input/SewagePipeInp.tsx index 04d1c5353..a05e9f982 100644 --- a/apps/st/src/components/statistics/input/SewagePipeInp.tsx +++ b/apps/st/src/components/statistics/input/SewagePipeInp.tsx @@ -82,7 +82,10 @@ import { } from "@/components/util/input/verification-failed-flag"; import { nullableTrimmedString } from "@/components/util/input/nullableString"; import { + formatVerificationTooltip, getVerificationErrorsForField, + normalizeVerificationResultMap, + withNormalizedVerificationResult, type VerificationResultLikeMap, } from "@/components/util/input/verification-result-display"; import { useAdminStore } from "@/store/admin-store"; @@ -289,7 +292,10 @@ function normalizePipeTypeStatusRow( lyngPrlg: Number(lyngPrlgWhol ?? 0), dmltnPrlg: Number(dmltnPrlgWhol ?? 0), ...yearFields, - verificationResult: remapPipeTypePrlgWholVerification(verificationResult) ?? undefined, + verificationResult: + normalizeVerificationResultMap( + remapPipeTypePrlgWholVerification(verificationResult), + ) ?? undefined, }; } @@ -719,10 +725,12 @@ const SewagePipeInp = () => { const response = await statisticsInputSewPipeApiService.getRegionalFactStatus(searchParam); if (response.status === 'success') { const commonInfo = response.data.commonInfo; - const convertedData = filterFetched(response.data.regionalFactStatusList).map((d) => ({ - ctpvNm: commonInfo.ctpvNm, - ...d, - })); + const convertedData = filterFetched(response.data.regionalFactStatusList).map((d) => + withNormalizedVerificationResult({ + ctpvNm: commonInfo.ctpvNm, + ...d, + }), + ); applyRowMap((m) => m.set('ST00020001', toGridRows(convertedData))); setTabStatusInfoMap((prev) => new Map(prev).set(tabKey, response.data.statusInfo)); setTabStatsCdMap((prev) => new Map(prev).set(tabKey, commonInfo.statsCd)); @@ -749,10 +757,12 @@ const SewagePipeInp = () => { const response = await statisticsInputSewPipeApiService.getFactInstallStatus(searchParam); if (response.status === 'success') { const commonInfo = response.data.commonInfo; - const convertedData = filterFetched(response.data.factInstallStatusList).map((d) => ({ - ctpvNm: commonInfo.ctpvNm, - ...d, - })); + const convertedData = filterFetched(response.data.factInstallStatusList).map((d) => + withNormalizedVerificationResult({ + ctpvNm: commonInfo.ctpvNm, + ...d, + }), + ); if (convertedData.length === 0) { const alreadyHandledForSameSearch = factInstallNoDataHandledKeyRef.current === searchConditionKey; @@ -829,10 +839,12 @@ const SewagePipeInp = () => { const response = await statisticsInputSewPipeApiService.getPipeDiameterStatus(searchParam); if (response.status === 'success') { const commonInfo = response.data.commonInfo; - const convertedData = filterFetched(response.data.pipeDiameterStatusList).map((d) => ({ - ctpvNm: commonInfo.ctpvNm, - ...d, - })); + const convertedData = filterFetched(response.data.pipeDiameterStatusList).map((d) => + withNormalizedVerificationResult({ + ctpvNm: commonInfo.ctpvNm, + ...d, + }), + ); applyRowMap((m) => m.set('ST00020004', toGridRows(convertedData))); setTabStatusInfoMap((prev) => new Map(prev).set(tabKey, response.data.statusInfo)); setTabStatsCdMap((prev) => new Map(prev).set(tabKey, commonInfo.statsCd)); @@ -859,10 +871,12 @@ const SewagePipeInp = () => { const response = await statisticsInputSewPipeApiService.getManholeStatus(searchParam); if (response.status === 'success') { const commonInfo = response.data.commonInfo; - const convertedData = filterFetched(response.data.manholeStatusList).map((d) => ({ - ctpvNm: commonInfo.ctpvNm, - ...d, - })); + const convertedData = filterFetched(response.data.manholeStatusList).map((d) => + withNormalizedVerificationResult({ + ctpvNm: commonInfo.ctpvNm, + ...d, + }), + ); applyRowMap((m) => m.set('ST00020005', toGridRows(convertedData))); setTabStatusInfoMap((prev) => new Map(prev).set(tabKey, response.data.statusInfo)); setTabStatsCdMap((prev) => new Map(prev).set(tabKey, commonInfo.statsCd)); @@ -889,13 +903,15 @@ const SewagePipeInp = () => { const response = await statisticsInputSewPipeApiService.getConnectionStatus(searchParam); if (response.status === 'success') { const commonInfo = response.data.commonInfo; - const facilityConvertedData = filterFetched(response.data.facilityConnectionList).map((d) => ({ - ...d, - })); - const adminConvertedData = filterFetched(response.data.adminAreaConnectionList).map((d) => ({ - ...d, - ctpvNm: d.ctpvNm || commonInfo.ctpvNm, - })); + const facilityConvertedData = filterFetched(response.data.facilityConnectionList).map((d) => + withNormalizedVerificationResult({ ...d }), + ); + const adminConvertedData = filterFetched(response.data.adminAreaConnectionList).map((d) => + withNormalizedVerificationResult({ + ...d, + ctpvNm: d.ctpvNm || commonInfo.ctpvNm, + }), + ); applyRowMap((m) => { m.set('treatmentPlantConnectionInp', toGridRows(facilityConvertedData)); m.set('adminAreaConnectionInp', toGridRows(adminConvertedData)); @@ -2235,17 +2251,7 @@ const SewagePipeInp = () => { tabKey, ); if (Array.isArray(errors) && errors.length > 0) { - return errors - .map((e) => { - const prefix = - e.vrfcCd === "IMR" ? "시계열 검증 - " : (e.vrfcCd === "NV" ? "일반검증 - " : "모집단 검증 - "); - const comp = e.cmprCn ?? ""; - const now = e.nowCn ?? ""; - return `${prefix}${e.rsnExpln} - (검증 기준값: ${comp} / 현재 입력값: ${now})`; - }) - .filter(Boolean) - .join("\n"); + return formatVerificationTooltip(errors); } }} /> diff --git a/apps/st/src/components/statistics/input/SewagePipeMaintainInp.tsx b/apps/st/src/components/statistics/input/SewagePipeMaintainInp.tsx index 1b566a94f..a7bba0621 100644 --- a/apps/st/src/components/statistics/input/SewagePipeMaintainInp.tsx +++ b/apps/st/src/components/statistics/input/SewagePipeMaintainInp.tsx @@ -69,7 +69,11 @@ import { patchFooterVerificationByTab, patchVerificationFailedByTab, } from "@/components/util/input/verification-failed-flag"; -import type { VerificationResultLikeMap } from "@/components/util/input/verification-result-display"; +import { + formatVerificationTooltip, + withNormalizedVerificationResult, + type VerificationResultLikeMap, +} from "@/components/util/input/verification-result-display"; import { nullableTrimmedString } from "@/components/util/input/nullableString"; import { useAdminStore } from "@/store/admin-store"; import { useStatisticsInputFocusStore } from "@/store/statistics-input-focus-store"; @@ -444,10 +448,12 @@ const SewagePipeMaintainInp = () => { repairStatusList ?? [], sidoListRef.current, searchContext, - ).map((d) => ({ - ctpvNm: commonInfo.ctpvNm, - ...d, - })); + ).map((d) => + withNormalizedVerificationResult({ + ctpvNm: commonInfo.ctpvNm, + ...d, + }), + ); const gridRows = toGridRows(convertedData); applyRowMap(mergeIntoExistingMap, (m) => { @@ -487,10 +493,12 @@ const SewagePipeMaintainInp = () => { pipeDredgingStatusList ?? [], sidoListRef.current, searchContext, - ).map((d) => ({ - ctpvNm: commonInfo.ctpvNm, - ...d, - })); + ).map((d) => + withNormalizedVerificationResult({ + ctpvNm: commonInfo.ctpvNm, + ...d, + }), + ); const gridRows = toGridRows(convertedData); applyRowMap(mergeIntoExistingMap, (m) => { @@ -1103,14 +1111,7 @@ const SewagePipeMaintainInp = () => { if (!verification) return; const errors = verification[fieldName]; if (Array.isArray(errors) && errors.length > 0) { - return errors - .map((e) => { - const prefix = - e.vrfcCd === "IMR" ? "시계열 검증 - " : e.vrfcCd === "NV" ? "일반검증 - " : "모집단 검증 - "; - return `${prefix}${e.rsnExpln}(검증 기준값: ${e.cmprCn} / 현재 입력값: ${e.nowCn})`; - }) - .filter(Boolean) - .join("\n"); + return formatVerificationTooltip(errors); } }; diff --git a/apps/st/src/components/statistics/input/SewageReservoirPumpInp.tsx b/apps/st/src/components/statistics/input/SewageReservoirPumpInp.tsx index f16d6ed52..161434a8a 100644 --- a/apps/st/src/components/statistics/input/SewageReservoirPumpInp.tsx +++ b/apps/st/src/components/statistics/input/SewageReservoirPumpInp.tsx @@ -89,7 +89,11 @@ import { patchFooterVerificationByTab, patchVerificationFailedByTab, } from "@/components/util/input/verification-failed-flag"; -import type { VerificationResultLikeMap } from "@/components/util/input/verification-result-display"; +import { + formatVerificationTooltip, + normalizeVerificationResultMap, + type VerificationResultLikeMap, +} from "@/components/util/input/verification-result-display"; import { useAdminStore } from "@/store/admin-store"; import { useTabStore } from "@/store/tab-store"; @@ -279,7 +283,7 @@ function mapSewageReservoirListToGridRows( }, ])[0] as GridRowWithVerification; if (item.verificationResult) { - row.verificationResult = item.verificationResult; + row.verificationResult = normalizeVerificationResultMap(item.verificationResult); } return row; }); @@ -435,7 +439,7 @@ function mapRainfallPumpStationListToGridRows( ])[0] as GridRowWithVerification; if (item.verificationResult) { - row.verificationResult = item.verificationResult; + row.verificationResult = normalizeVerificationResultMap(item.verificationResult); } return row; }); @@ -1516,19 +1520,7 @@ const SewageReservoirPumpInp = () => { if (!verification) return; const errors = verification[fieldName]; if (Array.isArray(errors) && errors.length > 0) { - return errors - .map((e) => { - const prefix = - e.vrfcCd === "IMR" - ? "시계열 검증 - " - : e.vrfcCd === "NV" - ? "일반검증 - " - : "모집단 검증 -"; - return `${prefix}${e.rsnExpln} -(검증 기준값: ${e.cmprCn} / 현재 입력값: ${e.nowCn})`; - }) - .filter(Boolean) - .join("\n"); + return formatVerificationTooltip(errors); } }; diff --git a/apps/st/src/components/statistics/input/SewerageFinanceInp.tsx b/apps/st/src/components/statistics/input/SewerageFinanceInp.tsx index 05ec4fba3..0d974602e 100644 --- a/apps/st/src/components/statistics/input/SewerageFinanceInp.tsx +++ b/apps/st/src/components/statistics/input/SewerageFinanceInp.tsx @@ -73,7 +73,11 @@ import { patchFooterVerificationByTab, patchVerificationFailedByTab, } from "@/components/util/input/verification-failed-flag"; -import type { VerificationResultLikeMap } from "@/components/util/input/verification-result-display"; +import { + formatVerificationTooltip, + normalizeVerificationResultMap, + type VerificationResultLikeMap, +} from "@/components/util/input/verification-result-display"; import { nullableTrimmedString } from "@/components/util/input/nullableString"; import { useAdminStore } from "@/store/admin-store"; import { useTabStore } from "@/store/tab-store"; @@ -174,7 +178,9 @@ function mapRegionalListToGridRows< return filterBySelectedAdmdstCd(list, sidoListRef.current, values).map((item) => { const { verificationResult, ...rest } = item; const row = toGridRows([{ ctpvNm, ...rest }])[0] as GridRowWithVerification; - if (verificationResult) row.verificationResult = verificationResult; + if (verificationResult) { + row.verificationResult = normalizeVerificationResultMap(verificationResult); + } return row; }); } @@ -1215,18 +1221,7 @@ const SewerageFinanceInp = () => { if (!verification) return; const errors = verification[fieldName]; if (Array.isArray(errors) && errors.length > 0) { - return errors - .map((e) => { - const prefix = - e.vrfcCd === "IMR" - ? "시계열 검증 - " - : e.vrfcCd === "NV" - ? "일반검증 - " - : "모집단 검증 - "; - return `${prefix}${e.rsnExpln}(검증 기준값: ${e.cmprCn} / 현재 입력값: ${e.nowCn})`; - }) - .filter(Boolean) - .join("\n"); + return formatVerificationTooltip(errors); } }; diff --git a/apps/st/src/components/statistics/input/SeweragePenetrationRateInp.tsx b/apps/st/src/components/statistics/input/SeweragePenetrationRateInp.tsx index d08827945..f83a04af8 100644 --- a/apps/st/src/components/statistics/input/SeweragePenetrationRateInp.tsx +++ b/apps/st/src/components/statistics/input/SeweragePenetrationRateInp.tsx @@ -58,7 +58,11 @@ import { toGridRows } from "@/components/util/grid/gridFunction"; import { toExcelExportSearchContextFromSearchForm } from "@/components/util/gridExcelExport"; import { nullableTrimmedString } from "@/components/util/input/nullableString"; import { isVerificationFailedFromCommonInfo } from "@/components/util/input/verification-failed-flag"; -import type { VerificationResultLikeMap } from "@/components/util/input/verification-result-display"; +import { + formatVerificationTooltip, + normalizeVerificationResultMap, + type VerificationResultLikeMap, +} from "@/components/util/input/verification-result-display"; import { useAdminStore } from "@/store/admin-store"; import { useStatisticsInputFocusStore } from "@/store/statistics-input-focus-store"; import { useTabStore } from "@/store/tab-store"; @@ -87,7 +91,7 @@ const remapCommonInfoVerificationToGridFields = ( // API 키로도 조회 가능하도록 유지 if (gridField !== key) remapped[key] = errors; } - return remapped; + return normalizeVerificationResultMap(remapped) ?? remapped; }; /** 컬럼 가이드 API 조회용 통계코드 (하수도 보급률) */ @@ -302,7 +306,11 @@ const SeweragePenetrationRateInp = () => { setRowData( convertedData.map((d) => { const row = toGridRows([d])[0] as GridRowWithVerification; - if (d.verificationResult) row.verificationResult = d.verificationResult; + if (d.verificationResult) { + row.verificationResult = normalizeVerificationResultMap( + d.verificationResult, + ); + } return row; }) ); @@ -867,14 +875,7 @@ const SeweragePenetrationRateInp = () => { if (!verification) return; const errors = verification[fieldName]; if (Array.isArray(errors) && errors.length > 0) { - return errors - .map((e) => { - const prefix = e.vrfcCd === 'IMR' ? '시계열 검증 - ' : (e.vrfcCd === 'NV' ? '일반검증 - ' : '모집단 검증 -'); - return `${prefix}${e.rsnExpln} - (검증 기준값: ${e.cmprCn} / 현재 입력값: ${e.nowCn})`; - }) - .filter(Boolean) - .join('\n'); + return formatVerificationTooltip(errors); } }} /> diff --git a/apps/st/src/components/statistics/input/SewerageServiceLevelInp.tsx b/apps/st/src/components/statistics/input/SewerageServiceLevelInp.tsx index 6bfcf4299..3b1d8a529 100644 --- a/apps/st/src/components/statistics/input/SewerageServiceLevelInp.tsx +++ b/apps/st/src/components/statistics/input/SewerageServiceLevelInp.tsx @@ -63,7 +63,11 @@ import { patchFooterVerificationByTab, patchVerificationFailedByTab, } from "@/components/util/input/verification-failed-flag"; -import type { VerificationResultLikeMap } from "@/components/util/input/verification-result-display"; +import { + formatVerificationTooltip, + normalizeVerificationResultMap, + type VerificationResultLikeMap, +} from "@/components/util/input/verification-result-display"; import { nullableTrimmedString } from "@/components/util/input/nullableString"; import { useAdminStore } from "@/store/admin-store"; import { useTabStore } from "@/store/tab-store"; @@ -187,7 +191,9 @@ function mapServiceLevelListToGridRows< return filterBySelectedAdmdstCd(list, sidoListRef.current, values).map((item) => { const { verificationResult, ...rest } = item; const row = toGridRows([{ ctpvNm, ...rest }])[0] as GridRowWithVerification; - if (verificationResult) row.verificationResult = verificationResult; + if (verificationResult) { + row.verificationResult = normalizeVerificationResultMap(verificationResult); + } return row; }); } @@ -1146,18 +1152,7 @@ const SewerageServiceLevelInp = () => { if (!verification) return; const errors = verification[fieldName]; if (Array.isArray(errors) && errors.length > 0) { - return errors - .map((e) => { - const prefix = - e.vrfcCd === "IMR" - ? "시계열 검증 - " - : e.vrfcCd === "NV" - ? "일반검증 - " - : "모집단 검증 - "; - return `${prefix}${e.rsnExpln}(검증 기준값: ${e.cmprCn} / 현재 입력값: ${e.nowCn})`; - }) - .filter(Boolean) - .join("\n"); + return formatVerificationTooltip(errors); } }; diff --git a/apps/st/src/components/statistics/input/WaterReuseInp.tsx b/apps/st/src/components/statistics/input/WaterReuseInp.tsx index 5a86b5ecd..ef27b944d 100644 --- a/apps/st/src/components/statistics/input/WaterReuseInp.tsx +++ b/apps/st/src/components/statistics/input/WaterReuseInp.tsx @@ -93,7 +93,11 @@ import { patchFooterVerificationByTab, patchVerificationFailedByTab, } from "@/components/util/input/verification-failed-flag"; -import type { VerificationResultLikeMap } from "@/components/util/input/verification-result-display"; +import { + formatVerificationTooltip, + normalizeVerificationResultMap, + type VerificationResultLikeMap, +} from "@/components/util/input/verification-result-display"; import { useAdminStore } from "@/store/admin-store"; import { useTabStore } from "@/store/tab-store"; @@ -443,7 +447,9 @@ function mapPublicSewageTreatWaterReuseListToGridRows( instlAprvYmd: formatCompactYmdForGrid(item.instlAprvYmd), }, ])[0] as GridRowWithVerification; - if (verificationResult) row.verificationResult = verificationResult; + if (verificationResult) { + row.verificationResult = normalizeVerificationResultMap(verificationResult); + } return row; }); } @@ -462,7 +468,9 @@ function mapRecycledWaterStatusListToGridRows( oprtngStrtYmd: formatCompactYmdForGrid(item.oprtngStrtYmd), }, ])[0] as GridRowWithVerification; - if (verificationResult) row.verificationResult = verificationResult; + if (verificationResult) { + row.verificationResult = normalizeVerificationResultMap(verificationResult); + } return row; }); } @@ -480,7 +488,9 @@ function mapRainFallUtilFactStatusListToGridRows( cmptnYmd: formatCompactYmdForGrid(item.cmptnYmd), }, ])[0] as GridRowWithVerification; - if (verificationResult) row.verificationResult = verificationResult; + if (verificationResult) { + row.verificationResult = normalizeVerificationResultMap(verificationResult); + } return row; }); } @@ -497,7 +507,9 @@ function mapTreatWaterReuseStatusListToGridRows< coRegYmd: formatCompactYmdForGrid(item.coRegYmd), }, ])[0] as GridRowWithVerification; - if (verificationResult) row.verificationResult = verificationResult; + if (verificationResult) { + row.verificationResult = normalizeVerificationResultMap(verificationResult); + } return row; }); } @@ -2063,18 +2075,7 @@ const WaterReuseInp = () => { if (!verification) return; const errors = verification[fieldName]; if (Array.isArray(errors) && errors.length > 0) { - return errors - .map((e) => { - const prefix = - e.vrfcCd === "IMR" - ? "시계열 검증 - " - : e.vrfcCd === "NV" - ? "일반검증 - " - : "모집단 검증 - "; - return `${prefix}${e.rsnExpln}(검증 기준값: ${e.cmprCn} / 현재 입력값: ${e.nowCn})`; - }) - .filter(Boolean) - .join("\n"); + return formatVerificationTooltip(errors); } }; diff --git a/apps/st/src/components/util/input/verification-failed-flag.ts b/apps/st/src/components/util/input/verification-failed-flag.ts index 7d6c986e9..092c7bae4 100644 --- a/apps/st/src/components/util/input/verification-failed-flag.ts +++ b/apps/st/src/components/util/input/verification-failed-flag.ts @@ -1,6 +1,9 @@ import type { StatisticsInputCommonInfoBase } from "@/entities/statistics/model/statistics-interface"; -import type { VerificationResultLikeMap } from "./verification-result-display"; +import { + normalizeVerificationResultMap, + type VerificationResultLikeMap, +} from "./verification-result-display"; export function isVerificationFailedFromCommonInfo( commonInfo: Pick | null | undefined, @@ -28,6 +31,9 @@ export function patchFooterVerificationByTab( | undefined, ): Map { const next = new Map(prev); - next.set(tabKey, commonInfo?.verificationResult ?? null); + next.set( + tabKey, + normalizeVerificationResultMap(commonInfo?.verificationResult) ?? null, + ); return next; } diff --git a/apps/st/src/components/util/input/verification-result-display.ts b/apps/st/src/components/util/input/verification-result-display.ts index 75dbbe459..78a3952d5 100644 --- a/apps/st/src/components/util/input/verification-result-display.ts +++ b/apps/st/src/components/util/input/verification-result-display.ts @@ -13,6 +13,49 @@ export type VerificationResultLikeMap = Record< VerificationResultLikeItem[] >; +/** 입력 조회 verificationResult의 기준값/입력값이 null이면 "-" */ +export function displayVerificationCn( + value: string | null | undefined, +): string { + if (value == null || String(value).trim() === "") return "-"; + return String(value); +} + +export function normalizeVerificationResultItem( + item: T, +): T { + return { + ...item, + nowCn: displayVerificationCn(item.nowCn ?? item.nowVlCn), + cmprCn: displayVerificationCn(item.cmprCn ?? item.compVlCn), + }; +} + +export function normalizeVerificationResultMap( + result: T | null | undefined, +): T | undefined { + if (!result || typeof result !== "object") return undefined; + const next = {} as T; + for (const [key, items] of Object.entries(result)) { + (next as VerificationResultLikeMap)[key] = Array.isArray(items) + ? items.map((item) => normalizeVerificationResultItem(item)) + : []; + } + return next; +} + +/** 조회 행/객체에 붙은 verificationResult의 cmprCn·nowCn null을 "-"로 바꿈 */ +export function withNormalizedVerificationResult(item: T): T { + if (!("verificationResult" in item) || item.verificationResult == null) { + return item; + } + const verificationResult = normalizeVerificationResultMap( + item.verificationResult as VerificationResultLikeMap, + ); + if (!verificationResult) return item; + return { ...item, verificationResult }; +} + /** 검증 오류 배열 → RealGrid styleName */ export function getVerificationStyleName( errors: VerificationResultLikeItem[] | null | undefined, @@ -37,8 +80,8 @@ export function formatVerificationTooltip( : e.vrfcCd === "NV" ? "일반검증 - " : "모집단 검증 - "; - const comp = e.cmprCn ?? e.compVlCn ?? ""; - const now = e.nowCn ?? e.nowVlCn ?? ""; + const comp = displayVerificationCn(e.cmprCn ?? e.compVlCn); + const now = displayVerificationCn(e.nowCn ?? e.nowVlCn); return `${prefix}${e.rsnExpln}\n(검증 기준값: ${comp} / 현재 입력값: ${now})`; }) .filter(Boolean)