This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import { useEffect, useLayoutEffect, useRef, type MutableRefObject } from "react";
|
import { useEffect, useLayoutEffect, useRef, useState, type MutableRefObject } from "react";
|
||||||
|
import { createPortal } from "react-dom";
|
||||||
|
|
||||||
import JSZip from "jszip";
|
import JSZip from "jszip";
|
||||||
import { ValueType } from "realgrid";
|
import { ValueType } from "realgrid";
|
||||||
@@ -385,6 +386,20 @@ function toNum(v: unknown): number {
|
|||||||
return parseFloat(String(v).replace(/,/g, "")) || NaN;
|
return parseFloat(String(v).replace(/,/g, "")) || NaN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** RealGrid 기본 hover가 안 되는 빈 셀 여부 */
|
||||||
|
function isEmptyGridCellValue(value: unknown): boolean {
|
||||||
|
if (value == null) return true;
|
||||||
|
if (typeof value === "number") return Number.isNaN(value);
|
||||||
|
return String(value).trim() === "";
|
||||||
|
}
|
||||||
|
|
||||||
|
type EmptyCellHoverTooltip = {
|
||||||
|
message: string;
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
key: string;
|
||||||
|
};
|
||||||
|
|
||||||
/** onSorting: mergeCells: true인 컬럼이 있으면 그룹 내 순서 유지 정렬, 없으면 RealGrid 기본 정렬 */
|
/** onSorting: mergeCells: true인 컬럼이 있으면 그룹 내 순서 유지 정렬, 없으면 RealGrid 기본 정렬 */
|
||||||
function createOnSortingHandler(
|
function createOnSortingHandler(
|
||||||
columns: {
|
columns: {
|
||||||
@@ -761,6 +776,7 @@ const DataGrid = ({
|
|||||||
}: DataGridProps) => {
|
}: DataGridProps) => {
|
||||||
const { columns, columnLayout } = columnInfo;
|
const { columns, columnLayout } = columnInfo;
|
||||||
const gridRef = useRef<RealGridReact>(null);
|
const gridRef = useRef<RealGridReact>(null);
|
||||||
|
const gridHostRef = useRef<HTMLDivElement>(null);
|
||||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||||
const columnsRef = useRef(columns);
|
const columnsRef = useRef(columns);
|
||||||
const sortDirRef = useRef<Record<string, "asc" | "desc" | "none">>({});
|
const sortDirRef = useRef<Record<string, "asc" | "desc" | "none">>({});
|
||||||
@@ -784,6 +800,10 @@ const DataGrid = ({
|
|||||||
useRef<typeof onItemAllChecked>(onItemAllChecked);
|
useRef<typeof onItemAllChecked>(onItemAllChecked);
|
||||||
const isSyncingChecksFromRowDataRef = useRef(false);
|
const isSyncingChecksFromRowDataRef = useRef(false);
|
||||||
const columnsFieldsSigRef = useRef("");
|
const columnsFieldsSigRef = useRef("");
|
||||||
|
const emptyCellHoverKeyRef = useRef("");
|
||||||
|
const emptyCellHoverRafRef = useRef<number | null>(null);
|
||||||
|
const [emptyCellHoverTooltip, setEmptyCellHoverTooltip] =
|
||||||
|
useState<EmptyCellHoverTooltip | null>(null);
|
||||||
columnsRef.current = columns;
|
columnsRef.current = columns;
|
||||||
rowDataRef.current = rowData;
|
rowDataRef.current = rowData;
|
||||||
cellStyleCallbackRef.current = cellStyleCallback;
|
cellStyleCallbackRef.current = cellStyleCallback;
|
||||||
@@ -1758,34 +1778,43 @@ useEffect(() => {
|
|||||||
const fieldName =
|
const fieldName =
|
||||||
cellIndex?.fieldName ?? cellIndex?.column ?? "";
|
cellIndex?.fieldName ?? cellIndex?.column ?? "";
|
||||||
// cellTooltipCallback 있으면 해당 셀 메시지로 showMessage (공식 문서: TooltipOptions + CellIndex)
|
// cellTooltipCallback 있으면 해당 셀 메시지로 showMessage (공식 문서: TooltipOptions + CellIndex)
|
||||||
|
// 빈 셀은 hover 오버레이로만 표시하므로 클릭 showMessage는 생략 (이중 표출 방지)
|
||||||
if (cellTooltipCallbackRef.current && row && cellIndex) {
|
if (cellTooltipCallbackRef.current && row && cellIndex) {
|
||||||
const msg = cellTooltipCallbackRef.current({
|
if (!isEmptyGridCellValue(row[fieldName])) {
|
||||||
row: row as Record<string, string | number>,
|
const msg = cellTooltipCallbackRef.current({
|
||||||
fieldName,
|
row: row as Record<string, string | number>,
|
||||||
});
|
fieldName,
|
||||||
if (msg) {
|
});
|
||||||
const colName =
|
if (msg) {
|
||||||
columns.find(
|
const colName =
|
||||||
(c) =>
|
columns.find(
|
||||||
c.fieldName === fieldName ||
|
(c) =>
|
||||||
c.name === cellIndex.column,
|
c.fieldName === fieldName ||
|
||||||
)?.name ??
|
c.name === cellIndex.column,
|
||||||
cellIndex.column ??
|
)?.name ??
|
||||||
fieldName;
|
cellIndex.column ??
|
||||||
(
|
fieldName;
|
||||||
gridView as {
|
(
|
||||||
showMessage?: (
|
gridView as {
|
||||||
opts: {
|
showMessage?: (
|
||||||
message: string;
|
opts: {
|
||||||
hideDuration?: number;
|
message: string;
|
||||||
},
|
hideDuration?: number;
|
||||||
idx: { itemIndex: number; column: string },
|
},
|
||||||
) => void;
|
idx: {
|
||||||
}
|
itemIndex: number;
|
||||||
).showMessage?.(
|
column: string;
|
||||||
{ message: msg, hideDuration: 5000 },
|
},
|
||||||
{ itemIndex: cellIndex.itemIndex, column: colName },
|
) => void;
|
||||||
);
|
}
|
||||||
|
).showMessage?.(
|
||||||
|
{ message: msg, hideDuration: 5000 },
|
||||||
|
{
|
||||||
|
itemIndex: cellIndex.itemIndex,
|
||||||
|
column: colName,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (onDataCellClickRef.current && row !== undefined) {
|
if (onDataCellClickRef.current && row !== undefined) {
|
||||||
@@ -1807,6 +1836,144 @@ useEffect(() => {
|
|||||||
};
|
};
|
||||||
}, [onRowClick, onDataCellClick, cellTooltipCallback, columns]);
|
}, [onRowClick, onDataCellClick, cellTooltipCallback, columns]);
|
||||||
|
|
||||||
|
// 빈 셀은 RealGrid hover 툴팁이 안 뜸 → React 오버레이로만 보완 (값 있는 셀은 기존 onShowTooltip)
|
||||||
|
useEffect(() => {
|
||||||
|
if (!cellTooltipCallback) {
|
||||||
|
emptyCellHoverKeyRef.current = "";
|
||||||
|
setEmptyCellHoverTooltip(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const host = gridHostRef.current;
|
||||||
|
if (!host) return;
|
||||||
|
|
||||||
|
const clearEmptyCellHoverTooltip = () => {
|
||||||
|
if (emptyCellHoverKeyRef.current === "") return;
|
||||||
|
emptyCellHoverKeyRef.current = "";
|
||||||
|
setEmptyCellHoverTooltip(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMouseMove = (ev: MouseEvent) => {
|
||||||
|
if (emptyCellHoverRafRef.current != null) {
|
||||||
|
cancelAnimationFrame(emptyCellHoverRafRef.current);
|
||||||
|
}
|
||||||
|
emptyCellHoverRafRef.current = requestAnimationFrame(() => {
|
||||||
|
const gridView = gridRef.current?.gridView as
|
||||||
|
| {
|
||||||
|
mouseToIndex?: (
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
) => {
|
||||||
|
cellType?: string;
|
||||||
|
itemIndex?: number;
|
||||||
|
column?: string;
|
||||||
|
fieldName?: string;
|
||||||
|
} | null;
|
||||||
|
}
|
||||||
|
| undefined;
|
||||||
|
if (!gridView?.mouseToIndex) {
|
||||||
|
clearEmptyCellHoverTooltip();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rect = host.getBoundingClientRect();
|
||||||
|
let index: {
|
||||||
|
cellType?: string;
|
||||||
|
itemIndex?: number;
|
||||||
|
column?: string;
|
||||||
|
fieldName?: string;
|
||||||
|
} | null = null;
|
||||||
|
try {
|
||||||
|
index = gridView.mouseToIndex(
|
||||||
|
ev.clientX - rect.left,
|
||||||
|
ev.clientY - rect.top,
|
||||||
|
);
|
||||||
|
} catch {
|
||||||
|
clearEmptyCellHoverTooltip();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!index ||
|
||||||
|
(index.cellType != null && index.cellType !== "data") ||
|
||||||
|
index.itemIndex == null ||
|
||||||
|
index.itemIndex < 0
|
||||||
|
) {
|
||||||
|
clearEmptyCellHoverTooltip();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const row = rowDataRef.current[index.itemIndex];
|
||||||
|
if (!row) {
|
||||||
|
clearEmptyCellHoverTooltip();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const col = columnsRef.current.find(
|
||||||
|
(c) =>
|
||||||
|
c.name === index.column ||
|
||||||
|
c.fieldName === index.column ||
|
||||||
|
c.name === index.fieldName ||
|
||||||
|
c.fieldName === index.fieldName,
|
||||||
|
);
|
||||||
|
const fieldName =
|
||||||
|
col?.fieldName ?? index.fieldName ?? index.column ?? "";
|
||||||
|
if (!fieldName) {
|
||||||
|
clearEmptyCellHoverTooltip();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 값이 있으면 RealGrid 기본 hover에 맡김
|
||||||
|
if (!isEmptyGridCellValue(row[fieldName])) {
|
||||||
|
clearEmptyCellHoverTooltip();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const msg = cellTooltipCallbackRef.current?.({
|
||||||
|
row: row as Record<string, string | number>,
|
||||||
|
fieldName,
|
||||||
|
});
|
||||||
|
if (!msg) {
|
||||||
|
clearEmptyCellHoverTooltip();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 값 있는 셀 RealGrid 툴팁과 같이: 본문 다음 줄에 (검증 기준값...)이 오도록
|
||||||
|
// 템플릿 리터럴 들여쓰기 공백은 제거
|
||||||
|
const normalizedMessage = msg.replace(/\n[ \t]+/g, "\n");
|
||||||
|
|
||||||
|
const key = `${index.itemIndex}:${fieldName}`;
|
||||||
|
if (emptyCellHoverKeyRef.current === key) return;
|
||||||
|
emptyCellHoverKeyRef.current = key;
|
||||||
|
setEmptyCellHoverTooltip({
|
||||||
|
message: normalizedMessage,
|
||||||
|
x: ev.clientX + 12,
|
||||||
|
y: ev.clientY + 16,
|
||||||
|
key,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMouseLeave = () => {
|
||||||
|
if (emptyCellHoverRafRef.current != null) {
|
||||||
|
cancelAnimationFrame(emptyCellHoverRafRef.current);
|
||||||
|
emptyCellHoverRafRef.current = null;
|
||||||
|
}
|
||||||
|
clearEmptyCellHoverTooltip();
|
||||||
|
};
|
||||||
|
|
||||||
|
host.addEventListener("mousemove", handleMouseMove);
|
||||||
|
host.addEventListener("mouseleave", handleMouseLeave);
|
||||||
|
return () => {
|
||||||
|
host.removeEventListener("mousemove", handleMouseMove);
|
||||||
|
host.removeEventListener("mouseleave", handleMouseLeave);
|
||||||
|
if (emptyCellHoverRafRef.current != null) {
|
||||||
|
cancelAnimationFrame(emptyCellHoverRafRef.current);
|
||||||
|
emptyCellHoverRafRef.current = null;
|
||||||
|
}
|
||||||
|
clearEmptyCellHoverTooltip();
|
||||||
|
};
|
||||||
|
}, [cellTooltipCallback]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const gridView = gridRef.current?.gridView;
|
const gridView = gridRef.current?.gridView;
|
||||||
if (!gridView || !onScrollToBottom) return;
|
if (!gridView || !onScrollToBottom) return;
|
||||||
@@ -1996,7 +2163,7 @@ useEffect(() => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ height: `${gridHeight}vh` }}>
|
<div ref={gridHostRef} className="relative" style={{ height: `${gridHeight}vh` }}>
|
||||||
<RealGridReact
|
<RealGridReact
|
||||||
ref={gridRef}
|
ref={gridRef}
|
||||||
autoGenerateField={true}
|
autoGenerateField={true}
|
||||||
@@ -2206,6 +2373,18 @@ useEffect(() => {
|
|||||||
>
|
>
|
||||||
{columns.map((column) => {
|
{columns.map((column) => {
|
||||||
const baseStyleName = column.styleName;
|
const baseStyleName = column.styleName;
|
||||||
|
const numberFormat =
|
||||||
|
column.dataFormat && column.dataFormat.trim() !== ""
|
||||||
|
? column.dataFormat
|
||||||
|
: "#,##0.000";
|
||||||
|
const resolvedEditor =
|
||||||
|
column.editor ??
|
||||||
|
(column.valueType === ValueType.NUMBER
|
||||||
|
? {
|
||||||
|
type: "number" as const,
|
||||||
|
editFormat: numberFormat,
|
||||||
|
}
|
||||||
|
: undefined);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RGDataColumn
|
<RGDataColumn
|
||||||
@@ -2218,17 +2397,15 @@ useEffect(() => {
|
|||||||
: ValueType.TEXT
|
: ValueType.TEXT
|
||||||
}
|
}
|
||||||
{...(column.valueType === ValueType.NUMBER && {
|
{...(column.valueType === ValueType.NUMBER && {
|
||||||
numberFormat: column.dataFormat
|
numberFormat,
|
||||||
? column.dataFormat
|
|
||||||
: "#,##0.000",
|
|
||||||
})}
|
})}
|
||||||
header={realGridHeaderWithTooltip({
|
header={realGridHeaderWithTooltip({
|
||||||
...column.header,
|
...column.header,
|
||||||
tooltip: resolveColumnTooltip(column),
|
tooltip: resolveColumnTooltip(column),
|
||||||
})}
|
})}
|
||||||
editable={column.editable}
|
editable={column.editable}
|
||||||
{...(column.editor != null && {
|
{...(resolvedEditor != null && {
|
||||||
editor: column.editor,
|
editor: resolvedEditor,
|
||||||
})}
|
})}
|
||||||
{...(column.values != null && {
|
{...(column.values != null && {
|
||||||
values: column.values,
|
values: column.values,
|
||||||
@@ -2287,6 +2464,21 @@ useEffect(() => {
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</RealGridReact>
|
</RealGridReact>
|
||||||
|
{emptyCellHoverTooltip &&
|
||||||
|
createPortal(
|
||||||
|
<div
|
||||||
|
className="rg-tooltip pointer-events-none !visible fixed z-[9999]"
|
||||||
|
style={{
|
||||||
|
left: emptyCellHoverTooltip.x,
|
||||||
|
top: emptyCellHoverTooltip.y,
|
||||||
|
whiteSpace: "pre-wrap",
|
||||||
|
}}
|
||||||
|
role="tooltip"
|
||||||
|
>
|
||||||
|
{emptyCellHoverTooltip.message}
|
||||||
|
</div>,
|
||||||
|
document.body,
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -56,8 +56,8 @@ type ExceptionHistoryTabKey = "NV" | "IMR" | "HB";
|
|||||||
|
|
||||||
const exceptionHistoryTabs: { value: ExceptionHistoryTabKey; label: string }[] = [
|
const exceptionHistoryTabs: { value: ExceptionHistoryTabKey; label: string }[] = [
|
||||||
{ value: "NV", label: "일반검증 내역" },
|
{ value: "NV", label: "일반검증 내역" },
|
||||||
{ value: "IMR", label: "I-MR 검증내역" },
|
{ value: "IMR", label: "시계열 검증내역" },
|
||||||
{ value: "HB", label: "HB 검증내역" },
|
{ value: "HB", label: "모집단 검증내역" },
|
||||||
];
|
];
|
||||||
|
|
||||||
const TITLE = "예외처리 내역 조회";
|
const TITLE = "예외처리 내역 조회";
|
||||||
@@ -274,7 +274,7 @@ export function ExceptionProcessingHistoryDialog({
|
|||||||
setImrPageCount(response?.data?.pageInfo?.totalPages ?? 1);
|
setImrPageCount(response?.data?.pageInfo?.totalPages ?? 1);
|
||||||
setImrPageIndex(pageIndex);
|
setImrPageIndex(pageIndex);
|
||||||
} catch {
|
} catch {
|
||||||
setErrorMessage("I-MR 검증내역 조회 중 오류가 발생했습니다.");
|
setErrorMessage("시계열 검증내역 조회 중 오류가 발생했습니다.");
|
||||||
} finally {
|
} finally {
|
||||||
setImrLoading(false);
|
setImrLoading(false);
|
||||||
}
|
}
|
||||||
@@ -293,7 +293,7 @@ export function ExceptionProcessingHistoryDialog({
|
|||||||
setHbPageCount(response?.data?.pageInfo?.totalPages ?? 1);
|
setHbPageCount(response?.data?.pageInfo?.totalPages ?? 1);
|
||||||
setHbPageIndex(pageIndex);
|
setHbPageIndex(pageIndex);
|
||||||
} catch {
|
} catch {
|
||||||
setErrorMessage("HB 검증내역 조회 중 오류가 발생했습니다.");
|
setErrorMessage("모집단 검증내역 조회 중 오류가 발생했습니다.");
|
||||||
} finally {
|
} finally {
|
||||||
setHbLoading(false);
|
setHbLoading(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import {
|
|||||||
LoadingWithAnimation,
|
LoadingWithAnimation,
|
||||||
} from "@common/components";
|
} from "@common/components";
|
||||||
import CompactDataGrid from "./CompactDataGrid";
|
import CompactDataGrid from "./CompactDataGrid";
|
||||||
import SaveButton from "../button/SaveButton";
|
|
||||||
|
|
||||||
import { useAlertStore } from "@/shared/lib";
|
import { useAlertStore } from "@/shared/lib";
|
||||||
import { statisticsApi } from "@/entities/statistics/api/statistics-service";
|
import { statisticsApi } from "@/entities/statistics/api/statistics-service";
|
||||||
@@ -182,6 +181,7 @@ const SidoSggFacilityNameSearchForm = ({
|
|||||||
Record<string, string | number> | null
|
Record<string, string | number> | null
|
||||||
>(null);
|
>(null);
|
||||||
const didAutoFetchRef = useRef(false);
|
const didAutoFetchRef = useRef(false);
|
||||||
|
const isSavingConnectionRef = useRef(false);
|
||||||
|
|
||||||
const sggKey: [string, string] | null =
|
const sggKey: [string, string] | null =
|
||||||
sido && sido !== "ALL" ? ["sgg-all", sido] : null;
|
sido && sido !== "ALL" ? ["sgg-all", sido] : null;
|
||||||
@@ -329,119 +329,213 @@ const SidoSggFacilityNameSearchForm = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSaveAdminRegions = async () => {
|
const canEditFactConnection = async (): Promise<boolean> => {
|
||||||
const statusCodeResponse = await statisticsApi.CheckStatusCode({
|
const statusCodeResponse = await statisticsApi.CheckStatusCode({
|
||||||
sewerBzmnCd: initialSearchValues?.bsnmCd?.trim() ?? "",
|
sewerBzmnCd: initialSearchValues?.bsnmCd?.trim() ?? "",
|
||||||
statsCd: 'ST00020002',
|
statsCd: "ST00020002",
|
||||||
statsYr: statsYr?.trim() ?? ''
|
statsYr: statsYr?.trim() ?? "",
|
||||||
});
|
});
|
||||||
|
|
||||||
if (statusCodeResponse.status === 'success') {
|
if (statusCodeResponse.status === "success") {
|
||||||
if (!['0', '1', '4'].includes(statusCodeResponse.data)) {
|
if (!["0", "1", "4"].includes(statusCodeResponse.data)) {
|
||||||
const codeName = statusCodeResponse.data === '2' ? '제출' : (statusCodeResponse.data === '3' ? '승인' : '검증중');
|
const codeName =
|
||||||
alert(`시설별 설치 현황 데이터가 ${codeName} 상태일 때는 처리 시설 정보를 변경할 수 없습니다.`);
|
statusCodeResponse.data === "2"
|
||||||
return;
|
? "제출"
|
||||||
}
|
: statusCodeResponse.data === "3"
|
||||||
} else {
|
? "승인"
|
||||||
alert('오류가 발생했습니다.\n관리자에게 문의하세요')
|
: "검증중";
|
||||||
|
alert(
|
||||||
|
`시설별 설치 현황 데이터가 ${codeName} 상태일 때는 처리 시설 정보를 변경할 수 없습니다.`,
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
alert("오류가 발생했습니다.\n관리자에게 문의하세요");
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const persistFactConnection = async (
|
||||||
|
saveList: SeweragePipeFactConnectionSaveItem[],
|
||||||
|
deleteList: string[],
|
||||||
|
): Promise<boolean> => {
|
||||||
if (!statsYr?.trim()) {
|
if (!statsYr?.trim()) {
|
||||||
alert("상단에서 통계연도로 조회한 뒤 이용해 주세요.");
|
alert("상단에서 통계연도로 조회한 뒤 이용해 주세요.");
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
if (!selectedFacilityRow) {
|
if (!selectedFacilityRow) {
|
||||||
alert("처리시설을 먼저 선택해주세요.");
|
alert("처리시설을 먼저 선택해주세요.");
|
||||||
return;
|
return false;
|
||||||
|
}
|
||||||
|
if (saveList.length === 0 && deleteList.length === 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!(await canEditFactConnection())) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasChanged = adminRegionRows.some(
|
try {
|
||||||
(row) =>
|
setIsLoading(true);
|
||||||
(row.__checked === "1" && row.isConnection !== "1") ||
|
const userInfo = await getUserInfo();
|
||||||
(row.__checked !== "1" && row.isConnection === "1"),
|
const request: SeweragePipeFactConnectionSaveRequest = {
|
||||||
);
|
ctpvNm: String(selectedFacilityRow.ctpvNm ?? ""),
|
||||||
|
sggNm: String(selectedFacilityRow.sggNm ?? ""),
|
||||||
|
fcltSn: String(selectedFacilityRow.fcltSn ?? ""),
|
||||||
|
statsYr: statsYr.trim(),
|
||||||
|
swtrmtFcltCd: String(selectedFacilityRow.fcltCd ?? ""),
|
||||||
|
swtrmtFcltNm: String(selectedFacilityRow.fcltNm ?? ""),
|
||||||
|
prcsFcltCpct: Number(selectedFacilityRow.fcltCpct ?? 0),
|
||||||
|
sewerBzmnCd: initialSearchValues?.bsnmCd?.trim() ?? "",
|
||||||
|
userId: userInfo.userUnqId,
|
||||||
|
saveList,
|
||||||
|
deleteList,
|
||||||
|
};
|
||||||
|
|
||||||
const saveList: SeweragePipeFactConnectionSaveItem[] = adminRegionRows
|
const response =
|
||||||
.filter((row) => row.__checked === "1")
|
await statisticsInputSewPipeApiService.saveFactConnection(
|
||||||
.map((row) => ({
|
request,
|
||||||
admdstCd: String(row.admdstCd ?? ""),
|
|
||||||
admdstNm: String(row.emdNm ?? ""),
|
|
||||||
}))
|
|
||||||
.filter((row) => row.admdstCd && row.admdstNm);
|
|
||||||
|
|
||||||
const deleteList = adminRegionRows
|
|
||||||
.filter((row) => row.__checked !== "1" && row.isConnection === "1")
|
|
||||||
.map((row) => String(row.admdstCd ?? ""))
|
|
||||||
.filter(Boolean);
|
|
||||||
|
|
||||||
if (!hasChanged) {
|
|
||||||
alert("변경된 연결 정보가 없습니다.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const run = async () => {
|
|
||||||
try {
|
|
||||||
setIsLoading(true);
|
|
||||||
const userInfo = await getUserInfo();
|
|
||||||
const request: SeweragePipeFactConnectionSaveRequest = {
|
|
||||||
ctpvNm: String(selectedFacilityRow.ctpvNm ?? ""),
|
|
||||||
sggNm: String(selectedFacilityRow.sggNm ?? ""),
|
|
||||||
fcltSn: String(selectedFacilityRow.fcltSn ?? ""),
|
|
||||||
statsYr: statsYr.trim(),
|
|
||||||
swtrmtFcltCd: String(selectedFacilityRow.fcltCd ?? ""),
|
|
||||||
swtrmtFcltNm: String(selectedFacilityRow.fcltNm ?? ""),
|
|
||||||
prcsFcltCpct: Number(selectedFacilityRow.fcltCpct ?? 0),
|
|
||||||
sewerBzmnCd: initialSearchValues?.bsnmCd?.trim() ?? "",
|
|
||||||
userId: userInfo.userUnqId,
|
|
||||||
saveList,
|
|
||||||
deleteList,
|
|
||||||
};
|
|
||||||
|
|
||||||
const response =
|
|
||||||
await statisticsInputSewPipeApiService.saveFactConnection(
|
|
||||||
request,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (response.status !== "success") {
|
|
||||||
alert(response.message ?? "연결관계 저장에 실패했습니다.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setAdminRegionRows((prev) =>
|
|
||||||
prev.map((row) => ({
|
|
||||||
...row,
|
|
||||||
isConnection: row.__checked === "1" ? "1" : "0",
|
|
||||||
})),
|
|
||||||
);
|
);
|
||||||
await onSaveSuccess?.();
|
|
||||||
alert("저장되었습니다.");
|
|
||||||
} catch {
|
|
||||||
alert("연결관계 저장 중 오류가 발생했습니다.");
|
|
||||||
} finally {
|
|
||||||
setIsLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void run();
|
if (response.status !== "success") {
|
||||||
|
alert(response.message ?? "연결관계 저장에 실패했습니다.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
await onSaveSuccess?.();
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
alert("연결관계 저장 중 오류가 발생했습니다.");
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAdminRegionItemChecked = (
|
const syncConnectionFlags = (
|
||||||
|
rows: Record<string, string | number>[],
|
||||||
|
): Record<string, string | number>[] =>
|
||||||
|
rows.map((row) => ({
|
||||||
|
...row,
|
||||||
|
isConnection: row.__checked === "1" ? "1" : "0",
|
||||||
|
}));
|
||||||
|
|
||||||
|
const revertCheckedToConnection = (
|
||||||
|
rows: Record<string, string | number>[],
|
||||||
|
): Record<string, string | number>[] =>
|
||||||
|
rows.map((row) => ({
|
||||||
|
...row,
|
||||||
|
__checked: row.isConnection === "1" ? "1" : "0",
|
||||||
|
}));
|
||||||
|
|
||||||
|
const handleAdminRegionItemChecked = async (
|
||||||
itemIndex: number,
|
itemIndex: number,
|
||||||
checked: boolean,
|
checked: boolean,
|
||||||
) => {
|
) => {
|
||||||
|
if (isSavingConnectionRef.current) {
|
||||||
|
// RealGrid가 이미 토글했을 수 있어 __checked 기준으로 재동기화
|
||||||
|
setAdminRegionRows((prev) => prev.map((row) => ({ ...row })));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const target = adminRegionRows[itemIndex];
|
||||||
|
if (!target) return;
|
||||||
|
|
||||||
|
const nextChecked = checked ? "1" : "0";
|
||||||
|
const prevChecked = target.isConnection === "1" ? "1" : "0";
|
||||||
|
|
||||||
setAdminRegionRows((prev) =>
|
setAdminRegionRows((prev) =>
|
||||||
prev.map((row, idx) =>
|
prev.map((row, idx) =>
|
||||||
idx === itemIndex
|
idx === itemIndex
|
||||||
? { ...row, __checked: checked ? "1" : "0" }
|
? { ...row, __checked: nextChecked }
|
||||||
: row,
|
: row,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 서버 상태와 동일하면 API 호출 없이 UI만 반영
|
||||||
|
if (nextChecked === prevChecked) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const saveList: SeweragePipeFactConnectionSaveItem[] =
|
||||||
|
checked
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
admdstCd: String(target.admdstCd ?? ""),
|
||||||
|
admdstNm: String(target.emdNm ?? ""),
|
||||||
|
},
|
||||||
|
].filter((row) => row.admdstCd && row.admdstNm)
|
||||||
|
: [];
|
||||||
|
const deleteList =
|
||||||
|
!checked && String(target.admdstCd ?? "")
|
||||||
|
? [String(target.admdstCd)]
|
||||||
|
: [];
|
||||||
|
|
||||||
|
if (saveList.length === 0 && deleteList.length === 0) {
|
||||||
|
setAdminRegionRows((prev) => revertCheckedToConnection(prev));
|
||||||
|
alert("행정구역 코드가 없어 저장할 수 없습니다.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isSavingConnectionRef.current = true;
|
||||||
|
const ok = await persistFactConnection(saveList, deleteList);
|
||||||
|
isSavingConnectionRef.current = false;
|
||||||
|
|
||||||
|
if (!ok) {
|
||||||
|
setAdminRegionRows((prev) => revertCheckedToConnection(prev));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setAdminRegionRows((prev) => syncConnectionFlags(prev));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAdminRegionItemAllChecked = (checked: boolean) => {
|
const handleAdminRegionItemAllChecked = async (checked: boolean) => {
|
||||||
|
if (isSavingConnectionRef.current) {
|
||||||
|
setAdminRegionRows((prev) => prev.map((row) => ({ ...row })));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const snapshot = adminRegionRows;
|
||||||
|
if (snapshot.length === 0) return;
|
||||||
|
|
||||||
setAdminRegionRows((prev) =>
|
setAdminRegionRows((prev) =>
|
||||||
prev.map((row) => ({ ...row, __checked: checked ? "1" : "0" })),
|
prev.map((row) => ({
|
||||||
|
...row,
|
||||||
|
__checked: checked ? "1" : "0",
|
||||||
|
})),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const saveList: SeweragePipeFactConnectionSaveItem[] = checked
|
||||||
|
? snapshot
|
||||||
|
.filter((row) => row.isConnection !== "1")
|
||||||
|
.map((row) => ({
|
||||||
|
admdstCd: String(row.admdstCd ?? ""),
|
||||||
|
admdstNm: String(row.emdNm ?? ""),
|
||||||
|
}))
|
||||||
|
.filter((row) => row.admdstCd && row.admdstNm)
|
||||||
|
: [];
|
||||||
|
const deleteList = !checked
|
||||||
|
? snapshot
|
||||||
|
.filter((row) => row.isConnection === "1")
|
||||||
|
.map((row) => String(row.admdstCd ?? ""))
|
||||||
|
.filter(Boolean)
|
||||||
|
: [];
|
||||||
|
|
||||||
|
if (saveList.length === 0 && deleteList.length === 0) {
|
||||||
|
setAdminRegionRows((prev) => syncConnectionFlags(prev));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isSavingConnectionRef.current = true;
|
||||||
|
const ok = await persistFactConnection(saveList, deleteList);
|
||||||
|
isSavingConnectionRef.current = false;
|
||||||
|
|
||||||
|
if (!ok) {
|
||||||
|
setAdminRegionRows((prev) => revertCheckedToConnection(prev));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setAdminRegionRows((prev) => syncConnectionFlags(prev));
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -575,8 +669,10 @@ const SidoSggFacilityNameSearchForm = ({
|
|||||||
조회
|
조회
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="ml-auto">
|
<div>
|
||||||
<SaveButton saveFunc={handleSaveAdminRegions} />
|
<span className="text-lg text-gray-500 float-end">
|
||||||
|
* 체크 박스를 선택 / 해제 시 반영됩니다.
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
{isLoading && (
|
{isLoading && (
|
||||||
|
|||||||
@@ -41,8 +41,8 @@ const VERIFICATION_METHOD_CODES: VerificationMethodCd[] = ["NV", "IMR", "HB"];
|
|||||||
|
|
||||||
const VRFC_MTHD_NAME_MAP: Record<VerificationMethodCd, string> = {
|
const VRFC_MTHD_NAME_MAP: Record<VerificationMethodCd, string> = {
|
||||||
NV: "일반",
|
NV: "일반",
|
||||||
IMR: "I-MR",
|
IMR: "시계열",
|
||||||
HB: "HB",
|
HB: "모집단",
|
||||||
};
|
};
|
||||||
|
|
||||||
export type VerificationExceptionSubmitItem = {
|
export type VerificationExceptionSubmitItem = {
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ type VerificationHistoryTabKey = "NV" | "IMR" | "HB";
|
|||||||
|
|
||||||
const verificationHistoryTabs: { value: VerificationHistoryTabKey; label: string }[] = [
|
const verificationHistoryTabs: { value: VerificationHistoryTabKey; label: string }[] = [
|
||||||
{ value: "NV", label: "일반검증 내역" },
|
{ value: "NV", label: "일반검증 내역" },
|
||||||
{ value: "IMR", label: "I-MR 검증내역" },
|
{ value: "IMR", label: "시계열 검증내역" },
|
||||||
{ value: "HB", label: "HB 검증내역" },
|
{ value: "HB", label: "모집단 검증내역" },
|
||||||
];
|
];
|
||||||
|
|
||||||
const PAGE_SIZE = 30;
|
const PAGE_SIZE = 30;
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ type VerificationReportTabKey = "NV" | "IMR" | "HB";
|
|||||||
|
|
||||||
const VERIFICATION_REPORT_TABS: { value: VerificationReportTabKey; label: string }[] = [
|
const VERIFICATION_REPORT_TABS: { value: VerificationReportTabKey; label: string }[] = [
|
||||||
{ value: "NV", label: "일반" },
|
{ value: "NV", label: "일반" },
|
||||||
{ value: "IMR", label: "I-MR" },
|
{ value: "IMR", label: "시계열" },
|
||||||
{ value: "HB", label: "HB" },
|
{ value: "HB", label: "모집단" },
|
||||||
];
|
];
|
||||||
|
|
||||||
const TYPE_CODE_COLOR_MAP: Record<string, BadgeProps["color"]> = {
|
const TYPE_CODE_COLOR_MAP: Record<string, BadgeProps["color"]> = {
|
||||||
|
|||||||
@@ -290,11 +290,11 @@ async function waitForImrJobCompleted(
|
|||||||
options?.onProgress?.(Math.max(0, Math.min(100, Number(res.job_prog) || 0)));
|
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 === "COMPLETED" && Number(res.job_prog) === 100) return;
|
||||||
if (res.status === "FAILED" || res.status === "ERROR" || res.status === "CANCELLED") {
|
if (res.status === "FAILED" || res.status === "ERROR" || res.status === "CANCELLED") {
|
||||||
throw new Error(res.error_msg || `IMR 검증이 ${res.status} 상태로 종료되었습니다.`);
|
throw new Error(res.error_msg || `시계열 검증이 ${res.status} 상태로 종료되었습니다.`);
|
||||||
}
|
}
|
||||||
await sleep(pollIntervalMs);
|
await sleep(pollIntervalMs);
|
||||||
}
|
}
|
||||||
throw new Error("IMR 검증 완료 대기 시간이 초과되었습니다.");
|
throw new Error("시계열 검증 완료 대기 시간이 초과되었습니다.");
|
||||||
}
|
}
|
||||||
|
|
||||||
function createEmptyExcretaTreatmentFacilityRow(
|
function createEmptyExcretaTreatmentFacilityRow(
|
||||||
@@ -786,7 +786,7 @@ const ExcretaTreatmentFacilityInp = () => {
|
|||||||
|
|
||||||
if (finalStatus === "5") {
|
if (finalStatus === "5") {
|
||||||
setGlobalProgress(50);
|
setGlobalProgress(50);
|
||||||
setGlobalProgressLabel("IMR 검증 진행 중...");
|
setGlobalProgressLabel("시계열 검증 진행 중...");
|
||||||
const imrParam = {
|
const imrParam = {
|
||||||
user_id: userInfo.userUnqId,
|
user_id: userInfo.userUnqId,
|
||||||
sewer_bzmn_cd: lastSearchedValues.bsnmCd ?? "",
|
sewer_bzmn_cd: lastSearchedValues.bsnmCd ?? "",
|
||||||
@@ -941,10 +941,10 @@ const ExcretaTreatmentFacilityInp = () => {
|
|||||||
.map((e) => {
|
.map((e) => {
|
||||||
const prefix =
|
const prefix =
|
||||||
e.vrfcCd === "IMR"
|
e.vrfcCd === "IMR"
|
||||||
? "IMR검증 - "
|
? "시계열 검증 - "
|
||||||
: e.vrfcCd === "NV"
|
: e.vrfcCd === "NV"
|
||||||
? "일반검증 - "
|
? "일반검증 - "
|
||||||
: "HB검증 -";
|
: "모집단 검증 -";
|
||||||
return `${prefix}${e.rsnExpln}
|
return `${prefix}${e.rsnExpln}
|
||||||
(검증 기준값: ${e.compVlCn} / 현재 입력값: ${e.nowVlCn})`;
|
(검증 기준값: ${e.compVlCn} / 현재 입력값: ${e.nowVlCn})`;
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -305,11 +305,11 @@ async function waitForImrJobCompleted(
|
|||||||
options?.onProgress?.(Math.max(0, Math.min(100, Number(res.job_prog) || 0)));
|
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 === "COMPLETED" && Number(res.job_prog) === 100) return;
|
||||||
if (res.status === "FAILED" || res.status === "ERROR" || res.status === "CANCELLED") {
|
if (res.status === "FAILED" || res.status === "ERROR" || res.status === "CANCELLED") {
|
||||||
throw new Error(res.error_msg || `IMR 검증이 ${res.status} 상태로 종료되었습니다.`);
|
throw new Error(res.error_msg || `시계열 검증이 ${res.status} 상태로 종료되었습니다.`);
|
||||||
}
|
}
|
||||||
await sleep(pollIntervalMs);
|
await sleep(pollIntervalMs);
|
||||||
}
|
}
|
||||||
throw new Error("IMR 검증 완료 대기 시간이 초과되었습니다.");
|
throw new Error("시계열 검증 완료 대기 시간이 초과되었습니다.");
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolvePrivateSewageFacilityTab(tabKey: string) {
|
function resolvePrivateSewageFacilityTab(tabKey: string) {
|
||||||
@@ -871,7 +871,7 @@ const PrivateSewageFacilityInp = () => {
|
|||||||
|
|
||||||
if (finalStatus === "5") {
|
if (finalStatus === "5") {
|
||||||
setGlobalProgress(50);
|
setGlobalProgress(50);
|
||||||
setGlobalProgressLabel("IMR 검증 진행 중...");
|
setGlobalProgressLabel("시계열 검증 진행 중...");
|
||||||
const imrParam = {
|
const imrParam = {
|
||||||
user_id: userInfo.userUnqId,
|
user_id: userInfo.userUnqId,
|
||||||
sewer_bzmn_cd: lastSearchedValues.bsnmCd ?? "",
|
sewer_bzmn_cd: lastSearchedValues.bsnmCd ?? "",
|
||||||
@@ -1146,10 +1146,10 @@ const PrivateSewageFacilityInp = () => {
|
|||||||
.map((e) => {
|
.map((e) => {
|
||||||
const prefix =
|
const prefix =
|
||||||
e.vrfcCd === "IMR"
|
e.vrfcCd === "IMR"
|
||||||
? "IMR검증 - "
|
? "시계열 검증 - "
|
||||||
: e.vrfcCd === "NV"
|
: e.vrfcCd === "NV"
|
||||||
? "일반검증 - "
|
? "일반검증 - "
|
||||||
: "HB검증 - ";
|
: "모집단 검증 - ";
|
||||||
return `${prefix}${e.rsnExpln}(검증 기준값: ${e.compVlCn} / 현재 입력값: ${e.nowVlCn})`;
|
return `${prefix}${e.rsnExpln}(검증 기준값: ${e.compVlCn} / 현재 입력값: ${e.nowVlCn})`;
|
||||||
})
|
})
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
|
|||||||
@@ -122,11 +122,11 @@ async function waitForImrJobCompleted(
|
|||||||
options?.onProgress?.(Math.max(0, Math.min(100, Number(res.job_prog) || 0)));
|
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 === 'COMPLETED' && Number(res.job_prog) === 100) return;
|
||||||
if (res.status === 'FAILED' || res.status === 'ERROR' || res.status === 'CANCELLED') {
|
if (res.status === 'FAILED' || res.status === 'ERROR' || res.status === 'CANCELLED') {
|
||||||
throw new Error(res.error_msg || `IMR 검증이 ${res.status} 상태로 종료되었습니다.`);
|
throw new Error(res.error_msg || `시계열 검증이 ${res.status} 상태로 종료되었습니다.`);
|
||||||
}
|
}
|
||||||
await sleep(pollIntervalMs);
|
await sleep(pollIntervalMs);
|
||||||
}
|
}
|
||||||
throw new Error('IMR 검증 완료 대기 시간이 초과되었습니다.');
|
throw new Error('시계열 검증 완료 대기 시간이 초과되었습니다.');
|
||||||
}
|
}
|
||||||
|
|
||||||
type MainFacilityMappingSaveDraft = {
|
type MainFacilityMappingSaveDraft = {
|
||||||
@@ -385,7 +385,7 @@ const PublicSewageFacilityInp = () => {
|
|||||||
|
|
||||||
if (finalStatus === '5') {
|
if (finalStatus === '5') {
|
||||||
setGlobalProgress(50);
|
setGlobalProgress(50);
|
||||||
setGlobalProgressLabel('IMR 검증 진행 중...');
|
setGlobalProgressLabel('시계열 검증 진행 중...');
|
||||||
const imrParam = {
|
const imrParam = {
|
||||||
user_id: userInfo.userUnqId,
|
user_id: userInfo.userUnqId,
|
||||||
sewer_bzmn_cd: lastSearchedValues.bsnmCd ?? '',
|
sewer_bzmn_cd: lastSearchedValues.bsnmCd ?? '',
|
||||||
@@ -1416,7 +1416,7 @@ const PublicSewageFacilityInp = () => {
|
|||||||
return errors
|
return errors
|
||||||
.map((e) => {
|
.map((e) => {
|
||||||
const prefix =
|
const prefix =
|
||||||
e.vrfcCd === "IMR" ? "IMR검증 - " : (e.vrfcCd === "NV" ? "일반검증 - " : "HB검증 - ");
|
e.vrfcCd === "IMR" ? "시계열 검증 - " : (e.vrfcCd === "NV" ? "일반검증 - " : "모집단 검증 - ");
|
||||||
const comp = e.compVlCn ?? "";
|
const comp = e.compVlCn ?? "";
|
||||||
const now = e.nowVlCn ?? "";
|
const now = e.nowVlCn ?? "";
|
||||||
return `${prefix}${e.rsnExpln}
|
return `${prefix}${e.rsnExpln}
|
||||||
|
|||||||
@@ -285,11 +285,11 @@ async function waitForImrJobCompleted(
|
|||||||
options?.onProgress?.(Math.max(0, Math.min(100, Number(res.job_prog) || 0)));
|
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 === "COMPLETED" && Number(res.job_prog) === 100) return;
|
||||||
if (res.status === "FAILED" || res.status === "ERROR" || res.status === "CANCELLED") {
|
if (res.status === "FAILED" || res.status === "ERROR" || res.status === "CANCELLED") {
|
||||||
throw new Error(res.error_msg || `IMR 검증이 ${res.status} 상태로 종료되었습니다.`);
|
throw new Error(res.error_msg || `시계열 검증이 ${res.status} 상태로 종료되었습니다.`);
|
||||||
}
|
}
|
||||||
await sleep(pollIntervalMs);
|
await sleep(pollIntervalMs);
|
||||||
}
|
}
|
||||||
throw new Error("IMR 검증 완료 대기 시간이 초과되었습니다.");
|
throw new Error("시계열 검증 완료 대기 시간이 초과되었습니다.");
|
||||||
}
|
}
|
||||||
|
|
||||||
function createEmptyPumpingStationRow(
|
function createEmptyPumpingStationRow(
|
||||||
@@ -770,7 +770,7 @@ const PumpingStationInp = () => {
|
|||||||
|
|
||||||
if (finalStatus === "5") {
|
if (finalStatus === "5") {
|
||||||
setGlobalProgress(50);
|
setGlobalProgress(50);
|
||||||
setGlobalProgressLabel("IMR 검증 진행 중...");
|
setGlobalProgressLabel("시계열 검증 진행 중...");
|
||||||
const imrParam = {
|
const imrParam = {
|
||||||
user_id: userInfo.userUnqId,
|
user_id: userInfo.userUnqId,
|
||||||
sewer_bzmn_cd: lastSearchedValues.bsnmCd ?? "",
|
sewer_bzmn_cd: lastSearchedValues.bsnmCd ?? "",
|
||||||
@@ -925,10 +925,10 @@ const PumpingStationInp = () => {
|
|||||||
.map((e) => {
|
.map((e) => {
|
||||||
const prefix =
|
const prefix =
|
||||||
e.vrfcCd === "IMR"
|
e.vrfcCd === "IMR"
|
||||||
? "IMR검증 - "
|
? "시계열 검증 - "
|
||||||
: e.vrfcCd === "NV"
|
: e.vrfcCd === "NV"
|
||||||
? "일반검증 - "
|
? "일반검증 - "
|
||||||
: "HB검증 -";
|
: "모집단 검증 -";
|
||||||
return `${prefix}${e.rsnExpln}
|
return `${prefix}${e.rsnExpln}
|
||||||
(검증 기준값: ${e.compVlCn} / 현재 입력값: ${e.nowVlCn})`;
|
(검증 기준값: ${e.compVlCn} / 현재 입력값: ${e.nowVlCn})`;
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -521,10 +521,10 @@ function verificationCellTooltip({
|
|||||||
.map((e) => {
|
.map((e) => {
|
||||||
const prefix =
|
const prefix =
|
||||||
e.vrfcCd === "IMR"
|
e.vrfcCd === "IMR"
|
||||||
? "IMR검증 - "
|
? "시계열 검증 - "
|
||||||
: e.vrfcCd === "NV"
|
: e.vrfcCd === "NV"
|
||||||
? "일반검증 - "
|
? "일반검증 - "
|
||||||
: "HB검증 - ";
|
: "모집단 검증 - ";
|
||||||
return `${prefix}${e.rsnExpln}
|
return `${prefix}${e.rsnExpln}
|
||||||
(검증 기준값: ${e.compVlCn} / 현재 입력값: ${e.nowVlCn})`;
|
(검증 기준값: ${e.compVlCn} / 현재 입력값: ${e.nowVlCn})`;
|
||||||
})
|
})
|
||||||
@@ -577,11 +577,11 @@ async function waitForImrJobCompleted(
|
|||||||
options?.onProgress?.(Math.max(0, Math.min(100, Number(res.job_prog) || 0)));
|
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 === "COMPLETED" && Number(res.job_prog) === 100) return;
|
||||||
if (res.status === "FAILED" || res.status === "ERROR" || res.status === "CANCELLED") {
|
if (res.status === "FAILED" || res.status === "ERROR" || res.status === "CANCELLED") {
|
||||||
throw new Error(res.error_msg || `IMR 검증이 ${res.status} 상태로 종료되었습니다.`);
|
throw new Error(res.error_msg || `시계열 검증이 ${res.status} 상태로 종료되었습니다.`);
|
||||||
}
|
}
|
||||||
await sleep(pollIntervalMs);
|
await sleep(pollIntervalMs);
|
||||||
}
|
}
|
||||||
throw new Error("IMR 검증 완료 대기 시간이 초과되었습니다.");
|
throw new Error("시계열 검증 완료 대기 시간이 초과되었습니다.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 하수도 통계 관리 > 12. 하수 및 분뇨관련산업
|
// 하수도 통계 관리 > 12. 하수 및 분뇨관련산업
|
||||||
@@ -1431,7 +1431,7 @@ const SewageExcretaRelatedInp = () => {
|
|||||||
|
|
||||||
if (finalStatus === "5") {
|
if (finalStatus === "5") {
|
||||||
setGlobalProgress(50);
|
setGlobalProgress(50);
|
||||||
setGlobalProgressLabel("IMR 검증 진행 중...");
|
setGlobalProgressLabel("시계열 검증 진행 중...");
|
||||||
const imrParam = {
|
const imrParam = {
|
||||||
user_id: userInfo.userUnqId,
|
user_id: userInfo.userUnqId,
|
||||||
sewer_bzmn_cd: lastSearchedValues.bsnmCd ?? "",
|
sewer_bzmn_cd: lastSearchedValues.bsnmCd ?? "",
|
||||||
|
|||||||
@@ -136,10 +136,10 @@ function verificationCellTooltip({
|
|||||||
.map((e) => {
|
.map((e) => {
|
||||||
const prefix =
|
const prefix =
|
||||||
e.vrfcCd === "IMR"
|
e.vrfcCd === "IMR"
|
||||||
? "IMR검증 - "
|
? "시계열 검증 - "
|
||||||
: e.vrfcCd === "NV"
|
: e.vrfcCd === "NV"
|
||||||
? "일반검증 - "
|
? "일반검증 - "
|
||||||
: "HB검증 - ";
|
: "모집단 검증 - ";
|
||||||
return `${prefix}${e.rsnExpln}
|
return `${prefix}${e.rsnExpln}
|
||||||
(검증 기준값: ${e.compVlCn} / 현재 입력값: ${e.nowVlCn})`;
|
(검증 기준값: ${e.compVlCn} / 현재 입력값: ${e.nowVlCn})`;
|
||||||
})
|
})
|
||||||
@@ -630,11 +630,11 @@ async function waitForImrJobCompleted(
|
|||||||
options?.onProgress?.(Math.max(0, Math.min(100, Number(res.job_prog) || 0)));
|
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 === "COMPLETED" && Number(res.job_prog) === 100) return;
|
||||||
if (res.status === "FAILED" || res.status === "ERROR" || res.status === "CANCELLED") {
|
if (res.status === "FAILED" || res.status === "ERROR" || res.status === "CANCELLED") {
|
||||||
throw new Error(res.error_msg || `IMR 검증이 ${res.status} 상태로 종료되었습니다.`);
|
throw new Error(res.error_msg || `시계열 검증이 ${res.status} 상태로 종료되었습니다.`);
|
||||||
}
|
}
|
||||||
await sleep(pollIntervalMs);
|
await sleep(pollIntervalMs);
|
||||||
}
|
}
|
||||||
throw new Error("IMR 검증 완료 대기 시간이 초과되었습니다.");
|
throw new Error("시계열 검증 완료 대기 시간이 초과되었습니다.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 하수도 통계 관리 > 11. 하수 및 분뇨찌꺼기(슬러지)처리
|
// 하수도 통계 관리 > 11. 하수 및 분뇨찌꺼기(슬러지)처리
|
||||||
@@ -1437,7 +1437,7 @@ const SewageExcretaSlurgeInp = () => {
|
|||||||
|
|
||||||
if (finalStatus === "5") {
|
if (finalStatus === "5") {
|
||||||
setGlobalProgress(50);
|
setGlobalProgress(50);
|
||||||
setGlobalProgressLabel("IMR 검증 진행 중...");
|
setGlobalProgressLabel("시계열 검증 진행 중...");
|
||||||
const imrParam = {
|
const imrParam = {
|
||||||
user_id: userInfo.userUnqId,
|
user_id: userInfo.userUnqId,
|
||||||
sewer_bzmn_cd: lastSearchedValues.bsnmCd ?? "",
|
sewer_bzmn_cd: lastSearchedValues.bsnmCd ?? "",
|
||||||
@@ -1898,7 +1898,7 @@ const SewageExcretaSlurgeInp = () => {
|
|||||||
col.fieldName === "sggNm"
|
col.fieldName === "sggNm"
|
||||||
) {
|
) {
|
||||||
next = buildSggNmColumn(col, canEdit, sggOptions);
|
next = buildSggNmColumn(col, canEdit, sggOptions);
|
||||||
} else if (col.fieldName === "sggNm" || col.fieldName === "swtrmtFcltNm") {
|
} else if (col.fieldName === "sggNm") {
|
||||||
next = { ...col, editable: false as const, styleName: "rg-cell-readonly" };
|
next = { ...col, editable: false as const, styleName: "rg-cell-readonly" };
|
||||||
}
|
}
|
||||||
const guide = columnGuidesByTab[tabKey]?.[col.fieldName]?.trim();
|
const guide = columnGuidesByTab[tabKey]?.[col.fieldName]?.trim();
|
||||||
|
|||||||
@@ -324,11 +324,11 @@ async function waitForImrJobCompleted(
|
|||||||
options?.onProgress?.(Math.max(0, Math.min(100, Number(res.job_prog) || 0)));
|
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 === "COMPLETED" && Number(res.job_prog) === 100) return;
|
||||||
if (res.status === "FAILED" || res.status === "ERROR" || res.status === "CANCELLED") {
|
if (res.status === "FAILED" || res.status === "ERROR" || res.status === "CANCELLED") {
|
||||||
throw new Error(res.error_msg || `IMR 검증이 ${res.status} 상태로 종료되었습니다.`);
|
throw new Error(res.error_msg || `시계열 검증이 ${res.status} 상태로 종료되었습니다.`);
|
||||||
}
|
}
|
||||||
await sleep(pollIntervalMs);
|
await sleep(pollIntervalMs);
|
||||||
}
|
}
|
||||||
throw new Error("IMR 검증 완료 대기 시간이 초과되었습니다.");
|
throw new Error("시계열 검증 완료 대기 시간이 초과되었습니다.");
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildRegionalFactSubmitListFromGrid(
|
function buildRegionalFactSubmitListFromGrid(
|
||||||
@@ -1428,7 +1428,7 @@ const SewagePipeInp = () => {
|
|||||||
|
|
||||||
if (finalStatus === "5") {
|
if (finalStatus === "5") {
|
||||||
setGlobalProgress(50);
|
setGlobalProgress(50);
|
||||||
setGlobalProgressLabel("IMR 검증 진행 중...");
|
setGlobalProgressLabel("시계열 검증 진행 중...");
|
||||||
const imrParam = {
|
const imrParam = {
|
||||||
user_id: userInfo.userUnqId,
|
user_id: userInfo.userUnqId,
|
||||||
sewer_bzmn_cd: values.bsnmCd ?? "",
|
sewer_bzmn_cd: values.bsnmCd ?? "",
|
||||||
@@ -2244,7 +2244,7 @@ const SewagePipeInp = () => {
|
|||||||
return errors
|
return errors
|
||||||
.map((e) => {
|
.map((e) => {
|
||||||
const prefix =
|
const prefix =
|
||||||
e.vrfcCd === "IMR" ? "IMR검증 - " : (e.vrfcCd === "NV" ? "일반검증 - " : "HB검증 - ");
|
e.vrfcCd === "IMR" ? "시계열 검증 - " : (e.vrfcCd === "NV" ? "일반검증 - " : "모집단 검증 - ");
|
||||||
const comp = e.compVlCn ?? "";
|
const comp = e.compVlCn ?? "";
|
||||||
const now = e.nowVlCn ?? "";
|
const now = e.nowVlCn ?? "";
|
||||||
return `${prefix}${e.rsnExpln}
|
return `${prefix}${e.rsnExpln}
|
||||||
|
|||||||
@@ -183,11 +183,11 @@ async function waitForImrJobCompleted(
|
|||||||
options?.onProgress?.(Math.max(0, Math.min(100, Number(res.job_prog) || 0)));
|
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 === "COMPLETED" && Number(res.job_prog) === 100) return;
|
||||||
if (res.status === "FAILED" || res.status === "ERROR" || res.status === "CANCELLED") {
|
if (res.status === "FAILED" || res.status === "ERROR" || res.status === "CANCELLED") {
|
||||||
throw new Error(res.error_msg || `IMR 검증이 ${res.status} 상태로 종료되었습니다.`);
|
throw new Error(res.error_msg || `시계열 검증이 ${res.status} 상태로 종료되었습니다.`);
|
||||||
}
|
}
|
||||||
await sleep(pollIntervalMs);
|
await sleep(pollIntervalMs);
|
||||||
}
|
}
|
||||||
throw new Error("IMR 검증 완료 대기 시간이 초과되었습니다.");
|
throw new Error("시계열 검증 완료 대기 시간이 초과되었습니다.");
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildColumnFormatNumber(
|
function buildColumnFormatNumber(
|
||||||
@@ -655,7 +655,7 @@ const SewagePipeMaintainInp = () => {
|
|||||||
|
|
||||||
if (finalStatus === "5") {
|
if (finalStatus === "5") {
|
||||||
setGlobalProgress(50);
|
setGlobalProgress(50);
|
||||||
setGlobalProgressLabel("IMR 검증 진행 중...");
|
setGlobalProgressLabel("시계열 검증 진행 중...");
|
||||||
const imrParam = {
|
const imrParam = {
|
||||||
user_id: userInfo.userUnqId,
|
user_id: userInfo.userUnqId,
|
||||||
sewer_bzmn_cd: lastSearchedValues.bsnmCd ?? "",
|
sewer_bzmn_cd: lastSearchedValues.bsnmCd ?? "",
|
||||||
@@ -1099,7 +1099,7 @@ const SewagePipeMaintainInp = () => {
|
|||||||
return errors
|
return errors
|
||||||
.map((e) => {
|
.map((e) => {
|
||||||
const prefix =
|
const prefix =
|
||||||
e.vrfcCd === "IMR" ? "IMR검증 - " : e.vrfcCd === "NV" ? "일반검증 - " : "HB검증 - ";
|
e.vrfcCd === "IMR" ? "시계열 검증 - " : e.vrfcCd === "NV" ? "일반검증 - " : "모집단 검증 - ";
|
||||||
return `${prefix}${e.rsnExpln}(검증 기준값: ${e.compVlCn} / 현재 입력값: ${e.nowVlCn})`;
|
return `${prefix}${e.rsnExpln}(검증 기준값: ${e.compVlCn} / 현재 입력값: ${e.nowVlCn})`;
|
||||||
})
|
})
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
|
|||||||
@@ -446,11 +446,11 @@ async function waitForImrJobCompleted(
|
|||||||
options?.onProgress?.(Math.max(0, Math.min(100, Number(res.job_prog) || 0)));
|
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 === "COMPLETED" && Number(res.job_prog) === 100) return;
|
||||||
if (res.status === "FAILED" || res.status === "ERROR" || res.status === "CANCELLED") {
|
if (res.status === "FAILED" || res.status === "ERROR" || res.status === "CANCELLED") {
|
||||||
throw new Error(res.error_msg || `IMR 검증이 ${res.status} 상태로 종료되었습니다.`);
|
throw new Error(res.error_msg || `시계열 검증이 ${res.status} 상태로 종료되었습니다.`);
|
||||||
}
|
}
|
||||||
await sleep(pollIntervalMs);
|
await sleep(pollIntervalMs);
|
||||||
}
|
}
|
||||||
throw new Error("IMR 검증 완료 대기 시간이 초과되었습니다.");
|
throw new Error("시계열 검증 완료 대기 시간이 초과되었습니다.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 하수도 통계 관리 > 04. 하수저류 시설 및 빗물 펌프장
|
// 하수도 통계 관리 > 04. 하수저류 시설 및 빗물 펌프장
|
||||||
@@ -852,7 +852,7 @@ const SewageReservoirPumpInp = () => {
|
|||||||
|
|
||||||
if (finalStatus === "5") {
|
if (finalStatus === "5") {
|
||||||
setGlobalProgress(50);
|
setGlobalProgress(50);
|
||||||
setGlobalProgressLabel("IMR 검증 진행 중...");
|
setGlobalProgressLabel("시계열 검증 진행 중...");
|
||||||
const imrParam = {
|
const imrParam = {
|
||||||
user_id: userInfo.userUnqId,
|
user_id: userInfo.userUnqId,
|
||||||
sewer_bzmn_cd: lastSearchedValues.bsnmCd ?? "",
|
sewer_bzmn_cd: lastSearchedValues.bsnmCd ?? "",
|
||||||
@@ -1500,10 +1500,10 @@ const SewageReservoirPumpInp = () => {
|
|||||||
.map((e) => {
|
.map((e) => {
|
||||||
const prefix =
|
const prefix =
|
||||||
e.vrfcCd === "IMR"
|
e.vrfcCd === "IMR"
|
||||||
? "IMR검증 - "
|
? "시계열 검증 - "
|
||||||
: e.vrfcCd === "NV"
|
: e.vrfcCd === "NV"
|
||||||
? "일반검증 - "
|
? "일반검증 - "
|
||||||
: "HB검증 -";
|
: "모집단 검증 -";
|
||||||
return `${prefix}${e.rsnExpln}
|
return `${prefix}${e.rsnExpln}
|
||||||
(검증 기준값: ${e.compVlCn} / 현재 입력값: ${e.nowVlCn})`;
|
(검증 기준값: ${e.compVlCn} / 현재 입력값: ${e.nowVlCn})`;
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -341,11 +341,11 @@ async function waitForImrJobCompleted(
|
|||||||
options?.onProgress?.(Math.max(0, Math.min(100, Number(res.job_prog) || 0)));
|
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 === "COMPLETED" && Number(res.job_prog) === 100) return;
|
||||||
if (res.status === "FAILED" || res.status === "ERROR" || res.status === "CANCELLED") {
|
if (res.status === "FAILED" || res.status === "ERROR" || res.status === "CANCELLED") {
|
||||||
throw new Error(res.error_msg || `IMR 검증이 ${res.status} 상태로 종료되었습니다.`);
|
throw new Error(res.error_msg || `시계열 검증이 ${res.status} 상태로 종료되었습니다.`);
|
||||||
}
|
}
|
||||||
await sleep(pollIntervalMs);
|
await sleep(pollIntervalMs);
|
||||||
}
|
}
|
||||||
throw new Error("IMR 검증 완료 대기 시간이 초과되었습니다.");
|
throw new Error("시계열 검증 완료 대기 시간이 초과되었습니다.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 하수도 통계 관리 > 09. 하수도재정
|
// 하수도 통계 관리 > 09. 하수도재정
|
||||||
@@ -918,7 +918,7 @@ const SewerageFinanceInp = () => {
|
|||||||
|
|
||||||
if (finalStatus === "5") {
|
if (finalStatus === "5") {
|
||||||
setGlobalProgress(50);
|
setGlobalProgress(50);
|
||||||
setGlobalProgressLabel("IMR 검증 진행 중...");
|
setGlobalProgressLabel("시계열 검증 진행 중...");
|
||||||
const imrParam = {
|
const imrParam = {
|
||||||
user_id: userInfo.userUnqId,
|
user_id: userInfo.userUnqId,
|
||||||
sewer_bzmn_cd: lastSearchedValues.bsnmCd ?? "",
|
sewer_bzmn_cd: lastSearchedValues.bsnmCd ?? "",
|
||||||
@@ -1212,10 +1212,10 @@ const SewerageFinanceInp = () => {
|
|||||||
.map((e) => {
|
.map((e) => {
|
||||||
const prefix =
|
const prefix =
|
||||||
e.vrfcCd === "IMR"
|
e.vrfcCd === "IMR"
|
||||||
? "IMR검증 - "
|
? "시계열 검증 - "
|
||||||
: e.vrfcCd === "NV"
|
: e.vrfcCd === "NV"
|
||||||
? "일반검증 - "
|
? "일반검증 - "
|
||||||
: "HB검증 - ";
|
: "모집단 검증 - ";
|
||||||
return `${prefix}${e.rsnExpln}(검증 기준값: ${e.compVlCn} / 현재 입력값: ${e.nowVlCn})`;
|
return `${prefix}${e.rsnExpln}(검증 기준값: ${e.compVlCn} / 현재 입력값: ${e.nowVlCn})`;
|
||||||
})
|
})
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
|
|||||||
@@ -87,11 +87,11 @@ async function waitForImrJobCompleted(
|
|||||||
options?.onProgress?.(Math.max(0, Math.min(100, Number(res.job_prog) || 0)));
|
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 === "COMPLETED" && Number(res.job_prog) === 100) return;
|
||||||
if (res.status === "FAILED" || res.status === "ERROR" || res.status === "CANCELLED") {
|
if (res.status === "FAILED" || res.status === "ERROR" || res.status === "CANCELLED") {
|
||||||
throw new Error(res.error_msg || `IMR 검증이 ${res.status} 상태로 종료되었습니다.`);
|
throw new Error(res.error_msg || `시계열 검증이 ${res.status} 상태로 종료되었습니다.`);
|
||||||
}
|
}
|
||||||
await sleep(pollIntervalMs);
|
await sleep(pollIntervalMs);
|
||||||
}
|
}
|
||||||
throw new Error("IMR 검증 완료 대기 시간이 초과되었습니다.");
|
throw new Error("시계열 검증 완료 대기 시간이 초과되었습니다.");
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSewerageSupplyRateParams(
|
function getSewerageSupplyRateParams(
|
||||||
@@ -507,7 +507,7 @@ const SeweragePenetrationRateInp = () => {
|
|||||||
setGlobalProgress(50);
|
setGlobalProgress(50);
|
||||||
|
|
||||||
if (response.data.finalStatus === '5') {
|
if (response.data.finalStatus === '5') {
|
||||||
setGlobalProgressLabel('IMR 검증 진행 중...');
|
setGlobalProgressLabel('시계열 검증 진행 중...');
|
||||||
const imrParam = {
|
const imrParam = {
|
||||||
user_id: userInfo.userUnqId,
|
user_id: userInfo.userUnqId,
|
||||||
sewer_bzmn_cd: searchValues?.bsnmCd ?? '',
|
sewer_bzmn_cd: searchValues?.bsnmCd ?? '',
|
||||||
@@ -833,7 +833,7 @@ const SeweragePenetrationRateInp = () => {
|
|||||||
if (Array.isArray(errors) && errors.length > 0) {
|
if (Array.isArray(errors) && errors.length > 0) {
|
||||||
return errors
|
return errors
|
||||||
.map((e) => {
|
.map((e) => {
|
||||||
const prefix = e.vrfcCd === 'IMR' ? 'IMR검증 - ' : (e.vrfcCd === 'NV' ? '일반검증 - ' : 'HB검증 -');
|
const prefix = e.vrfcCd === 'IMR' ? '시계열 검증 - ' : (e.vrfcCd === 'NV' ? '일반검증 - ' : '모집단 검증 -');
|
||||||
return `${prefix}${e.rsnExpln}
|
return `${prefix}${e.rsnExpln}
|
||||||
(검증 기준값: ${e.compVlCn} / 현재 입력값: ${e.nowVlCn})`;
|
(검증 기준값: ${e.compVlCn} / 현재 입력값: ${e.nowVlCn})`;
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -271,11 +271,11 @@ async function waitForImrJobCompleted(
|
|||||||
options?.onProgress?.(Math.max(0, Math.min(100, Number(res.job_prog) || 0)));
|
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 === "COMPLETED" && Number(res.job_prog) === 100) return;
|
||||||
if (res.status === "FAILED" || res.status === "ERROR" || res.status === "CANCELLED") {
|
if (res.status === "FAILED" || res.status === "ERROR" || res.status === "CANCELLED") {
|
||||||
throw new Error(res.error_msg || `IMR 검증이 ${res.status} 상태로 종료되었습니다.`);
|
throw new Error(res.error_msg || `시계열 검증이 ${res.status} 상태로 종료되었습니다.`);
|
||||||
}
|
}
|
||||||
await sleep(pollIntervalMs);
|
await sleep(pollIntervalMs);
|
||||||
}
|
}
|
||||||
throw new Error("IMR 검증 완료 대기 시간이 초과되었습니다.");
|
throw new Error("시계열 검증 완료 대기 시간이 초과되었습니다.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 하수도 통계 관리 > 13. 하수도 서비스 수준
|
// 하수도 통계 관리 > 13. 하수도 서비스 수준
|
||||||
@@ -791,7 +791,7 @@ const SewerageServiceLevelInp = () => {
|
|||||||
|
|
||||||
if (finalStatus === "5") {
|
if (finalStatus === "5") {
|
||||||
setGlobalProgress(50);
|
setGlobalProgress(50);
|
||||||
setGlobalProgressLabel("IMR 검증 진행 중...");
|
setGlobalProgressLabel("시계열 검증 진행 중...");
|
||||||
const imrParam = {
|
const imrParam = {
|
||||||
user_id: userInfo.userUnqId,
|
user_id: userInfo.userUnqId,
|
||||||
sewer_bzmn_cd: lastSearchedValues.bsnmCd ?? "",
|
sewer_bzmn_cd: lastSearchedValues.bsnmCd ?? "",
|
||||||
@@ -1110,10 +1110,10 @@ const SewerageServiceLevelInp = () => {
|
|||||||
.map((e) => {
|
.map((e) => {
|
||||||
const prefix =
|
const prefix =
|
||||||
e.vrfcCd === "IMR"
|
e.vrfcCd === "IMR"
|
||||||
? "IMR검증 - "
|
? "시계열 검증 - "
|
||||||
: e.vrfcCd === "NV"
|
: e.vrfcCd === "NV"
|
||||||
? "일반검증 - "
|
? "일반검증 - "
|
||||||
: "HB검증 - ";
|
: "모집단 검증 - ";
|
||||||
return `${prefix}${e.rsnExpln}(검증 기준값: ${e.compVlCn} / 현재 입력값: ${e.nowVlCn})`;
|
return `${prefix}${e.rsnExpln}(검증 기준값: ${e.compVlCn} / 현재 입력값: ${e.nowVlCn})`;
|
||||||
})
|
})
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
|
|||||||
@@ -508,11 +508,11 @@ async function waitForImrJobCompleted(
|
|||||||
options?.onProgress?.(Math.max(0, Math.min(100, Number(res.job_prog) || 0)));
|
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 === "COMPLETED" && Number(res.job_prog) === 100) return;
|
||||||
if (res.status === "FAILED" || res.status === "ERROR" || res.status === "CANCELLED") {
|
if (res.status === "FAILED" || res.status === "ERROR" || res.status === "CANCELLED") {
|
||||||
throw new Error(res.error_msg || `IMR 검증이 ${res.status} 상태로 종료되었습니다.`);
|
throw new Error(res.error_msg || `시계열 검증이 ${res.status} 상태로 종료되었습니다.`);
|
||||||
}
|
}
|
||||||
await sleep(pollIntervalMs);
|
await sleep(pollIntervalMs);
|
||||||
}
|
}
|
||||||
throw new Error("IMR 검증 완료 대기 시간이 초과되었습니다.");
|
throw new Error("시계열 검증 완료 대기 시간이 초과되었습니다.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 하수도 통계 관리 > 10. 물 재이용
|
// 하수도 통계 관리 > 10. 물 재이용
|
||||||
@@ -1318,7 +1318,7 @@ const WaterReuseInp = () => {
|
|||||||
|
|
||||||
if (finalStatus === "5") {
|
if (finalStatus === "5") {
|
||||||
setGlobalProgress(50);
|
setGlobalProgress(50);
|
||||||
setGlobalProgressLabel("IMR 검증 진행 중...");
|
setGlobalProgressLabel("시계열 검증 진행 중...");
|
||||||
const imrParam = {
|
const imrParam = {
|
||||||
user_id: userInfo.userUnqId,
|
user_id: userInfo.userUnqId,
|
||||||
sewer_bzmn_cd: lastSearchedValues.bsnmCd ?? "",
|
sewer_bzmn_cd: lastSearchedValues.bsnmCd ?? "",
|
||||||
@@ -2053,10 +2053,10 @@ const WaterReuseInp = () => {
|
|||||||
.map((e) => {
|
.map((e) => {
|
||||||
const prefix =
|
const prefix =
|
||||||
e.vrfcCd === "IMR"
|
e.vrfcCd === "IMR"
|
||||||
? "IMR검증 - "
|
? "시계열 검증 - "
|
||||||
: e.vrfcCd === "NV"
|
: e.vrfcCd === "NV"
|
||||||
? "일반검증 - "
|
? "일반검증 - "
|
||||||
: "HB검증 - ";
|
: "모집단 검증 - ";
|
||||||
return `${prefix}${e.rsnExpln}(검증 기준값: ${e.compVlCn} / 현재 입력값: ${e.nowVlCn})`;
|
return `${prefix}${e.rsnExpln}(검증 기준값: ${e.compVlCn} / 현재 입력값: ${e.nowVlCn})`;
|
||||||
})
|
})
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useCallback, useRef, useState } from "react";
|
import { useCallback, useEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
import { useAlertStore } from "@/shared/lib";
|
import { useAlertStore } from "@/shared/lib";
|
||||||
import { statisticsApi } from "@/entities/statistics/api/statistics-service";
|
import { statisticsApi } from "@/entities/statistics/api/statistics-service";
|
||||||
@@ -115,6 +115,14 @@ const StatisticsInputManagement = () => {
|
|||||||
setRowData([]);
|
setRowData([]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const defaultYear = String(new Date().getFullYear() - 1);
|
||||||
|
void handleSearch({
|
||||||
|
year: defaultYear,
|
||||||
|
clsfCd: "ALL",
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
const handleRegisterClick = () => {
|
const handleRegisterClick = () => {
|
||||||
const yr =
|
const yr =
|
||||||
searchFormValues?.year ?? lastSearchRef.current?.year ?? "";
|
searchFormValues?.year ?? lastSearchRef.current?.year ?? "";
|
||||||
|
|||||||
@@ -48,15 +48,21 @@ const GeneralVerificationManagement = () => {
|
|||||||
|
|
||||||
const { setMessage: alert } = useAlertStore((state) => state);
|
const { setMessage: alert } = useAlertStore((state) => state);
|
||||||
|
|
||||||
const handleSearch = async () => {
|
const handleSearch = async (
|
||||||
|
values: {
|
||||||
|
statsCd: string;
|
||||||
|
useYn: string;
|
||||||
|
vrfcClsfCd: string;
|
||||||
|
} = searchValues,
|
||||||
|
) => {
|
||||||
try {
|
try {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
||||||
const params = Object.fromEntries(
|
const params = Object.fromEntries(
|
||||||
Object.entries({
|
Object.entries({
|
||||||
trgtStatsCd: searchValues.statsCd,
|
trgtStatsCd: values.statsCd,
|
||||||
vrfcClsfCd: searchValues.vrfcClsfCd,
|
vrfcClsfCd: values.vrfcClsfCd,
|
||||||
useYn: searchValues.useYn,
|
useYn: values.useYn,
|
||||||
}).filter(([, value]) => value !== "" && value !== "ALL"),
|
}).filter(([, value]) => value !== "" && value !== "ALL"),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -83,7 +89,7 @@ const GeneralVerificationManagement = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const columns = useMemo(
|
const columns = useMemo(
|
||||||
() => getGeneralVerificationColumns(handleSearch),
|
() => getGeneralVerificationColumns(() => handleSearch()),
|
||||||
[searchValues.statsCd, searchValues.vrfcClsfCd, searchValues.useYn],
|
[searchValues.statsCd, searchValues.vrfcClsfCd, searchValues.useYn],
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -103,14 +109,16 @@ const GeneralVerificationManagement = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleReset = () => {
|
const handleReset = () => {
|
||||||
|
const resetValues = { statsCd: "", useYn: "", vrfcClsfCd: "" };
|
||||||
|
setSearchValues(resetValues);
|
||||||
setRowSelection({});
|
setRowSelection({});
|
||||||
setSelectedVrfcRow(null);
|
setSelectedVrfcRow(null);
|
||||||
handleSearch();
|
|
||||||
setShowRegistForm(false);
|
setShowRegistForm(false);
|
||||||
|
void handleSearch(resetValues);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
handleSearch();
|
void handleSearch();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -182,7 +190,7 @@ const GeneralVerificationManagement = () => {
|
|||||||
<Button
|
<Button
|
||||||
color="green"
|
color="green"
|
||||||
className="h-[3.2rem]"
|
className="h-[3.2rem]"
|
||||||
onClick={handleSearch}
|
onClick={() => void handleSearch()}
|
||||||
>
|
>
|
||||||
조회
|
조회
|
||||||
</Button>
|
</Button>
|
||||||
@@ -205,7 +213,7 @@ const GeneralVerificationManagement = () => {
|
|||||||
<RegistDialog
|
<RegistDialog
|
||||||
open={showRegistForm}
|
open={showRegistForm}
|
||||||
onOpenChange={setShowRegistForm}
|
onOpenChange={setShowRegistForm}
|
||||||
onSuccess={handleSearch}
|
onSuccess={() => void handleSearch()}
|
||||||
>
|
>
|
||||||
<Button>등록</Button>
|
<Button>등록</Button>
|
||||||
</RegistDialog>
|
</RegistDialog>
|
||||||
|
|||||||
@@ -233,13 +233,15 @@ export type ColumnConfig = {
|
|||||||
datetimeFormat?: string;
|
datetimeFormat?: string;
|
||||||
/** RealGrid 셀 편집기 */
|
/** RealGrid 셀 편집기 */
|
||||||
editor?: {
|
editor?: {
|
||||||
type?: "dropdown" | "list" | "date" | "text" | "line";
|
type?: "dropdown" | "list" | "date" | "text" | "line" | "number";
|
||||||
domainOnly?: boolean;
|
domainOnly?: boolean;
|
||||||
textReadOnly?: boolean;
|
textReadOnly?: boolean;
|
||||||
commitOnSelect?: boolean;
|
commitOnSelect?: boolean;
|
||||||
dropDownWhenClick?: boolean;
|
dropDownWhenClick?: boolean;
|
||||||
showToday?: boolean;
|
showToday?: boolean;
|
||||||
datetimeFormat?: string;
|
datetimeFormat?: string;
|
||||||
|
/** NUMBER 편집 시 천단위 구분·소수점 형식 (표시용 numberFormat과 동일하게 맞추는 것을 권장) */
|
||||||
|
editFormat?: string;
|
||||||
values?: string[];
|
values?: string[];
|
||||||
labels?: string[];
|
labels?: string[];
|
||||||
/** RealGrid 입력 마스크 (예: 위탁계약기간 `yyyymmdd~yyyymmdd`) */
|
/** RealGrid 입력 마스크 (예: 위탁계약기간 `yyyymmdd~yyyymmdd`) */
|
||||||
|
|||||||
+3
-3
@@ -34,9 +34,9 @@ export const PublicSewageFactColumn = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// (m³/일)
|
// (m³/일)
|
||||||
{ name: 'prcsFcltPhysCpct', fieldName: 'prcsFcltPhysCpct', header: { text: '물리적' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
|
{ name: 'prcsFcltPhysCpct', fieldName: 'prcsFcltPhysCpct', header: { text: '물리적' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0', editable: false, styleName: 'rg-cell-readonly'},
|
||||||
{ name: 'prcsFcltBoilCpct', fieldName: 'prcsFcltBoilCpct', header: { text: '생물학적' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
|
{ name: 'prcsFcltBoilCpct', fieldName: 'prcsFcltBoilCpct', header: { text: '생물학적' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0', editable: false, styleName: 'rg-cell-readonly'},
|
||||||
{ name: 'prcsFcltAltdCpct', fieldName: 'prcsFcltAltdCpct', header: { text: '고도' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
|
{ name: 'prcsFcltAltdCpct', fieldName: 'prcsFcltAltdCpct', header: { text: '고도' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0', editable: false, styleName: 'rg-cell-readonly'},
|
||||||
|
|
||||||
// 유입하수량
|
// 유입하수량
|
||||||
{ name: 'inflwSwageQnt', fieldName: 'inflwSwageQnt', header: { text: '유입하수량(500m³/일 이상/미만)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0', styleName: 'rg-cell-readonly' },
|
{ name: 'inflwSwageQnt', fieldName: 'inflwSwageQnt', header: { text: '유입하수량(500m³/일 이상/미만)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0', styleName: 'rg-cell-readonly' },
|
||||||
|
|||||||
@@ -199,10 +199,10 @@ export const RegionalFactStatusInpColumn = {
|
|||||||
editable: false,
|
editable: false,
|
||||||
styleName: 'rg-cell-total',
|
styleName: 'rg-cell-total',
|
||||||
valueType: ValueType.NUMBER,
|
valueType: ValueType.NUMBER,
|
||||||
dataFormat: '#,##0.0',
|
dataFormat: '#,##0.00',
|
||||||
computed: {
|
computed: {
|
||||||
numeratorFields: ['sewerLdgrEdpPrlg'],
|
numeratorFields: ['sewerLdgrEdpPrlg'],
|
||||||
denominatorFields: ['cbsysPplMtncPlanPrlg', 'drtwPplMtncPlanPrlg', 'exlnPplMtncPlanPrlg'],
|
denominatorFields: ['cbsysSqrePrlg', 'cbsysCiclPrlg', 'cbsysOpnPrlg', 'cbsysGttrPrlg', 'drtwPplSqrePrlg', 'drtwPplCiclPrlg', 'exlnPplSqrePrlg', 'exlnPplCiclPrlg', 'exlnPplOpnPrlg', 'exlnPplGttrPrlg'],
|
||||||
type: 'pblswrgPopltnSuprtRate' as const,
|
type: 'pblswrgPopltnSuprtRate' as const,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user