diff --git a/apps/sn/app/(map-layout)/map/SearchModal.tsx b/apps/sn/app/(map-layout)/map/SearchModal.tsx index 35c5a445a..96e81da93 100644 --- a/apps/sn/app/(map-layout)/map/SearchModal.tsx +++ b/apps/sn/app/(map-layout)/map/SearchModal.tsx @@ -125,8 +125,6 @@ const SearchModal: React.FC = ({ // 로딩바 const [isLoading, setIsLoading] = useState(false); - // 상세 정보 조회를 위한 상태 추가 - const [popupFeature, setPopupFeature] = useState(null); const sidos = useCityCd(); const siguns = useSggCd(); @@ -173,7 +171,7 @@ const SearchModal: React.FC = ({ }; // cellClick 함수 타입 지정 - const handleCellClick = (index?: number, headId?: string) => { + const handleCellClick = (index?: number) => { if (typeof index === "number") { onFeatureSelect(tableData[index % 10]); // 10은 page size 하드 코딩한 숫자 onOpenChange(false); // SearchModal 닫기 @@ -339,6 +337,7 @@ const SearchModal: React.FC = ({ return cqlFilters; }; + // 하수처리시설용 CQL 필터 생성 const facilityCqlFilters = () => { const cqlFilters: string[] = []; @@ -367,6 +366,7 @@ const SearchModal: React.FC = ({ if (변수1) cqlFilters.push(`swtrmt_fclt_nm ILIKE '%${변수1}%'`); return cqlFilters; }; + // 하수처리구역용 CQL 필터 생성 const zoneCqlFilters = () => { const cqlFilters: string[] = []; @@ -376,6 +376,7 @@ const SearchModal: React.FC = ({ if (변수2) cqlFilters.push(`swtrmt_fclt_nm ILIKE '%${변수2}%'`); return cqlFilters; }; + // 하수맨홀용 CQL 필터 생성 const mhCqlFilters = () => { const cqlFilters: string[] = []; @@ -404,6 +405,7 @@ const SearchModal: React.FC = ({ return cqlFilters; }; + // 하수펌프장용 CQL 필터 생성 const psmtnCqlFilters = () => { const cqlFilters: string[] = []; @@ -437,6 +439,7 @@ const SearchModal: React.FC = ({ return cqlFilters; }; + // 우수토실용 CQL 필터 생성 const otflCqlFilters = () => { const cqlFilters: string[] = []; @@ -466,6 +469,7 @@ const SearchModal: React.FC = ({ return cqlFilters; }; + // 운영정보용 CQL 필터 생성 const operationCqlFilters = () => { const cqlFilters: string[] = []; @@ -487,6 +491,7 @@ const SearchModal: React.FC = ({ ); return cqlFilters; }; + // 하수도정비기본계획보고서용 CQL 필터 생성 const reportCqlFilters = () => { const cqlFilters: string[] = []; @@ -1092,7 +1097,6 @@ const SearchModal: React.FC = ({ { - const resultCount = tableData.length; const label = activeTab === "basic" ? BASIC_TYPES.find((type) => type.val === basicType)?.text diff --git a/apps/sn/app/(map-layout)/map/components/common/index.ts b/apps/sn/app/(map-layout)/map/components/common/index.ts index e71e8ba3f..055c7062a 100644 --- a/apps/sn/app/(map-layout)/map/components/common/index.ts +++ b/apps/sn/app/(map-layout)/map/components/common/index.ts @@ -9,7 +9,7 @@ type LayerComponentProps = { featureData: any; isMainSsc?: boolean; isDrtwSewagePump?: boolean; - additionalData?: any; + rainMeasureData?: any; onSaveClick?: () => void; onFormDataChange?: (data: any) => void; // 추가 }; diff --git a/apps/sn/app/(map-layout)/map/components/common/table.ts b/apps/sn/app/(map-layout)/map/components/common/table.ts index 045b8db59..0b89ad652 100644 --- a/apps/sn/app/(map-layout)/map/components/common/table.ts +++ b/apps/sn/app/(map-layout)/map/components/common/table.ts @@ -619,25 +619,25 @@ export const untrtSwgRainCol2 = [ // 중점우수토실 강우정보 (id값 틀릴 수 있음) export const untrtSwgRainCol3 = [ - { id: 'rain_begin_dt', title: '강우시작일시' }, - { id: 'rain_end_dt', title: '강우종료일시' }, - { id: 'precdng_dry', title: '선행건기(h)' }, - { id: 'rain_info', title: '강우정보' }, - { id: 'rain_fall', title: '누적강우량(mm)' }, - { id: 'cntnc_time', title: '지속시간(h)' }, - { id: 'rain_in', title: '강우강도(mm/h)' } + { id: 'rainBeginDt', title: '강우시작일시' }, + { id: 'rainEndDt', title: '강우종료일시' }, + { id: 'precdngDry', title: '선행건기(h)' }, + { id: 'rainInfo', title: '강우정보' }, + { id: 'rainFall', title: '누적강우량(mm)' }, + { id: 'cntncTime', title: '지속시간(h)' }, + { id: 'rainIn', title: '강우강도(mm/h)' } ]; // 중점우수토실 측정정보 (id값 틀릴 수 있음) export const untrtSwgRainCol4 = [ - { id: 'mesure_begin_dt', title: '측정시작일시' }, - { id: 'mesure_end_dt', title: '측정종료일시' }, - { id: 'mesure_intrvl', title: '측정간격(분)' }, - { id: 'mesure_mhrls_instl_lc', title: '측정기기 설치위치' }, - { id: 'flwmtr_combined', title: '유량계 종류 및 모델명' }, // 합쳐진 컬럼 - { id: 'qltwtr_begin_dt', title: '채수시작일시' }, - { id: 'qltwtr_end_dt', title: '채수종료일시' }, - { id: 'qltwtr_co', title: '채수횟수' } + { id: 'mesureBeginDt', title: '측정시작일시' }, + { id: 'mesureEndDt', title: '측정종료일시' }, + { id: 'mesureIntrvl', title: '측정간격(분)' }, + { id: 'mesureMhrlsInstlLc', title: '측정기기 설치위치' }, + { id: 'flwmtrCombined', title: '유량계 종류 및 모델명' }, // 합쳐진 컬럼 + { id: 'qltwtrBeginDt', title: '채수시작일시' }, + { id: 'qltwtrEndDt', title: '채수종료일시' }, + { id: 'qltwtrCo', title: '채수횟수' } ]; // 중점우수토실 수량/수질측정 정보 diff --git a/apps/sn/app/(map-layout)/map/components/popupComponent/Ssc.tsx b/apps/sn/app/(map-layout)/map/components/popupComponent/Ssc.tsx index 2c5a9beab..76c6d806e 100644 --- a/apps/sn/app/(map-layout)/map/components/popupComponent/Ssc.tsx +++ b/apps/sn/app/(map-layout)/map/components/popupComponent/Ssc.tsx @@ -1,27 +1,31 @@ // 우수토실 import { UiModal, UiTable } from '@common/business_components/ui'; import { Button, TableHead, TableRow } from '@common/components/ui'; -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useCallback } from 'react'; import { untrtSwgRainCol3, untrtSwgRainCol4, untrtSwgRainCol5 } from '../common/table'; import { VerticalTable } from '@/components/ui/verticalTable'; -import LoadingOverlay from '@/components/ui/LoadingBar'; +import { getData } from "@/components/lib/api"; +import { formatDisplayData } from '@/components/lib/utils/formatDisplayValue'; const inputColumn = [ { id: "prrtymngYn", title: "중점여부", type: "select", options: ["중점우수토실", "우수토실"] }, { id: "swtrmtZoneCd", title: "처리구역", type: "input" }, ]; -const Ssc = ({ featureData, isMainSsc, additionalData, onSaveClick }: { +const Ssc = ({ featureData, isMainSsc, rainMeasureData, onSaveClick }: { featureData: any, isMainSsc?: boolean, - additionalData?: any, + rainMeasureData?: any, onSaveClick?: () => void }) => { const [isModalOpen, setIsModalOpen] = useState(false); const [isDataLoaded, setIsDataLoaded] = useState(false); const [formData, setFormData] = useState({}); - console.log('featureData data received:', featureData); // 추가 데이터 확인용 - console.log('Additional data received:', additionalData); // 추가 데이터 확인용 + const [mntrngsData, setMntrngsData] = useState(); // 수량/수질 측정 정보 + const [selectedRowIndex, setSelectedRowIndex] = useState(-1); + + // console.log('featureData data received:', featureData); // 추가 데이터 확인용 + // console.log('rainMeasureData received:', rainMeasureData); // 추가 데이터 확인용 const openModal = () => { setIsModalOpen(true); @@ -54,17 +58,75 @@ const Ssc = ({ featureData, isMainSsc, additionalData, onSaveClick }: { } }; + // 테이블 셀 클릭 핸들러 추가 + const handleCellClick = async (index: number | undefined, headId: string) => { + if (index !== undefined && rainMeasureData) { + const data = await fetchMntrngsData(rainMeasureData[index].adstDstrCd, rainMeasureData[index].manageNo); + + setMntrngsData(data); + setSelectedRowIndex(index); + } + }; + + // 🎯 추가 데이터 API 호출 함수 (독립적으로 실행) + const fetchMntrngsData = useCallback(async (adstDstrCd: string, manageNo: string) => { + if (!adstDstrCd || !manageNo) { + console.warn('추가 데이터 호출 실패: 필수 매개변수 누락', { adstDstrCd, manageNo }); + return []; + } + + try { + const queryParams = new URLSearchParams({ + pageable: `{}`, + }); + + console.log(`SSC 추가 데이터 API 호출 시작: v1/onsemiro/swgRain?${queryParams.toString()}`); + + const result = await getData(`v1/onsemiro/swgRain/${adstDstrCd}/${manageNo}/mntrngs?${queryParams.toString()}`); + + console.log('SSC 추가 데이터 API 호출 성공:', result); + + // 🎯 result가 페이지네이션 객체인 경우 content 배열 추출 + let dataArray: any[] = []; + if (result && typeof result === 'object' && 'content' in result && Array.isArray(result.content)) { + dataArray = result.content; + } else if (Array.isArray(result)) { + dataArray = result; + } else { + console.warn('예상치 못한 API 응답 형식:', result); + return []; + } + + // 🎯 각 아이템을 전처리하여 반환 + const processedDataArray = dataArray.map((data: any) => { + // 🎯 formatDisplayData로 기본 포맷팅 적용 + const formattedItem = formatDisplayData(data); + + // 🎯 추가 데이터 조합 + return { + ...formattedItem, + }; + }); + + console.log('processedDataArray', processedDataArray); + + return processedDataArray; + } catch (error) { + console.error('추가 데이터 로딩 실패:', error); + return []; + } + }, []); + const renderModalBody = () => (
{/* 강우정보 테이블 */} {/* 커스텀 헤더 */} @@ -102,11 +167,10 @@ const Ssc = ({ featureData, isMainSsc, additionalData, onSaveClick }: { {/* 수량/수질측정 정보 테이블 */} void, const { gisMap } = useGisContext() as unknown as { checkedLayerIds: React.MutableRefObject; treeData: { lyrSn: number }[] | null; gisCore: any; gisMap: any; }; const [layerNm, setLayerNm] = useState(""); // 레이어 이름 상태 const [tableData, setTableData] = useState(null); // 피처 데이터 + const [rainMeasureData, setRainMeasureData] = useState(false); // 강우/측정 측정 정보 const [mntrngsData, setMntrngsData] = useState(false); // 수량/수질 측정 정보 const [headlist, setHeadlist] = useState<{ id: string; title: string; }[]>([]); // 컬럼리스트 변경상태 const [headlist2, setHeadlist2] = useState<{ id: string; title: string; }[]>([]); // 컬럼리스트 변경상태 @@ -27,13 +28,10 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void, const [isModalOpen, setIsModalOpen] = useState(false); // 모달 열기 상태 const [isMainSsc, setIsMainSsc] = useState(false); // 중점우수토실 여부 const [isDrtwSewagePump, setIsDrtwSewagePump] = useState(false); // 오수펌프장 여부 - const [isLoading, setIsLoading] = useState(false); - // 🎯 추가 데이터 로딩 상태 분리 - const [isAdditionalDataLoading, setIsAdditionalDataLoading] = useState(false); - const [additionalDataError, setAdditionalDataError] = useState(null); // 🎯 InfilChart 및 RdiiChart 관련 상태 추가 const { setIsOpen: setIsModalInfil } = useInfilChartModalStore(); const { setIsOpen: setIsModalRdii } = useRdiiChartModalStore(); + const [curPage, setCurPage] = useState(1); // 현재 페이지 상태 // 🎯 관로관리번호 추출 const pplMngNo = featureData?.values_?.fclt_mng_no || ""; @@ -44,41 +42,13 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void, : featureData.fid; console.log('featureId', featureId); - // 🎯 추가 데이터 API 호출 함수 (독립적으로 실행) - const fetchAdditionalData = useCallback(async (adstDstrCd: string, manageNo: string) => { - if (!adstDstrCd || !manageNo) { - console.warn('추가 데이터 호출 실패: 필수 매개변수 누락', { adstDstrCd, manageNo }); - return []; - } - - setIsAdditionalDataLoading(true); - setAdditionalDataError(null); - - try { - console.log(`추가 데이터 API 호출 시작: v1/onsemiro/swgRain/${adstDstrCd}/${manageNo}/mntrngs`); - - const result = getData(`v1/onsemiro/swgRain/${adstDstrCd}/${manageNo}/mntrngs`); - - console.log('추가 데이터 API 호출 성공:', result); - return result || []; - } catch (error) { - console.error('추가 데이터 로딩 실패:', error); - setAdditionalDataError(error instanceof Error ? error.message : '알 수 없는 오류'); - return []; - } finally { - setIsAdditionalDataLoading(false); - } - }, []); - // 🎯 데이터 전처리 메모이제이션 const processedData = useMemo(() => { if (!featureData?.values_) return null; - const data = featureData.values_; return { - ...data, - discharge_pipe_spec: `직경: ${data.pipe_diameter || '-'} / 높이: ${data.pipe_hg || '-'} / 길이: ${data.pipe_lt || '-'}`, - flwmtr_combined: `${data.flwmtr_mesure_tp_etc || '-'} / ${data.flwmtr_knd || '-'}` + ...featureData.values_, + discharge_pipe_spec: `직경: ${featureData.values_.pipe_diameter || '-'} / 높이: ${featureData.values_.pipe_hg || '-'} / 길이: ${featureData.values_.pipe_lt || '-'}` }; }, [featureData?.values_]); @@ -86,7 +56,7 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void, const getSelectedLayers = () => { let result = null; const layers = gisMap.getLayers().getArray(); // gisMap에서 모든 레이어 가져오기 - let featureLayer: { get: (key: string) => any } = { get: (key: string) => null }; // 피처가 속한 레이어를 찾기 위한 변수 + let featureLayer: { get: (key: string) => any }; // 피처가 속한 레이어를 찾기 위한 변수 layers.forEach((layer: any) => { const source = layer.getSource(); if (source && source.hasFeature && featureData @@ -148,7 +118,7 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void, setLayerNm(selectedLayerName); // 상태 업데이트 console.log('gis data', featureData.values_); - let data = featureData.values_; + const data = featureData.values_; // 하수맨홀인 경우 용도코드를 텍스트로 변환 if (selectedLayerName === '하수맨홀' && data) { @@ -162,7 +132,7 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void, } // 🎯 기본 데이터 포맷팅 - let formattedData = formatDisplayData(data); + const formattedData = formatDisplayData(data); setfetchData(formattedData); setTableData(formattedData); @@ -187,18 +157,18 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void, setfetchData([processedFormattedData]) setTableData(processedFormattedData); - setIsLoading(false); // 기본 렌더링 완료 + // setIsLoading(false); // 기본 렌더링 완료 - // 수량/수질측정 정보 데이터 호출 - if (data?.adst_dstr_cd && data?.manage_no) { - fetchAdditionalData(data.adst_dstr_cd, data.manage_no) + // 강우/측정 정보 데이터 호출 + if (data?.chmbr_xcnts && data?.chmbr_ydnts) { + fetchRainMeasureData(data.chmbr_xcnts, data.chmbr_ydnts) .then(response => { console.log('additionalResponse', response); - setMntrngsData(response || []); + setRainMeasureData(response || []); }) .catch(error => { console.error('추가 데이터 로딩 실패:', error); - setMntrngsData(false); + setRainMeasureData(false); }); } break; @@ -231,7 +201,7 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void, // API 엔드포인트 선택 let apiEndpoint = ''; - let getFeatureId = featureId.toString().split('.').pop() || null; + const getFeatureId = featureId.toString().split('.').pop() || null; if (selectedLayerName === '하수관로') apiEndpoint = `v1/fcltppl/${getFeatureId}`; else if (selectedLayerName === '우수토실') apiEndpoint = `v1/fcltstofcbr/${getFeatureId}`; else if (selectedLayerName === '오수펌프' || selectedLayerName === '빗물펌프') apiEndpoint = `v1/fcltpump/${getFeatureId}`; @@ -289,25 +259,75 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void, fetchData(); }, [featureId, processedData]); + // 🎯 추가 데이터 API 호출 함수 (독립적으로 실행) + const fetchRainMeasureData = useCallback(async (chmbrXcnts: string, chmbrYdnts: string) => { + if (!chmbrXcnts || !chmbrYdnts) { + console.warn('추가 데이터 호출 실패: 필수 매개변수 누락', { chmbrXcnts, chmbrYdnts }); + return []; + } + + try { + const queryParams = new URLSearchParams({ + chmbrXcnts: chmbrXcnts, + chmbrYdnts: chmbrYdnts, + pageable: `{}`, + }); + + console.log(`추가 데이터 API 호출 시작: v1/onsemiro/getSwgRainsByGeomety?${queryParams.toString()}`); + + const result = await getData(`v1/onsemiro/getSwgRainsByGeomety?${queryParams.toString()}`); + + console.log('추가 데이터 API 호출 성공:', result); + + // 🎯 result가 페이지네이션 객체인 경우 content 배열 추출 + let dataArray: any[] = []; + if (result && typeof result === 'object' && 'content' in result && Array.isArray(result.content)) { + dataArray = result.content; + } else if (Array.isArray(result)) { + dataArray = result; + } else { + console.warn('예상치 못한 API 응답 형식:', result); + return []; + } + + // 🎯 각 아이템을 전처리하여 반환 + const processedDataArray = dataArray.map((data: any) => { + // 🎯 formatDisplayData로 기본 포맷팅 적용 + const formattedItem = formatDisplayData(data); + + // 🎯 추가 데이터 조합 + return { + ...formattedItem, + flwmtrCombined: `${data.flwmtrMesureTpEtc || '-'} / ${data.flwmtrKnd || '-'}`, + }; + }); + + return processedDataArray; + } catch (error) { + console.error('추가 데이터 로딩 실패:', error); + return []; + } + }, []); + // 🎯 추가 데이터는 별도 useEffect로 비동기 로딩 useEffect(() => { if (layerNm === '중점우수토실' && featureData?.values_) { const data = featureData.values_; - if (data?.adst_dstr_cd && data?.manage_no) { + if (data?.chmbr_xcnts && data?.chmbr_ydnts) { // 🎯 추가 데이터를 비동기로 로딩 (UI 블로킹 없음) - fetchAdditionalData(data.adst_dstr_cd, data.manage_no) + fetchRainMeasureData(data.chmbr_xcnts, data.chmbr_ydnts) .then(response => { console.log('추가 데이터 로딩 완료:', response); - setMntrngsData(response || []); + setRainMeasureData(response || []); }) .catch(error => { console.error('추가 데이터 최종 로딩 실패:', error); - setMntrngsData(false); + setRainMeasureData(false); }); } } - }, [layerNm, featureData?.values_, fetchAdditionalData]); + }, [layerNm, featureData?.values_, fetchRainMeasureData]); // 저장 시 모달 띄우기 const handleSaveClick = () => { @@ -434,9 +454,8 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void, headlist={headlist} pageSize={20} total={0} - children={undefined} cellClick={undefined} - onPageChange={function (page: number): void { + onPageChange={function (): void { throw new Error("Function not implemented."); }} // 두 번째 테이블 조건부 props @@ -451,10 +470,10 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void,
정보를 저장하시겠습니까?
{ContentComponent && } diff --git a/apps/sn/app/components/ui/verticalTable.tsx b/apps/sn/app/components/ui/verticalTable.tsx index 2f8143409..abdad03bb 100644 --- a/apps/sn/app/components/ui/verticalTable.tsx +++ b/apps/sn/app/components/ui/verticalTable.tsx @@ -9,7 +9,7 @@ type UiTableProps = { headlist: UiHeadProps[]; pageSize: number; total: number; - children: Record | undefined; + children?: Record | undefined; cellClick: ((index: number | undefined, headId: string) => void) | undefined; onPageChange: (page: number) => void; className?: string; @@ -48,21 +48,12 @@ export function VerticalTable({ pageSize, total, children, - cellClick, onPageChange, className = "", resetPageNo = false, isEditable = false, tableName2, - tableData2, headlist2, - pageSize2, - total2, - children2, - cellClick2, - onPageChange2, - className2 = "", - resetPageNo2 = false, isEditable2 = false, showSecondTable = false, showSaveButton = false,