st: fix: 입력단 api 수정

This commit is contained in:
2026-03-30 10:06:42 +09:00
parent 7423b778ee
commit 28681969e2
9 changed files with 99 additions and 47 deletions
@@ -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<string, string>();
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 = () => {
<div className="flex justify-end items-center gap-[0.6rem] mx-[0.6rem]">
{showEnterableToolbarButtons && (
<>
<SubmitExceptionButton SubmitExceptionFunc={SubmitExceptionFunc} />
{isAdminInfo && statusInfo && statusInfo.laeSbmsnSttsCd !== '0' && (
<SubmitExceptionButton SubmitExceptionFunc={SubmitExceptionFunc} />
)}
<LastYearDataButton getLastYearDataFunc={() => getLastYearDataFunc(searchValues ?? {})} />
<SubmitButton submitFunc={submitFunc} />
@@ -523,7 +557,7 @@ const SeweragePenetrationRateInp = () => {
/>
</div>
<ConfirmDialog isOpen={false} />
<SubmitDailog open={showDailog} onOpenChange={setShowDailog} statusInfo={statusInfo} onConfirm={executeSubmit} />
<SubmitDailog open={showDailog} onOpenChange={setShowDailog} statusInfo={statusInfo} onConfirm={executeSubmit} isLoading={isLoading} />
</>
);
};
@@ -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<StatsInputStatusListApiResponse> => {
return StatisticsInputStatusApi.getStatisticsInputStatusList(key[1]);
@@ -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<void>;
onExceptionConfirm?: (data: any) => void | Promise<void>;
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<StatisticsInputSubmitInfoFormData>({
resolver: zodResolver(statisticsInputSubmitInfoSchema),
defaultValues,
@@ -111,6 +115,11 @@ const SubmitDailog = ({ open, onOpenChange, statusInfo, onConfirm }: SubmitDialo
<DialogHeader>
<DialogTitle>제출 정보</DialogTitle>
</DialogHeader>
{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>}
<form onSubmit={handleSubmit(onSubmit)}>
<Table variant="secondary">
<TableBody>
@@ -126,22 +135,22 @@ const SubmitDailog = ({ open, onOpenChange, statusInfo, onConfirm }: SubmitDialo
render={({ field }) => {
const isPhone = PHONE_FIELDS.includes(name);
return (
<div className="space-y-1">
<Input
{...field}
value={field.value ?? ""}
placeholder={placeholder}
onChange={(e) => {
const v = e.target.value;
field.onChange(isPhone ? formatPhoneWithHyphen(v) : v);
}}
/>
{errors[name]?.message && (
<p className="text-sm text-destructive ml-1">
{errors[name]?.message}
</p>
)}
</div>
<div className="space-y-1">
<Input
{...field}
value={field.value ?? ""}
placeholder={placeholder}
onChange={(e) => {
const v = e.target.value;
field.onChange(isPhone ? formatPhoneWithHyphen(v) : v);
}}
/>
{errors[name]?.message && (
<p className="text-sm text-destructive ml-1">
{errors[name]?.message}
</p>
)}
</div>
);
}}
/>
@@ -154,7 +163,7 @@ const SubmitDailog = ({ open, onOpenChange, statusInfo, onConfirm }: SubmitDialo
<Button type="button" color="gray" onClick={handleCancel}>
취소
</Button>
<Button type="submit" color="green">
<Button type="submit" color="primary">
제출
</Button>
</DialogFooter>
@@ -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<SewerageSupplyRateApiResponse> {
@@ -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<StatsInputStatusListApiResponse> {
@@ -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<any> {
const response = await stClient.post<any>(
'http://dev.onasys.co.kr:8000/statverify-service/api/v1/imr/outlier/detect',
public async callImrService(param: ImrRequest): Promise<null> {
const response = await client.post<null>(
`${ENDPOINT.STATVERIFY_SERVICE.IMR}`,
param
);
return response.data;
@@ -1,4 +1,4 @@
import type { ApiResponse } from "./statistics-interface";
import type { ApiResponse } from "../statistics-interface";
export interface StatsInputStatusListRequestParams {
ctpvNm?: string;
@@ -1,4 +1,4 @@
import type { ApiResponse } from "./statistics-interface";
import type { ApiResponse } from "../statistics-interface";
// 하수도 보급률 데이터 항목
export interface SewerageSupplyRateStatusInfo {
@@ -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<T> {
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<ApiResponse<SearchCondition>>;
getSggList(sidoName: string): Promise<ApiResponse<Sgg[]>>;
cancelSubmit(param: cancelSubmitRequest): Promise<cancelSubmitResponse>;
callImrService(param: any): Promise<any>;
callImrService(param: ImrRequest): Promise<null>;
}