diff --git a/apps/sn/app/components/ui/modal.tsx b/apps/sn/app/components/ui/modal.tsx new file mode 100644 index 000000000..bc8085f71 --- /dev/null +++ b/apps/sn/app/components/ui/modal.tsx @@ -0,0 +1,31 @@ +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogClose, DialogOverlay } from '@common/components/ui' +import React from 'react' + +interface ModalProps { + isModalOpen: boolean; + setIsModalOpen: (open: boolean) => void; + title?: string; + children: React.ReactNode; + closeModal: () => void; +} + +const Modal: React.FC = ({ isModalOpen, setIsModalOpen, title, children, closeModal }) => { + return ( + + + + {title ? + {title} + : null} + {children} + + + + + + + + ) +} + +export default Modal \ No newline at end of file diff --git a/apps/sn/app/components/verticalTable.tsx b/apps/sn/app/components/ui/verticalTable.tsx similarity index 100% rename from apps/sn/app/components/verticalTable.tsx rename to apps/sn/app/components/ui/verticalTable.tsx diff --git a/apps/sn/app/map/SearchModal.tsx b/apps/sn/app/map/SearchModal.tsx new file mode 100644 index 000000000..84624e45b --- /dev/null +++ b/apps/sn/app/map/SearchModal.tsx @@ -0,0 +1,796 @@ +import React from 'react'; +import axios from 'axios'; +import { UiModal } from '@common/business_components/ui/dialog_ui'; +import { Button } from '@common/components'; +import { UiTable2 } from '@common/business_components/ui/table/index-2'; +import { CommonTableColumn } from "./table"; + + +const API_URL = `${process.env.NEXT_PUBLIC_SEWERAGE_API_URL}`; + +const TABS = [ + { key: 'basic', label: '기본정보' }, + { key: 'operation', label: '운영정보' }, + { key: 'report', label: '하수도정비기본계획보고서' }, +]; + +const WORK_TYPES = [ + { key: 'maintenance', label: '보수 및 갱신' }, + { key: 'cleaning', label: '청소 및 준설' }, + { key: 'inspection', label: '점검 및 진단' }, +]; + +const YEARS = Array.from({ length: 100 }, (_, i) => `${2025 - i}`); + +// 하수처리구역 테이블 헤더 리스트 +const swageZoneTableHeadlist = [ + // { id: 'ctpvCd', title: '시도코드' }, + { id: 'ctpvNm', title: '시도명' }, + // { id: 'sggCd', title: '시군구코드' }, + { id: 'sggNm', title: '시군구명' }, + // { id: 'swtrmtZoneSn', title: '하수처리구역번호' }, + { id: 'swtrmtZoneNm', title: '하수처리구역명' }, + { id: 'swtrmtFcltCd', title: '하수처리시설번호' }, + { id: 'swtrmtFcltNm', title: '하수처리시설명' }, +] +// 하수처리시설 테이블 헤더 리스트 +const swageFcltTableHeadlist = [ + { id: 'swtrmtFcltCd', title: '하수처리시설코드' }, + { id: 'swtrmtFcltNm', title: '하수처리시설명' }, + { id: 'fcltCpct', title: '시설용량' }, + { id: 'rgnExpln', title: '지역설명' }, + { id: 'cmcnYmd', title: '준공일자' }, + { id: 'oprtngStrtYmd', title: '가동개시일자' }, + { id: 'clsgYmd', title: '폐쇄일자' }, + { id: 'laeNm', title: '지자체명' }, + { id: 'ctpvNm', title: '시도명' }, + { id: 'sggNm', title: '시군구명' }, + { id: 'sareaNm', title: '권역명' }, + { id: 'lrgeSareaNm', title: '대권역명' }, + { id: 'rvrNm', title: '하천명' }, + { id: 'addr', title: '주소' }, + { id: 'daddr', title: '상세주소' }, + { id: 'rdnmAddr', title: '도로명주소' }, + { id: 'dtlRdnmAddr', title: '상세도로명주소' }, + { id: 'dschgWtstNm', title: '방류수계명' }, + { id: 'pllnArea', title: '부지면적' }, + { id: 'mngRprtvBzentyNm', title: '관리대행업제명' }, + { id: 'swtrmtPpltnCnt', title: '하수처리인구수' }, + { id: 'prcsZoneAreaSum', title: '처리구역면적합계' }, + { id: 'geom', title: 'geometry' }, + { id: 'xcrd', title: 'X좌표' }, + { id: 'ycrd', title: 'Y좌표' }, + { id: 'rprsMsrcCn', title: '대표공법내용' }, + { id: 'sttyCrtrBod', title: '법정기준생물화학적산소요구량' }, + { id: 'sttyCrtrToc', title: '법정기준TOC' }, + { id: 'sttyCrtrSs', title: '법정기준SS' }, + { id: 'sttyCrtrTn', title: '법정기준총질소' }, + { id: 'sttyCrtrTph', title: '법정기준총인' }, + { id: 'sttyCrtrClbclCnt', title: '법정기준대장균수' }, + { id: 'bsnNm', title: '유역명' }, +]; + +// '구분' 선택 목록 +const BASIC_TYPES = [ + { key: 'SEWER_PIPE', label: '하수관로' }, + { key: 'tb_il_swage_prcs_fclt', label: '하수처리시설' }, + { key: 'tb_ms_swage_prcs_zone', label: '하수처리구역' }, + { key: 'MANHOLE', label: '하수맨홀' }, + { key: 'WASTEWATER_PUMP', label: '오수펌프' }, + { key: 'RAINWATER_PUMP', label: '빗물펌프' }, + { key: 'OUTFALL', label: '우수토실' }, + { key: 'RETENTION_FACILITY', label: '하수저류시설' }, + { key: 'PRIORITY_OUTFALL', label: '중점우수토실' }, +]; + +const sidoList = [ + { id: 0, name: '선택' }, + { id: 26, name: '부산광역시' }, + { id: 28, name: '인천광역시' }, + { id: 30, name: '대전광역시' }, + { id: 41, name: '경기도' }, + { id: 42, name: '강원도' }, + { id: 44, name: '충청남도' }, + { id: 45, name: '전라북도' }, + { id: 46, name: '전라남도' }, + { id: 47, name: '경상북도' }, + { id: 48, name: '경상남도' }, +]; + +// 시도별 시군구 리스트 매핑 +const sigunguMap: Record = { + 26: [ + { id: 0, name: '선택' }, + // ...부산산광역시의 시군구 추가 + ], + 28: [ + { id: 0, name: '선택' }, + // ...인천광역시의 시군구 추가 + ], + 30: [ + { id: 0, name: '선택' }, + // ...대전광역시의 시군구 추가 + ], + 41: [ + { id: 0, name: '선택' }, + { id: 41820, name: '가평군' }, + { id: 41550, name: '안성시' }, + { id: 41830, name: '양평군' }, + { id: 41670, name: '여주시' }, + { id: 41500, name: '이천시' }, + { id: 41430, name: '의왕시' }, + { id: 41150, name: '의정부시' }, + { id: 41480, name: '파주시' }, + { id: 41650, name: '포천시' }, + // ...경기도의 시군구 추가 + ], + 42: [ + { id: 0, name: '선택' }, + { id: 42170, name: '동해시' }, + { id: 42110, name: '춘천시' }, + { id: 42720, name: '홍천군' }, + // ...강원도의 시군구 추가 + ], + 44: [ + { id: 0, name: '선택' }, + { id: 44770, name: '서천군' }, + // ...충청남도의 시군구 추가 + ], + 45: [ + { id: 0, name: '선택' }, + { id: 45130, name: '군산시' }, + { id: 45190, name: '남원시' }, + { id: 45730, name: '무주군' }, + { id: 45800, name: '부안군' }, + { id: 45770, name: '순창군' }, + { id: 45140, name: '익산시' }, + { id: 45750, name: '임실군' }, + { id: 45710, name: '완주군' }, + { id: 45110, name: '전주시' }, + // ...전라북도의 시군구 추가 + ], + 46: [ + { id: 0, name: '선택' }, + { id: 46810, name: "강진군" }, + { id: 46770, name: "고흥군" }, + { id: 46720, name: "곡성군" }, + { id: 46730, name: "구례군" }, + { id: 46230, name: "광양시" }, + { id: 46170, name: "나주시" }, + { id: 46110, name: "목포시" }, + { id: 46840, name: "무안군" }, + { id: 46150, name: "순천시" }, + { id: 46910, name: "신안군" }, + { id: 46130, name: "여수시" }, + { id: 46890, name: "완도군" }, + // ...전라남도의 시군구 추가 + ], + 47: [ + { id: 0, name: '선택' }, + { id: 47130, name: "경주시" }, + { id: 47830, name: "고령군" }, + { id: 47190, name: "구미시" }, + { id: 47720, name: "군위군" }, + { id: 47150, name: "김천시" }, + { id: 47280, name: "문경시" }, + { id: 47920, name: "봉화군" }, + { id: 47250, name: "상주시" }, + { id: 47770, name: "영덕군" }, + { id: 47760, name: "영양군" }, + { id: 47210, name: "영주시" }, + { id: 47900, name: "예천군" }, + { id: 47930, name: "울진군" }, + { id: 47730, name: "의성군" }, + { id: 47750, name: "청송군" }, + { id: 47850, name: "칠곡군" }, + { id: 47110, name: "포항시" }, + // ...경상북도의 시군구 추가 + ], + 48: [ + { id: 0, name: '선택' }, + { id: 48310, name: "거제시" }, + { id: 48250, name: "김해시" }, + { id: 48270, name: "밀양시" }, + { id: 48220, name: "통영시" }, + // ...경상남도의 시군구 추가 + ], + // ...나머지 시도별 시군구 추가 +}; + +const 처리구역List = ['선택', 'A', 'B']; +const 설치년도List = ['선택', ...YEARS]; + +// swageZoneTableHeadlist, tableData 타입 예시 (실제 타입에 맞게 수정) +type TableHead = { + key: string; + label: string; +}; +type TableRow = Record; + +interface SearchModalProps { + open: boolean; + onOpenChange: (open: boolean) => void; +} + +// filterParams 타입 정의 추가 +type FilterParams = { + swtrmt_zone_nm?: string; + ctpv_cd?: number; + sgg_cd?: number; + swtrmt_fclt_nm?: string; + // 추가 필터 속성들... +}; + +// 해당 년/월의 마지막 일 구하는 함수 +function getLastDayOfMonth(year: string, month: string) { + if (!year || !month) return 31; + const y = parseInt(year, 10); + const m = parseInt(month, 10); + if (isNaN(y) || isNaN(m)) return 31; + return new Date(y, m, 0).getDate(); +} +// 날짜 조합 함수 +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}`; +}; + +const SearchModal: React.FC = ({ open, onOpenChange }) => { + const [activeTab, setActiveTab] = React.useState('basic'); + const [workType, setWorkType] = React.useState(WORK_TYPES[0]?.key ?? ''); + const [startYear, setStartYear] = React.useState(''); + const [endYear, setEndYear] = React.useState(''); + const [showTable, setShowTable] = React.useState(false); + const [curPage, setCurPage] = React.useState(1); + const [basicType, setBasicType] = React.useState(BASIC_TYPES[0]?.key ?? ''); + const [하수처리구역명, set하수처리구역명] = React.useState(''); + const [하수처리시설명, set하수처리시설명] = React.useState(''); + const [시도, set시도] = React.useState(0); + const [시군구, set시군구] = React.useState(0); + const [처리구역, set처리구역] = React.useState(''); + const [설치년도1, set설치년도1] = React.useState(''); + const [설치년도2, set설치년도2] = React.useState(''); + const [tableData, setTableData] = React.useState([]); + const [시설용량최소, set시설용량최소] = React.useState(''); + const [시설용량최대, set시설용량최대] = React.useState(''); + // 날짜 상태 선언 + const [가동개시_시작_년, set가동개시_시작_년] = React.useState(''); + const [가동개시_시작_월, set가동개시_시작_월] = React.useState(''); + const [가동개시_시작_일, set가동개시_시작_일] = React.useState(''); + const [가동개시_종료_년, set가동개시_종료_년] = React.useState(''); + const [가동개시_종료_월, set가동개시_종료_월] = React.useState(''); + const [가동개시_종료_일, set가동개시_종료_일] = React.useState(''); + + // 오늘 날짜 구하기 + 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'); + + // UiTable2에 넘길 headlist를 BASIC_TYPES 값에 따라 분기 처리 + let dynamicHeadlist = swageZoneTableHeadlist; + if (basicType === 'tb_ms_swage_prcs_zone') { + dynamicHeadlist = swageZoneTableHeadlist; // 하수처리구역 + } else if (basicType === 'tb_il_swage_prcs_fclt') { + dynamicHeadlist = swageFcltTableHeadlist; // 하수처리시설 + } + + + + + // 선택된 시도에 따라 시군구 리스트 가져오기 + const selectedSigunguList = sigunguMap[시도] || [{ id: 0, name: '선택' }]; + + // 조회 버튼 클릭 시 1페이지로 이동하도록 수정 + const handleSearch = () => { + setCurPage(1); // 페이지를 1로 초기화 + fetchFilteredData(); + setShowTable(true); + }; + + // 선택된 필터 값을 저장할 상태 추가 + const [filterParams, setFilterParams] = React.useState({}); + + // cellClick 함수 타입 지정 + const handleCellClick = (row: TableRow, col: TableHead) => { + // 원하는 동작 구현 + console.log(row, col); + }; + + const modalMinWidth = 'min-w-[900px]'; + const modalMaxWidth = 'max-w-6xl'; + + // 모달 높이 동적 계산 + const contentRef = React.useRef(null); + const [dynamicHeight, setDynamicHeight] = React.useState(undefined); + + // 구분(basicType)이 바뀔 때마다 queryParams, filterParams 초기화 + React.useEffect(() => { + // filterParams 초기화 + setFilterParams({}); + set시설용량최소(''); + set시설용량최대(''); + set가동개시_시작_년(''); + set가동개시_시작_월(''); + set가동개시_시작_일(''); + set가동개시_종료_년(''); + set가동개시_종료_월(''); + set가동개시_종료_일(''); + set하수처리구역명(''); + set하수처리시설명(''); + set시도(0); + set시군구(0); + set처리구역(''); + set설치년도1(''); + set설치년도2(''); + // queryParams는 fetchFilteredData 내에서 매번 새로 생성되므로 별도 상태 초기화는 필요 없음 + }, [basicType]); + + // 필터 값이 변경될 때 쿼리 파라미터 업데이트 + React.useEffect(() => { + const params: FilterParams = {}; + + if (하수처리구역명) { + params.swtrmt_zone_nm = 하수처리구역명; + } + + // 시도 선택 시 (0이 아닌 경우) + if (시도 !== 0) { + const selectedSido = sidoList.find(item => item.id === 시도); + if (selectedSido) { + params.ctpv_cd = selectedSido.id; + } + } + + // 시군구 선택 시 (0이 아닌 경우) + if (시군구 !== 0) { + const selectedSigungu = (sigunguMap[시도] || []).find((item: { id: number; name: string }) => item.id === 시군구); + if (selectedSigungu) { + params.sgg_cd = selectedSigungu.id; + } + } + + // 하수처리시설명이 있으면 추가 + if (하수처리시설명) { + params.swtrmt_fclt_nm = 하수처리시설명; + } + + setFilterParams(params); + }, [하수처리구역명, 시도, 시군구, 하수처리시설명]); + + + React.useEffect(() => { + if (contentRef.current) { + setDynamicHeight(contentRef.current.scrollHeight + 60); // 여유 padding + } + }, [activeTab, showTable, curPage, tableData.length]); + + + // 검색 API 호출 함수 + const fetchFilteredData = () => { + const queryParams = new URLSearchParams({ + lyrTypeNm: basicType + }); + + const cqlFilters: string[] = []; + + // 구분별 분기 처리 + if (basicType === 'tb_ms_swage_prcs_zone') { + // 하수처리구역 + // 시도 + if (filterParams.ctpv_cd) { + cqlFilters.push(`ctpv_cd='${filterParams.ctpv_cd}'`); + } + // 시군구 + if (filterParams.sgg_cd) { + cqlFilters.push(`sgg_cd='${filterParams.sgg_cd}'`); + } + // 하수처리구역명 + if (filterParams.swtrmt_zone_nm) { + cqlFilters.push(`swtrmt_zone_nm ILIKE '%${filterParams.swtrmt_zone_nm}%'`); + } + // 하수처리시설명 + if (filterParams.swtrmt_fclt_nm) { + cqlFilters.push(`swtrmt_fclt_nm ILIKE '%${filterParams.swtrmt_fclt_nm}%'`); + } + } else if (basicType === 'tb_il_swage_prcs_fclt') { + // 하수처리시설 + // 가동개시일자 범위 필터 추가 + // 기본값: 19000101 ~ 오늘 + const startDate = getDateString(가동개시_시작_년, 가동개시_시작_월, 가동개시_시작_일, '19000101'); + const endDate = getDateString(가동개시_종료_년, 가동개시_종료_월, 가동개시_종료_일, `${todayYear}${todayMonth}${todayDate}`); + cqlFilters.push(`oprtng_strt_ymd >= '${startDate}' and oprtng_strt_ymd <= '${endDate}'`); + // 시설용량 범위(fclt_cpct) 필터 추가 + if (시설용량최소 && 시설용량최대) cqlFilters.push(`fclt_cpct BETWEEN ${시설용량최소} AND ${시설용량최대}`); + else if (시설용량최소) cqlFilters.push(`fclt_cpct >= ${시설용량최소}`); + else if (시설용량최대) cqlFilters.push(`fclt_cpct <= ${시설용량최대}`); + // 하수처리시설명 + if (filterParams.swtrmt_fclt_nm) { + cqlFilters.push(`swtrmt_fclt_nm ILIKE '%${filterParams.swtrmt_fclt_nm}%'`); + } + } else { + // 기타 구분(예: 하수관로, 맨홀 등) - 필요시 추가 + // 예시: 이름으로 검색 + if (filterParams.swtrmt_fclt_nm) { + cqlFilters.push(`swtrmt_fclt_nm ILIKE '%${filterParams.swtrmt_fclt_nm}%'`); + } + } + + // 파라미터 이름 오타 주의! (Swagger와 동일하게) + if (cqlFilters.length > 0) { + queryParams.append('cqlFiter', cqlFilters.join(' and ')); + } + + console.log('API URL:', `${API_URL}/v1/map/features?${queryParams.toString()}`); + axios.get(`${API_URL}/v1/map/features?${queryParams.toString()}`) + .then(res => { + // 결과값을 표에 사용할 수 있도록 상태에 저장 + console.log('필터링된 API 결과:', res.data.length); + console.log('필터링된 API 결과:', res.data); + setTableData(res.data || []); // res.data 구조에 따라 조정 필요 + setShowTable(true); + }) + .catch(err => { + console.error('API ERROR:', err); + setTableData([]); // 에러 시 빈 배열로 초기화 + }); + }; + + + const renderBody = () => ( + <> + {/* 탭 영역 */} +
+ {TABS.map(tab => ( + + ))} +
+ + {/* 기본정보 탭 */} + {activeTab === 'basic' && ( +
+ {/* 구분 */} +
+
+ + +
+
+ {/* 분기처리: 하수처리구역 */} + {basicType === 'tb_ms_swage_prcs_zone' && ( +
+
+ + + + +
+
+ + set하수처리구역명(e.target.value)} /> +
+
+ + set하수처리시설명(e.target.value)} /> +
+
+ )} + {/* 분기처리: 하수처리시설 */} + {basicType === 'tb_il_swage_prcs_fclt' && ( +
+ {/* 가동개시일자 */} +
+ + {/* 시작일자 */} + + {/* + */} + ~ + {/* 종료일자 */} + + {/* + */} +
+ {/* 시설용량 */} +
+ + { + // 숫자만 입력 허용 + const val = e.target.value.replace(/[^0-9]/g, ''); + set시설용량최소(val); + }} + /> + ~ + { + // 숫자만 입력 허용 + const val = e.target.value.replace(/[^0-9]/g, ''); + set시설용량최대(val); + }} + /> +
+ {/* 하수처리시설명 */} +
+ + set하수처리시설명(e.target.value)} /> +
+
+ )} + {/* 조회 버튼 중앙 정렬 */} +
+ +
+
+ )} + + {/* 운영정보 탭 */} + {activeTab === 'operation' && ( +
+
+ + + + + + ~ + +
+
+ +
+
+ )} + + {/* 하수도정비기본계획보고서서 탭 */} + {activeTab === 'report' && ( +
+ {/* 시도, 시군구, 하수처리구역 한 줄 */} +
+ + + + + + + +
+ {/* 승인년도 한 줄 */} +
+ + + ~ + +
+ {/* 조회 버튼 중앙 정렬 */} +
+ +
+
+ )} + + {/* 📋 결과 리스트 */} +
+ {/* 선택된 탭의 label */} +
+ {TABS.find(tab => tab.key === activeTab)?.label} +
+ {/* "▶" + 선택된 탭 label + " 조회 결과" */} +
+ ▶ {TABS.find(tab => tab.key === activeTab)?.label} 조회 결과 +
+ { }} onPageChange={setCurPage} + resetPageNo={false} + cur={curPage} + /> +
+ + ); + {/* + + ); */} + + return ( + + ); +} + +export default React.memo(SearchModal); diff --git a/apps/sn/app/map/components/InputSection.tsx b/apps/sn/app/map/components/InputSection.tsx new file mode 100644 index 000000000..6c411e2cf --- /dev/null +++ b/apps/sn/app/map/components/InputSection.tsx @@ -0,0 +1,50 @@ +import { Table, TableHeader, TableRow, TableHead, TableBody, TableCell } from "@common/components/ui"; +import React from "react"; + +interface InputSectionProps { + title: string; + data: { year: string; type: string }[]; + sectionKey: string; + onInputChange: (section: string, index: number, field: string, value: string) => void; +} + +const InputSection: React.FC = ({ title, data, sectionKey, onInputChange }) => { + return ( +
+ {/* 섹션 제목 */} +
+

{title}

+
+ {/* 테이블 */} +
+ + + + 년도 + 구분 + + + + {data.map((row, index) => ( + + + onInputChange(sectionKey, index, "year", e.target.value)} + className="w-full text-xl border-radius-0 px-3 py-2 text-base text-center leading-normal" + /> + + + {row.type} + + + ))} + +
+
+
+ ); +}; + +export default InputSection; \ No newline at end of file diff --git a/apps/sn/app/map/components/common/index.ts b/apps/sn/app/map/components/common/index.ts new file mode 100644 index 000000000..1af83674c --- /dev/null +++ b/apps/sn/app/map/components/common/index.ts @@ -0,0 +1,46 @@ +import smh from "../popupComponent/smh"; +import Ssc from "../popupComponent/Ssc"; +import SwtrmtFclt from "../popupComponent/SwtrmtFclt"; + +// 레이어 이름과 컴포넌트를 매핑 +const layerContentMap: { [key: string]: { component?: React.FC ; props?: any; } } = { + // TODO: gis데이터 추가되면 추가 작업 필요 + "하수처리시설": { + component: SwtrmtFclt, + }, + "하수처리구역": { }, + "중점우수토실":{component: Ssc,}, + "하수관로":{component: SwtrmtFclt,}, + "하수맨홀":{component: smh,}, +}; + +// 동적 컴포넌트 반환 함수 +export const getLayerContentComponent = (layerNm: string): React.FC => { + const component = layerContentMap[layerNm]; + if (!component || !component.component) { + // component가 없으면 null 반환 + return () => null; + } + return component.component; +}; +// 사용자 입력 필드 (보수 및 갱신, 청소 및 준설, 점검 및 진단) +export const handleInputChange = (section: string, + index: number, + field: string, + value: string, + dataMap: { + [key: string]: { + data: { year: string; type: string }[]; + setData: React.Dispatch>; + }; + } +) => { + const target = dataMap[section]; + if (!target) return; + + const updated = [...target.data]; + if (updated[index]) { + updated[index][field as keyof typeof updated[number]] = value; + } + target.setData(updated); +}; \ No newline at end of file diff --git a/apps/sn/app/map/components/common/table.ts b/apps/sn/app/map/components/common/table.ts new file mode 100644 index 000000000..a64e610d3 --- /dev/null +++ b/apps/sn/app/map/components/common/table.ts @@ -0,0 +1,51 @@ +// 하수처리시설 +export const SwtrmtFcltCol = [ + { id: 'swtrmtFcltNm', title: '하수처리시설명' }, + { id: 'fcltCpct', title: '시설용량' }, + { id: 'rgnExpln', title: '지역설명' }, + { id: 'cmcnYmd', title: '준공일자' }, + { id: 'oprtngStrtYmd', title: '가동개시일자' }, + { id: 'clsgYmd', title: '폐쇄일자' }, + { id: 'prcsMsrcCd', title: '처리공법코드' }, + { id: 'laeCd', title: '지자체코드' }, + { id: 'laeNm', title: '지자체명' }, + { id: 'ctpvNm', title: '시도명' }, + { id: 'sggNm', title: '시군구명' }, + { id: 'sareaCd', title: '권역코드' }, + { id: 'sareaNm', title: '권역명' }, + { id: 'lrgeSareaNm', title: '대권역명' }, + { id: 'rvrNm', title: '하천명' }, + { id: 'addr', title: '주소' }, + { id: 'daddr', title: '상세주소' }, + { id: 'rdnmAddr', title: '도로명주소' }, + { id: 'dtlRdnmAddr', title: '상세도로명주소' }, + { id: 'dschgWtstNm', title: '방류수계명' }, + { id: 'pllnArea', title: '부지면적' }, + { id: 'mngRprtvBzentyNm', title: '관리대행업제명' }, + { id: 'swtrmtPpltnCnt', title: '하수처리인구수' }, + { id: 'prcsZoneAreaSum', title: '처리구역면적합계' }, + { id: 'xcrd', title: 'X좌표' }, + { id: 'ycrd', title: 'Y좌표' }, + { id: 'oprtngSttsCd', title: '가동상태코드' }, + { id: 'rprsMsrcCn', title: '대표공법내용' }, + { id: 'sttyCrtrBod', title: '법정기준생물화학적산소요구량' }, + { id: 'sttyCrtrToc', title: '법정기준TOC' }, + { id: 'sttyCrtrSs', title: '법정기준SS' }, + { id: 'sttyCrtrTn', title: '법정기준총질소' }, + { id: 'sttyCrtrTph', title: '법정기준총인' }, + { id: 'sttyCrtrClbclCnt', title: '법정기준대장균수' }, + { id: 'bsnNm', title: '유역명' }, + { id: 'obsrvnSptCd', title: '관측지점코드' }, +]; + +// 하수처리구역 +export const SwtrmtPrcsZoneCol = [ + { id: 'swtrmtZoneSn', title: '하수처리구역 일련번호' }, + { id: 'swtrmtZoneNm', title: '하수처리구역명' }, + { id: 'swtrmtFcltCd', title: '하수처리시설코드' }, + { id: 'ctpvCd', title: '시도코드' }, + { id: 'ctpvNm', title: '시도명' }, + { id: 'sggCd', title: '시군구코드' }, + { id: 'sggNm', title: '시군구명' }, + { id: 'swtrmtFcltNm', title: '하수처리시설명' }, +] diff --git a/apps/sn/app/map/components/popupComponent/Ssc.tsx b/apps/sn/app/map/components/popupComponent/Ssc.tsx new file mode 100644 index 000000000..a6e783ac9 --- /dev/null +++ b/apps/sn/app/map/components/popupComponent/Ssc.tsx @@ -0,0 +1,66 @@ +// 우수토실 +import Modal from '@/components/ui/modal'; +import { VerticalTable } from '@/components/ui/verticalTable'; +import { UiTable } from '@common/business_components/ui'; +import { Button, TableHead, TableRow } from '@common/components/ui'; +import React from 'react' +const inputColumn = [ + { id: "guyeok", title: "처리구역", type: "select", options: ["구역1", "구역2"] }, + { id: "mainSscYn", title: "중점우수토실 대상여부", type: "select", options: ['Y', 'N'] }, +]; +const headlist = [ + { id: 'startrain', title: '강우시작일시' }, { id: 'endrain', title: '강우종료일시' }, { id: '선행건기', title: '선행건기(h)' }, { id: '강우정보', title: '강우정보' }, { id: '누적강우량', title: '누적강우량(mm)' }, { id: '지속시간', title: '지속시간(h)' }, { id: '강우강도', title: '강우강도(mm/h)' }, +] +const headlist3 = [{ id: 'nm', title: '회차' }, { id: 'date', title: '측정일자' }, { id: 'measure', title: '수량(m)' }, { id: 'bod', title: 'BOD(mg/L)' }, { id: 'toc', title: 'TOC(mg/L)' }, { id: '오염부하량', title: '오염부하량(kg)' }] +const Ssc = () => { + const [isModalOpen, setIsModalOpen] = React.useState(false); + const openModal = () => { + setIsModalOpen(true); + }; + const closeModal = () => { + setIsModalOpen(false); + }; + return ( +
+
+
+

