From 5e9a49abe8c800b9cdc9a525436badcc9d54d7b6 Mon Sep 17 00:00:00 2001 From: ljpark Date: Wed, 13 Aug 2025 15:17:45 +0900 Subject: [PATCH] =?UTF-8?q?=ED=8C=8C=EC=9D=BC=EB=AA=A9=EB=A1=9D=20modal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../map/searchModal/reportFiles.tsx | 108 ++++++++++++++++++ .../map/searchModal/reportTab.tsx | 11 +- .../map/searchModal/searchModalReportApi.ts | 53 +++++++-- 3 files changed, 163 insertions(+), 9 deletions(-) create mode 100644 apps/sn/app/(map-layout)/map/searchModal/reportFiles.tsx diff --git a/apps/sn/app/(map-layout)/map/searchModal/reportFiles.tsx b/apps/sn/app/(map-layout)/map/searchModal/reportFiles.tsx new file mode 100644 index 000000000..300d0e58b --- /dev/null +++ b/apps/sn/app/(map-layout)/map/searchModal/reportFiles.tsx @@ -0,0 +1,108 @@ +'use client' +import React, { useEffect, useState } from 'react'; + +import LoadingOverlay from '@/components/ui/LoadingBar'; +import { getFileDownload, getDcFiles, useDcFiles } from './searchModalReportApi'; +import { type ReportDataFile } from './searchModalReportApi'; +import { useReportFileModalStore } from './searchModalReportApi'; // 모달 상태 관리 스토어 +import { UiTable } from '@common/business_components/ui'; +import { UiModal } from "@common/business_components"; + +type ModalOpenProp = { + clsfSystmSn: string, // 자료관 기본계획 일련번호 +} +// 하수도정비 기본계획 보고서 파일목록 +export const headlist = [ + { id: "no", title: "NO" }, + { id: "fileTitle", title: "파일명" }, + { id: "regDt", title: "등록일자" }, +]; + +const ReportFiles: React.FC = ({ clsfSystmSn }: ModalOpenProp) => { + const [isLoading, setIsLoading] = useState(false); + const [isResetPage, setIsResetPage] = useState(false); + const [isResetSelection, setIsResetSelection] = useState(false); + const { isOpen: isModalOpen, setIsOpen: setIsModalOpen } = useReportFileModalStore(); // 모달 상태 관리 스토어 + const [files, setFiles] = useState([]); + const [curPage, setCurPage] = useState(1); + + const filesOrigin = useDcFiles(clsfSystmSn); // 자료관 기본계획 일련번호에 해당하는 파일 목록을 가져오기 위한 훅 + useEffect(() => { + if (isModalOpen === true) { + getFiles(); // 파일 목록 조회 + } else { + setFiles([]); // 모달이 닫힐 때 파일 목록 초기화 + setCurPage(1); // 페이지를 1로 초기화 + setIsResetPage(true); // 페이지를 초기화 + setIsResetSelection(true); // 선택 초기화 + } + }, [filesOrigin, isModalOpen, curPage]); + + // 파일 목록 조회 + const getFiles = async () => { + setIsLoading(true); // 로딩 시작 + try { + //let data = await getDcFiles(clsfSystmSn); + let data = filesOrigin; + data = data?.slice((curPage - 1) * 10, curPage * 10); // paging + data = data?.map((item, index) => ({ ...item, no: (curPage - 1) * 10 + index + 1 })); + setFiles(data); + } catch (error) { + console.error("파일 목록 조회 에러:", error); + } finally { + setIsLoading(false); // 로딩 종료 + } + }; + + // cellClick 핸들러 + const handleCellClick = (index: number|undefined) => { + if (typeof index === "number" && files[index]) { + const fileName = files[index].fileTitle; + const filePath = files[index].filePath; + if (files[index].fileSn !== "") { + getFileDownload(files[index].fileSn, fileName, filePath); + } + } + }; + + const renderBody = () => ( + <> +
+ + { + return (<>조회 결과 ({filesOrigin.length.toLocaleString('ko-KR')})건); + })() + } + tableData={files} + headlist={headlist} + pageSize={10} + total={filesOrigin.length} + cellClick={handleCellClick} + onPageChange={setCurPage} + resetPageNo={isResetPage} + resetSelection={isResetSelection} + /> +
+ + ); + return ( + + ); +}; + +export default React.memo(ReportFiles); \ No newline at end of file diff --git a/apps/sn/app/(map-layout)/map/searchModal/reportTab.tsx b/apps/sn/app/(map-layout)/map/searchModal/reportTab.tsx index 0682ce8f5..7fe3bd0f5 100644 --- a/apps/sn/app/(map-layout)/map/searchModal/reportTab.tsx +++ b/apps/sn/app/(map-layout)/map/searchModal/reportTab.tsx @@ -7,6 +7,8 @@ import LoadingOverlay from '@/components/ui/LoadingBar'; import { addFileName, flattenFolders, getFileDownload, useDcTree, reportGubuns } from './searchModalReportApi'; import useStore from '../../../store/useStore'; import { CtpvSggPrcsFcltSelectBox } from '@/components/ui/ctpvSggPrcsFcltSelectBox'; +import { useReportFileModalStore } from './searchModalReportApi'; // 모달 상태 관리 스토어 +import ReportFiles from './reportFiles'; // 파일목록모달 // 하수도정비 기본계획 보고서 @@ -41,7 +43,8 @@ const ReportTab: React.FC = ({ open, isSearch = true, ctpvNm, sg const [sggCd, setSggCd] = useState('0'); const [reportGubun, setReportGubun] = useState("0"); // const [gubun, setGubun] = useState(""); - + const { isOpen: isModalOpen, setIsOpen: setIsModalOpen } = useReportFileModalStore(); // 모달 상태 관리 스토어 + const [clsfSystmSn, setClsfSystmSn] = useState(''); // 자료관 기본계획 일련번호 const { ctpv, sgg } = useStore(); // 하수정비기본계획 report @@ -105,7 +108,9 @@ const ReportTab: React.FC = ({ open, isSearch = true, ctpvNm, sg const fileName = reportData.fileName; const filePath = reportData.filePath; if (reportData.fileSn !== "") { - getFileDownload(reportData.fileSn, fileName, filePath); + //getFileDownload(reportData.fileSn, fileName, filePath); + setIsModalOpen(true); // 모달 열기 + setClsfSystmSn(reportData.clsfSystmSn); // 자료관 기본계획 일련번호 설정 } } }; @@ -228,6 +233,8 @@ const ReportTab: React.FC = ({ open, isSearch = true, ctpvNm, sg /> + {/* 모달 컴포넌트 */} + {isModalOpen && } ); }; diff --git a/apps/sn/app/(map-layout)/map/searchModal/searchModalReportApi.ts b/apps/sn/app/(map-layout)/map/searchModal/searchModalReportApi.ts index 4a5df24a1..33d9e3fed 100644 --- a/apps/sn/app/(map-layout)/map/searchModal/searchModalReportApi.ts +++ b/apps/sn/app/(map-layout)/map/searchModal/searchModalReportApi.ts @@ -1,6 +1,6 @@ import { useState, useEffect } from 'react'; import { getDcData } from '@/components/lib/api'; - +import { create } from 'zustand'; export type Folder = { clsfSystmSn: string; @@ -16,13 +16,14 @@ export type SelectDataType = { val: string; sido?: string; // 시도 코드 (선택 사항) } -type ReportDataFile = { +export type ReportDataFile = { no: number; fileTitle: string; regDt: string; fileSn: string; filePath: string; } + const isYear = (value: string): boolean => { return /^[0-9]{4}년$/.test(value); }; @@ -107,23 +108,53 @@ export const useDcTree = (clsfSystmNm: string) => { return data; }; -export const getDcFile = async (clsfSystmSn: string) => { +export const getDcFiles = async (clsfSystmSn: string) : Promise => { + if (!clsfSystmSn) { + console.error("clsfSystmSn is required to get files."); + return []; + } const queryString = 'clsfSystmSn=' + clsfSystmSn; const result = await getDcData(`/api/v1/dc/file${queryString ? `?${queryString}` : ''}`); return result; }; + +export const useDcFiles = (clsfSystmNm: string) => { + const [data, setData] = useState([]); + + useEffect(() => { + const fetch = async () => { + const result = await getDcFiles(clsfSystmNm); + if (result) { + setData(result); + } + console.log("useDcFiles 호출"); + console.log("clsfSystmNm:", clsfSystmNm, "data:", result); + }; + fetch(); + }, [clsfSystmNm]); + + return data; +}; + +// 파일 이름 추가 export const addFileName = async (data: FlattenedFolder[]) => { const fileReportData = await Promise.all( data.map(async (item) => { - const filenameArr: ReportDataFile[] = await getDcFile(String(item.clsfSystmSn)); - const f = filenameArr[0]; - const result = { ...item, fileName: f?.fileTitle, regDate: f?.regDt, fileSn: f?.fileSn, filePath: f?.filePath }; - + const fileArr: ReportDataFile[] = await getDcFiles(String(item.clsfSystmSn)); + let result:FlattenedFolder; + if (fileArr.length > 0) { + const f = fileArr[0]; + const fCount = fileArr.length; + result = { ...item, fileName: f?.fileTitle+" 외 "+fCount+"건", regDate: f?.regDt, fileSn: f?.fileSn, filePath: f?.filePath }; + } else { + result = { ...item, fileName: "", regDate: "", fileSn: "", filePath: "" }; + } return result; }) ); return fileReportData; }; + export const getDcFileprcs = async (fileSn: string) => { const queryString = 'fileSn=' + fileSn; const result = await getDcData(`/api/v1/dc/fileprcs${queryString ? `?${queryString}` : ''}`); @@ -157,3 +188,11 @@ export const getFileDownload = async (fileSn: string, fileName: string, filePath fileDownload(blobData, fileName); } +interface ModalStore { + isOpen: boolean; + setIsOpen: (open: boolean) => void; +} +export const useReportFileModalStore = create((set) => ({ + isOpen: false, + setIsOpen: (open: boolean) => set({ isOpen: open }), +}));