Merge branch 'iipbs/sn/dev' of https://www.onasys.co.kr:3000/ONAANDALL/frontend into iipbs/sn/dev
This commit is contained in:
@@ -341,9 +341,10 @@ export const downloadExmnBusinessReport = async (
|
||||
export const downloadExmnPipeReport = async (
|
||||
exmnBizId: string,
|
||||
pipeNumber: string,
|
||||
jobType: "PPL" | "MNHL" = "PPL",
|
||||
): Promise<Blob> =>
|
||||
getBlobData(
|
||||
`${EXMN_BUSINESS_API_PATH}/${encodeURIComponent(exmnBizId)}/pipeReports/${encodeURIComponent(pipeNumber)}`,
|
||||
`${EXMN_BUSINESS_API_PATH}/${encodeURIComponent(exmnBizId)}/${jobType === "MNHL" ? "manholeReports" : "pipeReports"}/${encodeURIComponent(pipeNumber)}`,
|
||||
);
|
||||
|
||||
/** 사업 파일별 첨부서비스 키 쌍을 조사사업에 연결한다. */
|
||||
@@ -351,9 +352,10 @@ export const downloadExmnPipeReport = async (
|
||||
export const getExmnPipeReportAvailability = async (
|
||||
exmnBizId: string,
|
||||
pipeNumber: string,
|
||||
jobType: "PPL" | "MNHL" = "PPL",
|
||||
): Promise<ExmnPipeReportAvailability> =>
|
||||
getData(
|
||||
`${EXMN_BUSINESS_API_PATH}/${encodeURIComponent(exmnBizId)}/pipeReports/${encodeURIComponent(pipeNumber)}/availability`,
|
||||
`${EXMN_BUSINESS_API_PATH}/${encodeURIComponent(exmnBizId)}/${jobType === "MNHL" ? "manholeReports" : "pipeReports"}/${encodeURIComponent(pipeNumber)}/availability`,
|
||||
) as Promise<ExmnPipeReportAvailability>;
|
||||
|
||||
export const updateExmnAttachments = async (
|
||||
|
||||
@@ -385,7 +385,7 @@ export function ExmnRegistrationPage() {
|
||||
if (refreshed)
|
||||
setNotice({
|
||||
type: "success",
|
||||
text: "조사결과 최종 저장과 PPL/MNHL 집계를 완료했습니다.",
|
||||
text: "조사결과 저장을 완료했습니다.",
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ const VALIDATION_CHECK_GUIDES: Partial<
|
||||
},
|
||||
MNHL_EXMN_PDF: {
|
||||
checkedItems: ["PDF 파일 형식", "조사결과 Excel 구조 및 저장 가능 여부"],
|
||||
pendingNote: "PDF 내부 맨홀번호 대조는 검토 기준 협의 후 적용 예정입니다.",
|
||||
pendingNote: "맨홀보고서의 맨홀번호에는 실제 GIS 맨홀번호를 입력해주세요. 보고서 열람 시 번호를 대조하며, 업로드 검토에는 PDF 내부 번호 대조가 포함되지 않습니다.",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -179,6 +179,9 @@ export function ExmnResultRegisterPopupPage({
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [notice, setNotice] = useState<Notice>(null);
|
||||
|
||||
/** 맨홀 PDF 하나만 선택한 경우 기존 조사자료의 첨부 교체로 처리한다. */
|
||||
const isManholeOnly = Boolean(files.MNHL_EXMN_PDF) && Object.keys(files).length === 1;
|
||||
|
||||
/** popup layout 상위 html/body에서 생기는 문서 스크롤바를 팝업이 열린 동안 잠근다. */
|
||||
useEffect(() => {
|
||||
const previousHtmlOverflow = document.documentElement.style.overflow;
|
||||
@@ -310,7 +313,9 @@ export function ExmnResultRegisterPopupPage({
|
||||
const fileMap = Object.fromEntries(
|
||||
Object.entries(files).map(([type, selected]) => [type, selected?.file]),
|
||||
) as Partial<Record<ExmnFileType, File>>;
|
||||
const validationMessage = validateExmnFiles(fileMap);
|
||||
const validationMessage = isManholeOnly
|
||||
? validateExmnFile("MNHL_EXMN_PDF", files.MNHL_EXMN_PDF!.file)
|
||||
: validateExmnFiles(fileMap);
|
||||
if (validationMessage) {
|
||||
setFileValidationStatuses((current) => ({
|
||||
...current,
|
||||
@@ -373,22 +378,22 @@ export function ExmnResultRegisterPopupPage({
|
||||
/** 정상 검토된 조사결과 Excel을 저장하고 부모 화면에 완료 메시지를 보낸 뒤 팝업을 닫는다. */
|
||||
const handleCommit = async () => {
|
||||
const excelValidation = validationResults.EXMN_EXCEL;
|
||||
if (
|
||||
!excelValidation?.valid ||
|
||||
fileValidationStatuses.EXMN_EXCEL !== "success" ||
|
||||
fileValidationStatuses.PPL_EXMN_PDF !== "success"
|
||||
)
|
||||
return;
|
||||
if (!canCommit) return;
|
||||
setIsLoading(true);
|
||||
setNotice(null);
|
||||
try {
|
||||
if (!(await ensureTemplateReady())) return;
|
||||
if (!isManholeOnly && !(await ensureTemplateReady())) return;
|
||||
// 로딩 화면이 저장 확인창을 가리지 않도록 사전 확인과 업로드 상태를 나눈다.
|
||||
setIsLoading(false);
|
||||
if (!(await confirm("조사결과를 저장하시겠습니까?"))) return;
|
||||
setIsLoading(true);
|
||||
if (isManholeOnly) {
|
||||
// 검토 이후 기존 조사자료가 변경됐을 수 있어 첨부 업로드 직전에 다시 대조한다.
|
||||
const result = await validateExmnImport(exmnBizId, buildImportFormData(files, null, "MNHL_EXMN_PDF"));
|
||||
if (!result.valid) throw new Error(result.errors.map((item) => item.message).join("\n"));
|
||||
}
|
||||
await uploadAndLinkAttachments();
|
||||
await commitExmnImport(
|
||||
if (!isManholeOnly) await commitExmnImport(
|
||||
exmnBizId,
|
||||
buildImportFormData(files, excelValidation),
|
||||
);
|
||||
@@ -418,6 +423,11 @@ export function ExmnResultRegisterPopupPage({
|
||||
(fileType) => fileValidationStatuses[fileType],
|
||||
);
|
||||
const excelValidation = validationResults.EXMN_EXCEL;
|
||||
/** 단독 첨부 교체와 신규 Excel 결과 저장의 필수 검토 조건을 분리한다. */
|
||||
const canCommit = isManholeOnly
|
||||
? fileValidationStatuses.MNHL_EXMN_PDF === "success" && Boolean(validationResults.MNHL_EXMN_PDF?.valid)
|
||||
: Boolean(excelValidation?.valid) && fileValidationStatuses.EXMN_EXCEL === "success"
|
||||
&& fileValidationStatuses.PPL_EXMN_PDF === "success";
|
||||
|
||||
return (
|
||||
<StandardPopupLayout
|
||||
@@ -429,10 +439,7 @@ export function ExmnResultRegisterPopupPage({
|
||||
type="button"
|
||||
color="primary"
|
||||
disabled={
|
||||
isLoading ||
|
||||
!excelValidation?.valid ||
|
||||
fileValidationStatuses.EXMN_EXCEL !== "success" ||
|
||||
fileValidationStatuses.PPL_EXMN_PDF !== "success"
|
||||
isLoading || !canCommit
|
||||
}
|
||||
onClick={() => void handleCommit()}
|
||||
>
|
||||
@@ -614,7 +621,9 @@ export function ExmnResultRegisterPopupPage({
|
||||
const status = fileValidationStatuses[fileType]!;
|
||||
const result = validationResults[fileType];
|
||||
const messages = fileValidationMessages[fileType] ?? [];
|
||||
const guide = VALIDATION_CHECK_GUIDES[fileType];
|
||||
const guide = fileType === "MNHL_EXMN_PDF" && isManholeOnly
|
||||
? { checkedItems: ["PDF 맨홀번호 판독", "기존 조사자료에 연결된 실제 GIS 맨홀번호와 대조"], pendingNote: "저장 시 기존 조사자료는 유지하고 맨홀 보고서만 교체합니다." }
|
||||
: VALIDATION_CHECK_GUIDES[fileType];
|
||||
const countText =
|
||||
fileType === "EXMN_EXCEL"
|
||||
? `관로 ${result?.summary.pplCount ?? 0}건 · 맨홀 ${result?.summary.mnhlCount ?? 0}건`
|
||||
|
||||
@@ -52,21 +52,22 @@ export default function ExmnSurveyResultSection({
|
||||
const alert = useAlertStore((state) => state.setMessage);
|
||||
const exmnBizId = readFeatureProperty(featureData, ["exmnBizId"]);
|
||||
const jobType = readFeatureProperty(featureData, ["jobType"]);
|
||||
const pipeNumber = readFeatureProperty(featureData, [
|
||||
"gisManagementNo",
|
||||
"fclt_mng_no",
|
||||
"fcltMngNo",
|
||||
]);
|
||||
// 맨홀은 조사 관로번호가 아니라 지도 원천의 실제 시설관리번호로 보고서를 조회한다.
|
||||
const pipeNumber = readFeatureProperty(featureData, jobType === "MNHL"
|
||||
? ["fclt_mng_no", "fcltMngNo"]
|
||||
: ["gisManagementNo", "fclt_mng_no", "fcltMngNo"]);
|
||||
const reportAvailable = readBooleanFeatureProperty(featureData, [
|
||||
"reportAvailable",
|
||||
]);
|
||||
/** 지도는 상류맨홀 위치를 유지하고 맨홀 보고서는 실제 GIS 맨홀번호로 연결한다. */
|
||||
const reportLabel = jobType === "MNHL" ? "맨홀" : "관로";
|
||||
const canOpenPipeReport =
|
||||
jobType === "PPL" && Boolean(exmnBizId && pipeNumber);
|
||||
(jobType === "PPL" || jobType === "MNHL") && Boolean(exmnBizId && pipeNumber);
|
||||
|
||||
const openPipeReport = async () => {
|
||||
if (!canOpenPipeReport) return;
|
||||
if (!reportAvailable) {
|
||||
alert("등록된 관로 조사보고서가 없습니다.");
|
||||
alert(`등록된 ${reportLabel} 조사보고서가 없습니다.`);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -74,11 +75,12 @@ export default function ExmnSurveyResultSection({
|
||||
const availability = await getExmnPipeReportAvailability(
|
||||
exmnBizId,
|
||||
pipeNumber,
|
||||
jobType === "MNHL" ? "MNHL" : "PPL",
|
||||
);
|
||||
if (!availability.available) {
|
||||
alert(
|
||||
availability.message?.trim() ||
|
||||
`선택한 관로번호의 조사보고서가 등록된 PDF에 없습니다: ${pipeNumber}`,
|
||||
`선택한 GIS 번호의 조사보고서가 등록된 PDF에 없습니다: ${pipeNumber}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -86,6 +88,7 @@ export default function ExmnSurveyResultSection({
|
||||
const reportWindow = openWindowPopup("exmnPipeReport", {
|
||||
exmnBizId,
|
||||
pipeNumber,
|
||||
jobType,
|
||||
reportNo: readFeatureProperty(featureData, ["reportNo"]),
|
||||
});
|
||||
if (!reportWindow) {
|
||||
@@ -97,7 +100,7 @@ export default function ExmnSurveyResultSection({
|
||||
alert(
|
||||
error instanceof Error && error.message
|
||||
? error.message
|
||||
: "관로 조사보고서 열람 가능 여부를 확인하지 못했습니다.",
|
||||
: `${reportLabel} 조사보고서 열람 가능 여부를 확인하지 못했습니다.`,
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -107,6 +110,7 @@ export default function ExmnSurveyResultSection({
|
||||
["사업차수", readFeatureProperty(featureData, ["bizCycl"])],
|
||||
["처리분구", readFeatureProperty(featureData, ["trtdstNm"])],
|
||||
["조사유형", jobType === "MNHL" ? "맨홀조사" : "관로조사"],
|
||||
[jobType === "MNHL" ? "GIS 맨홀번호" : "GIS 관로번호", pipeNumber],
|
||||
["보고서번호", readFeatureProperty(featureData, ["reportNo"])],
|
||||
["구조적결함 등급", readFeatureProperty(featureData, ["structuralGrade"])],
|
||||
["운영적결함 등급", readFeatureProperty(featureData, ["operationalGrade"])],
|
||||
@@ -116,7 +120,7 @@ export default function ExmnSurveyResultSection({
|
||||
<section className="border-b border-border bg-background">
|
||||
<div className="card-header flex items-center justify-between">
|
||||
<h3 className="mx-3 text-2xl font-medium text-label">조사 결과</h3>
|
||||
{jobType === "PPL" && (
|
||||
{(jobType === "PPL" || jobType === "MNHL") && (
|
||||
<Button
|
||||
onClick={openPipeReport}
|
||||
disabled={!canOpenPipeReport}
|
||||
|
||||
@@ -14,7 +14,7 @@ const getParam = (value: string | string[] | undefined) =>
|
||||
/** 공통 Blob API 오류 문자열에서 백엔드의 사용자 안내 메시지만 추출한다. */
|
||||
const getReportErrorMessage = (error: unknown) => {
|
||||
if (!(error instanceof Error)) {
|
||||
return "관로 조사보고서를 불러오지 못했습니다.";
|
||||
return "조사보고서를 불러오지 못했습니다.";
|
||||
}
|
||||
|
||||
const backendMessage = error.message.match(
|
||||
@@ -35,6 +35,9 @@ export default function ExmnPipeReportPopupContent({
|
||||
}: WindowPopupContentProps) {
|
||||
const exmnBizId = getParam(searchParams.exmnBizId);
|
||||
const pipeNumber = getParam(searchParams.pipeNumber);
|
||||
/** 조사유형별 첨부 API와 표시 제목을 선택한다. */
|
||||
const jobType = getParam(searchParams.jobType) === "MNHL" ? "MNHL" : "PPL";
|
||||
const reportLabel = jobType === "MNHL" ? "맨홀" : "관로";
|
||||
const reportNo = getParam(searchParams.reportNo);
|
||||
const [reportUrl, setReportUrl] = useState("");
|
||||
const [errorMessage, setErrorMessage] = useState("");
|
||||
@@ -46,7 +49,7 @@ export default function ExmnPipeReportPopupContent({
|
||||
setReportUrl("");
|
||||
setErrorMessage("");
|
||||
|
||||
void downloadExmnPipeReport(exmnBizId, pipeNumber)
|
||||
void downloadExmnPipeReport(exmnBizId, pipeNumber, jobType)
|
||||
.then((blob) => {
|
||||
if (!active) return;
|
||||
|
||||
@@ -59,7 +62,7 @@ export default function ExmnPipeReportPopupContent({
|
||||
})
|
||||
.catch((error) => {
|
||||
if (!active) return;
|
||||
console.warn("관로 조사보고서 조회 실패:", error);
|
||||
console.warn("조사보고서 조회 실패:", error);
|
||||
setErrorMessage(getReportErrorMessage(error));
|
||||
});
|
||||
|
||||
@@ -67,16 +70,16 @@ export default function ExmnPipeReportPopupContent({
|
||||
active = false;
|
||||
if (objectUrl) URL.revokeObjectURL(objectUrl);
|
||||
};
|
||||
}, [exmnBizId, pipeNumber]);
|
||||
}, [exmnBizId, pipeNumber, jobType]);
|
||||
|
||||
return (
|
||||
<StandardPopupLayout
|
||||
title="관로 조사보고서"
|
||||
title={`${reportLabel} 조사보고서`}
|
||||
showDefaultCancelAction={false}
|
||||
actions={
|
||||
<>
|
||||
<span className="text-[1.05rem] text-label">
|
||||
관로번호 {pipeNumber}
|
||||
{jobType === "MNHL" ? "GIS 번호" : "관로번호"} {pipeNumber}
|
||||
{reportNo ? ` · 보고서번호 ${reportNo}` : ""}
|
||||
</span>
|
||||
<Button type="button" color="white" onClick={() => window.close()}>
|
||||
@@ -93,7 +96,7 @@ export default function ExmnPipeReportPopupContent({
|
||||
</div>
|
||||
) : reportUrl ? (
|
||||
<iframe
|
||||
title={`${pipeNumber} 관로 조사보고서`}
|
||||
title={`${pipeNumber} ${reportLabel} 조사보고서`}
|
||||
src={reportUrl}
|
||||
className="h-full w-full border border-border bg-background"
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user