중점우수토실 운영정보

+
+ + +
+ {/* 커스텀 헤더 */} + + + 수량 + + + 수질 + + + + 측정시작일시 + 측정종료일시 + 측정간격(분) + 측정기기설치위치 + 유량계 종류 및 모델명 + 채수시작일시 + 채수종료일시 + 채수횟수 + +
+
+
+
+ ) +} + +export default Ssc \ No newline at end of file diff --git a/apps/sn/app/map/components/popupComponent/SwtrmtFclt.tsx b/apps/sn/app/map/components/popupComponent/SwtrmtFclt.tsx new file mode 100644 index 000000000..b6d785414 --- /dev/null +++ b/apps/sn/app/map/components/popupComponent/SwtrmtFclt.tsx @@ -0,0 +1,45 @@ +import { VerticalTable } from '@/components/ui/verticalTable'; +import React, { useState } from 'react' +import InputSection from '../InputSection'; +import { handleInputChange } from '../common'; +// 하수처리시설 +// TODO: 컬럼에 보고서 엉 ? 이거 버튼누르면 팝업으로 상세화면 띄워주는 건지 확인 필요 +const inputColumn = [ + { id: "title", title: "설치주제", type: "select", options: ["BTL", "재정사업"] }, + { id: "guyeok", title: "처리구역", type: "select", options: ["구역1", "구역2"] }, + { id: "basu", title: "배수구역", type: "input" }, + { id: "bungu", title: "배수분구", type: "input" }, + { id: "wltjs", title: "지선/간선", type: "select", options: ["지선", "간선"] }, + { id: "flow", title: "흐름방식", type: "select", options: ["자연유하", "압송"] }, + { id: "place", title: "매설위치", type: "select", options: ["도로", "하상"] }, +]; +const SwtrmtFclt = () => { + const [repairData, setRepairData] = useState([ + { year: "2025", type: "개대체" }, + { year: "2025", type: "보수" }, + ]); + const [cleaningData, setCleaningData] = useState([ + { year: "2025", type: "청소" }, + { year: "2025", type: "준설" }, + ]); + const [inspectionData, setInspectionData] = useState([ + { year: "2025", type: "관로기술진단" }, + { year: "2025", type: "관로기술진단" }, + ]); + const dataMap = { + repair: { data: repairData, setData: setRepairData }, + cleaning: { data: cleaningData, setData: setCleaningData }, + inspection: { data: inspectionData, setData: setInspectionData }, + }; + return ( +
+ + handleInputChange(section, index, field, value, dataMap)} /> + handleInputChange(section, index, field, value, dataMap)} /> + handleInputChange(section, index, field, value, dataMap)} />
+ ) +} + +export default SwtrmtFclt \ No newline at end of file diff --git a/apps/sn/app/map/components/popupComponent/smh.tsx b/apps/sn/app/map/components/popupComponent/smh.tsx new file mode 100644 index 000000000..e693a28ce --- /dev/null +++ b/apps/sn/app/map/components/popupComponent/smh.tsx @@ -0,0 +1,21 @@ +// 하수맨홀 +import { VerticalTable } from '@/components/ui/verticalTable'; +import React from 'react' +const inputColumn = [ + { id: "guyeok", title: "처리구역", type: "select", options: ["구역1", "구역2"] }, + { id: "basu", title: "배수구역", type: "input" }, + { id: "bungu", title: "배수분구", type: "input" }, + { id: "place", title: "매설위치", type: "select", options: ["도로", "하상"] }, + {id:'spec', title:'규격', type:'input'}, +]; +const Smh = () => { + return ( +
+ +
+ ) +} + +export default Smh; \ No newline at end of file diff --git a/apps/sn/app/map/featureClickEventPopup.tsx b/apps/sn/app/map/featureClickEventPopup.tsx index 6dd573732..cc63caf76 100644 --- a/apps/sn/app/map/featureClickEventPopup.tsx +++ b/apps/sn/app/map/featureClickEventPopup.tsx @@ -1,24 +1,19 @@ 'use client' -import { VerticalTable } from "@/components/verticalTable"; +import { VerticalTable } from "@/components/ui/verticalTable"; import { Button, Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@common/components/ui"; -import { use, useEffect, useState } from "react"; -import { SwtrmtFcltCol } from "./table"; +import { useEffect, useState } from "react"; +import { SwtrmtFcltCol, SwtrmtPrcsZoneCol } from "./components/common/table"; import { useGisContext } from "@common/business_components/ui/map/Gis"; import { getData } from "@/components/lib/api"; -const inputColumn = [ - { id: "title", title: "설치주제", type: "select", options: ["BTL", "재정사업"] }, - { id: "guyeok", title: "처리구역", type: "select", options: ["구역1", "구역2"] }, - { id: "basu", title: "배수구역", type: "input" }, - { id: "bungu", title: "배수분구", type: "input" }, - { id: "wltjs", title: "지선/간선", type: "select", options: ["지선", "간선"] }, - { id: "flow", title: "흐름방식", type: "select", options: ["자연유하", "압송"] }, - { id: "place", title: "매설위치", type: "select", options: ["도로", "하상"] }, -]; +import { getLayerContentComponent } from "./components/common"; // TODO: const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void, featureData: any }) => { const { gisMap } = useGisContext() as unknown as { checkedLayerIds: React.MutableRefObject; treeData: { lyrSn: number }[] | null; gisCore: any; gisMap: any; }; - const featureId = featureData.getId(); // 피처의 속성 가져오기 + const featureId = featureData.getId(); // 피처의 id 가져오기 const [layerNm, setLayerNm] = useState(""); // 레이어 이름 상태 + const [tableData, setTableData] = useState(null); // 피처 데이터 상태 + const [headlist, setHeadlist] = useState<{ id: string; title: string; }[]>([]); // 컬럼리스트 변경상태 + let [buttonChk,setButtonChk] = useState(true) // 피쳐데이터의 현재 레이어 이름 가져오는 함수 const getSelectedLayers = () => { const layers = gisMap.getLayers().getArray(); // gisMap에서 모든 레이어 가져오기 @@ -31,88 +26,45 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void, }); return featureLayer.get('lyrNm'); // 레이어 이름 출력 }; - const [repairData, setRepairData] = useState([ - { year: "2025", type: "개대체" }, - { year: "2025", type: "보수" }, - ]); + useEffect(() => { + const fetchData = async () => { + if (featureId) { + const selectedLayerName = getSelectedLayers(); // 레이어 이름 가져오기 + setLayerNm(selectedLayerName); // 상태 업데이트 - const [cleaningData, setCleaningData] = useState([ - { year: "2025", type: "청소" }, - { year: "2025", type: "준설" }, - ]); - - const [inspectionData, setInspectionData] = useState([ - { year: "2025", type: "관로기술진단" }, - { year: "2025", type: "관로기술진단" }, - ]); - const handleInputChange = (section: string, index: number, field: string, value: string) => { - const updateData = (data: { year: string; type: string }[], setData: React.Dispatch>) => { - const updated = [...data]; - if (updated[index]) { - updated[index][field as keyof typeof updated[number]] = value; - } - setData(updated); - }; - - if (section === "repair") { - updateData(repairData, setRepairData); - } else if (section === "cleaning") { - updateData(cleaningData, setCleaningData); - } else if (section === "inspection") { - updateData(inspectionData, setInspectionData); + const data = await getData(`v1/map/features/${featureId}`); + switch (selectedLayerName) { + case '하수처리시설': + setHeadlist(SwtrmtFcltCol); + setButtonChk(false) + break; + case '하수처리구역': + setHeadlist(SwtrmtPrcsZoneCol) + setButtonChk(false) + break; + case '중점우수토실': + setButtonChk(false) + break; + case '우수토실': + setButtonChk(true) + break; + case '하수관로': + setButtonChk(true) + break; + case '하수맨홀': + setButtonChk(true) + break; + default: + break; + } + setTableData(data); + }; } - }; - async function transformFeatureDataToArray(featureId: any, keys: string[]) { - const data = await getData(`v1/map/features/${featureId}`); - // 필요한 키만 추출하여 새로운 객체 생성 - console.log("data", data); - const transformedData = keys.reduce((acc, key) => { - acc[key] = data[key] !== undefined ? data[key] : "N/A"; // 값이 없으면 "N/A"로 처리 - return acc; - }, {} as Record); - - // 배열로 감싸서 반환 - return [transformedData]; - } - const keys = SwtrmtFcltCol.map((column) => column.id); - const transformedFeatureData = transformFeatureDataToArray(featureId, keys); - useEffect(() => { setLayerNm(getSelectedLayers()) }, [featureId]); - const renderSection = (title: string, data: { year: string; type: string }[], sectionKey: string) => ( -
- {/* 섹션 제목 */} -
-

{title}

-
- {/* 테이블 */} -
- - - - 년도 - 구분 - - - - {data.map((row, index) => ( - - - handleInputChange(sectionKey, index, "year", e.target.value)} - className="w-full text-xl border-radius-0 px-3 py-2 text-base text-center leading-normal" - /> - - {row.type} - - ))} - -
-
-
- ); + fetchData(); + }, [featureId]); + const ContentComponent = layerNm ? getLayerContentComponent(layerNm) : null; return ( -