diff --git a/apps/st/src/components/statistics/input/PublicSewageFacilityInp.tsx b/apps/st/src/components/statistics/input/PublicSewageFacilityInp.tsx index fc3d1e66a..e84926824 100644 --- a/apps/st/src/components/statistics/input/PublicSewageFacilityInp.tsx +++ b/apps/st/src/components/statistics/input/PublicSewageFacilityInp.tsx @@ -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(false); + const [hasSearched, setHasSearched] = useState(false); + const submitFunc = () => { console.log('submit'); // 제출 로직 @@ -39,6 +47,129 @@ const PublicSewageFacilityInp = () => { const [activeTab, setActiveTab] = useState( PublicSewageFactInpColumnList[0]?.tabKey ?? 'ST00060001', ); + const activeTabRef = useRef(PublicSewageFactInpColumnList[0]?.tabKey ?? 'ST00060001'); + const firstTabKey = PublicSewageFactInpColumnList[0]?.tabKey ?? 'ST00060001'; + const searchParamRef = useRef(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 => { + 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 ( <> - -
-
- - - - +
+ +
+
+ + + + +
-
- - - {PublicSewageFactInpColumnList.map((tab) => ( - - {tab.tabLabel || tab.tabKey} - - ))} - + {isLoading &&
+
+ +
+
+ } + void handleActiveTabChange(v)}> + + {PublicSewageFactInpColumnList.map((tab) => ( + + {tab.tabLabel || tab.tabKey} + + ))} + {PublicSewageFactInpColumnList.map((tab) => { if (tab.isSplitGrid && tab.left && tab.right) { @@ -137,7 +275,8 @@ const PublicSewageFacilityInp = () => { ); })} - +
+
); }; diff --git a/apps/st/src/components/statistics/input/SewagePipeInp.tsx b/apps/st/src/components/statistics/input/SewagePipeInp.tsx index 754038519..e833c60bd 100644 --- a/apps/st/src/components/statistics/input/SewagePipeInp.tsx +++ b/apps/st/src/components/statistics/input/SewagePipeInp.tsx @@ -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(); + 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(); + 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(); + 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(); + 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("데이터 제출 중 오류가 발생했습니다."); diff --git a/apps/st/src/components/util/column/input/PublicSewageFactInpColumn/MainFacilityInpColumn.ts b/apps/st/src/components/util/column/input/PublicSewageFactInpColumn/MainFacilityInpColumn.ts index eed27df03..cd85731e6 100644 --- a/apps/st/src/components/util/column/input/PublicSewageFactInpColumn/MainFacilityInpColumn.ts +++ b/apps/st/src/components/util/column/input/PublicSewageFactInpColumn/MainFacilityInpColumn.ts @@ -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', diff --git a/apps/st/src/components/util/column/input/PublicSewageFactInpColumn/PublicSewageFactColumn.ts b/apps/st/src/components/util/column/input/PublicSewageFactInpColumn/PublicSewageFactColumn.ts index 091286300..a97ba549f 100644 --- a/apps/st/src/components/util/column/input/PublicSewageFactInpColumn/PublicSewageFactColumn.ts +++ b/apps/st/src/components/util/column/input/PublicSewageFactInpColumn/PublicSewageFactColumn.ts @@ -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', diff --git a/apps/st/src/entities/statistics/api/input/statistics-public-sewage-facility-input-service.ts b/apps/st/src/entities/statistics/api/input/statistics-public-sewage-facility-input-service.ts new file mode 100644 index 000000000..d2a900077 --- /dev/null +++ b/apps/st/src/entities/statistics/api/input/statistics-public-sewage-facility-input-service.ts @@ -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 { + const response = await stClient.get( + `${ENDPOINT.ST_SERVICE.INPUT}/public-sewage-facility/facility`, + { params: param }, + ); + return response.data; + } + + public async getPublicSewageMainFacility(param: PublicSewageFacilityRequestParams): Promise { + const response = await stClient.get( + `${ENDPOINT.ST_SERVICE.INPUT}/public-sewage-facility/main-facility`, + { params: param }, + ); + return response.data; + } +} + +export const statisticsInputPublicSewageFacilityApiService = new StatisticsInputPublicSewageFacilityApiService(); diff --git a/apps/st/src/entities/statistics/api/input/statistics-sewage-pipe-input-service.ts b/apps/st/src/entities/statistics/api/input/statistics-sewage-pipe-input-service.ts index daefd245a..956abbd6b 100644 --- a/apps/st/src/entities/statistics/api/input/statistics-sewage-pipe-input-service.ts +++ b/apps/st/src/entities/statistics/api/input/statistics-sewage-pipe-input-service.ts @@ -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 { + const response = await stClient.post( + `${ENDPOINT.ST_SERVICE.INPUT}/sewage-pipe/facility-install-status/submit`, + param, + ); + return response.data; + } + public async savePipeTypeStatus(param: SeweragePipeInputPipeTypeStatusSaveRequest): Promise { const response = await stClient.post( `${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 { + const response = await stClient.post( + `${ENDPOINT.ST_SERVICE.INPUT}/sewage-pipe/pipe-type-status/submit`, + param, + ); + return response.data; + } + public async savePipeDiameterStatus(param: SeweragePipeInputPipeDiameterStatusSaveRequest): Promise { const response = await stClient.post( `${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 { + const response = await stClient.post( + `${ENDPOINT.ST_SERVICE.INPUT}/sewage-pipe/pipe-diameter-status/submit`, + param, + ); + return response.data; + } + public async saveManholeStatus(param: SeweragePipeInputManholeStatusSaveRequest): Promise { const response = await stClient.post( `${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 { + const response = await stClient.post( + `${ENDPOINT.ST_SERVICE.INPUT}/sewage-pipe/manhole-status/submit`, + param, + ); + return response.data; + } }; export const statisticsInputSewPipeApiService = new StatisticsInputSewPipeApiService(); \ No newline at end of file diff --git a/apps/st/src/entities/statistics/model/input/statistics-public-sewage-facility-input-interface.ts b/apps/st/src/entities/statistics/model/input/statistics-public-sewage-facility-input-interface.ts new file mode 100644 index 000000000..e56755f83 --- /dev/null +++ b/apps/st/src/entities/statistics/model/input/statistics-public-sewage-facility-input-interface.ts @@ -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; + getPublicSewageMainFacility(param: PublicSewageFacilityRequestParams): Promise; +} diff --git a/apps/st/src/entities/statistics/model/input/statistics-sewage-pipe-input-interface.ts b/apps/st/src/entities/statistics/model/input/statistics-sewage-pipe-input-interface.ts index 9a3db3ea1..d6357d78f 100644 --- a/apps/st/src/entities/statistics/model/input/statistics-sewage-pipe-input-interface.ts +++ b/apps/st/src/entities/statistics/model/input/statistics-sewage-pipe-input-interface.ts @@ -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; submitRegionalFactStatus(param: SeweragePipeInputRegionalFactStatusSubmitRequest): Promise; saveFactInstallStatus(param: SeweragePipeInputFactInstallStatusSaveRequest): Promise; + submitFactInstallStatus(param: SeweragePipeInputFactInstallStatusSubmitRequest): Promise; savePipeTypeStatus(param: SeweragePipeInputPipeTypeStatusSaveRequest): Promise; + submitPipeTypeStatus(param: SeweragePipeInputPipeTypeStatusSubmitRequest): Promise; savePipeDiameterStatus(param: SeweragePipeInputPipeDiameterStatusSaveRequest): Promise; + submitPipeDiameterStatus(param: SeweragePipeInputPipeDiameterStatusSubmitRequest): Promise; saveManholeStatus(param: SeweragePipeInputManholeStatusSaveRequest): Promise; + submitManholeStatus(param: SeweragePipeInputManholeStatusSubmitRequest): Promise; } \ No newline at end of file