Merge branch 'iipbs/sn/dev'

This commit is contained in:
2025-07-27 21:59:04 +09:00
4 changed files with 185 additions and 170 deletions
@@ -1,13 +1,15 @@
'use client';
import React, { useEffect, useState } from 'react';
import { Button } from '@common/components';
import { YearRangeSelectBox } from '@/components/ui/YearRangeSelectBox';
import { SelectBox } from '@common/business_components/ui';
import { Button, Input } from '@common/components';
import React from 'react';
import { reportGubuns } from './searchModalReportApi';
import { SelectBox, UiTable } from '@common/business_components/ui';
import LoadingOverlay from '@/components/ui/LoadingBar';
import { addFileName, flattenFolders, getFileDownload, useCityCd, useDcTree, useSggCd, reportGubuns } from './searchModalReportApi';
import { type SelectDataType } from '../commonStore';
import { useCityCd, useSggCd } from '../components/searchModalReportApi';
// // 하수도정비 기본계획 보고서
// 하수도정비 기본계획 보고서
export const reportTableHeadlist = [
{ id: "no", title: "NO" },
{ id: "sido", title: "시도" },
@@ -22,58 +24,159 @@ export const reportTableHeadlist = [
interface BasicTabProps {
open?: boolean;
gubun: string;
sidos: SelectDataType[];
ctpvCd: string | number;
sigungu: string;
filteredSiguns: SelectDataType[];
reportGubun: string;
setReportGubun: (gubun: string) => void;
yearFilter: { start: string; end: string; };
handleSearch: () => void;
handleCtpvCdChange: (val: string | number) => void;
setYearFilter: (filter: { start: string; end: string }) => void;
setGubun: (gubun: string) => void;
setSigungu: (sigungu: string) => void;
}
const ReportTab: React.FC<BasicTabProps> = ({
open,
gubun,
sidos,
ctpvCd,
sigungu,
filteredSiguns = [{ val: '0', text: '전체', sido: '0' }],
yearFilter,
reportGubun,
setReportGubun,
handleSearch,
handleCtpvCdChange,
setYearFilter,
setGubun,
setSigungu,
}) => {
const ReportTab: React.FC<BasicTabProps> = ({ open }) => {
const [isLoading, setIsLoading] = useState(false);
const [isResetPage, setIsResetPage] = useState<boolean>(false);
const [isResetSelection, setIsResetSelection] = useState<boolean>(false);
const [showTable, setShowTable] = useState(false);
const [tableData, setTableData] = useState<any[]>([]);
const [tableCount, setTableCount] = useState(0);
const [curPage, setCurPage] = useState(1);
const [yearFilter, setYearFilter] = useState({ start: "", end: "" });
const [ctpvCd, setCtpvCd] = useState('0'); // 하수정비기본계획 report
const [sigungu, setSigungu] = useState('0'); // 하수정비기본계획 report
const [filteredSiguns, setFilteredSiguns] = useState<SelectDataType[]>([{ val: '0', text: '전체', sido: '0' }]);
const [gubun, setGubun] = useState(""); // 하수정비기본계획 report
const [reportGubun, setReportGubun] = useState("0"); // 하수정비기본계획 report
// 하수정비기본계획 report
const sidos = useCityCd();
const siguns = useSggCd();
const reportOriginData = useDcTree(""); //:Folder[]
const dynamicHeadlist: { id: string, title: string }[] = reportTableHeadlist;
useEffect(() => {
// filterParams 초기화
setCtpvCd('0');
setSigungu('0');
setReportGubun('0');
setYearFilter({ start: "", end: "" });
setShowTable(false);
setTableData([]);
setTableCount(0);
setCurPage(1); // 페이지를 1로 초기화
setIsResetPage(true); // 페이지를 초기화
setIsResetSelection(true); // 선택 초기화
}, [open]);
// 페이지 변경 시마다 데이터 fetch
useEffect(() => {
// showTable이 true일 때만 fetch (최초 렌더 방지)
setIsResetPage(false);
setIsResetSelection(true); // 선택 초기화
if (showTable) fetchFilteredData();
}, [curPage]);
const handleCtpvCdChange = (val: string | number) => { // 하수도정비기본계획.시도
const sidoOption = sidos.find((n) => n.val === val);
setCtpvCd(String(val));
if (val !== '0') {
const filterSiguns = siguns.filter(item => item.sido === sidoOption?.text);
filterSiguns.unshift({ val: '0', text: '전체', sido: '0' });
setFilteredSiguns(filterSiguns);
}
setSigungu("0");
setGubun("");
setYearFilter({ start: "", end: "", });
}
// 조회 버튼 클릭 시 1페이지로 이동 및 데이터 초기화
const handleSearch = () => {
setCurPage(1); // 페이지를 1로 초기화
fetchFilteredData();
setIsResetPage(true); // 페이지를 초기화
setIsResetSelection(false); // 선택 초기화
setShowTable(true);
};
// 하수도정비기본계획 테이블 cellClick 핸들러
const handleReportCellClick = (index: number | undefined ) => {
console.log(index);
if (typeof index === "number" && tableData[index]) {
const reportData = tableData[index];
const fileName = reportData.fileName;
const filePath = reportData.filePath;
if (reportData.fileSn !== "") {
getFileDownload(reportData.fileSn, fileName, filePath);
}
}
};
// 검색 API 호출 함수
const fetchFilteredData = async () => {
setIsLoading(true); // 로딩 시작
setIsResetSelection(true); // 선택 초기화
try {
//const reportOriginData = await getDcTree('경기도');
const reportData = flattenFolders(reportOriginData);
let filterReportData = reportData;
if (ctpvCd !== "0") { // 시도검색
const sidoOption = sidos.find((n) => n.val === ctpvCd);
filterReportData = filterReportData.filter(item => item.sido === sidoOption?.text);
}
if (sigungu !== "0") { // 시군구검색
const sigunguOption = filteredSiguns.find((n) => n.val === sigungu);
filterReportData = filterReportData.filter(item => item.sigungu === sigunguOption?.text);
}
if (reportGubun !== "0") { // 보고서구분검색
const reportGubunOption = reportGubuns.find((n) => n.val === reportGubun);
filterReportData = filterReportData.filter(item => item.reportGubun === reportGubunOption?.text);
}
if (yearFilter.start != "" && yearFilter.end == "") //승인년도검색
filterReportData = filterReportData.filter(item => (item.approvalYear) ? item.approvalYear >= yearFilter.start + "년" : null);
if (yearFilter.start == "" && yearFilter.end != "")
filterReportData = filterReportData.filter(item => (item.approvalYear) ? item.approvalYear <= yearFilter.end + "년" : null);
if (yearFilter.start != "" && yearFilter.end != "") {
filterReportData = filterReportData.filter(item => {
return (item.approvalYear) ? item.approvalYear >= yearFilter.start + "년" && item.approvalYear <= yearFilter.end + "년" : null;
});
}
if (gubun !== "") { // 구분검색
filterReportData = filterReportData.filter(item => (item.plantZoneGugun) ? String(item.plantZoneGugun).includes(String(gubun)) : null);
}
console.log('report count:', filterReportData.length);
setTableCount(filterReportData.length || 0);
filterReportData = filterReportData.slice((curPage - 1) * 10, curPage * 10); // paging
filterReportData = filterReportData?.map((item, index) => ({ ...item, no: (curPage - 1) * 10 + index + 1 }));
//const fileList = await getDcFile("CLS20250530000041");
addFileName(filterReportData).then(data => {
filterReportData = data;
setTableData(filterReportData || []);
});
setTableData(filterReportData || []);
setShowTable(true);
} catch (err) {
console.error('API ERROR:', err);
setTableData([]);
} finally {
setIsLoading(false); // 로딩 종료
}
};
return (
<>
<div className="relative flex flex-col border rounded-lg p-6 w-full h-[10vh]">
<div className="flex items-center mb-3">
<SelectBox
className="w-[150px] mr-8"
<SelectBox className="w-[150px] mr-8"
selectClass="rounded-lg bg-white"
label="시도"
selectArray={sidos}
value={ctpvCd}
onSelectValue={handleCtpvCdChange}
label="시도" selectArray={sidos}
value={ctpvCd} onSelectValue={handleCtpvCdChange}
selectContentClass="z-[101]"
/>
<SelectBox
className="w-48"
<SelectBox className="w-48"
selectClass="w-48 rounded-lg bg-white truncate"
label="시군구"
selectArray={filteredSiguns}
value={sigungu}
onSelectValue={val => setSigungu(String(val))}
label="시군구" selectArray={filteredSiguns}
value={sigungu} onSelectValue={val => setSigungu(String(val))}
selectContentClass="z-[101]"
/>
</div>
@@ -85,23 +188,16 @@ const ReportTab: React.FC<BasicTabProps> = ({
value={gubun}
onChange={(e) => { setGubun(e.target.value); }}
/> */}
<SelectBox
className="w-[150px] mr-8"
<SelectBox className="w-[150px] mr-8"
selectClass="rounded-lg bg-white"
label="보고서구분"
selectArray={[...reportGubuns]}
value={reportGubun}
onSelectValue={val => setReportGubun(String(val))}
selectContentClass="z-[101]"
labelClass='whitespace-nowrap text-xl text-gray-600 pr-2'
label="보고서구분" selectArray={[...reportGubuns]}
value={reportGubun} onSelectValue={val => setReportGubun(String(val))}
selectContentClass="z-[101]" labelClass='whitespace-nowrap text-xl text-gray-600 pr-2'
/>
{/* 승인년도 필터 */}
<YearRangeSelectBox
start={yearFilter.start}
end={yearFilter.end}
<YearRangeSelectBox start={yearFilter.start} end={yearFilter.end}
onChange={range => setYearFilter({ ...yearFilter, start: range.start, end: range.end })}
startLabel="승인년도"
endLabel=""
startLabel="승인년도" endLabel=""
/>
{/* 조회 버튼 */}
<div className="flex-1 flex justify-end">
@@ -109,6 +205,28 @@ const ReportTab: React.FC<BasicTabProps> = ({
</div>
</div>
</div>
<div className="h-[79vh] w-full overflow-hidden rounded-lg border">
{/* 테이블2 영역 (가로/세로 스크롤 가능) */}
<div className="flex-1 px-2 pb-2">
<LoadingOverlay isLoading={isLoading} />
<UiTable
headName={(() => {
return (<>조회 결과 ({tableCount.toLocaleString('ko-KR')})건</>);
})()
}
tableData={tableData}
headlist={dynamicHeadlist}
pageSize={10}
total={tableCount}
cellClick={handleReportCellClick}
onPageChange={setCurPage}
resetPageNo={isResetPage}
resetSelection={isResetSelection}
/>
</div>
</div>
</>
);
};
@@ -3,11 +3,8 @@ import { UiModal, UITab, UiTable } from '@common/business_components/ui';
import axios from 'axios';
import React, { useEffect, useMemo, useState } from 'react';
import { ExclnSewagePumpCol2, MsSwageMhlCol2, MsSwagePplCol2, stofcbrCol2, SwtrmtFcltCol2, SwtrmtPrcsZoneCol2 } from '../components/common/table';
//import { useDcTree, flattenFolders, useSggCd, useCityCd } from './components/searchModalReportApi';
import { type SelectDataType } from '../commonStore';
import { MS_SWAGE_PPL_CODES, MS_SWAGE_PPL_USE_CODES, MS_SWAGE_MHL_CODES } from '../components/common/table';
import { addFileName, flattenFolders, getFileDownload, useCityCd, useDcTree, useSggCd, reportGubuns } from './searchModalReportApi';
// import { useBizByFcltStore } from "../../../store/StoreProvider";
import { getData } from "@/components/lib/api";
import BasicTab, { BASIC_TYPES } from './basicTab';
import OperationTab, { BIZ_TY_CD, operationCleaningTableHeadlist, operationInspectionTableHeadlist, operationMaintenanceTableHeadlist, operationTableHeadlist, WORK_TYPES } from './operationTab';
@@ -84,11 +81,7 @@ const SearchModal: React.FC<SearchModalProps> = ({
const [yearFilter, setYearFilter] = useState({ start: "", end: "" });
const [filteredSiguns, setFilteredSiguns] = useState<SelectDataType[]>([{ val: '0', text: '전체', sido: '0' }]);
// 하수정비기본계획 report
const sidos = useCityCd();
const siguns = useSggCd();
const reportOriginData = useDcTree(""); //:Folder[]
// 오늘 날짜 구하기
const today = new Date();
const todayYear = today.getFullYear().toString();
@@ -107,7 +100,6 @@ const SearchModal: React.FC<SearchModalProps> = ({
return [];
}
if (activeTab === 'operation') return operationTableHeadlist;
if (activeTab === 'report') return reportTableHeadlist;
return [];
}, [activeTab, basicType, workType]);
@@ -136,9 +128,6 @@ const SearchModal: React.FC<SearchModalProps> = ({
setVar4("");
setCity(0);
setSgg(0);
setCtpvCd('0');
setSigungu('0');
setReportGubun('0');
setYearFilter({
start: "",
end: "",
@@ -284,46 +273,7 @@ const SearchModal: React.FC<SearchModalProps> = ({
setDetailTableData([]);
setDetailTableCount(0);
} else if (activeTab === 'report') {
//const reportOriginData = await getDcTree('경기도');
const reportData = flattenFolders(reportOriginData);
let filterReportData = reportData;
if (ctpvCd !== "0") { // 시도검색
const sidoOption = sidos.find((n) => n.val === ctpvCd);
filterReportData = filterReportData.filter(item => item.sido === sidoOption?.text);
}
if (sigungu !== "0") { // 시군구검색
const sigunguOption = filteredSiguns.find((n) => n.val === sigungu);
filterReportData = filterReportData.filter(item => item.sigungu === sigunguOption?.text);
}
if (reportGubun !== "0") { // 보고서구분검색
const reportGubunOption = reportGubuns.find((n) => n.val === reportGubun);
filterReportData = filterReportData.filter(item => item.reportGubun === reportGubunOption?.text);
}
if (yearFilter.start != "" && yearFilter.end == "") //승인년도검색
filterReportData = filterReportData.filter(item => (item.approvalYear) ? item.approvalYear >= yearFilter.start + "년" : null);
if (yearFilter.start == "" && yearFilter.end != "")
filterReportData = filterReportData.filter(item => (item.approvalYear) ? item.approvalYear <= yearFilter.end + "년" : null);
if (yearFilter.start != "" && yearFilter.end != "") {
filterReportData = filterReportData.filter(item => {
return (item.approvalYear) ? item.approvalYear >= yearFilter.start + "년" && item.approvalYear <= yearFilter.end + "년" : null;
});
}
if (gubun !== "") { // 구분검색
filterReportData = filterReportData.filter(item => (item.plantZoneGugun) ? String(item.plantZoneGugun).includes(String(gubun)) : null);
}
console.log('report count:', filterReportData.length);
setTableCount(filterReportData.length || 0);
filterReportData = filterReportData.slice((curPage - 1) * 10, curPage * 10); // paging
filterReportData = filterReportData?.map((item, index) => ({ ...item, no: (curPage - 1) * 10 + index + 1 }));
//const fileList = await getDcFile("CLS20250530000041");
addFileName(filterReportData).then(data => {
filterReportData = data;
setTableData(filterReportData || []);
});
setTableData(filterReportData || []);
}
}
setShowTable(true);
} catch (err) {
@@ -596,31 +546,7 @@ const SearchModal: React.FC<SearchModalProps> = ({
}
}
};
// 하수도정비기본계획 테이블 cellClick 핸들러
const handleReportCellClick = (index: number | undefined, headId?: String) => {
console.log(index);
if (typeof index === "number" && tableData[index]) {
const reportData = tableData[index];
const fileName = reportData.fileName;
const filePath = reportData.filePath;
if (reportData.fileSn !== "") {
getFileDownload(reportData.fileSn, fileName, filePath);
}
}
};
const handleCtpvCdChange = (val: string | number) => { // 하수도정비기본계획.시도
const sidoOption = sidos.find((n) => n.val === val);
setCtpvCd(String(val));
if (val !== '0') {
const filterSiguns = siguns.filter(item => item.sido === sidoOption?.text);
filterSiguns.unshift({ val: '0', text: '전체', sido: '0' });
setFilteredSiguns(filterSiguns);
}
setSigungu("0");
setGubun("");
setYearFilter({ start: "", end: "", });
}
const renderBody = () => (
<>
@@ -681,19 +607,6 @@ const SearchModal: React.FC<SearchModalProps> = ({
{activeTab === 'report' && (
<ReportTab
open={open}
gubun={gubun}
sidos={sidos}
ctpvCd={ctpvCd}
sigungu={sigungu}
filteredSiguns={filteredSiguns}
reportGubun={reportGubun}
setReportGubun={setReportGubun}
yearFilter={yearFilter}
handleSearch={handleSearch}
handleCtpvCdChange={handleCtpvCdChange}
setYearFilter={setYearFilter}
setGubun={setGubun}
setSigungu={setSigungu}
/>
)}
{/* </UITab2> */}
@@ -701,7 +614,7 @@ const SearchModal: React.FC<SearchModalProps> = ({
{/* 📋 결과 리스트 */}
{/* 기본정보 탭, 하수도정비기본계획보고서 탭 */}
{(activeTab === 'basic' || activeTab === 'report') && (
{(activeTab === 'basic' ) && (
<div className="h-[79vh] w-full overflow-hidden rounded-lg border">
{/* 테이블2 영역 (가로/세로 스크롤 가능) */}
<div className="flex-1 px-2 pb-2">
@@ -716,7 +629,7 @@ const SearchModal: React.FC<SearchModalProps> = ({
headlist={dynamicHeadlist}
pageSize={10}
total={tableCount}
cellClick={activeTab === 'report' ? handleReportCellClick : handleBasicCellClick}
cellClick={handleBasicCellClick}
onPageChange={setCurPage}
resetPageNo={isResetPage}
resetSelection={isResetSelection}
@@ -80,15 +80,10 @@ export function flattenFolders(data: Folder[]): FlattenedFolder[] {
}
data.forEach((root) => traverse(root, {}, 0));
console.log(result.slice(0, 10));
return result;
}
export const getDcTree = async (clsfSystmNm:string) => {
//const cityName = encodeURIComponent(clsfSystmNm);
const queryString = 'clsfSystmNm='+clsfSystmNm;
console.log("getDcTree queryString:", queryString);
//const result = await getDcData(`/api/v1/dc/clsf${queryString ? `?${queryString}` : ''}`);
const result = await getDcData("/api/v1/dc/clsf?clsfSystmNm=" + clsfSystmNm);
return result;
};
@@ -118,13 +113,8 @@ export const addFileName = async (data:FlattenedFolder[]) => {
data.map(async (item) => {
const filenameArr:ReportDataFile[] = await getDcFile(String(item.clsfSystmSn));
const f = filenameArr[0];
//console.log("clsfSystmSn:", item.clsfSystmSn, "f:", f);
const result = {...item, fileName:f?.fileTitle, regDate:f?.regDt, fileSn:f?.fileSn, filePath:f?.filePath};
// if (f !== undefined && f.fileSn !== "") {
// const fprcs = await getDcFileprcs(f?.fileSn);
// if (fprcs.length>0)
// result.filePath = fprcs[0].filePath;
// }
return result;
})
);
@@ -132,7 +122,6 @@ export const addFileName = async (data:FlattenedFolder[]) => {
}
export const getDcFileprcs = async (fileSn:string) => {
const queryString = 'fileSn='+fileSn;
console.log("getDcTree queryString:", queryString);
const result = await getDcData(`/api/v1/dc/fileprcs${queryString ? `?${queryString}` : ''}`);
return result;
};
@@ -159,9 +148,6 @@ export const fileDownload = (data:Blob, fileName:string) => { // Blob데이터
window.URL.revokeObjectURL(url);
}
export const getFileDownload = async (fileSn:string, fileName:string, filePath:string) => {
//const fileprcsData = await getDcFileprcs(fileSn); // filePath 조회
//if (fileprcsData?.length === 0 ) { alert("filePath 조회 오류"); }
//if (fileprcsData[0].filePath === "") alert("filePath 정보 없으므로 다운로드 불가");
if (filePath === "") alert("filePath 정보 없으므로 다운로드 불가:filesn"+ fileSn);
const blobData = await getDcFileDownload(filePath); // 실제 다운로드 API
fileDownload(blobData, fileName);
@@ -179,7 +165,6 @@ export const getCityCd = async (): Promise<SelectDataType[]> => {
}));
citys = citys.sort((a:SelectDataType, b:SelectDataType) => a.text.localeCompare(b.text));
citys.unshift({ val: '0', text: '전체' });
console.log("citys:", citys);
return citys;
} catch (error) {
console.error('시도 데이터를 가져오는데 실패했습니다:', error);
@@ -210,7 +195,7 @@ export const getSggCd = async (): Promise<SelectDataType[]> => {
if (!response.ok) throw new Error(`시군구 데이터 오류: ${response.status}`);
const res = await response.json();
const sgg = res.data;
const sggCd = sgg.map((item: any) => ({
const sggCd = sgg.map((item: {adstDstrCd:string, sido:string, gugun:string}) => ({
val: item.adstDstrCd,
sido: item.sido,
text: item.gugun,
-1
View File
@@ -87,7 +87,6 @@ export const deleteData = async (url: string, data?: object): Promise<any> => {
// RSONE DC API 호출
export const getDcData = async (url: string, data?: object): Promise<any> => {
console.log(url);
const res = await fetch('/dc-service' + url, {
method: "GET",
});