diff --git a/apps/st/src/components/module/CompactDataGrid.tsx b/apps/st/src/components/module/CompactDataGrid.tsx
index 47f6c3afd..c96f22973 100644
--- a/apps/st/src/components/module/CompactDataGrid.tsx
+++ b/apps/st/src/components/module/CompactDataGrid.tsx
@@ -41,7 +41,10 @@ function buildFieldsFromColumns(columns: { fieldName: string; valueType?: ValueT
if (col.computed) {
const hasFields = "fields" in col.computed && col.computed.fields.length > 0;
const hasNumeratorDenominator = "numeratorFields" in col.computed && col.computed.numeratorFields.length > 0 && col.computed.denominatorFields.length > 0;
- if (hasFields || hasNumeratorDenominator) {
+ const hasMaxOfFieldAndSum = "baseField" in col.computed && "sumFields" in col.computed && col.computed.sumFields.length > 0;
+ const hasDifferenceOfSums = "minuendFields" in col.computed && "subtrahendFields" in col.computed && col.computed.minuendFields.length > 0;
+ const hasSumOfMaxGroups = "groups" in col.computed && Array.isArray(col.computed.groups) && col.computed.groups.length > 0;
+ if (hasFields || hasNumeratorDenominator || hasMaxOfFieldAndSum || hasDifferenceOfSums || hasSumOfMaxGroups) {
field.valueCallback = getComputedValueCallback(col.computed) as (prod: unknown, dataRow: number, fieldName: string, fieldNames: string[], values: unknown[]) => number;
}
}
diff --git a/apps/st/src/components/module/DataGrid.tsx b/apps/st/src/components/module/DataGrid.tsx
index 82a9313d0..3ec75073f 100644
--- a/apps/st/src/components/module/DataGrid.tsx
+++ b/apps/st/src/components/module/DataGrid.tsx
@@ -292,7 +292,19 @@ function buildFieldsFromColumns(
"numeratorFields" in col.computed &&
col.computed.numeratorFields.length > 0 &&
col.computed.denominatorFields.length > 0;
- if (hasFields || hasNumeratorDenominator) {
+ const hasMaxOfFieldAndSum =
+ "baseField" in col.computed &&
+ "sumFields" in col.computed &&
+ col.computed.sumFields.length > 0;
+ const hasDifferenceOfSums =
+ "minuendFields" in col.computed &&
+ "subtrahendFields" in col.computed &&
+ col.computed.minuendFields.length > 0;
+ const hasSumOfMaxGroups =
+ "groups" in col.computed &&
+ Array.isArray(col.computed.groups) &&
+ col.computed.groups.length > 0;
+ if (hasFields || hasNumeratorDenominator || hasMaxOfFieldAndSum || hasDifferenceOfSums || hasSumOfMaxGroups) {
field.valueCallback = getComputedValueCallback(
col.computed,
) as (
@@ -1215,6 +1227,10 @@ const DataGrid = ({
)
: 검증중}
+
+ 반려사유:{" "}
+ {statusInfo.rjctOpnnCn ? statusInfo.rjctOpnnCn : "-"}
+
제출일:{" "}
{statusInfo.laeSbmsnYmd
diff --git a/apps/st/src/components/module/TabDataGrid.tsx b/apps/st/src/components/module/TabDataGrid.tsx
index 2aba5350b..a2327c11b 100644
--- a/apps/st/src/components/module/TabDataGrid.tsx
+++ b/apps/st/src/components/module/TabDataGrid.tsx
@@ -215,7 +215,10 @@ function buildFieldsFromColumns(columns: { fieldName: string; valueType?: ValueT
if (col.computed) {
const hasFields = "fields" in col.computed && col.computed.fields.length > 0;
const hasNumeratorDenominator = "numeratorFields" in col.computed && col.computed.numeratorFields.length > 0 && col.computed.denominatorFields.length > 0;
- if (hasFields || hasNumeratorDenominator) {
+ const hasMaxOfFieldAndSum = "baseField" in col.computed && "sumFields" in col.computed && col.computed.sumFields.length > 0;
+ const hasDifferenceOfSums = "minuendFields" in col.computed && "subtrahendFields" in col.computed && col.computed.minuendFields.length > 0;
+ const hasSumOfMaxGroups = "groups" in col.computed && Array.isArray(col.computed.groups) && col.computed.groups.length > 0;
+ if (hasFields || hasNumeratorDenominator || hasMaxOfFieldAndSum || hasDifferenceOfSums || hasSumOfMaxGroups) {
field.valueCallback = getComputedValueCallback(col.computed) as (prod: unknown, dataRow: number, fieldName: string, fieldNames: string[], values: unknown[]) => number;
}
}
diff --git a/apps/st/src/components/module/VerificationResultDialog.tsx b/apps/st/src/components/module/VerificationResultDialog.tsx
index fda48ba75..b72132bbc 100644
--- a/apps/st/src/components/module/VerificationResultDialog.tsx
+++ b/apps/st/src/components/module/VerificationResultDialog.tsx
@@ -24,6 +24,7 @@ import {
import DataGrid from "@/components/module/DataGrid";
import { VerificationResultHistoryPagination } from "@/components/module/VerificationResultHistoryPagination";
import type { CellEditedCallbackParams, ColumnInfo } from "@/components/type/DataGrid.type";
+import { getUserInfo } from "../../../../../packages/common/components/src/api";
import { statisticsApi } from "@/entities/statistics/api/statistics-service";
import type { VerificationResultListRequest, VerificationResultReportItem } from "@/entities/statistics/model/statistics-interface";
import { useAlertStore } from "@/shared/lib";
@@ -146,20 +147,15 @@ export function VerificationResultDialog({
const normalGridRef = useRef(null);
const imrGridRef = useRef(null);
+ /** 메인 입력 화면처럼 편집 값은 그리드에 두고, 제출 시에만 스냅샷 조회 → Enter 시 rows 재주입 충돌 방지 */
+ const getNormalGridDataRef = useRef<(() => VerificationResultGridRow[]) | null>(null);
+ const getImrGridDataRef = useRef<(() => VerificationResultGridRow[]) | null>(null);
const alert = useAlertStore((state) => state.setMessage);
const onNormalPrcsOpnnExplnEdited = useCallback((params: CellEditedCallbackParams) => {
if (params.fieldName !== "prcsOpnnExpln") return;
const text = String(params.newValue ?? "");
const hasValue = text.trim().length > 0;
- /** DataGrid는 onCellEdited 시 React rowData를 갱신하지 않아, 제출용 상태를 여기서 맞춤 */
- setNormalRows((prev) => {
- const next = [...prev];
- const row = next[params.itemIndex];
- if (!row) return prev;
- next[params.itemIndex] = { ...row, prcsOpnnExpln: text };
- return next;
- });
setRowCheckByItemIndex(normalGridRef, params.itemIndex, hasValue);
setNormalCheckedIndices((prev) => {
const next = new Set(prev);
@@ -173,13 +169,6 @@ export function VerificationResultDialog({
if (params.fieldName !== "prcsOpnnExpln") return;
const text = String(params.newValue ?? "");
const hasValue = text.trim().length > 0;
- setImrRows((prev) => {
- const next = [...prev];
- const row = next[params.itemIndex];
- if (!row) return prev;
- next[params.itemIndex] = { ...row, prcsOpnnExpln: text };
- return next;
- });
setRowCheckByItemIndex(imrGridRef, params.itemIndex, hasValue);
setImrCheckedIndices((prev) => {
const next = new Set(prev);
@@ -358,25 +347,60 @@ export function VerificationResultDialog({
return () => cancelAnimationFrame(id);
}, [imrRows, imrLoading]);
- const handleExceptionSubmitClick = useCallback(() => {
+ const handleExceptionSubmitClick = useCallback(async () => {
+ const nvGrid = getNormalGridDataRef.current?.() ?? [];
+ const imGrid = getImrGridDataRef.current?.() ?? [];
const payload: VerificationExceptionSubmitItem[] = [];
- const pushIfHasExpln = (row: VerificationResultGridRow | undefined) => {
- if (!row) return;
- const prcsOpnnExpln = String(row.prcsOpnnExpln ?? "").trim();
- if (!prcsOpnnExpln) return;
- payload.push({
- vrfcSn: Number(row.vrfcSn),
- prcsOpnnExpln,
+ const pushChecked = (
+ indices: Set,
+ gridRows: VerificationResultGridRow[],
+ idRows: VerificationResultGridRow[],
+ ) => {
+ indices.forEach((idx) => {
+ const prcsOpnnExpln = String(gridRows[idx]?.prcsOpnnExpln ?? "").trim();
+ if (!prcsOpnnExpln) return;
+ const vrfcSn = Number(idRows[idx]?.vrfcSn);
+ if (!Number.isFinite(vrfcSn)) return;
+ payload.push({ vrfcSn, prcsOpnnExpln });
});
};
- normalCheckedIndices.forEach((idx) => pushIfHasExpln(normalRows[idx]));
- imrCheckedIndices.forEach((idx) => pushIfHasExpln(imrRows[idx]));
+ pushChecked(normalCheckedIndices, nvGrid, normalRows);
+ pushChecked(imrCheckedIndices, imGrid, imrRows);
if (payload.length === 0) {
alert("작성된 예외 제출 항목이 없습니다.");
return;
}
- console.log(payload);
- }, [alert, imrCheckedIndices, imrRows, normalCheckedIndices, normalRows]);
+ if (!stableQueryContext) {
+ alert("조회 조건이 없어 예외 제출을 할 수 없습니다.");
+ return;
+ }
+ try {
+ const userInfo = await getUserInfo();
+ const response = await statisticsApi.SubmitVerifyException({
+ sewerBzmnCd: stableQueryContext.sewerBzmnCd,
+ statsYr: stableQueryContext.statsYr,
+ statsCd: stableQueryContext.statsCd,
+ userId: userInfo.userUnqId,
+ exceptionList: payload,
+ });
+ if (response.status === "success") {
+ alert("예외 제출 처리되었습니다.");
+ onOpenChange(false);
+ } else {
+ alert(response.message ?? "예외 제출 처리 중 오류가 발생했습니다.");
+ }
+ } catch {
+ alert("예외 제출 처리 중 오류가 발생했습니다.");
+ }
+ }, [
+ alert,
+ imrCheckedIndices,
+ imrRows,
+ loadInitial,
+ normalCheckedIndices,
+ normalRows,
+ stableQueryContext,
+ ]);
const renderGrid = (
rows: VerificationResultGridRow[],
@@ -386,6 +410,7 @@ export function VerificationResultDialog({
onPageIndexChange: (page: number) => void,
loading: boolean,
gridRef: MutableRefObject,
+ getGridDataRef: MutableRefObject<(() => VerificationResultGridRow[]) | null>,
onPrcsOpnnExplnCellEdited: (params: CellEditedCallbackParams) => void,
setCheckedIndices: Dispatch>>,
) => {
@@ -404,6 +429,7 @@ export function VerificationResultDialog({
showHeaderCheckbox
enableSort={false}
getGridRef={gridRef}
+ getGridDataRef={getGridDataRef}
onCellEdited={onPrcsOpnnExplnCellEdited}
onItemChecked={(itemIndex, checked) => {
setCheckedIndices((prev) => {
@@ -467,6 +493,7 @@ export function VerificationResultDialog({
handleNormalPageChange,
normalLoading,
normalGridRef,
+ getNormalGridDataRef,
onNormalPrcsOpnnExplnEdited,
setNormalCheckedIndices,
)}
@@ -481,6 +508,7 @@ export function VerificationResultDialog({
handleImrPageChange,
imrLoading,
imrGridRef,
+ getImrGridDataRef,
onImrPrcsOpnnExplnEdited,
setImrCheckedIndices,
)}
diff --git a/apps/st/src/components/statistics/input/SewagePipeInp.tsx b/apps/st/src/components/statistics/input/SewagePipeInp.tsx
index 7bd155cc7..754038519 100644
--- a/apps/st/src/components/statistics/input/SewagePipeInp.tsx
+++ b/apps/st/src/components/statistics/input/SewagePipeInp.tsx
@@ -10,6 +10,8 @@ import LastYearDataButton from "@/components/button/LastYearDataButton";
import VerificationResultButton from "@/components/button/VerificationResultButton";
import CancelSubmitButton from "@/components/button/CancelSubmitButton";
import { VerificationResultHistoryDialog } from "@/components/module/VerificationResultHistoryDialog";
+import { VerificationResultDialog } from "@/components/module/VerificationResultDialog";
+import SubmitDailog from "./SubmitDailog";
import { SewagePipeInpColumnInfoList } from "@/components/util/column/input/sewagePipe/SewagePipeInpColumn";
import type { ColumnInfo, ColumnInfoList, ColumnConfig } from "@/components/type/DataGrid.type";
@@ -26,29 +28,43 @@ import { doList } from "@/components/util/column/input/DoList";
import { statisticsInputSewPipeApiService } from "@/entities/statistics/api/input/statistics-sewage-pipe-input-service";
import type {
SeweragePipeInputFactInstallStatusSaveRequest,
+ SeweragePipeInputManholeStatusItem,
+ SeweragePipeInputManholeStatusSaveRequest,
+ SeweragePipeInputPipeDiameterStatusSaveRequest,
+ SeweragePipeInputPipeTypeStatusItem,
+ SeweragePipeInputPipeTypeStatusSaveRequest,
SeweragePipeInputRegionalFactStatusSaveRequest,
- SeweragePipeInputStatusInfo,
+ SeweragePipeInputRegionalFactStatusSaveItem,
SeweragePipeInputVerificationResult,
SeweragePipeRequestParams,
} from "@/entities/statistics/model/input/statistics-sewage-pipe-input-interface";
+import type {
+ cancelSubmitRequest,
+ ImrStatusResponse,
+ SewerageStatusInfo,
+ SubmitExceptionRequest,
+} from "@/entities/statistics/model/statistics-interface";
+import type { StatisticsInputSubmitInfoFormData } from "@/entities/statistics/model/statistics-input-submit-info-schema";
import {
type CheckInputPeriodItem,
getVerificationPeriodActionBlockedMessage,
isInputPeriodCurrent,
} from "@/entities/statistics/model/input/statistics-sewage-supply-rate-input-interface";
import { FactInstallStatusInpColumn } from "@/components/util/column/input/sewagePipe/FactInstallStatusInpColumn";
+import { ManholeStatusInpColumn } from "@/components/util/column/input/sewagePipe/ManholeStatusInpColumn";
+import { PipeDiameterStatusInpColumn } from "@/components/util/column/input/sewagePipe/PipeDiameterStatusInpColumn";
+import { PipeTypeStatusInpColumn } from "@/components/util/column/input/sewagePipe/PipeTypeStatusInpColumn";
import { RegionalFactStatusInpColumn } from "@/components/util/column/input/sewagePipe/RegionalFactStatusInpColumn";
import { toGridRows } from "@/components/util/grid/gridFunction";
import { LoadingWithAnimation, Tabs, TabsContent, TabsList, TabsTrigger } from "@common/components";
import { getUserInfo } from "../../../../../../packages/common/components/src/api";
import { statisticsApi } from "@/entities/statistics/api/statistics-service";
-import type { cancelSubmitRequest } from "@/entities/statistics/model/statistics-interface";
import { useStatisticsInputFocusStore } from "@/store/statistics-input-focus-store";
import { useTabStore } from "@/store/tab-store";
/** 탭별 status + 입력기간으로 편집 가능 여부 */
const computeTabEnterable = (
- statusInfo: SeweragePipeInputStatusInfo | null | undefined,
+ statusInfo: SewerageStatusInfo | null | undefined,
periodEnterable: boolean,
): boolean => {
if (statusInfo?.laeSbmsnSttsCd) {
@@ -73,11 +89,192 @@ function buildSewagePipeSearchParam(
};
}
+const getPipeTypeLastDataYear = () => {
+ const lastDataYear = new Date().getFullYear() - 1;
+ return Math.max(1995, lastDataYear);
+};
+
+const getManholeLastDataYear = () => {
+ const lastDataYear = new Date().getFullYear() - 1;
+ return Math.max(2014, lastDataYear);
+};
+
+/** 관종별 신 응답(yearlyStatList) -> 그리드 y{year}* 필드로 정규화 */
+function normalizePipeTypeStatusRow(
+ row: SeweragePipeInputPipeTypeStatusItem,
+ ctpvNm: string,
+): Record {
+ const lastDataYear = getPipeTypeLastDataYear();
+ const {
+ yearlyStatList,
+ lyngPrlgWhol,
+ dmltnPrlgWhol,
+ ...rest
+ } = row;
+ const yearFields: Record = {};
+ for (let year = 1995; year <= lastDataYear; year++) {
+ yearFields[`y${year}PrlgWhol`] = 0;
+ yearFields[`y${year}LyngPrlg`] = 0;
+ yearFields[`y${year}DmltnPrlg`] = 0;
+ }
+
+ for (const y of yearlyStatList ?? []) {
+ const year = Number(y.lyngYr);
+ if (!Number.isFinite(year)) continue;
+ const targetYear = Math.min(lastDataYear, Math.max(1995, year));
+ yearFields[`y${targetYear}PrlgWhol`] =
+ (yearFields[`y${targetYear}PrlgWhol`] ?? 0) + Number(y.prlgWhol ?? 0);
+ yearFields[`y${targetYear}LyngPrlg`] =
+ (yearFields[`y${targetYear}LyngPrlg`] ?? 0) + Number(y.lyngPrlg ?? 0);
+ yearFields[`y${targetYear}DmltnPrlg`] =
+ (yearFields[`y${targetYear}DmltnPrlg`] ?? 0) + Number(y.dmltnPrlg ?? 0);
+ }
+
+ return {
+ ctpvNm,
+ ...rest,
+ lyngPrlg: Number(row.lyngPrlg ?? lyngPrlgWhol ?? 0),
+ dmltnPrlg: Number(row.dmltnPrlg ?? dmltnPrlgWhol ?? 0),
+ ...yearFields,
+ };
+}
+
+/** 맨홀 신 응답(yearlyStats) -> 그리드 yy{14..25}* 필드로 정규화 */
+function normalizeManholeStatusRow(
+ row: SeweragePipeInputManholeStatusItem,
+ ctpvNm: string,
+): Record {
+ const maxYy = getManholeLastDataYear() % 100;
+ const { yearlyStats, ...rest } = row;
+ const yearFields: Record = {};
+ for (let yy = 14; yy <= maxYy; yy++) {
+ yearFields[`yy${yy}WholMnhlCnt`] = 0;
+ yearFields[`yy${yy}Step1MnhlCnt`] = 0;
+ yearFields[`yy${yy}Step2MnhlCnt`] = 0;
+ yearFields[`yy${yy}Step3MnhlCnt`] = 0;
+ yearFields[`yy${yy}Step4MnhlCnt`] = 0;
+ yearFields[`yy${yy}Step5MnhlCnt`] = 0;
+ }
+
+ for (const y of yearlyStats ?? []) {
+ const year = Number(y.manhYr);
+ if (!Number.isFinite(year)) continue;
+ const yy = year % 100;
+ if (yy < 14 || yy > maxYy) continue;
+ yearFields[`yy${yy}WholMnhlCnt`] = Number(y.wholMnhlCnt ?? 0);
+ yearFields[`yy${yy}Step1MnhlCnt`] = Number(y.step1MnhlCnt ?? 0);
+ yearFields[`yy${yy}Step2MnhlCnt`] = Number(y.step2MnhlCnt ?? 0);
+ yearFields[`yy${yy}Step3MnhlCnt`] = Number(y.step3MnhlCnt ?? 0);
+ yearFields[`yy${yy}Step4MnhlCnt`] = Number(y.step4MnhlCnt ?? 0);
+ yearFields[`yy${yy}Step5MnhlCnt`] = Number(y.step5MnhlCnt ?? 0);
+ }
+
+ return {
+ ctpvNm,
+ ...rest,
+ ...yearFields,
+ };
+}
+
/** ST00020006(연결)은 행이 좌·우 그리드 키에만 있음 */
type GridRowWithVerification = Record & {
verificationResult?: SeweragePipeInputVerificationResult;
};
+const REGIONAL_FACT_TAB_KEY = "ST00020001";
+
+function hasRegionalFactVerificationErrors(verification?: SeweragePipeInputVerificationResult | null): boolean {
+ if (!verification) return false;
+ return Object.values(verification).some((errors) => Array.isArray(errors) && errors.length > 0);
+}
+
+const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
+
+async function waitForImrJobCompleted(
+ jobId: string,
+ getStatus: (id: string) => Promise,
+ options?: {
+ pollIntervalMs?: number;
+ maxAttempts?: number;
+ onProgress?: (jobProgress: number) => void;
+ },
+): Promise {
+ const pollIntervalMs = options?.pollIntervalMs ?? 2000;
+ const maxAttempts = options?.maxAttempts ?? 150;
+ for (let i = 0; i < maxAttempts; i++) {
+ const res = await getStatus(jobId);
+ options?.onProgress?.(Math.max(0, Math.min(100, Number(res.job_prog) || 0)));
+ if (res.status === "COMPLETED" && Number(res.job_prog) === 100) return;
+ if (res.status === "FAILED" || res.status === "ERROR" || res.status === "CANCELLED") {
+ throw new Error(res.error_msg || `IMR 검증이 ${res.status} 상태로 종료되었습니다.`);
+ }
+ await sleep(pollIntervalMs);
+ }
+ throw new Error("IMR 검증 완료 대기 시간이 초과되었습니다.");
+}
+
+function buildRegionalFactSubmitListFromGrid(
+ currentData: Record[],
+): SeweragePipeInputRegionalFactStatusSaveItem[] {
+ const columnFormatMap = new Map();
+ RegionalFactStatusInpColumn.columns.forEach((col) => {
+ if (col.fieldName && col.dataFormat) {
+ columnFormatMap.set(col.fieldName, col.dataFormat);
+ }
+ });
+ const formatNumber = (value: string | number | null | undefined, fieldName: string): number => {
+ if (value === null || value === undefined || value === "") return 0;
+ const num = typeof value === "string" ? parseFloat(value.replace(/,/g, "")) : value;
+ if (isNaN(num)) return 0;
+ const format = columnFormatMap.get(fieldName);
+ if (!format) return num;
+ const decimalPlaces = format.match(/\.(\d+)/)?.[1]?.length ?? 0;
+ return decimalPlaces > 0 ? parseFloat(num.toFixed(decimalPlaces)) : Math.round(num);
+ };
+ return currentData.map((d) => ({
+ admdstCd: String(d.admdstCd ?? ""),
+ sggNm: String(d.sggNm ?? ""),
+ admdstNm: String(d.admdstNm ?? ""),
+ goalYr: String(d.goalYr ?? ""),
+ wholPplMtncPlanPrlg: formatNumber(d.wholPplMtncPlanPrlg, "wholPplMtncPlanPrlg"),
+ wholPlanPrlg: formatNumber(d.wholPlanPrlg, "wholPlanPrlg"),
+ wholFcltPrlg: formatNumber(d.wholFcltPrlg, "wholFcltPrlg"),
+ swagePplPsplyRt: formatNumber(d.swagePplPsplyRt, "swagePplPsplyRt"),
+ cbsysPplMtncPlanArea: formatNumber(d.cbsysPplMtncPlanArea, "cbsysPplMtncPlanArea"),
+ cbsysPplMtncPlanPrlg: formatNumber(d.cbsysPplMtncPlanPrlg, "cbsysPplMtncPlanPrlg"),
+ cbsysPlanPrlg: formatNumber(d.cbsysPlanPrlg, "cbsysPlanPrlg"),
+ cbsysFcltPrlg: formatNumber(d.cbsysFcltPrlg, "cbsysFcltPrlg"),
+ cbsysSqrePrlg: formatNumber(d.cbsysSqrePrlg, "cbsysSqrePrlg"),
+ cbsysCiclPrlg: formatNumber(d.cbsysCiclPrlg, "cbsysCiclPrlg"),
+ cbsysOpnPrlg: formatNumber(d.cbsysOpnPrlg, "cbsysOpnPrlg"),
+ cbsysGttrPrlg: formatNumber(d.cbsysGttrPrlg, "cbsysGttrPrlg"),
+ drtwPplMtncPlanArea: formatNumber(d.drtwPplMtncPlanArea, "drtwPplMtncPlanArea"),
+ drtwPplMtncPlanPrlg: formatNumber(d.drtwPplMtncPlanPrlg, "drtwPplMtncPlanPrlg"),
+ drtwPplPlanPrlg: formatNumber(d.drtwPplPlanPrlg, "drtwPplPlanPrlg"),
+ drtwPplFcltPrlg: formatNumber(d.drtwPplFcltPrlg, "drtwPplFcltPrlg"),
+ drtwPplSqrePrlg: formatNumber(d.drtwPplSqrePrlg, "drtwPplSqrePrlg"),
+ drtwPplCiclPrlg: formatNumber(d.drtwPplCiclPrlg, "drtwPplCiclPrlg"),
+ exlnPplMtncPlanArea: formatNumber(d.exlnPplMtncPlanArea, "exlnPplMtncPlanArea"),
+ exlnPplMtncPlanPrlg: formatNumber(d.exlnPplMtncPlanPrlg, "exlnPplMtncPlanPrlg"),
+ exlnPplPlanPrlg: formatNumber(d.exlnPplPlanPrlg, "exlnPplPlanPrlg"),
+ exlnPplFcltPrlg: formatNumber(d.exlnPplFcltPrlg, "exlnPplFcltPrlg"),
+ exlnPplSqrePrlg: formatNumber(d.exlnPplSqrePrlg, "exlnPplSqrePrlg"),
+ exlnPplCiclPrlg: formatNumber(d.exlnPplCiclPrlg, "exlnPplCiclPrlg"),
+ exlnPplOpnPrlg: formatNumber(d.exlnPplOpnPrlg, "exlnPplOpnPrlg"),
+ exlnPplGttrPrlg: formatNumber(d.exlnPplGttrPrlg, "exlnPplGttrPrlg"),
+ cbsysMnhlCnt: formatNumber(d.cbsysMnhlCnt, "cbsysMnhlCnt"),
+ sprsysDrtwMnhlCnt: formatNumber(d.sprsysDrtwMnhlCnt, "sprsysDrtwMnhlCnt"),
+ sprsysExlnMnhlCnt: formatNumber(d.sprsysExlnMnhlCnt, "sprsysExlnMnhlCnt"),
+ drtwRcvprpCnt: formatNumber(d.drtwRcvprpCnt, "drtwRcvprpCnt"),
+ exlnRcvprpCnt: formatNumber(d.exlnRcvprpCnt, "exlnRcvprpCnt"),
+ stofcbrCnt: formatNumber(d.stofcbrCnt, "stofcbrCnt"),
+ exlnOtflCnt: formatNumber(d.exlnOtflCnt, "exlnOtflCnt"),
+ sewerLdgrEdpPrlg: formatNumber(d.sewerLdgrEdpPrlg, "sewerLdgrEdpPrlg"),
+ sewerLdgrEdpRt: formatNumber(d.sewerLdgrEdpRt, "sewerLdgrEdpRt"),
+ rmrk: (d.rmrk as string | null) ?? null,
+ }));
+}
+
function tabRowDataExists(
m: Map[]>,
tabKey: string,
@@ -112,7 +309,7 @@ function buildGugunOkForToolbar(values: SearchFormValues | null): boolean {
* @param treatAsSearched (hasRow || 이 값) 일 때 카드 후보 — load 내부에서는 searchDoneRef 반영용
*/
function computeTabToolbarSnapshot(
- statusInfo: SeweragePipeInputStatusInfo | null,
+ statusInfo: SewerageStatusInfo | null,
hasRowData: boolean,
treatAsSearched: boolean,
searchContext: SearchFormValues | null,
@@ -140,9 +337,10 @@ const SewagePipeInp = () => {
const [searchValues, setSearchValues] = useState(null);
const [lastSearchedYear, setLastSearchedYear] = useState(null);
const [lastSearchedValues, setLastSearchedValues] = useState(null);
- const [tabStatusInfoMap, setTabStatusInfoMap] = useState