import LoadingOverlay from '@/components/ui/LoadingBar'; 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 { MS_SWAGE_PPL_CODES, MS_SWAGE_PPL_USE_CODES, MS_SWAGE_MHL_CODES } from '../components/common/table'; import { getData } from "@/components/lib/api"; import BasicTab, { BASIC_TYPES } from './basicTab'; import OperationTab, { operationCleaningTableHeadlist, operationInspectionTableHeadlist, operationMaintenanceTableHeadlist, operationTableHeadlist, WORK_TYPES } from './operationTab'; import ReportTab from './reportTab'; import { formatDisplayData } from '@/components/lib/utils/formatDisplayValue'; import useStore from '../../../store/useStore'; const API_URL = `${process.env.NEXT_PUBLIC_SEWERAGE_API_URL}`; const TABS = [ { val: "basic", text: "기본정보" }, { val: "operation", text: "운영정보" }, { val: "report", text: "하수도정비기본계획보고서" }, ]; // 날짜 조합 함수 const getDateString = ( year: string, month: string, day: string, defaultDate: string, ) => { if (!year && !month && !day) return defaultDate; const y = year || defaultDate.slice(0, 4); const m = month || defaultDate.slice(4, 6); const d = day || defaultDate.slice(6, 8); return `${y}${m}${d}`; }; interface SearchModalProps { open: boolean; onOpenChange: (open: boolean) => void; onFeatureSelect: (row: any) => void; } const SearchModal: React.FC = ({ open, onOpenChange, onFeatureSelect, }) => { const [activeTab, setActiveTab] = useState(TABS[0]?.val ?? "basic"); const [basicType, setBasicType] = useState(BASIC_TYPES[0]?.val ?? ""); const [workType, setWorkType] = useState(WORK_TYPES[0]?.val ?? ""); const [showTable, setShowTable] = useState(false); const [tableData, setTableData] = useState([]); const [detailTableData, setDetailTableData] = useState([]); const [tableCount, setTableCount] = useState(0); const [detailTableCount, setDetailTableCount] = useState(0); const [curPage, setCurPage] = useState(1); const [detailCurPage, setDetailCurPage] = useState(1); const [isLoading, setIsLoading] = useState(false); const [isDetailLoading, setIsDetailLoading] = useState(false); const [isResetPage, setIsResetPage] = useState(false); const [isResetDetailPage, setIsResetDetailPage] = useState(false); const [isResetSelection, setIsResetSelection] = useState(false); const [isResetDetailSelection, setIsResetDetailSelection] = useState(false); const [detailBizId, setDetailBizId] = useState(0); const [city, setCity] = useState(0); const [ctpvName, setCtpvName] = useState(''); const [sgg, setSgg] = useState(0); const [sggName, setSggName] = useState(''); const [start1, setStart1] = useState(""); const [start2, setStart2] = useState(""); const [end1, setEnd1] = useState(""); const [end2, setEnd2] = useState(""); const [var1, setVar1] = useState(""); const [var2, setVar2] = useState(""); const [var3, setVar3] = useState(""); const [var4, setVar4] = useState(""); // 시작, 종료 const [yearFilter, setYearFilter] = useState({ start: "", end: "" }); const { BIZ_TY_CD } = useStore(); // 오늘 날짜 구하기 const today = new Date(); const todayYear = today.getFullYear().toString(); const todayMonth = (today.getMonth() + 1).toString().padStart(2, "0"); const todayDate = today.getDate().toString().padStart(2, "0"); const dynamicHeadlist: { id: string, title: string }[] = useMemo(() => { if (activeTab === 'basic') { if (basicType === 'tb_ms_swage_ppl') return MsSwagePplCol2; if (basicType === 'tb_il_swage_prcs_fclt') return SwtrmtFcltCol2; if (basicType === 'tb_ms_swage_prcs_zone') return SwtrmtPrcsZoneCol2; if (basicType === 'tb_ms_swage_mhl') return MsSwageMhlCol2; if (basicType === 'tb_ms_swage_pmstn') return ExclnSewagePumpCol2; if (basicType === 'tb_ms_stofcbr') return stofcbrCol2; return []; } if (activeTab === 'operation') return operationTableHeadlist; return []; }, [activeTab, basicType, workType]); const dynamicHeadlist2: { id: string, title: string }[] = useMemo(() => { if (activeTab === 'operation') { if (workType === WORK_TYPES[0]?.val) return operationMaintenanceTableHeadlist; if (workType === WORK_TYPES[1]?.val) return operationCleaningTableHeadlist; if (workType === WORK_TYPES[2]?.val) return operationInspectionTableHeadlist; } return []; }, [activeTab, workType]); useEffect(() => { // filterParams 초기화 setStart1(""); setStart2(""); setEnd1(""); setEnd2(""); setVar1(""); setVar2(""); setVar3(""); setVar4(""); setCity(0); setSgg(0); setYearFilter({ start: "", end: "", }); setShowTable(false); setTableData([]); setDetailTableData([]); setTableCount(0); setDetailTableCount(0); setCurPage(1); // 페이지를 1로 초기화 setDetailCurPage(1); // 페이지를 1로 초기화 setIsResetPage(true); // 페이지를 초기화 setIsResetDetailPage(true); // 디테일 페이지를 초기화 setIsResetSelection(true); // 선택 초기화 setIsResetDetailSelection(true); // 선택 초기화 // queryParams는 fetchFilteredData 내에서 매번 새로 생성되므로 별도 상태 초기화는 필요 없음 }, [activeTab, basicType, workType, open]); useEffect(() => { setActiveTab(TABS[0]?.val ?? "basic"); setBasicType(BASIC_TYPES[0]?.val ?? ""); setWorkType(WORK_TYPES[0]?.val ?? ""); setTableCount(0); setTableData([]); setDetailTableCount(0); setDetailTableData([]); }, [open]); // 페이지 변경 시마다 데이터 fetch useEffect(() => { // showTable이 true일 때만 fetch (최초 렌더 방지) setIsResetPage(false); setIsResetDetailPage(true); if (showTable) fetchFilteredData(); }, [curPage]); // 페이지 변경 시마다 데이터 fetch useEffect(() => { setIsResetDetailSelection(false); // showTable이 true일 때만 fetch (최초 렌더 방지) if (showTable) { handleOperationPageNation(Number(detailBizId)); } }, [detailCurPage]); // 검색 API 호출 함수 const fetchFilteredData = async () => { setIsLoading(true); // 로딩 시작 setIsResetSelection(true); // 선택 초기화 setIsResetDetailSelection(true); // 선택 초기화 try { let cqlFilters: string[] = []; if (activeTab === 'basic') { const queryParams = new URLSearchParams({ lyrTypeNm: basicType, page: `${curPage - 1}`, size: '10', sort: 'string', }); console.log('basicType', basicType); if (basicType === 'tb_ms_swage_prcs_zone') cqlFilters = zoneCqlFilters(); // 하수처리구역 else if (basicType === 'tb_il_swage_prcs_fclt') cqlFilters = facilityCqlFilters(); // 하수처리시설 else if (basicType === 'tb_ms_swage_ppl') cqlFilters = pplCqlFilters(); // 하수관로 else if (basicType === 'tb_ms_swage_mhl') cqlFilters = mhCqlFilters(); // 하수맨홀 else if (basicType === 'tb_ms_swage_pmstn') cqlFilters = psmtnCqlFilters(); // 하수펌프장 else if (basicType === 'tb_ms_stofcbr') cqlFilters = otflCqlFilters(); // 우수토실 if (cqlFilters.length > 0) queryParams.append('cqlFiter', cqlFilters.join(' and ')); console.log('API URL:', `${API_URL}/v1/fclts?${queryParams.toString()}`); const res = await axios.get(`${API_URL}/v1/fclts?${queryParams.toString()}`); console.log('필터링된 API 결과(개수):', res.data.count); console.log('필터링된 API 결과(데이터):', res.data.data); // 🎯 각 아이템을 개별적으로 포맷팅하면서 일련번호 추가 const dataWithRowNumber = (res.data.data || []).map((item: any, index: number) => { // 🎯 먼저 각 아이템을 포맷팅 const formattedItem = formatDisplayData(item); const matched = MS_SWAGE_MHL_CODES.find(code => code.val === item?.mqltCd?.trim()); // 추가 처리 및 일련번호 추가 return { ...formattedItem, rowNumber: (curPage - 1) * 10 + index + 1, // 1부터 시작하는 일련번호 추가 scsYn: item.scsYn === null || item.scsYn === undefined ? 'N' : item.scsYn, // scsYn이 null이면 'N'으로 변경 mqltCd: matched ? matched.text : item.mqltCd, }; }); setTableData(dataWithRowNumber); setTableCount(res.data.count || []); console.log('tableCount 상태 업데이트 후:', res.data.count); // tableCount 로그 추가 } else if (activeTab === 'operation') { const RequestDTO: Record = { page: `${curPage - 1}`, size: '5', }; if (var1) RequestDTO.bizInfoNm = var1; if (var2 != '0' && var2) RequestDTO.bizTypeCd = var2; // 수행기간 범위 const startDate = getDateString( yearFilter.start, '', '', "19000101", ); const endDate = getDateString( yearFilter.end, '', '', `${todayYear}${todayMonth}${todayDate}`, ); RequestDTO.fromflfmtEndYmd = startDate; RequestDTO.toflfmtEndYmd = endDate; // JSON.parse 사용으로 문자열 -> 배열 변환 RequestDTO.workTypes = JSON.parse(workType.replace(/'/g, '"')); const params = new URLSearchParams(RequestDTO).toString(); console.log('API URL:', `/v1/bizInfo?${params}`); const res = await getData(`v1/bizInfo?${params}`); console.log('필터링된 API 결과(개수):', res.totalElements); console.log('필터링된 API 결과(데이터):', res.content); const mappedContent = (res.content || []).map((item: any, index: number) => { console.log("item:", item); const formattedItem = formatDisplayData(item); const matched = BIZ_TY_CD.find(code => code.lwpoCd === item.bizTypeCd); return { ...formattedItem, rowNumber: (curPage - 1) * 5 + index + 1, // 1부터 시작하는 일련번호 추가 bizTypeCd: matched ? matched.lwpoCdNm : item.bizTypeCd, }; }); setTableData(mappedContent || []); setTableCount(res.totalElements || 0); setDetailTableData([]); setDetailTableCount(0); } setShowTable(true); } catch (err) { console.error('API ERROR:', err); setTableData([]); } finally { setIsLoading(false); // 로딩 종료 // setIsResetSelection(false); // setIsResetDetailSelection(false); } }; // 하수관로용 CQL 필터 생성 const pplCqlFilters = () => { const cqlFilters: string[] = []; // 추후 작성 예정 if (var1) cqlFilters.push(`usg_cd='${var1}'`); if (var2) cqlFilters.push(`mqlt_cd='${var2}'`); if (var3) cqlFilters.push(`pbadms_emd_cd='${var3}'`); // 설치년도 범위 const startDate = getDateString( yearFilter.start, '', '', "19000101", ); const endDate = getDateString( yearFilter.end, '', '', `${todayYear}${todayMonth}${todayDate}`, ); cqlFilters.push( `instl_ymd >= '${startDate}' and instl_ymd <= '${endDate}'`, ); // 관경(mm) 범위 if (start1 && end1) cqlFilters.push(`pdmt BETWEEN ${start1} AND ${end1}`); else if (start1) cqlFilters.push(`pdmt >= ${start1}`); else if (end1) cqlFilters.push(`pdmt <= ${end1}`); // 연장(m) 범위 if (start2 && end2) cqlFilters.push(`prlg BETWEEN ${start2} AND ${end2}`); else if (start2) cqlFilters.push(`prlg >= ${start2}`); else if (end2) cqlFilters.push(`prlg <= ${end2}`); return cqlFilters; }; // 하수처리시설용 CQL 필터 생성 const facilityCqlFilters = () => { const cqlFilters: string[] = []; // 가동개시일자 범위 const startDate = getDateString( yearFilter.start, '', '', "19000101", ); const endDate = getDateString( yearFilter.end, '', '', `${todayYear}${todayMonth}${todayDate}`, ); cqlFilters.push( `oprtng_strt_ymd >= '${startDate}' and oprtng_strt_ymd <= '${endDate}'`, ); // 시설용량 범위 if (start1 && end1) cqlFilters.push(`fclt_cpct BETWEEN ${start1} AND ${end1}`); else if (start1) cqlFilters.push(`fclt_cpct >= ${start1}`); else if (end1) cqlFilters.push(`fclt_cpct <= ${end1}`); // 시도 if (ctpvName) cqlFilters.push(`ctpv_nm ILIKE '%${ctpvName}%'`); if (sggName) cqlFilters.push(`sgg_nm ILIKE '%${sggName}%'`); // // 처리구역 면적 // if (start2 && end2) // cqlFilters.push(`prcsZoneAreaSum BETWEEN ${start2} AND ${end2}`); // else if (start2) cqlFilters.push(`prcsZoneAreaSum >= ${start2}`); // else if (end2) cqlFilters.push(`prcsZoneAreaSum <= ${end2}`); // // 하수처리시설명 // if (var1) cqlFilters.push(`swtrmt_fclt_nm ILIKE '%${var1}%'`); return cqlFilters; }; // 하수처리구역용 CQL 필터 생성 const zoneCqlFilters = () => { const cqlFilters: string[] = []; if (city) cqlFilters.push(`ctpv_cd='${city}'`); if (sgg) cqlFilters.push(`sgg_cd='${sgg}'`); if (var1) cqlFilters.push(`swtrmt_zone_nm ILIKE '%${var1}%'`); if (var2) cqlFilters.push(`swtrmt_fclt_nm ILIKE '%${var2}%'`); return cqlFilters; }; // 하수맨홀용 CQL 필터 생성 const mhCqlFilters = () => { const cqlFilters: string[] = []; // // 설치년도 범위 const startDate = getDateString( yearFilter.start, '', '', "19000101", ); const endDate = getDateString( yearFilter.end, '', '', `${todayYear}${todayMonth}${todayDate}`, ); cqlFilters.push( `instl_ymd >= '${startDate}' and instl_ymd <= '${endDate}'`, ); if (var1) cqlFilters.push(`usg_cd='${var1}'`); // 구경(mm) 범위 if (start1 && end1) cqlFilters.push(`swage_mh_diam BETWEEN ${start1} AND ${end1}`); else if (start1) cqlFilters.push(`swage_mh_diam >= ${start1}`); else if (end1) cqlFilters.push(`swage_mh_diam <= ${end1}`); return cqlFilters; }; // 하수펌프장용 CQL 필터 생성 const psmtnCqlFilters = () => { const cqlFilters: string[] = []; // 설치년도 범위 const startDate = getDateString( yearFilter.start, '', '', "19000101", ); const endDate = getDateString( yearFilter.end, '', '', `${todayYear}${todayMonth}${todayDate}`, ); cqlFilters.push( `instl_ymd >= '${startDate}' and instl_ymd <= '${endDate}'`, ); // 일일처리용량 범위 if (start1 && end1) cqlFilters.push(`daily_prcs_cpct BETWEEN ${start1} AND ${end1}`); else if (start1) cqlFilters.push(`daily_prcs_cpct >= ${start1}`); else if (end1) cqlFilters.push(`daily_prcs_cpct <= ${end1}`); // 양수능력 범위 if (start2 && end2) cqlFilters.push(`excln_wpmp_ablt BETWEEN ${start2} AND ${end2}`); else if (start2) cqlFilters.push(`excln_wpmp_ablt >= ${start2}`); else if (end2) cqlFilters.push(`excln_wpmp_ablt <= ${end2}`); return cqlFilters; }; // 우수토실용 CQL 필터 생성 const otflCqlFilters = () => { const cqlFilters: string[] = []; // 설치년도 범위 const startDate = getDateString( yearFilter.start, '', '', "19000101", ); const endDate = getDateString( yearFilter.end, '', '', `${todayYear}${todayMonth}${todayDate}`, ); cqlFilters.push( `instl_ymd >= '${startDate}' and instl_ymd <= '${endDate}'`, ); return cqlFilters; }; // 조회 버튼 클릭 시 1페이지로 이동 및 데이터 초기화 const handleSearch = () => { setCurPage(1); // 페이지를 1로 초기화 fetchFilteredData(); setIsResetPage(true); // 페이지를 초기화 setIsResetDetailPage(true); // 디테일 페이지를 초기화 setIsResetSelection(false); // 선택 초기화 setIsResetDetailSelection(false); // 선택 초기화 setShowTable(true); }; // 기본정보 테이블 cellClick 핸들러 const handleBasicCellClick = (index?: number | undefined, headId?: String) => { if (typeof index === "number") { onFeatureSelect(tableData[index % 10]); // 10은 page size 하드 코딩한 숫자 onOpenChange(false); // SearchModal 닫기 } }; // 운영정보 테이블 cellClick 핸들러 const handleOperationCellClick = async (index?: number | undefined, headId?: String) => { setIsResetDetailPage(true); // 페이지를 1로 초기화 setIsResetDetailSelection(true); // 선택 초기화 setIsResetDetailPage(true); // 디테일 페이지를 초기화 if (typeof index === "number") { setIsDetailLoading(true); setDetailBizId(tableData[index].bizId); setDetailCurPage(1); // 상세 페이지 초기화 try { const RequestDTO: Record = { page: `${detailCurPage - 1}`, size: '5', bizId: tableData[index % 5].bizId, }; RequestDTO.workTypes = JSON.parse(workType.replace(/'/g, '"')); const res = await getData(`v1/bizInfo/fclts?${new URLSearchParams(RequestDTO).toString()}`); console.log('운영정보 cellClick 데이터:', res); console.log('운영정보 cellClick.data:', res.data); console.log('운영정보 length:', res.data.length); const mappedData = (res.data || []).map((item: any, detailIndex: number) => { const formattedItem = formatDisplayData(item); const usgCdObj = MS_SWAGE_PPL_USE_CODES.find(code => code.val === item.usgCd.trim()); const mqltCdObj = MS_SWAGE_PPL_CODES.find(code => code.val === item.mqltCd.trim()); return { ...formattedItem, rowNumber: (detailCurPage - 1) * 5 + detailIndex + 1, usgCd: usgCdObj ? usgCdObj.text : item.usgCd, mqltCd: mqltCdObj ? mqltCdObj.text : item.mqltCd, }; }); console.log('운영정보 cellClick mappedData:', mappedData); setDetailTableData(mappedData); setDetailTableCount(res.count || 0); } catch (error) { console.error('운영정보 cellClick 에러:', error); } finally { setIsDetailLoading(false); setIsResetDetailPage(false); setIsResetDetailSelection(false); } } }; // 운영정보 디테일 테이블 페이지네이션 핸들러 const handleOperationPageNation = async (index?: number | undefined) => { if (typeof index === "number") { setIsDetailLoading(true); try { const RequestDTO: Record = { page: `${detailCurPage - 1}`, size: '5', bizId: String(detailBizId), }; const res = await getData(`v1/bizInfo/fclts?${new URLSearchParams(RequestDTO).toString()}`); const mappedData = (res.data || []).map((item: any, detailIndex: number) => { const formattedItem = formatDisplayData(item); const usgCdObj = MS_SWAGE_PPL_USE_CODES.find(code => code.val === item.usgCd.trim()); const mqltCdObj = MS_SWAGE_PPL_CODES.find(code => code.val === item.mqltCd.trim()); return { ...formattedItem, rowNumber: (detailCurPage - 1) * 5 + detailIndex + 1, usgCd: usgCdObj ? usgCdObj.text : item.usgCd, mqltCd: mqltCdObj ? mqltCdObj.text : item.mqltCd, }; }); setDetailTableData(mappedData); setDetailTableCount(res.count || 0); } catch (error) { console.error('운영정보 페이지네이션 에러:', error); } finally { setIsDetailLoading(false); } } }; const renderBody = () => ( <> {/* 탭 영역 */} ({ value: tab.val, label: tab.text }))} onValueChange={(value: string) => setActiveTab(value)} > {/* 각각의 탭 영역 */} {/* 기본정보 탭 */} {activeTab === 'basic' && ( )} {/* 운영정보 탭 */} {activeTab === 'operation' && ( )} {/* 하수도정비기본계획보고서 탭 */} {activeTab === 'report' && ( )} {/* */} {/* 📋 결과 리스트 */} {/* 하수도정비기본계획보고서 탭 */} {activeTab === 'basic' && (
{ const label = BASIC_TYPES.find((type) => type.val === basicType)?.text return (<>{label} 조회 결과 ({tableCount.toLocaleString('ko-KR')})건); })() } tableData={tableData} headlist={dynamicHeadlist} pageSize={10} total={tableCount} cellClick={handleBasicCellClick} onPageChange={setCurPage} resetPageNo={isResetPage} resetSelection={isResetSelection} />
)} {/* 운영정보 탭 */} {activeTab === 'operation' && ( <>
{ const label = WORK_TYPES.find((type) => type.val === workType)?.text return (<>{label} 사업 현황 ({tableCount.toLocaleString('ko-KR')})건); })() } tableData={tableData} headlist={dynamicHeadlist} pageSize={5} total={tableCount} cellClick={handleOperationCellClick} onPageChange={setCurPage} resetPageNo={isResetPage} resetSelection={isResetSelection} />
{/* 테이블2 영역 */}
{ const label = WORK_TYPES.find((type) => type.val === workType)?.text return (<>{label} 결과표 ({detailTableCount.toLocaleString('ko-KR')})건); })() } tableData={detailTableData} headlist={dynamicHeadlist2} pageSize={5} total={detailTableCount} cellClick={undefined} onPageChange={setDetailCurPage} resetPageNo={isResetDetailPage} resetSelection={isResetDetailSelection} />
)} ); return ( ); }; export default React.memo(SearchModal);