diff --git a/apps/sn/app/ii/BaseLayout.tsx b/apps/sn/app/ii/BaseLayout.tsx index 308f5e350..18d9f9bf0 100644 --- a/apps/sn/app/ii/BaseLayout.tsx +++ b/apps/sn/app/ii/BaseLayout.tsx @@ -1,5 +1,5 @@ 'use client' -import { getData, deleteData } from '@/components/lib/api' +import { getData } from '@/components/lib/api' import ContentLayout from '@/components/ui/contentLayout' import { GovSidoSigunguSearchForm } from '@/components/ui/govSidoSigunguSearchForm' import { DatePicker, Form, SearchFormRight, SelectBox } from '@common/business_components/ui' @@ -7,7 +7,6 @@ import ListDiv from '@common/business_components/ui/list_div' import { SelectBox2 } from '@common/business_components/ui/select/index-2' import { Button, Label } from '@common/components/ui' import { useEffect, useState, type ReactNode } from 'react' -import { handleDownload } from '@/components/lib/utils/fileDownload' type TitleName = { mainTitle: string @@ -38,7 +37,7 @@ type BaseLayout = { onSubmitEvent: Function graphBtn?: boolean firstDivBtn?: boolean - searchBox?: SelectBox + selectBox?: SelectBox showDatePicker?: boolean; enoCd?: string | number setEnoCd?: (enoCd: string) => void @@ -52,6 +51,10 @@ type BaseLayout = { onSwageProcessingZoneChange?: (value: string | number) => void; aplcnYm?: Date setAplcnYm?: (aplcnYm: Date | undefined) => void; + // Excel 다운로드 관련 props 추가 + onExcelDownload?: () => void; + onDeleteFile?: () => void; + selectedRowData?: any; } // TODO: 나중에 이벤트 추가되면 파라미터 추가 필요 ? /** @@ -69,7 +72,7 @@ const BaseLayout = ({ form, onSubmitEvent, graphBtn = false, - searchBox, + selectBox, showDatePicker = false, enoCd, setEnoCd, @@ -82,7 +85,11 @@ const BaseLayout = ({ selectedSwageProcessingZone, onSwageProcessingZoneChange, aplcnYm, - setAplcnYm + setAplcnYm, + // Excel 다운로드 관련 props 추가 + onExcelDownload, + onDeleteFile, + selectedRowData }: BaseLayout) => { const defaultOption: ApiSelectOption = { val: '0', text: '선택' }; @@ -171,28 +178,19 @@ const BaseLayout = ({ // 파일 다운로드 const isChckFile = () => { - if (fileNm === '' || fileNm === undefined) { - alert('파일을 선택해주세요'); - return false + // Excel 다운로드 함수가 있으면 Excel 다운로드 실행 + if (onExcelDownload) { + onExcelDownload(); + return; } - handleDownload(`/attach-service/api/v1/download/${atflId}`, fileNm) return true } // 파일 삭제 const isDeleteFile = async () => { - if (dcrpPplMtncBizSn === '' || dcrpPplMtncBizSn === undefined) { - alert('삭제할 파일을 선택해주세요'); - return false - } - const data = await deleteData(`/attach-service/api/v1/atfl/attachments/${atflId}`) - if (data) { - const delData = await deleteData(`v1/dcrpPpl/${dcrpPplMtncBizSn}`); - if (delData.status == 200) { - alert('삭제되었습니다.') - searchBox?.onSearchClick?.(); - } else { - alert('삭제에 실패했습니다.') - } + // Excel 다운로드 함수가 있으면 Excel 다운로드 실행 + if (onDeleteFile) { + onDeleteFile(); + return; } } @@ -201,9 +199,9 @@ const BaseLayout = ({ {/* searchBox.title이 있으면 h3 태그로 제목을 표시합니다. */} - {searchBox?.title && ( + {selectBox?.title && (
-

{searchBox.title}

+

{selectBox.title}

)}
@@ -228,7 +226,7 @@ const BaseLayout = ({ onSwageTreatmentPlantChange?.(val)} /> onSwageProcessingZoneChange?.(val)} /> - {searchBox?.searchChk ? : null} + {selectBox?.searchChk ? : null}
diff --git a/apps/sn/app/ii/irsetting/page.tsx b/apps/sn/app/ii/irsetting/page.tsx index 2b9085ccd..8cb36c2bf 100644 --- a/apps/sn/app/ii/irsetting/page.tsx +++ b/apps/sn/app/ii/irsetting/page.tsx @@ -141,18 +141,27 @@ const IrSetting = () => { console.log("유량자료 정보:", file4); // 모든 필수 파일이 선택되었는지 확인 (UploadedFileInfo 객체와 그 안의 file 객체 모두 확인) - if (!file1?.file || !file2?.file || !file3?.file || !file4?.file) { + // 유량자료는 선택값 + if (!file1?.file || !file2?.file || !file3?.file) { alert('모든 분석용 파일을 선택해주세요.'); return; } - const filePickers = [ - // filePicked 유틸리티가 File 객체를 직접 받는지, ArrayBuffer 등을 받는지 확인 필요 - // 여기서는 File 객체를 받고, 내부적으로 ArrayBuffer로 변환하여 parser에 전달한다고 가정 - { file: file2, parser: excelToMatchingResultData }, - { file: file3, parser: excelToTimePatternData }, - { file: file4, parser: excelToFlowRateData } - ]; + type MatchingResult = { anlsSn: number; mhlMngNo: string; rcptrNo: string; }; + type TimePattern = { hrId: number; rt?: number | null | undefined; }; + type FlowRate = { pplMngNo: string; mtrngDt: string; oilq?: number | null | undefined; }; + + const filePickers: { + file: UploadedFileInfo; + parser: (excelData: any) => MatchingResult[] | TimePattern[] | FlowRate[]; + }[] = [ + { file: file2, parser: excelToMatchingResultData }, + { file: file3, parser: excelToTimePatternData } + ]; + + if (file4?.file) { + filePickers.push({ file: file4, parser: excelToFlowRateData }); + } try { const parsedResults = await Promise.all( @@ -186,7 +195,9 @@ const IrSetting = () => { // 각 파싱 결과 접근 const matchingResultData = parsedResults.find(r => r && r.fileName === file2.file?.name)?.parsedData; const timePatternData = parsedResults.find(r => r && r.fileName === file3.file?.name)?.parsedData; - const flowRateData = parsedResults.find(r => r && r.fileName === file4.file?.name)?.parsedData; + const flowRateData = file4?.file + ? parsedResults.find(r => r && r.fileName === file4.file?.name)?.parsedData + : null; // 새로운 JSON 데이터 생성 const combinedAnalysisData = { @@ -203,10 +214,10 @@ const IrSetting = () => { atflInpNm: file1?.file?.name, // 오수관망도 파일명 atflHrPttnNm: file3?.file?.name, // 오수량 시간패턴 파일명 atflMpngNm: file2?.file?.name, // 수용가-맨홀 매칭결과 파일명 - atflOilqNm: file4?.file?.name, // 유량자료 파일명 + atflOilqNm: file4?.file?.name || null, // 유량자료 파일명 (없을 수 있음) tbSnRcptrMhlMpng: matchingResultData, // 수용가-맨홀 매칭결과 tbSnHrPttn: timePatternData, // 오수량 시간패턴 - tbSnOilqData: flowRateData, // 유량자료 + tbSnOilqData: flowRateData, // 유량자료 (없을 수 있음) }; console.log("통합 분석 데이터 (JSON):", combinedAnalysisData); @@ -228,7 +239,7 @@ const IrSetting = () => { form={form} onSubmitEvent={() => { }} graphBtn={false} - searchBox={{ title: '분석 대상지역 선택' }} + selectBox={{ title: '분석 대상지역 선택' }} enoCd={select1} setEnoCd={setSelect1} ctpvCd={select2} diff --git a/apps/sn/app/ii/rdii/page.tsx b/apps/sn/app/ii/rdii/page.tsx index 5f87c52b0..bbfe2ce08 100644 --- a/apps/sn/app/ii/rdii/page.tsx +++ b/apps/sn/app/ii/rdii/page.tsx @@ -313,7 +313,7 @@ export default function RTKForm() { return ( // { }} graphBtn={false} > { }} // BaseLayout의 form 태그에 연결될 onSubmit diff --git a/apps/sn/app/ii/result/page.tsx b/apps/sn/app/ii/result/page.tsx index fe1ca129c..1c9863b32 100644 --- a/apps/sn/app/ii/result/page.tsx +++ b/apps/sn/app/ii/result/page.tsx @@ -1,13 +1,12 @@ 'use client' -import { getData } from '@/components/lib/api' -import { UiTable } from '@common/business_components/ui' +import { getData, deleteData } from '@/components/lib/api' +import { UiTable, downloadExcel } from '@common/business_components/ui' import { zodResolver } from '@hookform/resolvers/zod' import { useState } from 'react' import { useForm } from 'react-hook-form' import { z } from 'zod' import BaseLayout from '../BaseLayout' - interface ApiSelectOption { text: string; val: string; @@ -27,7 +26,6 @@ const formSchema = z.object({ type ResultFormValues = z.infer -// TODO: 분석구분에 따라서 테이블 컬럼 변경필요 const tableColumn = [ // { id: 'no', title: '번호' }, { id: 'inftwtAnlsSn', title: '분석번호' }, @@ -36,14 +34,8 @@ const tableColumn = [ { id: 'aplcnYm', title: '적용연월' }, { id: 'drtwSwtcRt', title: '오수 전환율' }, { id: 'anlsInfo', title: '침입수 분석정보' }, - { - id: 'regDt', - title: '분석일자', - }, - { - id: 'rgtrNm', - title: '분석자', - } + { id: 'regDt', title: '분석일자' }, + { id: 'rgtrNm', title: '분석자' } ] const tableColumn2 = [ { id: 'no', title: '번호' }, @@ -75,21 +67,36 @@ const Result = () => { const [tableData, setTableData] = useState([]); // API 조회 결과 저장 상태 const [totalData, setTotalData] = useState(0) //총 데이터 수 - const [rdiiResultData, setRdiiResultData] = useState([]); // RDII 결과 저장 상태 (필요시) const [isLoadingResult, setIsLoadingResult] = useState(false); // 로딩 상태 + const [selectedRowData, setSelectedRowData] = useState(null); + const [selectedRowIndex, setSelectedRowIndex] = useState(undefined); - const handleSearchButtonClick = async () => { + + const handleSearchButtonClick = async (customFormValues?: Partial) => { setIsLoadingResult(true); - const formValues = form.getValues(); // react-hook-form에서 현재 값 가져오기 + const formValues = customFormValues || form.getValues(); // react-hook-form에서 현재 값 가져오기 + console.log("formValues", formValues); - const inftwtAnlsSearchDto = { - "aplcnYm": formValues.aplcnYm.toISOString().substring(0, 7).replace("-", ""), - "enoCd": formValues.enoCd === '0' ? null : formValues.enoCd, - "ctpvCd": formValues.ctpvCd === '0' ? null : formValues.ctpvCd, - "sggCd": formValues.sggCd.val === '0' ? null : formValues.sggCd.val, - // "swageTmplntCd": formValues.swageTmplntCd === '0' ? null : formValues.swageTmplntCd, - "swtrmtZoneCd": formValues.swtrmtZoneCd === '0' ? null : formValues.swtrmtZoneCd, - }; + let inftwtAnlsSearchDto; + + if (customFormValues) { + inftwtAnlsSearchDto = { + "aplcnYm": formValues.aplcnYm, + "enoCd": formValues.enoCd, + "ctpvCd": formValues.ctpvCd, + "sggCd": formValues.sggCd, + "swtrmtZoneCd": formValues.swtrmtZoneCd, + }; + } else { + inftwtAnlsSearchDto = { + "aplcnYm": formValues.aplcnYm ? formValues.aplcnYm.toISOString().substring(0, 7).replace("-", "") : null, + "enoCd": formValues.enoCd === '0' ? null : formValues.enoCd, + "ctpvCd": formValues.ctpvCd === '0' ? null : formValues.ctpvCd, + "sggCd": formValues.sggCd?.val === '0' ? null : formValues.sggCd?.val, + "swtrmtZoneCd": formValues.swtrmtZoneCd === '0' ? null : formValues.swtrmtZoneCd, + }; + } + console.log("Search DTO:", inftwtAnlsSearchDto); const queryParams = new URLSearchParams({ page: '0', @@ -103,7 +110,7 @@ const Result = () => { }); const queryString = queryParams.toString(); - console.log("API: ", `v1/inftwtAnls${queryString ? `?${queryString}` : ''}`); // 디버깅용 로그 + console.log("API: ", `v1/inftwtAnls${queryString ? `?${queryString}` : ''}`); try { const data = await getData(`v1/inftwtAnls${queryString ? `?${queryString}` : ''}`); setTableData(data.content || []); @@ -120,9 +127,89 @@ const Result = () => { } }; + // 테이블 셀 클릭 핸들러 추가 + const handleCellClick = (index: number | undefined, headId: string) => { + if (index !== undefined && tableData[index]) { + setSelectedRowData(tableData[index]); + setSelectedRowIndex(index); + console.log("Selected row data:", tableData[index]); + } + }; + + // Excel 다운로드 핸들러 추가 + const handleExcelDownload = async () => { + console.log("Selected row data for download:", selectedRowData); + if (!selectedRowData || selectedRowData.inftwtAnlsSn === undefined) { + alert('다운로드할 데이터를 선택해주세요.'); + return; + } + + try { + const data = await getData(`v1/inftwtAnlsRslt/ppl/${selectedRowData.inftwtAnlsSn}`); + + // 선택된 행 데이터를 Excel 다운로드 형식으로 변환 + const excelData = data.content; // 배열로 감싸서 전달 + + // 파일명 생성 (분석번호 기반) + const formValues = form.getValues(); + const fileName = `침입수분석결과_${selectedRowData.inftwtAnlsSn}_${selectedRowData.aplcnYm}`; + + downloadExcel({ + data: excelData as JSON[], + fileName: fileName, + sheetNameArray: ["침입수 분석결과"], + headerRange: "A1:D1" // 테이블 컬럼 수에 맞춰 조정 + }); + } catch (error) { + console.error("Excel 다운로드 중 오류 발생:", error); + alert("Excel 다운로드 중 오류가 발생했습니다. 다시 시도해주세요."); + } + }; + + // 파일 삭제 핸들러 추가 + const handleFileDelete = async () => { + console.log("Selected row data for deletion:", selectedRowData); + if (!selectedRowData || selectedRowData.inftwtAnlsSn === undefined) { + alert('삭제할 파일을 선택해주세요.'); + return; + } + // 삭제 확인 대화상자 + const confirmDelete = window.confirm('선택된 분석 결과를 삭제하시겠습니까?'); + if (!confirmDelete) { + return; + } + + try { + // 삭제 API 호출 + const response1 = await deleteData(`v1/inftwtAnlsRslt/${selectedRowData.inftwtAnlsSn}`); + const response2 = await deleteData(`v1/inftwtAnls/${selectedRowData.inftwtAnlsSn}`); + console.log("File delete response1:", response1); + console.log("File delete response2:", response2); + + // 삭제 후 테이블 새로고침을 위해 재검색 + const customFormValues: Partial = { + aplcnYm: selectedRowData.aplcnYm || '0', + enoCd: selectedRowData.enoCd || '0', + ctpvCd: selectedRowData.ctpvCd || '0', + sggCd: selectedRowData.sggCd || '0', + swtrmtZoneCd: selectedRowData.swtrmtZoneCd || '0', + }; + await handleSearchButtonClick(customFormValues); + + // 선택된 행 초기화 + setSelectedRowData(null); + setSelectedRowIndex(undefined); + + alert('삭제가 완료되었습니다.'); + } catch (error) { + console.error("분석결과 파일 삭제 중 오류 발생:", error); + alert("분석결과 파일 삭제 중 오류가 발생했습니다. 다시 시도해주세요."); + } + }; + return ( { }} // BaseLayout의 form 태그에 연결될 onSubmit @@ -146,7 +233,10 @@ const Result = () => { form.setValue("aplcnYm", new Date(val)) } }} - // DatePicker는 BaseLayout 내부에서 form.watch("aplcnYm")과 form.setValue("aplcnYm", ...)을 사용하도록 수정 필요 + // Excel 다운로드 핸들러 추가 + onExcelDownload={handleExcelDownload} + onDeleteFile={handleFileDelete} + selectedRowData={selectedRowData} > {[
○ 침입수 산정결과 현황
@@ -158,7 +248,7 @@ const Result = () => { total={totalData} pageSize={10} children={undefined} - cellClick={(e) => { }} + cellClick={handleCellClick} onPageChange={(e) => { }} />