From 2db685b5339f6a194643739184604ae3a97f6c39 Mon Sep 17 00:00:00 2001 From: swcho Date: Tue, 26 Aug 2025 11:37:58 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20-=20fileUploadForm=20=EC=B2=A8=EB=B6=80?= =?UTF-8?q?=20=EC=8B=9C=EA=B0=80=20=EC=95=84=EB=8B=8C=20=EB=93=B1=EB=A1=9D?= =?UTF-8?q?=20=EC=8B=9C=EC=97=90=20=EC=97=85=EB=A1=9C=EB=93=9C=20=EB=90=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95=20-=20rdii=20=EB=B6=84?= =?UTF-8?q?=EC=84=9D=20=EC=98=A4=EC=88=98=EA=B4=80=EB=A7=9D=EB=8F=84=20inp?= =?UTF-8?q?=20=ED=8C=8C=EC=9D=BC=EC=9D=B4=20=EB=A7=88=EC=9D=B8=EB=93=9C?= =?UTF-8?q?=EC=9B=90=20DB=EC=97=90=20=EC=A0=80=EC=9E=A5=EB=90=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../(with-base-layout)/ii/irsetting/page.tsx | 23 ++-- .../app/(with-base-layout)/ii/rdii/page.tsx | 39 ++++-- apps/sn/app/components/ui/fileUploadForm.tsx | 122 +++++++++++------- 3 files changed, 111 insertions(+), 73 deletions(-) diff --git a/apps/sn/app/(with-base-layout)/ii/irsetting/page.tsx b/apps/sn/app/(with-base-layout)/ii/irsetting/page.tsx index 721931a0f..97864aa20 100644 --- a/apps/sn/app/(with-base-layout)/ii/irsetting/page.tsx +++ b/apps/sn/app/(with-base-layout)/ii/irsetting/page.tsx @@ -1,12 +1,12 @@ 'use client' -import { postData, postMutiPart } from '@/components/lib/api' +import { postMutiPart } from '@/components/lib/api' import FileUploadForm from '@/components/ui/fileUploadForm' import FormSection from '@/components/ui/formSection' -import { DateMonthPicker, filePicked, downloadExcel } from '@common/business_components/ui' +import { DateMonthPicker, filePicked } from '@common/business_components/ui' import ListDiv from '@common/business_components/ui/list_div' -import { Input, Button } from '@common/components/ui' +import { Input } from '@common/components/ui' import { zodResolver } from '@hookform/resolvers/zod' -import { useState, useCallback } from 'react' +import { useState } from 'react' import { useForm } from 'react-hook-form' import { z } from 'zod' import { excelToFlowRateData, excelToMatchingResultData, excelToTimePatternData, excelToRcptrInfoData } from '@/components/lib/utils' @@ -135,16 +135,16 @@ const IrSetting = () => { const registFile = async (analysisData: any) => { - setIsLoadingResult(true); // 로딩 상태 시작 + setIsLoadingResult(true); console.log("Submitting combined analysis data to backend:", analysisData); const data = await postMutiPart('v1/inftwtAnls/savewithfile', analysisData); console.log("Response from backend:", data); if (data) { - setIsLoadingResult(false); // 로딩 상태 시작 + setIsLoadingResult(false); alert("분석 데이터가 성공적으로 제출되었습니다."); resetAllForms(); // 성공 시 폼 초기화 함수 호출 } else { - setIsLoadingResult(false); // 로딩 상태 시작 + setIsLoadingResult(false); alert("분석 데이터 제출 중 오류가 발생했습니다."); } }; @@ -327,18 +327,15 @@ const IrSetting = () => { tbSnRcptrInfo: rcptrInfoData, // 수용가 상수사용량 (없을 수 있음) }; + console.log("통합 분석 데이터 (JSON):", jsonData); + const jsonBlob = new Blob([JSON.stringify(jsonData)], { type: 'application/json' }); const combinedAnalysisData = new FormData(); combinedAnalysisData.append('data', jsonBlob); - if (file1?.file) { - combinedAnalysisData.append('inpFile', file1.file); - } + if (file1?.file) combinedAnalysisData.append('inpFile', file1.file); - console.log("통합 분석 데이터 (JSON):", jsonData); - - // 수정된 registFile 함수를 호출하여 combinedAnalysisData를 전송합니다. await registFile(combinedAnalysisData); setIsLoadingResult(false); } catch (error) { diff --git a/apps/sn/app/(with-base-layout)/ii/rdii/page.tsx b/apps/sn/app/(with-base-layout)/ii/rdii/page.tsx index 72782a791..8a27abb81 100644 --- a/apps/sn/app/(with-base-layout)/ii/rdii/page.tsx +++ b/apps/sn/app/(with-base-layout)/ii/rdii/page.tsx @@ -1,11 +1,11 @@ "use client" -import { postData } from '@/components/lib/api' +import { postMutiPart } from '@/components/lib/api' import FileUploadForm from "@/components/ui/fileUploadForm" import FormSection from "@/components/ui/formSection" import BaseLayout from "../BaseLayout" import { DateMonthPicker, filePicked } from "@common/business_components/ui" -import { Input, Button } from "@common/components/ui" +import { Input } from "@common/components/ui" import { zodResolver } from "@hookform/resolvers/zod" import { Label } from '@radix-ui/react-label' import { useState } from "react" @@ -157,13 +157,18 @@ export default function RTKForm() { }; const registFile = async (analysisData: any) => { - console.log("Submitting combined analysis data to backend"); - // 추후 API 엔드포인트에 맞게 URL 수정 필요 - const data = await postData('v1/rdiiAnls', analysisData); + setIsLoadingResult(true); + console.log("Submitting combined analysis data to backend", analysisData); + const data = await postMutiPart('v1/rdiiAnls/savewithfile', analysisData); + console.log("Response from backend:", data); if (data) { + setIsLoadingResult(false); alert("분석 데이터가 성공적으로 제출되었습니다."); resetAllForms(); // 성공 시 폼 초기화 함수 호출 - } else alert("분석 데이터 제출 중 오류가 발생했습니다."); + } else { + setIsLoadingResult(false); + alert("분석 데이터 제출 중 오류가 발생했습니다."); + } }; // 침입수 분석 실행 버튼 클릭 핸들러 @@ -182,7 +187,7 @@ export default function RTKForm() { } const formData = form.getValues(); - console.log(uploadedFilesData); + console.log('uploadedFilesData', uploadedFilesData); const file1 = uploadedFilesData[0]; // 오수관망도 const file2 = uploadedFilesData[1]; // 수용가-맨홀 매칭결과 const file3 = uploadedFilesData[2]; // 강우자료 @@ -253,9 +258,9 @@ export default function RTKForm() { if (!file || !file.file) return null; // fileInfo 객체 및 file 객체 null 체크 // filePicked 함수에 File 객체와 headerNum 전달 - const jsonData = await filePicked(file.file, 0); + const ExcelToJsonData = await filePicked(file.file, 0); - if (jsonData === undefined) { + if (ExcelToJsonData === undefined) { console.warn(`Warning: filePicked returned undefined for ${file.file.name}`); return { fileName: file.file.name, @@ -265,9 +270,9 @@ export default function RTKForm() { }; } - // jsonData를 각 parser 함수로 처리 + // ExcelToJsonData를 각 parser 함수로 처리 // parser 함수가 filePicked가 반환하는 JSON 형식을 직접 처리한다고 가정 - const parsedDataBySpecificFunction = parser(jsonData); + const parsedDataBySpecificFunction = parser(ExcelToJsonData); return { fileName: file.file.name, @@ -300,7 +305,7 @@ export default function RTKForm() { // 새로운 JSON 데이터 생성 - const combinedAnalysisData = { + const jsonData = { aplcnYm: formData.date, // 적용연월 (YYYYMM 형식) ctpvCd: ctpvCd !== '0' ? String(ctpvCd).trim() : null, // 시도코드 (선택값) sggCd: sggCd !== '0' ? String(sggCd) : null, // 시군구코드 (선택값) @@ -346,9 +351,15 @@ export default function RTKForm() { tbSnRainData: rainData, // 강우자료 }; - console.log("통합 분석 데이터 (JSON):", combinedAnalysisData); + console.log("통합 분석 데이터 (JSON):", jsonData); + + const jsonBlob = new Blob([JSON.stringify(jsonData)], { + type: 'application/json' + }); + const combinedAnalysisData = new FormData(); + combinedAnalysisData.append('data', jsonBlob); + if (file1?.file) combinedAnalysisData.append('inpFile', file1.file); - // 수정된 registFile 함수를 호출하여 combinedAnalysisData를 전송합니다. await registFile(combinedAnalysisData); setIsLoadingResult(false); } catch (error) { diff --git a/apps/sn/app/components/ui/fileUploadForm.tsx b/apps/sn/app/components/ui/fileUploadForm.tsx index 64bb5dc10..1272488a1 100644 --- a/apps/sn/app/components/ui/fileUploadForm.tsx +++ b/apps/sn/app/components/ui/fileUploadForm.tsx @@ -41,23 +41,84 @@ const FileUploadForm = ({ const [selectedYear, setSelectedYear] = useState(yearOptions[0]?.val || ''); const [selectedQuarter, setSelectedQuarter] = useState(quater[0]?.val || ''); - const internalHandleUploadResponse = (file: File) => async (res: HttpResponse) => { - const fileInfo = res.getHeader("X-File-Info"); - if (!fileInfo) { - if (onFileSelect) onFileSelect(file, null, null); + function registFile() { + console.log(files?.[0]?.name) + if (!currentFile) { + alert('파일을 선택해주세요.'); return; } - const { atflCd: newAtflCd, atflId: newAtflId } = JSON.parse(fileInfo) ?? {}; - const orgnlFileNm = file.name; - console.log(`FileUploadForm (idx: ${fileIdx ?? 'N/A'}) - orgnlFileNm:`, orgnlFileNm, 'New AtflCd:', newAtflCd, 'New AtflId:', newAtflId); - setFileName(orgnlFileNm); - setInternalAtflCd(newAtflCd); - setInternalAtflId(newAtflId); - - if (onFileSelect) { - onFileSelect(file, newAtflCd, newAtflId); + if (!selectedYear) { + alert('등록년도를 선택해주세요.'); + return; } - }; + if (!selectedQuarter) { + alert('분기를 선택해주세요.'); + return; + } + + const fileNm = currentFile.name; + if (onSubmit && files && currentFile) { + // Promise로 handleUpload 완료를 기다림 + handleUploadWithResponse(files, currentFile) + .then(({ atflCd, atflId }) => { + // 업로드 완료 후 onSubmit 실행 + onSubmit(fileNm, selectedYear, selectedQuarter, atflCd || '', atflId || ''); + + // UI 초기화 + setFiles(null); + setFileName(''); + setUploaderKey(prev => prev + 1); + }) + .catch((error) => { + console.error('파일 업로드 실패:', error); + alert('파일 업로드 중 오류가 발생했습니다.'); + }); + } + } + + // handleUpload를 Promise로 감싸는 헬퍼 함수 + function handleUploadWithResponse(files: FileList, file: File): Promise<{ atflCd: string | null, atflId: string | null }> { + return new Promise((resolve, reject) => { + const uploadResponseHandler = async (res: HttpResponse) => { + try { + const fileInfo = res.getHeader("X-File-Info"); + if (!fileInfo) { + resolve({ atflCd: null, atflId: null }); + return; + } + + const { atflCd: newAtflCd, atflId: newAtflId } = JSON.parse(fileInfo) ?? {}; + console.log(`FileUploadForm (idx: ${fileIdx ?? 'N/A'}) - Upload completed:`, newAtflCd, newAtflId); + + // 내부 상태도 업데이트 (다른 용도로 사용할 수 있음) + setInternalAtflCd(newAtflCd); + setInternalAtflId(newAtflId); + setFileName(file.name); + + if (onFileSelect) { + onFileSelect(file, newAtflCd, newAtflId); + } + + // Promise 완료 + resolve({ atflCd: newAtflCd, atflId: newAtflId }); + } catch (error) { + reject(error); + } + }; + + const uploadErrorHandler = (error: Error) => { + console.error('Upload failed:', error); + reject(error); + }; + + // 기존 handleUpload 유틸리티 사용 (수정하지 않음) + handleUpload(files, { + handleUploadStart: setUpload, + handleSuccess: setUpload, + handleResponse: uploadResponseHandler, + }); + }); + } return ( @@ -93,14 +154,6 @@ const FileUploadForm = ({ if (onFileSelect) { onFileSelect(firstFile, internalAtflCd, internalAtflId); } - - // handleUpload(selectedFiles, { - // handleUploadStart: setUpload, - // handleSuccess: setUpload, - // // handleResponse는 tus-js-client의 Upload 인스턴스 콜백과 연동 필요 - // // 여기서는 handleUpload 유틸리티가 응답을 직접 처리한다고 가정 - // handleResponse: internalHandleUploadResponse(firstFile), - // }); }} customPreview={ showPreview ? ( @@ -117,30 +170,7 @@ const FileUploadForm = ({