diff --git a/apps/st/src/components/statistics/input/SeweragePenetrationRateInp.tsx b/apps/st/src/components/statistics/input/SeweragePenetrationRateInp.tsx index dc0b8a53e..e3253995e 100644 --- a/apps/st/src/components/statistics/input/SeweragePenetrationRateInp.tsx +++ b/apps/st/src/components/statistics/input/SeweragePenetrationRateInp.tsx @@ -14,7 +14,7 @@ import SubmitDailog from "./SubmitDailog"; import { useAlertStore } from "@/shared/lib"; import { useAdminStore } from "@/store/admin-store"; -import { statisticsInputSewSupplyRateApi } from "@/entities/statistics/api/statistics-input-sew-supply-rate-service"; +import { statisticsInputSewSupplyRateApi } from "@/entities/statistics/api/input/statistics-input-sew-supply-rate-service"; import { statisticsAPi } from "@/entities/statistics/api/statistics-service"; import { toGridRows } from "@/components/util/grid/gridFunction"; import { getFooterSummary } from "@/components/util/grid/footerSummary"; @@ -29,7 +29,7 @@ import type { SewerageSupplyRateVerificationResult, SewerageSupplyRateSaveRequest, SewerageSupplyRateStatusInfo, -} from "@/entities/statistics/model/statistics-sewage-supply-rate-input-interface"; +} from "@/entities/statistics/model/input/statistics-sewage-supply-rate-input-interface"; import type { cancelSubmitRequest } from "@/entities/statistics/model/statistics-interface"; import type { StatisticsInputSubmitInfoFormData } from "@/entities/statistics/model/statistics-input-submit-info-schema"; import { SeweragePenetrationRateInpColumn } from "@/components/util/column/input/SeweragePenetrationRateInpColumn"; @@ -118,7 +118,7 @@ const SeweragePenetrationRateInp = () => { if (response.status === 'success' && response.data) { const commonInfo = response.data.commonInfo; const statusInfo = response.data.statusInfo; - const convertedData: SewerageSupplyRateItem[] = response.data.sewerageSupplyRateList.map((d) => ({ + const convertedData: SewerageSupplyRateItem[] = response.data.sewerageSupplyRateList.map((d: SewerageSupplyRateItem) => ({ ...d, ctpvNm: commonInfo.ctpvNm, spcCtpnRgnCn: d.spcCtpnRgnCn === '' || d.spcCtpnRgnCn === null ? '해당없음' : d.spcCtpnRgnCn, @@ -191,7 +191,7 @@ const SeweragePenetrationRateInp = () => { }; const SubmitExceptionFunc = () => { - // 예외 제출 로직 + }; const getLastYearDataFunc = async (values: SearchFormValues) => { @@ -213,7 +213,7 @@ const SeweragePenetrationRateInp = () => { const isError = rowData.filter((d) => hasVerificationErrors((d as GridRowWithVerification).verificationResult) ); - console.log(isError); + if (isError.length > 0) { alert(`데이터에 오류가 있습니다. 수정 후 제출해주세요.`); @@ -229,16 +229,20 @@ const SeweragePenetrationRateInp = () => { setIsLoading(true); const userInfo = await getUserInfo(); const currentData = getGridDataRef.current?.(); + if (!currentData || currentData.length === 0) { alert('제출할 데이터가 없습니다.'); return; } + const columnFormatMap = new Map(); + SeweragePenetrationRateInpColumn.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; @@ -248,6 +252,7 @@ const SeweragePenetrationRateInp = () => { const decimalPlaces = format.match(/\.(\d+)/)?.[1]?.length ?? 0; return decimalPlaces > 0 ? parseFloat(num.toFixed(decimalPlaces)) : Math.round(num); }; + const convertedData: SewerageSupplyRateSaveItem[] = currentData.map((d) => ({ sggNm: (d.sggNm as string) || null, admdstCd: d.admdstCd as string, @@ -277,6 +282,7 @@ const SeweragePenetrationRateInp = () => { altdPrcsPsplyRt: formatNumber(d.altdPrcsPsplyRt, 'altdPrcsPsplyRt'), sewerInstlRt: formatNumber(d.sewerInstlRt, 'sewerInstlRt'), })); + const requestData: SewerageSupplyRateSaveRequest = { sewerBzmnCd: searchValues?.bsnmCd ?? '', ctpvNm: searchValues?.sido ?? '', @@ -297,6 +303,23 @@ const SeweragePenetrationRateInp = () => { const response = await statisticsInputSewSupplyRateApi.submitSewerageSupplyRate(requestData); if (response.status === 'success') { + const imrParam = { + user_id: userInfo.userUnqId, + sewer_bzmn_cd: searchValues?.bsnmCd ?? '', + stats_cd: statsCd, + stats_yr: searchValues?.year ?? '', + lae_sbmsn_stts_cd: (statusInfo?.laeSbmsnSttsCd === '4' ? '4' : '2'), + }; + + try { + await statisticsAPi.callImrService(imrParam); + } catch { + await fetchSewerageSupplyRate(getSewerageSupplyRateParams(searchValues ?? {}), true); + setIsLoading(false); + alert('IMR 검증 중 오류가 발생했습니다.\n관리자에게 문의하세요.'); + return; + } + await fetchSewerageSupplyRate(getSewerageSupplyRateParams(searchValues ?? {}), false); alert('제출되었습니다.'); } else { @@ -402,9 +425,18 @@ const SeweragePenetrationRateInp = () => { sewer_bzmn_cd: searchValues?.bsnmCd ?? '', stats_cd: statsCd, stats_yr: searchValues?.year ?? '', + lae_sbmsn_stts_cd: (statusInfo?.laeSbmsnSttsCd === '4' ? '3' : '1'), }; - const response = await statisticsAPi.callImrService(imrParam) - console.log(response); + + try { + await statisticsAPi.callImrService(imrParam); + } catch { + await fetchSewerageSupplyRate(getSewerageSupplyRateParams(searchValues ?? {}), true); + setIsLoading(false); + alert('IMR 검증 중 오류가 발생했습니다.\n관리자에게 문의하세요.'); + return; + } + await fetchSewerageSupplyRate(getSewerageSupplyRateParams(searchValues ?? {}), true); alert('저장되었습니다.'); } else { @@ -431,7 +463,7 @@ const SeweragePenetrationRateInp = () => { ctpvNm: searchValues?.sido ?? '', sggNm: searchValues?.gugun ?? '', statsYr: searchValues?.year ?? '', - userId: userInfo.userUnqId, + mdfrId: userInfo.userUnqId, statsCd: statsCd, }; @@ -470,7 +502,9 @@ const SeweragePenetrationRateInp = () => {
{showEnterableToolbarButtons && ( <> - + {isAdminInfo && statusInfo && statusInfo.laeSbmsnSttsCd !== '0' && ( + + )} getLastYearDataFunc(searchValues ?? {})} /> @@ -523,7 +557,7 @@ const SeweragePenetrationRateInp = () => { />
- + ); }; diff --git a/apps/st/src/components/statistics/input/StatsInputStatusInp.tsx b/apps/st/src/components/statistics/input/StatsInputStatusInp.tsx index 4390777b9..919231fde 100644 --- a/apps/st/src/components/statistics/input/StatsInputStatusInp.tsx +++ b/apps/st/src/components/statistics/input/StatsInputStatusInp.tsx @@ -9,12 +9,12 @@ import RejectButton from "@/components/button/RejectButton"; import { LoadingWithAnimation } from "@common/components"; import { StatsInputStatusInpColumn } from "@/components/util/column/input/StatsInputStatusInpColumn"; -import { StatisticsInputStatusApi } from "@/entities/statistics/api/statistics-input-status-service"; -import type { StatsInputStatusListRequestParams } from "@/entities/statistics/model/statistics-input-status-interface"; +import { StatisticsInputStatusApi } from "@/entities/statistics/api/input/statistics-input-status-service"; +import type { StatsInputStatusListRequestParams } from "@/entities/statistics/model/input/statistics-input-status-interface"; import type { SearchFormValues } from "@/components/type/SearchForm.type"; import { useAlertStore } from "@/shared/lib"; import { toGridRows } from "@/components/util/grid/gridFunction"; -import type { StatsInputStatusListApiResponse } from "@/entities/statistics/model/statistics-input-status-interface"; +import type { StatsInputStatusListApiResponse } from "@/entities/statistics/model/input/statistics-input-status-interface"; const getStatsInputStatusFetcher = async (key: [string, StatsInputStatusListRequestParams]): Promise => { return StatisticsInputStatusApi.getStatisticsInputStatusList(key[1]); diff --git a/apps/st/src/components/statistics/input/SubmitDailog.tsx b/apps/st/src/components/statistics/input/SubmitDailog.tsx index 7b67d4cb7..d2de6ab14 100644 --- a/apps/st/src/components/statistics/input/SubmitDailog.tsx +++ b/apps/st/src/components/statistics/input/SubmitDailog.tsx @@ -1,5 +1,11 @@ "use client"; +import { useEffect } from "react"; +import { useForm, Controller } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { statisticsInputSubmitInfoSchema, type StatisticsInputSubmitInfoFormData } from "@/entities/statistics/model/statistics-input-submit-info-schema"; +import { type SewerageSupplyRateStatusInfo } from "@/entities/statistics/model/input/statistics-sewage-supply-rate-input-interface"; + import { Button, Dialog, @@ -13,12 +19,8 @@ import { TableCell, TableHead, TableRow, + LoadingWithAnimation, } from "@common/components"; -import { useEffect } from "react"; -import { useForm, Controller } from "react-hook-form"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { statisticsInputSubmitInfoSchema, type StatisticsInputSubmitInfoFormData } from "@/entities/statistics/model/statistics-input-submit-info-schema"; -import { type SewerageSupplyRateStatusInfo } from "@/entities/statistics/model/statistics-sewage-supply-rate-input-interface"; /** 숫자만 추출 후 하이픈 포맷 (010-1234-5678, 02-123-4567 등) */ function formatPhoneWithHyphen(value: string): string { @@ -45,6 +47,8 @@ interface SubmitDialogProps { statusInfo: SewerageSupplyRateStatusInfo | null; /** 다이얼로그에서 저장 클릭 시 호출. 제출 정보를 넘기면 부모에서 requestData에 포함해 제출 API 호출 */ onConfirm?: (data: StatisticsInputSubmitInfoFormData) => void | Promise; + onExceptionConfirm?: (data: any) => void | Promise; + isLoading?: boolean; } const defaultValues: StatisticsInputSubmitInfoFormData = { @@ -69,9 +73,9 @@ function statusInfoToFormData(info: SewerageSupplyRateStatusInfo): StatisticsInp laeIdfrDeptNm: info.laeIdfrDeptNm ?? "", laeIdfrTelNo: info.laeIdfrTelNo ?? "", }; -} +}; -const SubmitDailog = ({ open, onOpenChange, statusInfo, onConfirm }: SubmitDialogProps) => { +const SubmitDailog = ({ open, onOpenChange, statusInfo, onConfirm, onExceptionConfirm, isLoading }: SubmitDialogProps) => { const { control, handleSubmit, reset, formState: { errors } } = useForm({ resolver: zodResolver(statisticsInputSubmitInfoSchema), defaultValues, @@ -111,6 +115,11 @@ const SubmitDailog = ({ open, onOpenChange, statusInfo, onConfirm }: SubmitDialo 제출 정보 + {isLoading &&
+
+ +
+
}
@@ -126,22 +135,22 @@ const SubmitDailog = ({ open, onOpenChange, statusInfo, onConfirm }: SubmitDialo render={({ field }) => { const isPhone = PHONE_FIELDS.includes(name); return ( -
- { - const v = e.target.value; - field.onChange(isPhone ? formatPhoneWithHyphen(v) : v); - }} - /> - {errors[name]?.message && ( -

- {errors[name]?.message} -

- )} -
+
+ { + const v = e.target.value; + field.onChange(isPhone ? formatPhoneWithHyphen(v) : v); + }} + /> + {errors[name]?.message && ( +

+ {errors[name]?.message} +

+ )} +
); }} /> @@ -154,7 +163,7 @@ const SubmitDailog = ({ open, onOpenChange, statusInfo, onConfirm }: SubmitDialo - diff --git a/apps/st/src/entities/statistics/api/statistics-input-sew-supply-rate-service.ts b/apps/st/src/entities/statistics/api/input/statistics-input-sew-supply-rate-service.ts similarity index 96% rename from apps/st/src/entities/statistics/api/statistics-input-sew-supply-rate-service.ts rename to apps/st/src/entities/statistics/api/input/statistics-input-sew-supply-rate-service.ts index b6de9c8d4..32dfb9211 100644 --- a/apps/st/src/entities/statistics/api/statistics-input-sew-supply-rate-service.ts +++ b/apps/st/src/entities/statistics/api/input/statistics-input-sew-supply-rate-service.ts @@ -8,7 +8,7 @@ import { type CheckInputPeriodResponse, type SewerageSupplyRateSaveRequest, type SewerageSupplyRateSaveApiResponse, -} from "../model/statistics-sewage-supply-rate-input-interface"; +} from "../../model/input/statistics-sewage-supply-rate-input-interface"; export class StatisticsInputSewSupplyRateApiService implements StatisticsInputSewSupplyRateService { public async getSewerageSupplyRateList(params: SewerageSupplyRateRequestParams): Promise { diff --git a/apps/st/src/entities/statistics/api/statistics-input-status-service.ts b/apps/st/src/entities/statistics/api/input/statistics-input-status-service.ts similarity index 92% rename from apps/st/src/entities/statistics/api/statistics-input-status-service.ts rename to apps/st/src/entities/statistics/api/input/statistics-input-status-service.ts index cd3c55a5f..bed214f5b 100644 --- a/apps/st/src/entities/statistics/api/statistics-input-status-service.ts +++ b/apps/st/src/entities/statistics/api/input/statistics-input-status-service.ts @@ -5,7 +5,7 @@ import type { StatisticsInputStatusService, StatsInputStatusListRequestParams, StatsInputStatusListApiResponse, -} from "../model/statistics-input-status-interface"; +} from "../../model/input/statistics-input-status-interface"; export class StatisticsInputStatusApiService implements StatisticsInputStatusService { public async getStatisticsInputStatusList(param: StatsInputStatusListRequestParams): Promise { diff --git a/apps/st/src/entities/statistics/api/statistics-service.ts b/apps/st/src/entities/statistics/api/statistics-service.ts index 20ad4a0c6..1f2b66bd8 100644 --- a/apps/st/src/entities/statistics/api/statistics-service.ts +++ b/apps/st/src/entities/statistics/api/statistics-service.ts @@ -1,5 +1,5 @@ import { ENDPOINT } from "@/shared/config"; -import { stClient } from "@/shared/api/client"; +import { stClient, client } from "@/shared/api/client"; import { type StatisticsService, @@ -8,6 +8,7 @@ import { type Sgg, type cancelSubmitRequest, type cancelSubmitResponse, + type ImrRequest, } from "../model/statistics-interface"; export class StatisticsAPiService implements StatisticsService { @@ -35,9 +36,9 @@ export class StatisticsAPiService implements StatisticsService { return response.data; } - public async callImrService(param: any): Promise { - const response = await stClient.post( - 'http://dev.onasys.co.kr:8000/statverify-service/api/v1/imr/outlier/detect', + public async callImrService(param: ImrRequest): Promise { + const response = await client.post( + `${ENDPOINT.STATVERIFY_SERVICE.IMR}`, param ); return response.data; diff --git a/apps/st/src/entities/statistics/model/statistics-input-status-interface.ts b/apps/st/src/entities/statistics/model/input/statistics-input-status-interface.ts similarity index 93% rename from apps/st/src/entities/statistics/model/statistics-input-status-interface.ts rename to apps/st/src/entities/statistics/model/input/statistics-input-status-interface.ts index 9eb5e89af..ae721e462 100644 --- a/apps/st/src/entities/statistics/model/statistics-input-status-interface.ts +++ b/apps/st/src/entities/statistics/model/input/statistics-input-status-interface.ts @@ -1,4 +1,4 @@ -import type { ApiResponse } from "./statistics-interface"; +import type { ApiResponse } from "../statistics-interface"; export interface StatsInputStatusListRequestParams { ctpvNm?: string; diff --git a/apps/st/src/entities/statistics/model/statistics-sewage-supply-rate-input-interface.ts b/apps/st/src/entities/statistics/model/input/statistics-sewage-supply-rate-input-interface.ts similarity index 98% rename from apps/st/src/entities/statistics/model/statistics-sewage-supply-rate-input-interface.ts rename to apps/st/src/entities/statistics/model/input/statistics-sewage-supply-rate-input-interface.ts index b3f97fbb1..59481dc5b 100644 --- a/apps/st/src/entities/statistics/model/statistics-sewage-supply-rate-input-interface.ts +++ b/apps/st/src/entities/statistics/model/input/statistics-sewage-supply-rate-input-interface.ts @@ -1,4 +1,4 @@ -import type { ApiResponse } from "./statistics-interface"; +import type { ApiResponse } from "../statistics-interface"; // 하수도 보급률 데이터 항목 export interface SewerageSupplyRateStatusInfo { diff --git a/apps/st/src/entities/statistics/model/statistics-interface.ts b/apps/st/src/entities/statistics/model/statistics-interface.ts index f771289f0..5a2b9eab5 100644 --- a/apps/st/src/entities/statistics/model/statistics-interface.ts +++ b/apps/st/src/entities/statistics/model/statistics-interface.ts @@ -21,7 +21,7 @@ export interface cancelSubmitRequest { sggNm: string; sewerBzmnCd: string; statsCd: string; - userId: string; + mdfrId: string; } export interface cancelSubmitResponse { @@ -35,9 +35,17 @@ export interface ApiResponse { message?: string; } +export interface ImrRequest { + user_id?: string; + sewer_bzmn_cd: string; + stats_cd: string; + stats_yr: string; + lae_sbmsn_stts_cd: string; +} + export interface StatisticsService { getSidoList(): Promise>; getSggList(sidoName: string): Promise>; cancelSubmit(param: cancelSubmitRequest): Promise; - callImrService(param: any): Promise; + callImrService(param: ImrRequest): Promise; } \ No newline at end of file