272 lines
10 KiB
TypeScript
272 lines
10 KiB
TypeScript
/**
|
|
* ➡️ 경로 : 하수관망도 메인 페이지 (오른쪽 돋보기 모양 클릭) - 검색 모달 창 내 하수도정비기본계획보고서 탭
|
|
*
|
|
* 📄 Report Tab
|
|
* Next.js App Router (app/) 기반 검색 모달 보고서 탭 컴포넌트
|
|
*
|
|
* - 하수도정비 기본계획 보고서 목록 표시 및 필터링
|
|
* - 시도/시군구/보고서 구분/승인년도 기반 검색 기능
|
|
* - 테이블 데이터 표시 및 페이지네이션 지원
|
|
* - 보고서 클릭 시 파일 목록 모달 열기
|
|
* - Zustand를 통한 모달 상태 및 데이터 관리
|
|
* - 로딩 오버레이 및 선택 초기화 기능
|
|
*/
|
|
'use client';
|
|
import React, { useEffect, useState } from 'react';
|
|
import { Button, Label } from '@common/components';
|
|
import { YearRangeSelectBox } from '@/components/ui/YearRangeSelectBox';
|
|
import { SelectBox, UiTable } from '@common/business_components/ui';
|
|
import LoadingOverlay from '@/components/ui/LoadingBar';
|
|
import useReportTab from './useReportTab';
|
|
import useStore from '@/store/useStore';
|
|
import { CtpvSggPrcsFcltSelectBox } from '@/components/ui/ctpvSggPrcsFcltSelectBox';
|
|
import useSearchModalStore from '@/store/useSearchModalStore';
|
|
import { useReportTabStore } from '@/store/useReportTabStore';
|
|
import ReportFilesModal from './reportFilesModal'; // 파일목록모달
|
|
|
|
|
|
// 하수도정비 기본계획 보고서
|
|
export const reportTableHeadlist = [
|
|
{ id: "no", title: "NO" },
|
|
{ id: "sido", title: "시도" },
|
|
{ id: "sigungu", title: "시군구" },
|
|
{ id: "approvalYear", title: "승인년도" },
|
|
{ id: "plantZoneGubun", title: "구분" },
|
|
{ id: "reportGubun", title: "보고서구분" },
|
|
{ id: "fileName", title: "파일명" },
|
|
{ id: "regDate", title: "등록일자" },
|
|
];
|
|
interface ReportTabProps {
|
|
open?: boolean;
|
|
isSearch?: boolean;
|
|
ctpvNm?: string;
|
|
sggNm?: string;
|
|
}
|
|
|
|
const ReportTab: React.FC<ReportTabProps> = ({ open, isSearch = true, ctpvNm, sggNm }) => {
|
|
|
|
// 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');
|
|
const [sggCd, setSggCd] = useState('0');
|
|
const [reportGubun, setReportGubun] = useState("0");
|
|
const [clsfSystmSn, setClsfSystmSn] = useState(''); // 자료관 기본계획 일련번호
|
|
|
|
const {
|
|
tableData,
|
|
tableCount,
|
|
isLoading,
|
|
yearFilter,
|
|
curPage,
|
|
setTableData,
|
|
setTableCount,
|
|
setIsLoading,
|
|
setYearFilter,
|
|
setCurPage
|
|
} = useSearchModalStore();
|
|
const { ctpv, sgg } = useStore();
|
|
const {
|
|
isOpen: isReportFilesModalOpen,
|
|
setIsOpen: setIsReportFilesModalOpen,
|
|
dcTree: reportOriginData,
|
|
fetchDcTree
|
|
} = useReportTabStore(); // 모달 상태 관리 스토어
|
|
const { REPORT_TYPES, flattenFolders, addFileName } = useReportTab();
|
|
|
|
useEffect(() => {
|
|
fetchDcTree("기본계획"); // 하수정비기본계획 report
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
// filterParams 초기화
|
|
setCtpvCd('0');
|
|
setSggCd('0');
|
|
setReportGubun('0');
|
|
setYearFilter({ start: "", end: "" });
|
|
setShowTable(false);
|
|
setTableData([]);
|
|
setTableCount(0);
|
|
setCurPage(1); // 페이지를 1로 초기화
|
|
setIsResetPage(true); // 페이지를 초기화
|
|
setIsResetSelection(true); // 선택 초기화
|
|
}, [open]);
|
|
|
|
useEffect(() => {
|
|
const newCtpvCd = ctpv.find((n) => n.text === ctpvNm)?.val || '0';
|
|
const newSggCd = sgg.find((n) => n.text === sggNm)?.val || '0';
|
|
|
|
setCtpvCd(newCtpvCd);
|
|
setSggCd(newSggCd);
|
|
}, [ctpvNm, sggNm]);
|
|
|
|
// ctpvCd, sggCd가 업데이트된 후에 fetchFilteredData 실행
|
|
useEffect(() => {
|
|
if (!isSearch && ctpvCd && sggCd) {
|
|
fetchFilteredData();
|
|
}
|
|
}, [reportOriginData, ctpvCd, sggCd, isSearch]);
|
|
|
|
// 페이지 변경 시마다 데이터 fetch
|
|
useEffect(() => {
|
|
// showTable이 true일 때만 fetch (최초 렌더 방지)
|
|
setIsResetPage(false);
|
|
|
|
if (showTable) fetchFilteredData();
|
|
}, [curPage]);
|
|
|
|
const handleCtpvCdChange = (sidoId: string) => {
|
|
setCtpvCd(sidoId);
|
|
setSggCd('0');
|
|
}
|
|
|
|
// 조회 버튼 클릭 시 1페이지로 이동 및 데이터 초기화
|
|
const handleSearch = () => {
|
|
setCurPage(1); // 페이지를 1로 초기화
|
|
fetchFilteredData();
|
|
setIsResetPage(true); // 페이지를 초기화
|
|
setIsResetSelection(false); // 선택 초기화
|
|
setShowTable(true);
|
|
};
|
|
|
|
// 하수도정비기본계획 테이블 cellClick 핸들러
|
|
const handleReportCellClick = (index: number | undefined) => {
|
|
if (typeof index === "number" && tableData[index]) {
|
|
const reportData = tableData[index];
|
|
if (reportData.fileSn !== "") {
|
|
setClsfSystmSn(reportData.clsfSystmSn); // 자료관 기본계획 일련번호 설정
|
|
setIsReportFilesModalOpen(true); // 모달 열기
|
|
}
|
|
}
|
|
};
|
|
|
|
// 검색 API 호출 함수
|
|
const fetchFilteredData = async () => {
|
|
setIsLoading(true); // 로딩 시작
|
|
setIsResetSelection(true); // 선택 초기화
|
|
|
|
try {
|
|
const reportData = flattenFolders(reportOriginData);
|
|
let filterReportData = reportData;
|
|
|
|
if (ctpvCd !== '0') { // 시도검색
|
|
const sidoOption = ctpv.find((n) => n.val.trim() === ctpvCd.trim());
|
|
filterReportData = filterReportData.filter(item => item.sido === sidoOption?.text);
|
|
}
|
|
if (sggCd !== '0') { // 시군구검색
|
|
const sigunguOption = sgg.find((n) => String(n.val) === String(sggCd));
|
|
filterReportData = filterReportData.filter(item => item.sigungu === sigunguOption?.text);
|
|
}
|
|
if (reportGubun !== "0") { // 보고서구분검색
|
|
const reportGubunOption = REPORT_TYPES.find((n) => n.val === reportGubun);
|
|
filterReportData = filterReportData.filter(item => item.reportGubun === reportGubunOption?.text);
|
|
}
|
|
|
|
const { start, end } = yearFilter;
|
|
if (start && !end) //승인년도검색
|
|
filterReportData = filterReportData.filter(item => (item.approvalYear) ? item.approvalYear >= yearFilter.start + "년" : null);
|
|
else if (!start && end)
|
|
filterReportData = filterReportData.filter(item => (item.approvalYear) ? item.approvalYear <= yearFilter.end + "년" : null);
|
|
else if (start && end) {
|
|
filterReportData = filterReportData.filter(item => {
|
|
return (item.approvalYear) ? item.approvalYear >= yearFilter.start + "년" && item.approvalYear <= yearFilter.end + "년" : null;
|
|
});
|
|
}
|
|
// if (gubun !== "") { // 구분검색 (추후 사용할 수도 있을 것 같아 남겨둠)
|
|
// filterReportData = filterReportData.filter(item => (item.plantZoneGubun) ? String(item.plantZoneGubun).includes(String(gubun)) : null);
|
|
// }
|
|
|
|
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 }));
|
|
|
|
addFileName(filterReportData).then(data => {
|
|
filterReportData = data;
|
|
setTableData(filterReportData || []);
|
|
setIsResetSelection(false); // 데이터 설정 후 선택 초기화 해제
|
|
});
|
|
|
|
setTableData(filterReportData || []);
|
|
setShowTable(true);
|
|
} catch (err) {
|
|
console.error('API ERROR:', err);
|
|
setTableData([]);
|
|
setIsResetSelection(false); // 에러 시에도 선택 초기화 해제
|
|
} finally {
|
|
setIsLoading(false); // 로딩 종료
|
|
}
|
|
};
|
|
|
|
|
|
return (
|
|
<>
|
|
<div className={`relative flex flex-col border rounded-lg p-5 w-full ${isSearch ? "h-[10vh]" : "h-[6vh]"}`}>
|
|
{isSearch && (
|
|
<>
|
|
<div className="flex items-center mb-3">
|
|
<CtpvSggPrcsFcltSelectBox
|
|
sido={ctpvCd}
|
|
sigungu={sggCd}
|
|
onSidoChange={(sidoId: string) => { handleCtpvCdChange(sidoId) }}
|
|
onSigunguChange={setSggCd}
|
|
setIsLoading={setIsLoading}
|
|
/>
|
|
</div>
|
|
</>
|
|
)}
|
|
<div className="flex items-center mb-6">
|
|
<Label className="text-xl text-gray-600 font-medium whitespace-nowrap mr-2">보고서 구분</Label>
|
|
<SelectBox className="w-44 mr-8"
|
|
selectClass="rounded-lg bg-white"
|
|
label=""
|
|
selectArray={[...REPORT_TYPES]}
|
|
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}
|
|
onChange={range => setYearFilter({ ...yearFilter, start: range.start, end: range.end })}
|
|
startLabel="승인년도" endLabel=""
|
|
/>
|
|
{/* 조회 버튼 */}
|
|
<div className="flex-1 flex justify-end">
|
|
<Button className="text-base py-2" onClick={handleSearch}>조회</Button>
|
|
</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={reportTableHeadlist}
|
|
pageSize={10}
|
|
total={tableCount}
|
|
cellClick={handleReportCellClick}
|
|
onPageChange={setCurPage}
|
|
resetPageNo={isResetPage}
|
|
resetSelection={isResetSelection}
|
|
/>
|
|
</div>
|
|
</div>
|
|
{/* 모달 컴포넌트 */}
|
|
{isReportFilesModalOpen && <ReportFilesModal clsfSystmSn={clsfSystmSn} />}
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default React.memo(ReportTab); |