[as instl service] 신청 저장 로직 추가
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { client } from "@/shared/api";
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
import type {
|
||||
InstlSncnApplySaveRequestDto,
|
||||
InstlSncnMng,
|
||||
InstlSncnMngSearchParams,
|
||||
InstlSncnMngService,
|
||||
@@ -19,6 +20,14 @@ export class InstlSncnApiService implements InstlSncnMngService {
|
||||
const response = await client.get<InstlSncnMng>(`${ENDPOINT.AS_INSTL_SERVICE.INSTL_SNCN}/${id}`);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
public async saveApply(payload: InstlSncnApplySaveRequestDto): Promise<string> {
|
||||
const response = await client.post<string>(
|
||||
`${ENDPOINT.AS_INSTL_SERVICE.INSTL_SNCN}/save-apply`,
|
||||
payload,
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
}
|
||||
|
||||
export const InstlSncnApi = new InstlSncnApiService();
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
export { InstlSncnApi } from "./api/instl-sncn-service";
|
||||
export {
|
||||
INSTL_SNCN_REGISTER_SELECTION_KEY,
|
||||
type InstlSncnApplySaveRequestDto,
|
||||
type InstlSncnBizCstSaveRequestDto,
|
||||
type InstlSncnBzScaleSaveDto,
|
||||
type InstlSncnCklstResultSaveDto,
|
||||
type InstlSncnMng,
|
||||
type InstlSncnMngSearchParams,
|
||||
type SpringPage,
|
||||
|
||||
@@ -80,7 +80,24 @@ export interface InstlSncnBizCstSaveRequestDto {
|
||||
tstmDesignAmt: number;
|
||||
}
|
||||
|
||||
/** 설치인가 신청 - 체크리스트 결과 저장 DTO(배열 원소) */
|
||||
export interface InstlSncnCklstResultSaveDto {
|
||||
cklstSn: number;
|
||||
cklstArtclSn: number;
|
||||
cklstRvwSn: number;
|
||||
selected: "Y" | "N" | null;
|
||||
}
|
||||
|
||||
/** 설치인가 신청 - 통합 저장 요청 DTO */
|
||||
export interface InstlSncnApplySaveRequestDto {
|
||||
instlSncnMng: InstlSncnMng;
|
||||
bzScaleList: InstlSncnBzScaleSaveDto[];
|
||||
bizCstList: InstlSncnBizCstSaveRequestDto[];
|
||||
cklstResultList: InstlSncnCklstResultSaveDto[];
|
||||
}
|
||||
|
||||
export interface InstlSncnMngService {
|
||||
getPage: (params: InstlSncnMngSearchParams) => Promise<SpringPage<InstlSncnMng>>;
|
||||
getById: (id: string) => Promise<InstlSncnMng>;
|
||||
saveApply: (payload: InstlSncnApplySaveRequestDto) => Promise<string>;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
@@ -13,7 +13,11 @@ import type { CnstpSeMngTreeDto } from "@/entities/cnstp";
|
||||
import { cnstpService } from "@/entities/cnstp";
|
||||
import {
|
||||
INSTL_SNCN_REGISTER_SELECTION_KEY,
|
||||
InstlSncnApi,
|
||||
type InstlSncnApplySaveRequestDto,
|
||||
type InstlSncnBizCstSaveRequestDto,
|
||||
type InstlSncnBzScaleSaveDto,
|
||||
type InstlSncnMng,
|
||||
} from "@/entities/instl-sncn";
|
||||
import { Button, Input } from "@common/components";
|
||||
|
||||
@@ -103,8 +107,9 @@ function CnfmRegistPage() {
|
||||
const [etcNoticeOpen, setEtcNoticeOpen] = useState(true);
|
||||
|
||||
/* ── 체크리스트 ── */
|
||||
type CheckResultVal = "Y" | "N" | "";
|
||||
type CheckResultVal = "Y" | "N" | null;
|
||||
const [checkResults, setCheckResults] = useState<Record<string, CheckResultVal>>({});
|
||||
const cklstRowRefs = useRef<Record<string, HTMLTableRowElement | null>>({});
|
||||
|
||||
const { data: cklstTree = [] } = useQuery<CklstTreeNode[]>({
|
||||
queryKey: ["cklstTree", "INSTL", taskSeComCd],
|
||||
@@ -159,7 +164,7 @@ function CnfmRegistPage() {
|
||||
const next: Record<string, CheckResultVal> = {};
|
||||
cklstRows.forEach((row) => {
|
||||
const key = cklstKey(row);
|
||||
next[key] = prev[key] ?? "";
|
||||
next[key] = prev[key] ?? null;
|
||||
});
|
||||
return next;
|
||||
});
|
||||
@@ -181,9 +186,8 @@ function CnfmRegistPage() {
|
||||
cklstSn: row.cklstSn,
|
||||
cklstArtclSn: row.cklstArtclSn,
|
||||
cklstRvwSn: row.cklstRvwSn,
|
||||
selected: checkResults[cklstKey(row)] ?? "",
|
||||
}))
|
||||
.filter((row) => row.selected !== "");
|
||||
selected: checkResults[cklstKey(row)] ?? null,
|
||||
}));
|
||||
|
||||
/* ── 사업규모 (공종구분 트리 연동) ── */
|
||||
// cnstpSeCd → { bscPl, budgetReflct, thtiDsgn } 단일 map
|
||||
@@ -277,6 +281,20 @@ function CnfmRegistPage() {
|
||||
[acntgSeCd]: { ...(prev[acntgSeCd] ?? emptyAmt()), [col]: val },
|
||||
}));
|
||||
|
||||
/** 총사업비 맵 -> 백엔드 저장용 배열 DTO 변환 */
|
||||
const toBizCstSaveDtoList = (): InstlSncnBizCstSaveRequestDto[] =>
|
||||
acntgTree
|
||||
.filter((node) => leafSet.has(node.acntgSeCd))
|
||||
.map((node) => {
|
||||
const row = amtMap[node.acntgSeCd] ?? emptyAmt();
|
||||
return {
|
||||
acntgSeCd: node.acntgSeCd,
|
||||
bscPlanAmt: num(row.bscPl),
|
||||
bgtRfltAmt: num(row.budgetReflct),
|
||||
tstmDesignAmt: num(row.thtiDsgn),
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* indAcntgSeCdNm 의 앞 공백 수를 파싱해 padding px 값으로 반환
|
||||
* (공백 4칸 = 1 depth = 1rem)
|
||||
@@ -306,6 +324,7 @@ function CnfmRegistPage() {
|
||||
|
||||
/* ── 검증 ── */
|
||||
function fnValidation(mode: "SAVE" | "PRESENTN") {
|
||||
/*
|
||||
if (!bzNm.trim()) {
|
||||
alert("사업명을 입력하세요.");
|
||||
return false;
|
||||
@@ -322,26 +341,96 @@ function CnfmRegistPage() {
|
||||
alert("개인정보 제공에 동의하셔야 제출이 가능합니다.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// 체크리스트 사용 항목(Y) 중 미선택 항목이 있으면 진행 불가
|
||||
const hasUncheckedChecklist = cklstRows.some(
|
||||
(row) => !checkResults[cklstKey(row)],
|
||||
);
|
||||
if (hasUncheckedChecklist) {
|
||||
alert("체크리스트를 모두 체크하세요.");
|
||||
return false;
|
||||
}
|
||||
// 저장 시: 사용 항목(Y)은 검토 결과가 반드시 예(Y)여야 함
|
||||
*/
|
||||
if (mode === "SAVE") {
|
||||
const invalidYesRow = cklstRows.find(
|
||||
(row) => row.bchckUseYn === "Y" && checkResults[cklstKey(row)] !== "Y",
|
||||
);
|
||||
if (invalidYesRow) {
|
||||
const invalidKey = cklstKey(invalidYesRow);
|
||||
const invalidRowEl = cklstRowRefs.current[invalidKey];
|
||||
invalidRowEl?.scrollIntoView({ behavior: "smooth", block: "center" });
|
||||
const yesRadio = invalidRowEl?.querySelector<HTMLInputElement>(
|
||||
`input[name="chk_${invalidKey}"][value="Y"]`,
|
||||
);
|
||||
yesRadio?.focus();
|
||||
alert(`${invalidYesRow.rvwCn} 검토내용은 예(Y)여야 합니다.`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function handleSave() {
|
||||
const toInstlSncnMngPayload = (prgrsSttsComCd: "SV" | "ST"): InstlSncnMng => ({
|
||||
instlSncnDocNo: "",
|
||||
dongId: "",
|
||||
sidoNm: "",
|
||||
sggNm: "",
|
||||
chrgrNm: "",
|
||||
instNm: "",
|
||||
envoSeComCd: instlCnfmApr,
|
||||
envoSeCdNm: "",
|
||||
taskSeComCd,
|
||||
taskSeCdNm: taskSeComCdNm,
|
||||
bgtBizCd: "",
|
||||
bgtAplyYr: "",
|
||||
bizNm: bzNm,
|
||||
bizBgngYr: bzBgngYr,
|
||||
bizEndYr: bzEndYr,
|
||||
bizCn: bzCn,
|
||||
aplcntNm: "",
|
||||
aplcntMblTelno: "",
|
||||
aplcntOgdpNm: "",
|
||||
aplcntEmlAddr: "",
|
||||
dsgnrNm,
|
||||
dsgnrTelno,
|
||||
dsgnrOgdpNm: dsgnrPsitn,
|
||||
dsgnrEmlAddr,
|
||||
atchFileGroupId: "",
|
||||
prgrsSttsComCd,
|
||||
prgrsSttsCdNm: "",
|
||||
regDt: "",
|
||||
mdfcnDt: "",
|
||||
rgtrNm: "",
|
||||
});
|
||||
|
||||
async function handleSave() {
|
||||
if (!fnValidation("SAVE")) return;
|
||||
const payload = {
|
||||
taskSeComCd,
|
||||
const payload: InstlSncnApplySaveRequestDto = {
|
||||
instlSncnMng: toInstlSncnMngPayload("SV"), // 저장 시 진행상태는 SV (임시저장)
|
||||
bzScaleList: toBzScaleSaveDtoList(),
|
||||
bizCstList: toBizCstSaveDtoList(),
|
||||
cklstResultList: toCklstSaveDtoList(),
|
||||
};
|
||||
void payload;
|
||||
alert("저장(임시) API 연동 후 progrsSttus=SV 전송",JSON.stringify(payload, null, 2));
|
||||
};
|
||||
|
||||
try {
|
||||
const issuedDocNo = await InstlSncnApi.saveApply(payload);
|
||||
alert(`저장되었습니다. 문서번호: ${issuedDocNo}`);
|
||||
} catch (error) {
|
||||
console.error("저장 API 호출 실패", error);
|
||||
alert("저장 중 오류가 발생했습니다.");
|
||||
}
|
||||
}
|
||||
|
||||
function handleSubmit() {
|
||||
if (!fnValidation("PRESENTN")) return;
|
||||
const payload = {
|
||||
taskSeComCd,
|
||||
instlSncnMng: toInstlSncnMngPayload("ST"),
|
||||
bzScaleList: toBzScaleSaveDtoList(),
|
||||
cklstResultList: toCklstSaveDtoList(),
|
||||
bizCstList: toBizCstSaveDtoList(),
|
||||
cklstRvwRsltList: toCklstSaveDtoList(),
|
||||
};
|
||||
void payload;
|
||||
alert("제출 API 연동 후 progrsSttus=ST 전송");
|
||||
@@ -812,7 +901,12 @@ function CnfmRegistPage() {
|
||||
const rvwPaddingLeft = `${Math.max(0, row.treeLevel - 1) * 16 + 8}px`;
|
||||
|
||||
return (
|
||||
<tr key={key}>
|
||||
<tr
|
||||
key={key}
|
||||
ref={(el) => {
|
||||
cklstRowRefs.current[key] = el;
|
||||
}}
|
||||
>
|
||||
<td className="text-center text-[1.2rem]">{row.cklstArtclNm}</td>
|
||||
<td className="!text-left pl-2 text-[1.2rem] text-gray-600" style={{ paddingLeft: rvwPaddingLeft }}>
|
||||
{row.treeLevel > 1 ? "└ " : ""}
|
||||
|
||||
Reference in New Issue
Block a user