[st] - feat: 입력 02. 제출 / 06. 작업중

This commit is contained in:
2026-05-07 18:03:08 +09:00
parent b6d7e47aeb
commit 9898251563
8 changed files with 721 additions and 103 deletions
@@ -1,20 +1,28 @@
import { useState } from 'react';
import { useRef, useState } from 'react';
import SearchForm from '@/components/module/SearchForm';
import DataGrid from '@/components/module/DataGrid';
import SplitDataGrid from '@/components/module/SplitDataGrid';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@common/components';
import { LoadingWithAnimation, Tabs, TabsContent, TabsList, TabsTrigger } from '@common/components';
import SubmitButton from '@/components/button/SumitButton';
import SaveButton from '@/components/button/SaveButton';
import OperateDataButton from '@/components/button/OperateDataButton';
import UpdateOperateDataButton from '@/components/button/UpdateOperateDataButton';
import type { ColumnInfo, ColumnInfoList } from '@/components/type/DataGrid.type';
import type { SearchFormValues } from '@/components/type/SearchForm.type';
import { PublicSewageFactInpColumnList } from '@/components/util/column/input/PublicSewageFactInpColumn/PublicSewageFactInpColumn';
import { statisticsInputPublicSewageFacilityApiService } from '@/entities/statistics/api/input/statistics-public-sewage-facility-input-service';
import type { PublicSewageFacilityRequestParams } from '@/entities/statistics/model/input/statistics-public-sewage-facility-input-interface';
import { toGridRows } from '@/components/util/grid/gridFunction';
import { useAlertStore } from '@/shared/lib';
// 하수도 통계 관리 > 06. 공공하수처리시설
const PublicSewageFacilityInp = () => {
const { setMessage: alert } = useAlertStore((state) => state);
const [isLoading, setIsLoading] = useState<boolean>(false);
const [hasSearched, setHasSearched] = useState(false);
const submitFunc = () => {
console.log('submit');
// 제출 로직
@@ -39,6 +47,129 @@ const PublicSewageFacilityInp = () => {
const [activeTab, setActiveTab] = useState<string>(
PublicSewageFactInpColumnList[0]?.tabKey ?? 'ST00060001',
);
const activeTabRef = useRef<string>(PublicSewageFactInpColumnList[0]?.tabKey ?? 'ST00060001');
const firstTabKey = PublicSewageFactInpColumnList[0]?.tabKey ?? 'ST00060001';
const searchParamRef = useRef<PublicSewageFacilityRequestParams | null>(null);
const buildPublicSewageFacilityParams = (values: SearchFormValues): PublicSewageFacilityRequestParams => ({
ctpvNm: values.sido,
sggNm: values.gugun === 'ALL' ? '' : values.gugun,
sewerBzmnCd: values.bsnmCd,
fcltNm: values.fctName,
statsYr: values.year,
});
const tabRowDataExists = (tabKey: string): boolean => {
if (tabKey === 'ST00060002') {
const left = rowDataMap.get('MainFacility');
const right = rowDataMap.get('MainFacilityTarget');
return (left?.length ?? 0) > 0 || (right?.length ?? 0) > 0;
}
return (rowDataMap.get(tabKey)?.length ?? 0) > 0;
};
const loadPublicSewageFacilityActiveTabData = async (
tabKey: string,
param: PublicSewageFacilityRequestParams,
): Promise<boolean> => {
try {
if (tabKey === 'ST00060001') {
const response = await statisticsInputPublicSewageFacilityApiService.getPublicSewageFacility(param);
if (response.status !== 'success') {
alert(response.message || '데이터 조회 중 오류가 발생했습니다.');
return false;
}
const commonInfo = response.data.commonInfo;
const list = response.data.publicSewageFacilityList ?? [];
const convertedData = list.map((item) => ({
ctpvNm: commonInfo.ctpvNm,
...item,
rmrk: item.rmrk ?? null,
}));
setRowDataMap((prev) => {
const next = new Map(prev);
next.set('ST00060001', toGridRows(convertedData));
return next;
});
return true;
}
if (tabKey === 'ST00060002') {
const response = await statisticsInputPublicSewageFacilityApiService.getPublicSewageMainFacility(param);
if (response.status !== 'success') {
alert(response.message || '데이터 조회 중 오류가 발생했습니다.');
return false;
}
const list = response.data.mainFacilityList ?? [];
const leftRows = list.map((item) => ({
swtrmtFcltNm: item.swtrmtFcltNm,
fcltPstnCn: item.fcltPstnCn,
prxyBzentyNmCn: item.prxyBzentyNmCn,
cnsgnCstVl: item.cnsgnCstVl,
prxyCtrtCn: item.prxyCtrtCn,
wholNope: item.wholNope,
isMainYn: item.isMainYn,
admdstCd: item.admdstCd,
fcltSn: item.fcltSn,
swtrmtFcltCd: item.swtrmtFcltCd,
sggNm: item.sggNm ?? '',
admdstNm: item.admdstNm,
}));
setRowDataMap((prev) => {
const next = new Map(prev);
next.set('MainFacility', toGridRows(leftRows));
next.set('MainFacilityTarget', []);
return next;
});
return true;
}
return true;
} catch {
alert('데이터 조회 중 오류가 발생했습니다.');
return false;
}
};
const handleSearch = async (values: SearchFormValues) => {
if (!values.sido || values.sido === 'ALL') {
alert('시도를 선택해주세요.');
return;
}
try {
setIsLoading(true);
const param = buildPublicSewageFacilityParams(values);
const yearChangedFromLastSearch =
hasSearched &&
(searchParamRef.current?.statsYr ?? '') !== (param.statsYr ?? '');
if (yearChangedFromLastSearch) {
setRowDataMap(new Map());
activeTabRef.current = firstTabKey;
setActiveTab(firstTabKey);
}
searchParamRef.current = param;
setHasSearched(true);
const tabKeyToLoad = yearChangedFromLastSearch ? firstTabKey : activeTabRef.current;
await loadPublicSewageFacilityActiveTabData(tabKeyToLoad, param);
} finally {
setIsLoading(false);
}
};
const handleActiveTabChange = async (tabKey: string) => {
activeTabRef.current = tabKey;
setActiveTab(tabKey);
if (!hasSearched || tabRowDataExists(tabKey) || !searchParamRef.current) return;
try {
setIsLoading(true);
await loadPublicSewageFacilityActiveTabData(tabKey, searchParamRef.current);
} finally {
setIsLoading(false);
}
};
const getGridColumnInfo = (
tab: ColumnInfoList,
@@ -49,23 +180,30 @@ const PublicSewageFacilityInp = () => {
return (
<>
<SearchForm fctName={true} />
<div className="card !py-0">
<div className="flex justify-end items-center gap-[0.6rem] m-[0.6rem]">
<UpdateOperateDataButton updateOperateDataButtonFunc={updateOperateDataButtonFunc} />
<OperateDataButton getOperateDataFunc={getOperateDataFunc} />
<SubmitButton submitFunc={submitFunc} />
<SaveButton saveFunc={saveFunc} />
<div className="relative">
<SearchForm fctName={true} onSubmit={handleSearch} />
<div className="card !py-0">
<div className="flex justify-end items-center gap-[0.6rem] m-[0.6rem]">
<UpdateOperateDataButton updateOperateDataButtonFunc={updateOperateDataButtonFunc} />
<OperateDataButton getOperateDataFunc={getOperateDataFunc} />
<SubmitButton submitFunc={submitFunc} />
<SaveButton saveFunc={saveFunc} />
</div>
</div>
</div>
<Tabs value={activeTab} onValueChange={setActiveTab}>
<TabsList>
{PublicSewageFactInpColumnList.map((tab) => (
<TabsTrigger key={tab.tabKey} value={tab.tabKey}>
{tab.tabLabel || tab.tabKey}
</TabsTrigger>
))}
</TabsList>
{isLoading && <div className="absolute inset-0 z-50 [data-theme='black']:bg-transparent rounded-lg p-10 flex justify-center items-center">
<div className="relative size-[5rem]">
<LoadingWithAnimation color="primary" />
</div>
</div>
}
<Tabs value={activeTab} onValueChange={(v) => void handleActiveTabChange(v)}>
<TabsList>
{PublicSewageFactInpColumnList.map((tab) => (
<TabsTrigger key={tab.tabKey} value={tab.tabKey}>
{tab.tabLabel || tab.tabKey}
</TabsTrigger>
))}
</TabsList>
{PublicSewageFactInpColumnList.map((tab) => {
if (tab.isSplitGrid && tab.left && tab.right) {
@@ -137,7 +275,8 @@ const PublicSewageFacilityInp = () => {
</TabsContent>
);
})}
</Tabs>
</Tabs>
</div>
</>
);
};
@@ -28,13 +28,19 @@ import { doList } from "@/components/util/column/input/DoList";
import { statisticsInputSewPipeApiService } from "@/entities/statistics/api/input/statistics-sewage-pipe-input-service";
import type {
SeweragePipeInputFactInstallStatusSaveRequest,
SeweragePipeInputFactInstallStatusSubmitRequest,
SeweragePipeInputManholeStatusItem,
SeweragePipeInputManholeStatusSaveRequest,
SeweragePipeInputManholeStatusSubmitRequest,
SeweragePipeInputPipeDiameterStatusSaveRequest,
SeweragePipeInputPipeDiameterStatusSubmitRequest,
SeweragePipeInputPipeTypeStatusItem,
SeweragePipeInputPipeTypeStatusSaveRequest,
SeweragePipeInputPipeTypeStatusSubmitRequest,
SeweragePipeInputRegionalFactStatusSaveRequest,
SeweragePipeInputRegionalFactStatusSaveItem,
SeweragePipeInputRegionalFactStatusSubmitRequest,
SeweragePipeInputSubmitResponse,
SeweragePipeInputVerificationResult,
SeweragePipeRequestParams,
} from "@/entities/statistics/model/input/statistics-sewage-pipe-input-interface";
@@ -903,6 +909,22 @@ const SewagePipeInp = () => {
const userInfo = await getUserInfo();
const statsCdForTab = tabStatsCdMap.get(tabKey) ?? statsCd;
const statusForTab = tabStatusInfoMap.get(tabKey) ?? null;
const submitBase = {
statsYr: values.year ?? "",
ctpvNm: values.sido ?? "",
sggNm: values.gugun === "ALL" ? "" : (values.gugun ?? ""),
sewerBzmnCd: values.bsnmCd ?? "",
userId: userInfo.userUnqId,
laePicNm: submitInfo.laePicNm,
laeKbrdrJbgdNm: submitInfo.laeKbrdrJbgdNm,
laeKbrdrDeptNm: submitInfo.laeKbrdrDeptNm,
laePicTelNo: submitInfo.laePicTelNo,
laeIdfrNm: submitInfo.laeIdfrNm,
laeIdfrJbgdNm: submitInfo.laeIdfrJbgdNm,
laeIdfrDeptNm: submitInfo.laeIdfrDeptNm,
laeIdfrTelNo: submitInfo.laeIdfrTelNo,
};
let response: SeweragePipeInputSubmitResponse | null = null;
switch (tabKey) {
case REGIONAL_FACT_TAB_KEY: {
@@ -914,97 +936,294 @@ const SewagePipeInp = () => {
const submitList = buildRegionalFactSubmitListFromGrid(currentData);
const requestData = {
statsYr: values.year ?? "",
ctpvNm: values.sido ?? "",
sggNm: values.gugun === "ALL" ? "" : (values.gugun ?? ""),
sewerBzmnCd: values.bsnmCd ?? "",
userId: userInfo.userUnqId,
const requestData: SeweragePipeInputRegionalFactStatusSubmitRequest = {
...submitBase,
submitList,
laePicNm: submitInfo.laePicNm,
laeKbrdrJbgdNm: submitInfo.laeKbrdrJbgdNm,
laeKbrdrDeptNm: submitInfo.laeKbrdrDeptNm,
laePicTelNo: submitInfo.laePicTelNo,
laeIdfrNm: submitInfo.laeIdfrNm,
laeIdfrJbgdNm: submitInfo.laeIdfrJbgdNm,
laeIdfrDeptNm: submitInfo.laeIdfrDeptNm,
laeIdfrTelNo: submitInfo.laeIdfrTelNo,
};
setGlobalProgressLabel("일반 검증 진행 중...");
const response =
response =
await statisticsInputSewPipeApiService.submitRegionalFactStatus(requestData);
if (response.status !== "success") {
alert(response.message || "데이터 제출 중 오류가 발생했습니다.");
break;
}
case "ST00020002": {
const currentData = getFactInstallGridDataRef.current?.();
if (!currentData || currentData.length === 0) {
alert("제출할 데이터가 없습니다.");
return;
}
const finalStatus = response.data?.finalStatus;
const nvpassed = response.data?.nvpassed;
if (finalStatus === "5") {
setGlobalProgress(50);
setGlobalProgressLabel("IMR 검증 진행 중...");
const imrParam = {
user_id: userInfo.userUnqId,
sewer_bzmn_cd: values.bsnmCd ?? "",
stats_cd: statsCdForTab,
stats_yr: values.year ?? "",
lae_sbmsn_stts_cd: statusForTab?.laeSbmsnSttsCd === "4" ? "4" : "2",
gen_verify_stts_cd: nvpassed ? "1" : "2",
};
try {
const imrResponse = await statisticsApi.callImrService(imrParam);
if (imrResponse.job_id) {
await waitForImrJobCompleted(
imrResponse.job_id,
(id) => statisticsApi.getImrStatus(id),
{
onProgress: (jobProgress) => {
setGlobalProgress(50 + Math.round(jobProgress / 2));
},
},
);
}
setGlobalProgress(100);
await loadSewagePipeActiveTabData(
tabKey,
buildSewagePipeSearchParam(values),
values,
true,
false,
);
setVerificationResultDialogOpen(true);
} catch (e) {
await loadSewagePipeActiveTabData(
tabKey,
buildSewagePipeSearchParam(values),
values,
true,
false,
);
const msg =
e instanceof Error && e.message
? e.message
: "IMR 검증 중 오류가 발생했습니다.\n관리자에게 문의하세요.";
alert(msg);
}
} else {
setGlobalProgress(100);
await loadSewagePipeActiveTabData(
tabKey,
buildSewagePipeSearchParam(values),
values,
true,
false,
);
alert("제출되었습니다.");
const columnFormatMap = new Map<string, string>();
FactInstallStatusInpColumn.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);
};
const submitList = currentData.map((d) => ({
admdstCd: String(d.admdstCd ?? ""),
sggNm: String(d.sggNm ?? ""),
admdstNm: String(d.admdstNm ?? ""),
fcltSn: String(d.fcltSn ?? ""),
swtrmtFcltCd: String(d.swtrmtFcltCd ?? ""),
swtrmtFcltNm: String(d.swtrmtFcltNm ?? ""),
prcsFcltCpct: formatNumber(d.prcsFcltCpct, "prcsFcltCpct"),
prcsZoneArea: formatNumber(d.prcsZoneArea, "prcsZoneArea"),
cbsysPrcsZoneArea: formatNumber(d.cbsysPrcsZoneArea, "cbsysPrcsZoneArea"),
sprsysPrcsZoneArea: formatNumber(d.sprsysPrcsZoneArea, "sprsysPrcsZoneArea"),
sprsysVartnRt: formatNumber(d.sprsysVartnRt, "sprsysVartnRt"),
mltvEqpmCnt: formatNumber(d.mltvEqpmCnt, "mltvEqpmCnt"),
cbsysMltvEqpmCnt: formatNumber(d.cbsysMltvEqpmCnt, "cbsysMltvEqpmCnt"),
sprsysMltvEqpmCnt: formatNumber(d.sprsysMltvEqpmCnt, "sprsysMltvEqpmCnt"),
sprsysMltvLnkgCnt: formatNumber(d.sprsysMltvLnkgCnt, "sprsysMltvLnkgCnt"),
sprsysMltvYetLnkgCnt: formatNumber(d.sprsysMltvYetLnkgCnt, "sprsysMltvYetLnkgCnt"),
wholFcltPrlg: formatNumber(d.wholFcltPrlg, "wholFcltPrlg"),
wholIcptgAtlfcsPrlg: formatNumber(d.wholIcptgAtlfcsPrlg, "wholIcptgAtlfcsPrlg"),
icptgPrlg: formatNumber(d.icptgPrlg, "icptgPrlg"),
atlfcsPrlg: formatNumber(d.atlfcsPrlg, "atlfcsPrlg"),
cbsysStofcbrCnt: formatNumber(d.cbsysStofcbrCnt, "cbsysStofcbrCnt"),
sprsysStofcbrCnt: formatNumber(d.sprsysStofcbrCnt, "sprsysStofcbrCnt"),
sprsysExlnOtflCnt: formatNumber(d.sprsysExlnOtflCnt, "sprsysExlnOtflCnt"),
wholBrchlnPrlg: formatNumber(d.wholBrchlnPrlg, "wholBrchlnPrlg"),
cbsysBrchlnPrlg: formatNumber(d.cbsysBrchlnPrlg, "cbsysBrchlnPrlg"),
exlnBrchlnPrlg: formatNumber(d.exlnBrchlnPrlg, "exlnBrchlnPrlg"),
drtwBrchlnPrlg: formatNumber(d.drtwBrchlnPrlg, "drtwBrchlnPrlg"),
rmrk: String(d.rmrk ?? ""),
}));
const requestData: SeweragePipeInputFactInstallStatusSubmitRequest = {
...submitBase,
submitList,
};
setGlobalProgressLabel("일반 검증 진행 중...");
response = await statisticsInputSewPipeApiService.submitFactInstallStatus(requestData);
break;
}
case "ST00020003": {
const currentData = getPipeTypeGridDataRef.current?.();
if (!currentData || currentData.length === 0) {
alert("제출할 데이터가 없습니다.");
return;
}
const columnFormatMap = new Map<string, string>();
PipeTypeStatusInpColumn.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);
};
const lastDataYear = getPipeTypeLastDataYear();
const submitList = currentData.map((d) => {
const yearlyStatList = [];
for (let year = 1995; year <= lastDataYear; year++) {
const wholKey = `y${year}PrlgWhol`;
const lyngKey = `y${year}LyngPrlg`;
const dmltnKey = `y${year}DmltnPrlg`;
yearlyStatList.push({
lyngYr: String(year),
lyngPrlg: formatNumber(d[lyngKey] as string | number | undefined, lyngKey),
dmltnPrlg: formatNumber(d[dmltnKey] as string | number | undefined, dmltnKey),
prlgWhol: formatNumber(d[wholKey] as string | number | undefined, wholKey),
});
}
return {
admdstCd: String(d.admdstCd ?? ""),
admdstNm: String(d.admdstNm ?? ""),
pipeSeCd: String(d.pipeSeCd ?? ""),
yearlyStatList,
rmrk: String(d.rmrk ?? ""),
lyngPrlgWhol: formatNumber(d.lyngPrlg, "lyngPrlg"),
dmltnPrlgWhol: formatNumber(d.dmltnPrlg, "dmltnPrlg"),
pipeByPrstWhol: formatNumber(d.pipeByPrstWhol, "pipeByPrstWhol"),
};
});
const requestData: SeweragePipeInputPipeTypeStatusSubmitRequest = {
...submitBase,
submitList,
};
setGlobalProgressLabel("일반 검증 진행 중...");
response = await statisticsInputSewPipeApiService.submitPipeTypeStatus(requestData);
break;
}
case "ST00020004": {
const currentData = getPipeDiameterGridDataRef.current?.();
if (!currentData || currentData.length === 0) {
alert("제출할 데이터가 없습니다.");
return;
}
const columnFormatMap = new Map<string, string>();
PipeDiameterStatusInpColumn.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);
};
const submitList = currentData.map((d) => ({
pipeSeCd: String(d.pipeSeCd ?? ""),
step1PdmtPrlg: formatNumber(d.step1PdmtPrlg, "step1PdmtPrlg"),
step2PdmtPrlg: formatNumber(d.step2PdmtPrlg, "step2PdmtPrlg"),
step3PdmtPrlg: formatNumber(d.step3PdmtPrlg, "step3PdmtPrlg"),
step4PdmtPrlg: formatNumber(d.step4PdmtPrlg, "step4PdmtPrlg"),
step5PdmtPrlg: formatNumber(d.step5PdmtPrlg, "step5PdmtPrlg"),
sqrePdmtPrlg: formatNumber(d.sqrePdmtPrlg, "sqrePdmtPrlg"),
etcPdmtPrlg: formatNumber(d.etcPdmtPrlg, "etcPdmtPrlg"),
rmrk: String(d.rmrk ?? ""),
pdmtByTsmWhol: formatNumber(d.pdmtByTsmWhol, "pdmtByTsmWhol"),
admdstCd: String(d.admdstCd ?? ""),
admdstNm: String(d.admdstNm ?? ""),
}));
const requestData: SeweragePipeInputPipeDiameterStatusSubmitRequest = {
...submitBase,
submitList,
};
setGlobalProgressLabel("일반 검증 진행 중...");
response = await statisticsInputSewPipeApiService.submitPipeDiameterStatus(requestData);
break;
}
case "ST00020005": {
const currentData = getManholeGridDataRef.current?.();
if (!currentData || currentData.length === 0) {
alert("제출할 데이터가 없습니다.");
return;
}
const columnFormatMap = new Map<string, string>();
ManholeStatusInpColumn.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);
};
const minYy = 14;
const maxYy = getManholeLastDataYear() % 100;
const submitList = currentData.map((d) => {
const yearlyStats = [];
for (let yy = minYy; yy <= maxYy; yy++) {
const year = 2000 + yy;
const wholKey = `yy${yy}WholMnhlCnt`;
const step1Key = `yy${yy}Step1MnhlCnt`;
const step2Key = `yy${yy}Step2MnhlCnt`;
const step3Key = `yy${yy}Step3MnhlCnt`;
const step4Key = `yy${yy}Step4MnhlCnt`;
const step5Key = `yy${yy}Step5MnhlCnt`;
yearlyStats.push({
manhYr: String(year),
wholMnhlCnt: formatNumber(d[wholKey] as string | number | undefined, wholKey),
step1MnhlCnt: formatNumber(d[step1Key] as string | number | undefined, step1Key),
step2MnhlCnt: formatNumber(d[step2Key] as string | number | undefined, step2Key),
step3MnhlCnt: formatNumber(d[step3Key] as string | number | undefined, step3Key),
step4MnhlCnt: formatNumber(d[step4Key] as string | number | undefined, step4Key),
step5MnhlCnt: formatNumber(d[step5Key] as string | number | undefined, step5Key),
});
}
return {
emphsMngSeCd: String(d.emphsMngSeCd ?? ""),
admdstCd: String(d.admdstCd ?? ""),
admdstNm: String(d.admdstNm ?? ""),
yearlyStats,
rmrk: String(d.rmrk ?? ""),
};
});
const requestData: SeweragePipeInputManholeStatusSubmitRequest = {
...submitBase,
submitList,
};
setGlobalProgressLabel("일반 검증 진행 중...");
response = await statisticsInputSewPipeApiService.submitManholeStatus(requestData);
break;
}
default:
alert("해당 탭 제출 API는 준비 중입니다.");
return;
}
if (!response || response.status !== "success") {
alert(response?.message || "데이터 제출 중 오류가 발생했습니다.");
return;
}
const finalStatus = response.data?.finalStatus;
const nvpassed = response.data?.nvpassed;
if (finalStatus === "5") {
setGlobalProgress(50);
setGlobalProgressLabel("IMR 검증 진행 중...");
const imrParam = {
user_id: userInfo.userUnqId,
sewer_bzmn_cd: values.bsnmCd ?? "",
stats_cd: statsCdForTab,
stats_yr: values.year ?? "",
lae_sbmsn_stts_cd: statusForTab?.laeSbmsnSttsCd === "4" ? "4" : "2",
gen_verify_stts_cd: nvpassed ? "1" : "2",
};
try {
const imrResponse = await statisticsApi.callImrService(imrParam);
if (imrResponse.job_id) {
await waitForImrJobCompleted(
imrResponse.job_id,
(id) => statisticsApi.getImrStatus(id),
{
onProgress: (jobProgress) => {
setGlobalProgress(50 + Math.round(jobProgress / 2));
},
},
);
}
setGlobalProgress(100);
await loadSewagePipeActiveTabData(
tabKey,
buildSewagePipeSearchParam(values),
values,
true,
false,
);
setVerificationResultDialogOpen(true);
} catch (e) {
await loadSewagePipeActiveTabData(
tabKey,
buildSewagePipeSearchParam(values),
values,
true,
false,
);
const msg =
e instanceof Error && e.message
? e.message
: "IMR 검증 중 오류가 발생했습니다.\n관리자에게 문의하세요.";
alert(msg);
}
} else {
setGlobalProgress(100);
await loadSewagePipeActiveTabData(
tabKey,
buildSewagePipeSearchParam(values),
values,
true,
false,
);
alert("제출되었습니다.");
}
} catch {
alert("데이터 제출 중 오류가 발생했습니다.");
@@ -7,6 +7,7 @@ export const MainFacilityColumn = {
{ name: 'prxyBzentyNmCn', fieldName: 'prxyBzentyNmCn', header: { text: '위탁업체명' }, editable: false },
{ name: 'cnsgnCstVl', fieldName: 'cnsgnCstVl', header: { text: '위탁비용(백만원/년)' }, editable: false },
{ name: 'prxyCtrtCn', fieldName: 'prxyCtrtCn', header: { text: '위탁계약기간' }, editable: false },
{ name: 'wholNope', fieldName: 'wholNope', header: { text: '직원수(명)' }, editable: false },
{ name: 'isMainYn', fieldName: 'isMainYn', header: { text: '중심처리시설여부' }, editable: false },
{ name: 'admdstCd', fieldName: 'admdstCd', header: { text: '행정구역코드' }, visible: false, editable: false, styleName: 'rg-cell-readonly' },
@@ -21,6 +22,7 @@ export const MainFacilityColumn = {
'prxyBzentyNmCn',
'cnsgnCstVl',
'prxyCtrtCn',
'wholNope',
'isMainYn',
'admdstCd',
'fcltSn',
@@ -110,7 +110,7 @@ export const PublicSewageFactColumn = {
{ name: 'intgPrcsFcltCn', fieldName: 'intgPrcsFcltCn', header: { text: '중심처리시설' }, editable: false, styleName: 'rg-cell-readonly' },
{ name: 'wholNope', fieldName: 'wholNope', header: { text: '직원총수(명)' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0', styleName: 'rg-cell-readonly' },
{ name: 'sftyAcdntEvntcsCnt', fieldName: 'sftyAcdntEvntcsCnt', header: { text: '안전사고건수' }, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'rmk', fieldName: '비고', header: { text: '비고' } },
{ name: 'rmrk', fieldName: 'rmrk', header: { text: '비고' } },
{ name: 'admdstCd', fieldName: 'admdstCd', header: { text: '행정구역코드' }, visible: false, editable: false, styleName: 'rg-cell-readonly' },
{ name: 'fcltSn', fieldName: 'fcltSn', header: { text: '처리시설일련번호' }, visible: false, editable: false, valueType: ValueType.TEXT, styleName: 'rg-cell-readonly' },
{ name: 'swtrmtFcltCd', fieldName: 'swtrmtFcltCd', header: { text: '하수처리시설코드' }, visible: false, editable: false, styleName: 'rg-cell-readonly' },
@@ -208,7 +208,7 @@ export const PublicSewageFactColumn = {
'intgPrcsFcltCn',
'wholNope',
'sftyAcdntEvntcsCnt',
'rmk',
'rmrk',
'admdstCd',
'fcltSn',
'swtrmtFcltCd',
@@ -0,0 +1,28 @@
import { ENDPOINT } from "@/shared/config";
import { stClient } from "@/shared/api/client";
import type {
PublicSewageMainFacilityResponse,
PublicSewageFacilityRequestParams,
PublicSewageFacilityResponse,
StatisticsInputPublicSewageFacilityService,
} from "../../model/input/statistics-public-sewage-facility-input-interface";
export class StatisticsInputPublicSewageFacilityApiService implements StatisticsInputPublicSewageFacilityService {
public async getPublicSewageFacility(param: PublicSewageFacilityRequestParams): Promise<PublicSewageFacilityResponse> {
const response = await stClient.get<PublicSewageFacilityResponse>(
`${ENDPOINT.ST_SERVICE.INPUT}/public-sewage-facility/facility`,
{ params: param },
);
return response.data;
}
public async getPublicSewageMainFacility(param: PublicSewageFacilityRequestParams): Promise<PublicSewageMainFacilityResponse> {
const response = await stClient.get<PublicSewageMainFacilityResponse>(
`${ENDPOINT.ST_SERVICE.INPUT}/public-sewage-facility/main-facility`,
{ params: param },
);
return response.data;
}
}
export const statisticsInputPublicSewageFacilityApiService = new StatisticsInputPublicSewageFacilityApiService();
@@ -16,6 +16,8 @@ import type {
SeweragePipeInputRegionalFactStatusSaveResponse,
SeweragePipeInputRegionalFactStatusSubmitRequest,
SeweragePipeInputRegionalFactStatusSubmitResponse,
SeweragePipeInputFactInstallStatusSubmitRequest,
SeweragePipeInputFactInstallStatusSubmitResponse,
SeweragePipeInputRegionalResponse,
SeweragePipeRequestParams,
StatisticsInputSewPipeService,
@@ -23,10 +25,14 @@ import type {
SeweragePipeInputFactInstallStatusSaveResponse,
SeweragePipeInputPipeTypeStatusSaveRequest,
SeweragePipeInputPipeTypeStatusSaveResponse,
SeweragePipeInputPipeTypeStatusSubmitRequest,
SeweragePipeInputPipeDiameterStatusSaveRequest,
SeweragePipeInputPipeDiameterStatusSaveResponse,
SeweragePipeInputPipeDiameterStatusSubmitRequest,
SeweragePipeInputManholeStatusSaveRequest,
SeweragePipeInputManholeStatusSaveResponse,
SeweragePipeInputManholeStatusSubmitRequest,
SeweragePipeInputSubmitResponse,
} from "../../model/input/statistics-sewage-pipe-input-interface";
export class StatisticsInputSewPipeApiService implements StatisticsInputSewPipeService {
@@ -126,6 +132,14 @@ export class StatisticsInputSewPipeApiService implements StatisticsInputSewPipeS
return response.data;
}
public async submitFactInstallStatus(param: SeweragePipeInputFactInstallStatusSubmitRequest): Promise<SeweragePipeInputFactInstallStatusSubmitResponse> {
const response = await stClient.post<SeweragePipeInputFactInstallStatusSubmitResponse>(
`${ENDPOINT.ST_SERVICE.INPUT}/sewage-pipe/facility-install-status/submit`,
param,
);
return response.data;
}
public async savePipeTypeStatus(param: SeweragePipeInputPipeTypeStatusSaveRequest): Promise<SeweragePipeInputPipeTypeStatusSaveResponse> {
const response = await stClient.post<SeweragePipeInputPipeTypeStatusSaveResponse>(
`${ENDPOINT.ST_SERVICE.INPUT}/sewage-pipe/pipe-type-status/save`,
@@ -134,6 +148,14 @@ export class StatisticsInputSewPipeApiService implements StatisticsInputSewPipeS
return response.data;
}
public async submitPipeTypeStatus(param: SeweragePipeInputPipeTypeStatusSubmitRequest): Promise<SeweragePipeInputSubmitResponse> {
const response = await stClient.post<SeweragePipeInputSubmitResponse>(
`${ENDPOINT.ST_SERVICE.INPUT}/sewage-pipe/pipe-type-status/submit`,
param,
);
return response.data;
}
public async savePipeDiameterStatus(param: SeweragePipeInputPipeDiameterStatusSaveRequest): Promise<SeweragePipeInputPipeDiameterStatusSaveResponse> {
const response = await stClient.post<SeweragePipeInputPipeDiameterStatusSaveResponse>(
`${ENDPOINT.ST_SERVICE.INPUT}/sewage-pipe/pipe-diameter-status/save`,
@@ -142,6 +164,14 @@ export class StatisticsInputSewPipeApiService implements StatisticsInputSewPipeS
return response.data;
}
public async submitPipeDiameterStatus(param: SeweragePipeInputPipeDiameterStatusSubmitRequest): Promise<SeweragePipeInputSubmitResponse> {
const response = await stClient.post<SeweragePipeInputSubmitResponse>(
`${ENDPOINT.ST_SERVICE.INPUT}/sewage-pipe/pipe-diameter-status/submit`,
param,
);
return response.data;
}
public async saveManholeStatus(param: SeweragePipeInputManholeStatusSaveRequest): Promise<SeweragePipeInputManholeStatusSaveResponse> {
const response = await stClient.post<SeweragePipeInputManholeStatusSaveResponse>(
`${ENDPOINT.ST_SERVICE.INPUT}/sewage-pipe/manhole-status/save`,
@@ -149,6 +179,14 @@ export class StatisticsInputSewPipeApiService implements StatisticsInputSewPipeS
);
return response.data;
}
public async submitManholeStatus(param: SeweragePipeInputManholeStatusSubmitRequest): Promise<SeweragePipeInputSubmitResponse> {
const response = await stClient.post<SeweragePipeInputSubmitResponse>(
`${ENDPOINT.ST_SERVICE.INPUT}/sewage-pipe/manhole-status/submit`,
param,
);
return response.data;
}
};
export const statisticsInputSewPipeApiService = new StatisticsInputSewPipeApiService();
@@ -0,0 +1,145 @@
import type { SewerageStatusInfo } from "../statistics-interface";
export interface PublicSewageFacilityRequestParams {
ctpvNm?: string;
sggNm?: string;
sewerBzmnCd?: string;
fcltNm?: string;
statsYr?: string;
mode?: string;
}
export interface PublicSewageFacilityVerifyResultItem {
vrfcCd: string;
rsnExpln: string;
nowVlCn?: string;
compVlCn?: string;
}
/** 필드명 -> 검증 결과 배열 */
export type PublicSewageFacilityVerificationResult = Record<
string,
PublicSewageFacilityVerifyResultItem[]
>;
export interface PublicSewageFacilityCommonInfo {
statsYr: string;
ctpvNm: string;
sewerBzmnCd: string;
statsCd: string;
verificationResult?: PublicSewageFacilityVerificationResult | null;
}
export interface PublicSewageFacilityItem {
admdstCd: string;
sggNm: string;
admdstNm: string;
fcltSn: number;
swtrmtFcltCd: string;
swtrmtFcltNm: string;
impdFcltPstnCn: string | null;
prcsFcltCpct: number;
prcsFcltPhysCpct: number;
prcsFcltBoilCpct: number;
prcsFcltAltdCpct: number;
inflwSwageQnt: number;
inflwSwagePhysQnt: number;
inflwSwageBoilQnt: number;
inflwSwageAltdQnt: number;
prcsQnt: number;
prcsPhysCpct: number;
prcsBoilCpct: number;
prcsAltdCpct: number;
inflwBodVl: number;
dschgBodVl: number;
inflwTocQnt: number;
dschgTocQnt: number;
inflwSsQnt: number;
dschgSsQnt: number;
inflwTnVl: number;
dschgTnVl: number;
inflwTpVl: number;
dschgTpVl: number;
inflwGrssClfgVl: number;
dschgGrssClfgVl: number;
prcsRt: number;
prcsLodQnt: number;
prcsMthdExpln: string | null;
wholHmnQnt: number;
wholLvstQnt: number;
wholEfntQnt: number;
wholEtcQnt: number;
telNo: string | null;
cmptnStrtYmd: string | null;
oprtngStrtYmd: string | null;
wholBizVl: number;
eflntDsnfMthdExpln: string | null;
wtsysNm: string | null;
trbtrNm: string | null;
dtlUnitZoneCn: string | null;
rgnSeNm: string | null;
msirbCn: string | null;
goalWqNo: string | null;
engySrlneRt: number;
engyItslfPrdctnQnt: number;
anlEngyWholUseQnt: number;
anlElpwrUseQnt: number;
itslfEngyUseQnt: number;
swtrmtEmsnQnt: number;
operMnbdCn: string | null;
prxyBzentyNmCn: string | null;
cnsgnCstVl: number;
prxyCtrtCn: string | null;
intgPrcsFcltCn: string | null;
wholNope: number;
sftyAcdntEvntcsCnt: number;
rmrk: string | null;
verificationResult?: PublicSewageFacilityVerificationResult | null;
}
export interface PublicSewageFacilityResponseData {
statusInfo: SewerageStatusInfo;
commonInfo: PublicSewageFacilityCommonInfo;
publicSewageFacilityList: PublicSewageFacilityItem[];
}
export interface PublicSewageFacilityResponse {
status: string;
message?: string;
data: PublicSewageFacilityResponseData;
}
/** ST00060002 지자체별 전체시설 목록 항목 */
export interface PublicSewageMainFacilityItem {
admdstCd: string;
sggNm: string | null;
admdstNm: string;
isMainYn: string;
fcltSn: string;
swtrmtFcltCd: string;
swtrmtFcltNm: string;
fcltPstnCn: string | null;
prxyBzentyNmCn: string | null;
cnsgnCstVl: number;
prxyCtrtCn: string | null;
wholNope: number;
}
/** ST00060002 조회 응답 data */
export interface PublicSewageMainFacilityResponseData {
statusInfo: SewerageStatusInfo;
commonInfo: PublicSewageFacilityCommonInfo;
mainFacilityList: PublicSewageMainFacilityItem[];
}
/** ST00060002 조회 API 응답 */
export interface PublicSewageMainFacilityResponse {
status: string;
message?: string;
data: PublicSewageMainFacilityResponseData;
}
export interface StatisticsInputPublicSewageFacilityService {
getPublicSewageFacility(param: PublicSewageFacilityRequestParams): Promise<PublicSewageFacilityResponse>;
getPublicSewageMainFacility(param: PublicSewageFacilityRequestParams): Promise<PublicSewageMainFacilityResponse>;
}
@@ -122,8 +122,8 @@ export interface SeweragePipeInputRegionalFactStatusSaveResponse {
status: string;
}
/** ST00020001 지역별 시설현황 제출 API 요청 (`submitList` 행은 저장과 동일) */
export interface SeweragePipeInputRegionalFactStatusSubmitRequest {
/** 하수관로 입력 제출 공통 본문 (`submitList` 제외) */
export interface SeweragePipeInputSubmitRequestBase {
statsYr: string;
ctpvNm: string;
sggNm: string;
@@ -137,6 +137,10 @@ export interface SeweragePipeInputRegionalFactStatusSubmitRequest {
laeIdfrTelNo: string;
laeIdfrDeptNm: string;
laeIdfrJbgdNm: string;
}
/** ST00020001 지역별 시설현황 제출 API 요청 (`submitList` 행은 저장과 동일) */
export interface SeweragePipeInputRegionalFactStatusSubmitRequest extends SeweragePipeInputSubmitRequestBase {
submitList: SeweragePipeInputRegionalFactStatusSaveItem[];
}
@@ -165,6 +169,30 @@ export interface SeweragePipeInputFactInstallStatusSaveResponse {
status: string;
}
/** ST00020002 시설별 설치현황 제출 API 요청 (`submitList` 행은 Tab2 저장과 동일) */
export interface SeweragePipeInputFactInstallStatusSubmitRequest extends SeweragePipeInputSubmitRequestBase {
submitList: SeweragePipeInputFactInstallStatusSaveItem[];
}
export interface SeweragePipeInputFactInstallStatusSubmitResponse {
data: {
finalStatus: string;
nvpassed: boolean;
}
message?: string;
status: string;
}
/** 하수관로 입력 제출 API 공통 응답 */
export interface SeweragePipeInputSubmitResponse {
data: {
finalStatus: string;
nvpassed: boolean;
}
message?: string;
status: string;
}
/** 시설별 설치 현황 — 목록 항목 */
export interface SeweragePipeInputFactInstallStatusItem {
admdstCd: string;
@@ -283,6 +311,11 @@ export interface SeweragePipeInputPipeTypeStatusSaveResponse {
status: string;
}
/** ST00020003 관종별 현황 제출 API 요청 */
export interface SeweragePipeInputPipeTypeStatusSubmitRequest extends SeweragePipeInputSubmitRequestBase {
submitList: SeweragePipeInputPipeTypeStatusSaveItem[];
}
/** 관경별 현황 — 목록 항목 */
export interface SeweragePipeInputPipeDiameterStatusItem {
admdstCd: string;
@@ -350,6 +383,11 @@ export interface SeweragePipeInputPipeDiameterStatusSaveResponse {
status: string;
}
/** ST00020004 관경별 현황 제출 API 요청 */
export interface SeweragePipeInputPipeDiameterStatusSubmitRequest extends SeweragePipeInputSubmitRequestBase {
submitList: SeweragePipeInputPipeDiameterStatusSaveItem[];
}
/** 맨홀 현황 — 연도별 통계 항목 */
export interface SeweragePipeInputManholeYearlyStatItem {
manhYr: string;
@@ -443,6 +481,11 @@ export interface SeweragePipeInputManholeStatusSaveResponse {
status: string;
}
/** ST00020005 맨홀 현황 제출 API 요청 */
export interface SeweragePipeInputManholeStatusSubmitRequest extends SeweragePipeInputSubmitRequestBase {
submitList: SeweragePipeInputManholeStatusSaveItem[];
}
/** 시설별 현황 관리 - 하수처리장별 연결 현황 목록 항목 */
export interface SeweragePipeInputFacilityConnectionItem {
fcltNm: string;
@@ -571,7 +614,11 @@ export interface StatisticsInputSewPipeService {
saveRegionalFactStatus(param: SeweragePipeInputRegionalFactStatusSaveRequest): Promise<SeweragePipeInputRegionalFactStatusSaveResponse>;
submitRegionalFactStatus(param: SeweragePipeInputRegionalFactStatusSubmitRequest): Promise<SeweragePipeInputRegionalFactStatusSubmitResponse>;
saveFactInstallStatus(param: SeweragePipeInputFactInstallStatusSaveRequest): Promise<SeweragePipeInputFactInstallStatusSaveResponse>;
submitFactInstallStatus(param: SeweragePipeInputFactInstallStatusSubmitRequest): Promise<SeweragePipeInputFactInstallStatusSubmitResponse>;
savePipeTypeStatus(param: SeweragePipeInputPipeTypeStatusSaveRequest): Promise<SeweragePipeInputPipeTypeStatusSaveResponse>;
submitPipeTypeStatus(param: SeweragePipeInputPipeTypeStatusSubmitRequest): Promise<SeweragePipeInputSubmitResponse>;
savePipeDiameterStatus(param: SeweragePipeInputPipeDiameterStatusSaveRequest): Promise<SeweragePipeInputPipeDiameterStatusSaveResponse>;
submitPipeDiameterStatus(param: SeweragePipeInputPipeDiameterStatusSubmitRequest): Promise<SeweragePipeInputSubmitResponse>;
saveManholeStatus(param: SeweragePipeInputManholeStatusSaveRequest): Promise<SeweragePipeInputManholeStatusSaveResponse>;
submitManholeStatus(param: SeweragePipeInputManholeStatusSubmitRequest): Promise<SeweragePipeInputSubmitResponse>;
}