Files
frontend/apps/sn/app/map/SearchModal.tsx
T

1334 lines
60 KiB
TypeScript

import FeatureClickEventPopup from './featureClickEventPopup';
import React, { useState } 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 { UITab2 } from '@common/business_components/ui/tab/index-2';
import { SelectBox } from '@common/business_components/ui';
const API_URL = `${process.env.NEXT_PUBLIC_SEWERAGE_API_URL}`;
// 탭
const TABS = [
{ key: 'basic', label: '기본정보' },
{ key: 'operation', label: '운영정보' },
{ key: 'report', label: '하수도정비기본계획보고서' },
];
// 기본정보 '구분' 선택 목록
const BASIC_TYPES = [
{ key: 'tb_ms_swage_ppl', label: '하수관로' },
{ key: 'tb_il_swage_prcs_fclt', label: '하수처리시설' },
{ key: 'tb_ms_swage_prcs_zone', label: '하수처리구역' },
{ key: 'tb_ms_swage_mh', label: '하수맨홀' },
{ key: 'tb_ms_swage_psmtn_1', label: '오수펌프' },
{ key: 'tb_ms_swage_psmtn_2', label: '빗물펌프' },
{ key: 'tb_ms_otfl', label: '우수토실' },
];
// const BASIC_TYPES = Array.from(
// [
// { key: 'tb_ms_swage_ppl', label: '하수관로' },
// { key: 'tb_il_swage_prcs_fclt', label: '하수처리시설' },
// { key: 'tb_ms_swage_prcs_zone', label: '하수처리구역' },
// { key: 'tb_ms_swage_mh', label: '하수맨홀' },
// { key: 'tb_ms_swage_psmtn_1', label: '오수펌프' },
// { key: 'tb_ms_swage_psmtn_2', label: '빗물펌프' },
// { key: 'tb_ms_otfl', label: '우수토실' },
// ],
// (item) => ({ text: item.label, val: item.key })
// );
// 운영정보 '구분' 선택 목록
const WORK_TYPES = [
{ key: 'maintenance', label: '보수 및 갱신' },
{ key: 'cleaning', label: '청소 및 준설' },
{ key: 'inspection', label: '점검 및 진단' },
];
const YEARS = Array.from({ length: 100 }, (_, i) => `${2025 - i}`);
// 테이블 헤더 리스트
// 하수관로 (추후 작성 예정)
const swagePplTableHeadlist = [
{ id: 'pipeNumber', title: '관로번호' },
{ id: 'gisManageNumber', title: '관리번호(GIS)' },
{ id: 'managingInstitution', title: '관리기관' },
{ id: 'drawingNumber', title: '도엽번호' },
{ id: 'installationLocation', title: '설치위치' },
{ id: 'usage', title: '용도' },
{ id: 'pipeDiameter', title: '관경' },
{ id: 'pipeLength', title: '관로연장' },
{ id: 'pipeType', title: '관종' },
{ id: 'burialDepth', title: '매설심도' },
{ id: 'constructionYear', title: '개보수 년도' },
{ id: 'installationSubject', title: '설치 주체' },
{ id: 'area', title: '구역' },
{ id: 'division', title: '분구' },
{ id: 'alignmentType', title: '지선/간선 구분' },
{ id: 'flowType', title: '흐름 방식' },
{ id: 'boxCount', title: '박스 열수' },
{ id: 'height', title: '높이' },
{ id: 'width', title: '폭' },
{ id: 'startManholeNumber', title: '시점 맨홀번호' },
{ id: 'endManholeNumber', title: '종점 맨홀번호' },
{ id: 'startElevation', title: '시점관저고' },
{ id: 'endElevation', title: '종점관저고' },
]
// 하수처리구역
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 swageMhTableHeadlist = [
{ id: 'swtrmtFcltCd', title: '하수처리시설코드' },
];
// 운영정보
const operationTableHeadlist = [
{ id: 'no', title: 'NO' },
{ id: 'manageNumber', title: '관리번호' },
{ id: 'diagnosisYear', title: '진단년도' },
{ id: 'null1', title: '...' },
{ id: 'null2', title: '...' },
{ id: 'installationLocation', title: '매설위치' },
];
// 하수도정비 기본계획 보고서
const reportTableHeadlist = [
{ id: 'no', title: 'NO' },
{ id: 'approvalYear', title: '승인년도' },
{ id: 'sido', title: '시도' },
{ id: 'sigungu', title: '시군구' },
{ id: 'treatmentPlant', title: '하수처리장' },
{ id: 'treatmentZone', title: '하수처리구역' },
{ id: 'reportCount', title: '보고서(00건)' },
{ id: 'planMapCount', title: '하수도대장 평면도(00건)' },
{ id: 'regDate', title: '등록일자' },
];
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<number, { id: number; name: string }[]> = {
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'];
interface SearchModalProps {
open: boolean;
onOpenChange: (open: boolean) => void;
onFeatureSelect: (row: any) => void;
}
// 날짜 조합 함수
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<SearchModalProps> = ({ open, onOpenChange, onFeatureSelect }) => {
const [activeTab, setActiveTab] = useState(TABS[0]?.key ?? 'basic');
const [workType, setWorkType] = useState(WORK_TYPES[0]?.key ?? '');
const [showTable, setShowTable] = useState(false);
const [curPage, setCurPage] = useState(1);
const [basicType, setBasicType] = useState(BASIC_TYPES[0]?.key ?? '');
const [하수처리구역명, set하수처리구역명] = useState('');
const [하수처리시설명, set하수처리시설명] = useState('');
const [시도, set시도] = useState(0);
const [시군구, set시군구] = useState(0);
const [처리구역, set처리구역] = useState('');
const [tableData, setTableData] = useState<any[]>([]);
// 날짜 상태 선언
const [시작1, set시작1] = useState('');
const [시작2, set시작2] = useState('');
const [종료1, set종료1] = useState('');
const [종료2, set종료2] = useState('');
const [변수1, set변수1] = useState('');
const [변수2, set변수2] = useState('');
const [변수3, set변수3] = useState('');
const [변수4, set변수4] = useState('');
// 하수관로 변수
const [하수관로용도코드, set하수관로용도코드] = useState('');
const [하수관로재질코드, set하수관로재질코드] = useState('');
const [행정읍면동코드, set행정읍면동코드] = useState('');
// 시작, 종료, 경과년도
const [yearFilter, setYearFilter] = useState({
시작: '',
종료: '',
경과년도: ''
});
const [가동개시_시작_월, set가동개시_시작_월] = useState('');
const [가동개시_종료_월, set가동개시_종료_월] = useState('');
const [가동개시_시작_일, set가동개시_시작_일] = useState('');
const [가동개시_종료_일, set가동개시_종료_일] = useState('');
// 상세 정보 조회를 위한 상태 추가
const [popupFeature, setPopupFeature] = useState<any>(null);
// 오늘 날짜 구하기
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 (activeTab === 'operation') dynamicHeadlist = operationTableHeadlist; // 운영정보
else if (activeTab === 'report') dynamicHeadlist = reportTableHeadlist; // 하수도정비기본계획보고서
else if (basicType === 'tb_ms_swage_ppl') dynamicHeadlist = swagePplTableHeadlist; // 하수관로
else if (basicType === 'tb_il_swage_prcs_fclt') dynamicHeadlist = swageFcltTableHeadlist; // 하수처리시설
else if (basicType === 'tb_ms_swage_prcs_zone') dynamicHeadlist = swageZoneTableHeadlist; // 하수처리구역
else if (basicType === 'tb_ms_swage_mh') dynamicHeadlist = swageMhTableHeadlist; // 하수맨홀
// 선택된 시도에 따라 시군구 리스트 가져오기
const selectedSigunguList = sigunguMap[시도] || [{ id: 0, name: '선택' }];
// 조회 버튼 클릭 시 1페이지로 이동하도록 수정
const handleSearch = () => {
setCurPage(1); // 페이지를 1로 초기화
fetchFilteredData();
setShowTable(true);
};
// 선택된 필터 값을 저장할 상태 추가
// const [filterParams, setFilterParams] = useState<FilterParams>({});
// cellClick 함수 타입 지정
const handleCellClick = (index?: number, headId?: string) => {
// 원하는 동작 구현
console.log('Cell clicked:', index, headId);
if (typeof index === "number") {
console.log('Selected row data:', tableData[index]);
onFeatureSelect(tableData[index]);
onOpenChange(false); // SearchModal 닫기
}
};
const modalMinWidth = 'min-w-[900px]';
const modalMaxWidth = 'max-w-6xl';
// 모달 높이 동적 계산
const contentRef = React.useRef<HTMLDivElement>(null);
const [dynamicHeight, setDynamicHeight] = useState<number | undefined>(undefined);
// 구분(basicType)이 바뀔 때마다 queryParams, filterParams 초기화
React.useEffect(() => {
// filterParams 초기화
// setFilterParams({});
set시작1('');
set시작2('');
set종료1('');
set종료2('');
set변수1('');
set변수2('');
set변수3('');
set변수4('');
set가동개시_시작_월('');
set가동개시_종료_월('');
set가동개시_시작_일('');
set가동개시_종료_일('');
set하수처리구역명('');
set하수처리시설명('');
set시도(0);
set시군구(0);
set처리구역('');
setYearFilter({
시작: '',
종료: '',
경과년도: ''
});
setShowTable(false);
setTableData([]);
// queryParams는 fetchFilteredData 내에서 매번 새로 생성되므로 별도 상태 초기화는 필요 없음
}, [activeTab, basicType]);
React.useEffect(() => {
if (contentRef.current) {
setDynamicHeight(contentRef.current.scrollHeight + 60); // 여유 padding
}
}, [activeTab, showTable, curPage, tableData.length]);
// 하수관로용 CQL 필터 생성
const pplCqlFilters = () => {
const cqlFilters: string[] = [];
// 추후 작성 예정
if (변수1) cqlFilters.push(`용도코드='${변수1}'`);
if (변수2) cqlFilters.push(`재질코드='${변수2}'`);
if (변수3) cqlFilters.push(`행정읍면동코드='${변수3}'`);
if (변수4) cqlFilters.push(`경과년도='${변수4}'`);
// 설치년도 범위
const startDate = getDateString(yearFilter.시작, 가동개시_시작_월, 가동개시_시작_일, '19000101');
const endDate = getDateString(yearFilter.종료, 가동개시_종료_월, 가동개시_종료_일, `${todayYear}${todayMonth}${todayDate}`);
cqlFilters.push(`설치시작년도 >= '${startDate}' and 설치시작년도 <= '${endDate}'`);
// 관경(mm) 범위
if (시작1 && 종료1) cqlFilters.push(`관경 BETWEEN ${시작1} AND ${종료1}`);
else if (시작1) cqlFilters.push(`관경 >= ${시작1}`);
else if (종료1) cqlFilters.push(`관경 <= ${종료1}`);
// 연장(m) 범위
if (시작2 && 종료2) cqlFilters.push(`연장 BETWEEN ${시작2} AND ${종료2}`);
else if (시작2) cqlFilters.push(`연장 >= ${시작2}`);
else if (종료2) cqlFilters.push(`연장 <= ${종료2}`);
return cqlFilters;
};
// 하수처리시설용 CQL 필터 생성
const facilityCqlFilters = () => {
const cqlFilters: string[] = [];
// 가동개시일자 범위
const startDate = getDateString(yearFilter.시작, 가동개시_시작_월, 가동개시_시작_일, '19000101');
const endDate = getDateString(yearFilter.종료, 가동개시_종료_월, 가동개시_종료_일, `${todayYear}${todayMonth}${todayDate}`);
cqlFilters.push(`oprtng_strt_ymd >= '${startDate}' and oprtng_strt_ymd <= '${endDate}'`);
// 시설용량 범위
if (시작1 && 종료1) cqlFilters.push(`fclt_cpct BETWEEN ${시작1} AND ${종료1}`);
else if (시작1) cqlFilters.push(`fclt_cpct >= ${시작1}`);
else if (종료1) cqlFilters.push(`fclt_cpct <= ${종료1}`);
// 하수처리시설명
if (변수1) cqlFilters.push(`swtrmt_fclt_nm ILIKE '%${변수1}%'`);
return cqlFilters;
};
// 하수처리구역용 CQL 필터 생성
const zoneCqlFilters = () => {
const cqlFilters: string[] = [];
if (시도) cqlFilters.push(`ctpv_cd='${시도}'`);
if (시군구) cqlFilters.push(`sgg_cd='${시군구}'`);
if (변수1) cqlFilters.push(`swtrmt_zone_nm ILIKE '%${변수1}%'`);
if (변수2) cqlFilters.push(`swtrmt_fclt_nm ILIKE '%${변수2}%'`);
return cqlFilters;
};
// 하수맨홀용 CQL 필터 생성
const mhCqlFilters = () => {
const cqlFilters: string[] = [];
// 설치년도 범위
const startDate = getDateString(yearFilter.시작, 가동개시_시작_월, 가동개시_시작_일, '19000101');
const endDate = getDateString(yearFilter.종료, 가동개시_종료_월, 가동개시_종료_일, `${todayYear}${todayMonth}${todayDate}`);
cqlFilters.push(`설치시작년도 >= '${startDate}' and 설치시작년도 <= '${endDate}'`);
if (변수1) cqlFilters.push(`경과년도='${변수1}'`);
if (변수2) cqlFilters.push(`용도코드='${변수2}'`);
// 구경(mm) 범위
if (시작1 && 종료1) cqlFilters.push(`구경 BETWEEN ${시작1} AND ${종료1}`);
else if (시작1) cqlFilters.push(`구경 >= ${시작1}`);
else if (종료1) cqlFilters.push(`구경 <= ${종료1}`);
return cqlFilters;
};
// 하수펌프장용 CQL 필터 생성
const psmtnCqlFilters = () => {
const cqlFilters: string[] = [];
// 설치년도 범위
const startDate = getDateString(yearFilter.시작, 가동개시_시작_월, 가동개시_시작_일, '19000101');
const endDate = getDateString(yearFilter.종료, 가동개시_종료_월, 가동개시_종료_일, `${todayYear}${todayMonth}${todayDate}`);
cqlFilters.push(`설치시작년도 >= '${startDate}' and 설치시작년도 <= '${endDate}'`);
if (변수1) cqlFilters.push(`경과년도='${변수1}'`);
// 일일처리용량 범위
if (시작1 && 종료1) cqlFilters.push(`일일처리용량 BETWEEN ${시작1} AND ${종료1}`);
else if (시작1) cqlFilters.push(`일일처리용량 >= ${시작1}`);
else if (종료1) cqlFilters.push(`일일처리용량 <= ${종료1}`);
// 우수양수능력 범위
if (시작2 && 종료2) cqlFilters.push(`우수양수능력 BETWEEN ${시작2} AND ${종료2}`);
else if (시작2) cqlFilters.push(`우수양수능력 >= ${시작2}`);
else if (종료2) cqlFilters.push(`우수양수능력 <= ${종료2}`);
return cqlFilters;
};
// 우수토실용 CQL 필터 생성
const otflCqlFilters = () => {
const cqlFilters: string[] = [];
// 설치년도 범위
const startDate = getDateString(yearFilter.시작, 가동개시_시작_월, 가동개시_시작_일, '19000101');
const endDate = getDateString(yearFilter.종료, 가동개시_종료_월, 가동개시_종료_일, `${todayYear}${todayMonth}${todayDate}`);
cqlFilters.push(`설치시작년도 >= '${startDate}' and 설치시작년도 <= '${endDate}'`);
if (변수1) cqlFilters.push(`경과년도='${변수1}'`);
if (변수2) cqlFilters.push(`하천명 ILIKE '%${변수1}%'`);
// 계획방류량 범위
if (시작1 && 종료1) cqlFilters.push(`계획방류량 BETWEEN ${시작1} AND ${종료1}`);
else if (시작1) cqlFilters.push(`계획방류량 >= ${시작1}`);
else if (종료1) cqlFilters.push(`계획방류량 <= ${종료1}`);
return cqlFilters;
};
// 운영정보용 CQL 필터 생성
const operationCqlFilters = () => {
const cqlFilters: string[] = [];
// 설치년도 범위
const startDate = getDateString(yearFilter.시작, 가동개시_시작_월, 가동개시_시작_일, '19000101');
const endDate = getDateString(yearFilter.종료, 가동개시_종료_월, 가동개시_종료_일, `${todayYear}${todayMonth}${todayDate}`);
cqlFilters.push(`설치시작년도 >= '${startDate}' and 설치시작년도 <= '${endDate}'`);
return cqlFilters;
};
// 하수도정비기본계획보고서용 CQL 필터 생성
const reportCqlFilters = () => {
const cqlFilters: string[] = [];
// 설치년도 범위
if (시도) cqlFilters.push(`ctpv_cd='${시도}'`);
if (시군구) cqlFilters.push(`sgg_cd='${시군구}'`);
if (처리구역) cqlFilters.push(`처리구역='${처리구역}'`);
if (변수1) cqlFilters.push(`처리구역코드='${변수1}'`);
const startDate = getDateString(yearFilter.시작, 가동개시_시작_월, 가동개시_시작_일, '19000101');
const endDate = getDateString(yearFilter.종료, 가동개시_종료_월, 가동개시_종료_일, `${todayYear}${todayMonth}${todayDate}`);
cqlFilters.push(`설치시작년도 >= '${startDate}' and 설치시작년도 <= '${endDate}'`);
return cqlFilters;
};
// 검색 API 호출 함수
const fetchFilteredData = () => {
const queryParams = new URLSearchParams({
lyrTypeNm: basicType
});
let cqlFilters: string[] = [];
if (activeTab === 'basic') {
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 (activeTab === 'operation') cqlFilters = zoneCqlFilters();
else cqlFilters = reportCqlFilters();
// 탭 + 구분 별로 cqlFilter 추가 예정
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 || []);
setShowTable(true);
})
.catch(err => {
console.error('API ERROR:', err);
setTableData([]);
});
};
const renderBody = () => (
<>
{/* 탭 영역 */}
<UITab2
defaultValue={TABS[0]?.key}
tabList={TABS.map(tab => ({ value: tab.key, label: tab.label, onClick: () => setActiveTab(tab.key), }))}
>
{/* 각각의 탭 영역 */}
{/* 기본정보 탭 */}
{activeTab === 'basic' && (
<div className="border rounded p-6 mb-4 w-full h-[22vh] overflow-y-auto">
{/* 구분 */}
<div className="flex justify-between items-center mb-3 gap-4">
<div className="flex items-center">
<label className="font-semibold text-xl mr-2">구분</label>
{/* <SelectBox
className="w-50 mr-3 ml-4"
selectClass="rounded-lg bg-white"
label="구분"
selectArray={BASIC_TYPES}
onSelectValue={(value: string | number) => setBasicType(String(value))}
/> */}
<select
className="border rounded text-xl px-2 py-1"
value={basicType}
onChange={e => setBasicType(e.target.value)}
>
{BASIC_TYPES.map(opt => (
<option key={opt.key} value={opt.key}>{opt.label}</option>
))}
</select>
</div>
</div>
{/* 분기처리: 하수관로 */}
{basicType === 'tb_ms_swage_ppl' && (
<div className="mb-6">
{/* 설치년도 */}
<div className="flex items-center mb-2">
<label className="font-semibold text-xl mr-2">설치년도</label>
{/* 시작년도 */}
<select
className="border rounded text-xl px-2 py-1 mr-1"
value={yearFilter.시작}
onChange={e => setYearFilter({ ...yearFilter, 시작: e.target.value })}
>
<option value="">년</option>
{Array.from({ length: today.getFullYear() - 1900 + 1 }, (_, i) => {
const y = (today.getFullYear() - i).toString();
if (yearFilter.종료 && parseInt(y) > parseInt(yearFilter.종료)) return null;
return <option key={y} value={y}>{y}</option>;
})}
</select>
<span className="font-semibold text-xl mx-2">~</span>
{/* 종료년도 */}
<select
className="border rounded text-xl px-2 py-1 mr-8"
value={yearFilter.종료}
onChange={e => setYearFilter({ ...yearFilter, 종료: e.target.value })}
>
<option value="">년</option>
{Array.from({ length: today.getFullYear() - 1900 + 1 }, (_, i) => {
const y = (today.getFullYear() - i).toString();
// 시작년이 선택된 경우, 종료년은 시작년 이상만 표시
if (yearFilter.시작 && parseInt(y) < parseInt(yearFilter.시작)) return null;
return <option key={y} value={y}>{y}</option>;
})}
</select>
{/* 경과년도 */}
<label className="font-semibold text-xl mr-2">경과년도</label>
<input
type="text"
className="border rounded text-xl px-2 py-1 mr-2 w-40 placeholder:text-center"
placeholder="입력"
value={yearFilter.경과년도}
onChange={e => {
// 숫자만 입력 허용
const val = e.target.value.replace(/[^0-9]/g, '');
setYearFilter({ ...yearFilter, 경과년도: val });
}}
/>
</div>
<div className="flex items-center mb-2">
{/* 관경 */}
<label className="font-semibold text-xl mr-2">관경(mm)</label>
<input
type="text"
className="border rounded text-xl px-2 py-1 mr-8 w-40 placeholder:text-center"
placeholder="입력"
value={시작1}
onChange={e => {
// 숫자만 입력 허용
const val = e.target.value.replace(/[^0-9]/g, '');
set시작1(val);
}}
/>
<span className="font-semibold text-xl mx-2">~</span>
<input
type="text"
className="border rounded text-xl px-2 py-1 mr-8 w-40 placeholder:text-center"
placeholder="입력"
value={종료1}
onChange={e => {
// 숫자만 입력 허용
const val = e.target.value.replace(/[^0-9]/g, '');
set종료1(val);
}}
/>
{/* 연장 */}
<label className="font-semibold text-xl mr-2">연장(m)</label>
<input
type="text"
className="border rounded text-xl px-2 py-1 mr-8 w-40 placeholder:text-center"
placeholder="입력"
value={시작2}
onChange={e => {
// 숫자만 입력 허용
const val = e.target.value.replace(/[^0-9]/g, '');
set시작2(val);
}}
/>
<span className="font-semibold text-xl mx-2">~</span>
<input
type="text"
className="border rounded text-xl px-2 py-1 mr-8 w-40 placeholder:text-center"
placeholder="입력"
value={종료2}
onChange={e => {
// 숫자만 입력 허용
const val = e.target.value.replace(/[^0-9]/g, '');
set종료2(val);
}}
/>
</div>
<div className="flex items-center mb-2">
{/* 용도코드 */}
<label className="font-semibold text-xl mr-2">용도코드</label>
<input
type="text"
className="border rounded text-xl px-2 py-1 mr-8 w-40 placeholder:text-center"
placeholder="입력"
value={하수관로용도코드}
onChange={e => {
// 숫자만 입력 허용
const val = e.target.value.replace(/[^0-9]/g, '');
set하수관로용도코드(val);
}}
/>
{/* 재질코드 */}
<label className="font-semibold text-xl mr-2">재질코드</label>
<input
type="text"
className="border rounded text-xl px-2 py-1 mr-8 w-40 placeholder:text-center"
placeholder="입력"
value={하수관로재질코드}
onChange={e => {
// 숫자만 입력 허용
const val = e.target.value.replace(/[^0-9]/g, '');
set하수관로재질코드(val);
}}
/>
{/* 행정읍면동코드 */}
<label className="font-semibold text-xl mr-2">행정읍면동코드</label>
<input
type="text"
className="border rounded text-xl px-2 py-1 mr-8 w-40 placeholder:text-center"
placeholder="입력"
value={행정읍면동코드}
onChange={e => {
// 숫자만 입력 허용
const val = e.target.value.replace(/[^0-9]/g, '');
set행정읍면동코드(val);
}}
/>
</div>
</div>
)}
{/* 분기처리: 하수처리시설 */}
{basicType === 'tb_il_swage_prcs_fclt' && (
<div className="mb-6">
{/* 가동개시일자 */}
<div className="flex items-center mb-2">
<label className="font-semibold text-xl mr-2">가동개시일자</label>
{/* 시작일자 */}
<select
className="border rounded text-xl px-2 py-1 mr-1"
value={yearFilter.시작}
onChange={e => setYearFilter({ ...yearFilter, 시작: e.target.value })}
>
<option value="">년</option>
{Array.from({ length: today.getFullYear() - 1900 + 1 }, (_, i) => {
const y = (today.getFullYear() - i).toString();
if (yearFilter.종료 && parseInt(y) > parseInt(yearFilter.종료)) return null;
return <option key={y} value={y}>{y}</option>;
})}
</select>
<span className="font-semibold text-xl mx-2">~</span>
{/* 종료일자 */}
<select
className="border rounded text-xl px-2 py-1 mr-8"
value={yearFilter.종료}
onChange={e => setYearFilter({ ...yearFilter, 종료: e.target.value })}
>
<option value="">년</option>
{Array.from({ length: today.getFullYear() - 1900 + 1 }, (_, i) => {
const y = (today.getFullYear() - i).toString();
// 시작년이 선택된 경우, 종료년은 시작년 이상만 표시
if (yearFilter.시작 && parseInt(y) < parseInt(yearFilter.시작)) return null;
return <option key={y} value={y}>{y}</option>;
})}
</select>
{/* 시설용량 */}
<label className="font-semibold text-xl mr-2">시설용량</label>
<input
type="text"
className="border rounded text-xl px-2 py-1 mr-2 w-40 placeholder:text-center"
placeholder="최소"
value={시작1}
onChange={e => {
// 숫자만 입력 허용
const val = e.target.value.replace(/[^0-9]/g, '');
set시작1(val);
}}
/>
<span className="text-xl mr-2">~</span>
<input
type="text"
className="border rounded text-xl px-2 py-1 mr-8 w-40 placeholder:text-center"
placeholder="최대"
value={종료1}
onChange={e => {
// 숫자만 입력 허용
const val = e.target.value.replace(/[^0-9]/g, '');
set종료1(val);
}}
/>
{/* 하수처리시설명 */}
<label className="font-semibold text-xl mr-2">하수처리시설명</label>
<input className="border rounded text-xl px-2 py-1 mr-4" value={변수1} onChange={e => set변수1(e.target.value)} />
</div>
</div>
)}
{/* 분기처리: 하수처리구역 */}
{basicType === 'tb_ms_swage_prcs_zone' && (
<div className="mb-3">
<div className="flex items-center mb-2">
<label className="font-semibold text-xl mr-2">시도</label>
<select className="border rounded text-xl px-2 py-1 mr-8" value={시도} onChange={e => set시도(parseInt(e.target.value))}>
{sidoList.map(opt => (
<option key={opt.id} value={opt.id}>{opt.name}</option>
))}
</select>
<label className="font-semibold text-xl mr-2">시군구</label>
<select className="border rounded text-xl px-2 py-1 mr-8" value={시군구} onChange={e => set시군구(parseInt(e.target.value))}>
{selectedSigunguList.map(opt => (
<option key={opt.id} value={opt.id}>{opt.name}</option>
))}
</select>
<label className="font-semibold text-xl mr-2">하수처리구역명</label>
<input className="border rounded text-xl px-2 py-1 mr-8" value={변수1} onChange={e => set변수1(e.target.value)} />
<label className="font-semibold text-xl mr-2">하수처리시설명</label>
<input className="border rounded text-xl px-2 py-1 mr-8" value={변수2} onChange={e => set변수2(e.target.value)} />
</div>
</div>
)}
{/* 분기처리: 하수맨홀 */}
{basicType === 'tb_ms_swage_mh' && (
<div className="mb-3">
<div className="flex items-center mb-2">
{/* 설치년도 */}
<div className="flex items-center mb-2">
<label className="font-semibold text-xl mr-2">설치년도</label>
{/* 시작년도 */}
<select
className="border rounded text-xl px-2 py-1 mr-1"
value={yearFilter.시작}
onChange={e => setYearFilter({ ...yearFilter, 시작: e.target.value })}
>
<option value="">년</option>
{Array.from({ length: today.getFullYear() - 1900 + 1 }, (_, i) => {
const y = (today.getFullYear() - i).toString();
if (yearFilter.종료 && parseInt(y) > parseInt(yearFilter.종료)) return null;
return <option key={y} value={y}>{y}</option>;
})}
</select>
<span className="font-semibold text-xl mx-2">~</span>
{/* 종료년도 */}
<select
className="border rounded text-xl px-2 py-1 mr-8"
value={yearFilter.종료}
onChange={e => setYearFilter({ ...yearFilter, 종료: e.target.value })}
>
<option value="">년</option>
{Array.from({ length: today.getFullYear() - 1900 + 1 }, (_, i) => {
const y = (today.getFullYear() - i).toString();
// 시작년이 선택된 경우, 종료년은 시작년 이상만 표시
if (yearFilter.시작 && parseInt(y) < parseInt(yearFilter.시작)) return null;
return <option key={y} value={y}>{y}</option>;
})}
</select>
{/* 경과년도 */}
<label className="font-semibold text-xl mr-2">경과년도</label>
<input
type="text"
className="border rounded text-xl px-2 py-1 mr-2 w-40 placeholder:text-center"
placeholder="입력"
value={yearFilter.경과년도}
onChange={e => {
// 숫자만 입력 허용
const val = e.target.value.replace(/[^0-9]/g, '');
setYearFilter({ ...yearFilter, 경과년도: val });
}}
/>
</div>
</div>
<div>
{/* 용도코드 */}
<label className="font-semibold text-xl mr-2">용도코드</label>
<input
type="text"
className="border rounded text-xl px-2 py-1 mr-8 w-40 placeholder:text-center"
placeholder="입력"
value={변수1}
onChange={e => {
// 숫자만 입력 허용
const val = e.target.value.replace(/[^0-9]/g, '');
set변수1(val);
}}
/>
{/* 하수맨홀 구경 */}
<label className="font-semibold text-xl mr-2">구경(mm)</label>
<input
type="text"
className="border rounded text-xl px-2 py-1 mr-8 w-40 placeholder:text-center"
placeholder="입력"
value={시작1}
onChange={e => {
// 숫자만 입력 허용
const val = e.target.value.replace(/[^0-9]/g, '');
set시작1(val);
}}
/>
<span className="font-semibold text-xl mx-2">~</span>
<input
type="text"
className="border rounded text-xl px-2 py-1 mr-8 w-40 placeholder:text-center"
placeholder="입력"
value={종료1}
onChange={e => {
// 숫자만 입력 허용
const val = e.target.value.replace(/[^0-9]/g, '');
set종료1(val);
}}
/>
</div>
</div>
)}
{/* 분기처리: 하수펌프장 */}
{(basicType === 'tb_ms_swage_psmtn_1' || basicType === 'tb_ms_swage_psmtn_2') && (
<div className="mb-3">
<div className="flex items-center mb-2">
{/* 설치년도 */}
<div className="flex items-center mb-2">
<label className="font-semibold text-xl mr-2">설치년도</label>
{/* 시작년도 */}
<select
className="border rounded text-xl px-2 py-1 mr-1"
value={yearFilter.시작}
onChange={e => setYearFilter({ ...yearFilter, 시작: e.target.value })}
>
<option value="">년</option>
{Array.from({ length: today.getFullYear() - 1900 + 1 }, (_, i) => {
const y = (today.getFullYear() - i).toString();
if (yearFilter.종료 && parseInt(y) > parseInt(yearFilter.종료)) return null;
return <option key={y} value={y}>{y}</option>;
})}
</select>
<span className="font-semibold text-xl mx-2">~</span>
{/* 종료년도 */}
<select
className="border rounded text-xl px-2 py-1 mr-8"
value={yearFilter.종료}
onChange={e => setYearFilter({ ...yearFilter, 종료: e.target.value })}
>
<option value="">년</option>
{Array.from({ length: today.getFullYear() - 1900 + 1 }, (_, i) => {
const y = (today.getFullYear() - i).toString();
// 시작년이 선택된 경우, 종료년은 시작년 이상만 표시
if (yearFilter.시작 && parseInt(y) < parseInt(yearFilter.시작)) return null;
return <option key={y} value={y}>{y}</option>;
})}
</select>
{/* 경과년도 */}
<label className="font-semibold text-xl mr-2">경과년도</label>
<input
type="text"
className="border rounded text-xl px-2 py-1 mr-2 w-40 placeholder:text-center"
placeholder="입력"
value={yearFilter.경과년도}
onChange={e => {
// 숫자만 입력 허용
const val = e.target.value.replace(/[^0-9]/g, '');
setYearFilter({ ...yearFilter, 경과년도: val });
}}
/>
</div>
</div>
<div>
{/* 일일처리용량 - 오수맨홀 */}
<label className="font-semibold text-xl mr-2">일일처리용량</label>
<input
type="text"
className="border rounded text-xl px-2 py-1 mr-8 w-40 placeholder:text-center"
placeholder="입력"
value={시작1}
onChange={e => {
// 숫자만 입력 허용
const val = e.target.value.replace(/[^0-9]/g, '');
set시작1(val);
}}
/>
<span className="font-semibold text-xl mx-2">~</span>
<input
type="text"
className="border rounded text-xl px-2 py-1 mr-8 w-40 placeholder:text-center"
placeholder="입력"
value={종료1}
onChange={e => {
// 숫자만 입력 허용
const val = e.target.value.replace(/[^0-9]/g, '');
set종료1(val);
}}
/>
{/* 우수양수능력 - 우수맨홀 */}
<label className="font-semibold text-xl mr-2">우수양수능력</label>
<input
type="text"
className="border rounded text-xl px-2 py-1 mr-8 w-40 placeholder:text-center"
placeholder="입력"
value={시작2}
onChange={e => {
// 숫자만 입력 허용
const val = e.target.value.replace(/[^0-9]/g, '');
set시작2(val);
}}
/>
<span className="font-semibold text-xl mx-2">~</span>
<input
type="text"
className="border rounded text-xl px-2 py-1 mr-8 w-40 placeholder:text-center"
placeholder="입력"
value={종료2}
onChange={e => {
// 숫자만 입력 허용
const val = e.target.value.replace(/[^0-9]/g, '');
set종료2(val);
}}
/>
</div>
</div>
)}
{/* 분기처리: 우수토실? 토구? */}
{basicType === 'tb_ms_otfl' && (
<div className="mb-3">
<div className="flex items-center mb-2">
{/* 설치년도 */}
<div className="flex items-center mb-2">
<label className="font-semibold text-xl mr-2">설치년도</label>
{/* 시작년도 */}
<select
className="border rounded text-xl px-2 py-1 mr-1"
value={yearFilter.시작}
onChange={e => setYearFilter({ ...yearFilter, 시작: e.target.value })}
>
<option value="">년</option>
{Array.from({ length: today.getFullYear() - 1900 + 1 }, (_, i) => {
const y = (today.getFullYear() - i).toString();
if (yearFilter.종료 && parseInt(y) > parseInt(yearFilter.종료)) return null;
return <option key={y} value={y}>{y}</option>;
})}
</select>
<span className="font-semibold text-xl mx-2">~</span>
{/* 종료년도 */}
<select
className="border rounded text-xl px-2 py-1 mr-8"
value={yearFilter.종료}
onChange={e => setYearFilter({ ...yearFilter, 종료: e.target.value })}
>
<option value="">년</option>
{Array.from({ length: today.getFullYear() - 1900 + 1 }, (_, i) => {
const y = (today.getFullYear() - i).toString();
// 시작년이 선택된 경우, 종료년은 시작년 이상만 표시
if (yearFilter.시작 && parseInt(y) < parseInt(yearFilter.시작)) return null;
return <option key={y} value={y}>{y}</option>;
})}
</select>
{/* 경과년도 */}
<label className="font-semibold text-xl mr-2">경과년도</label>
<input
type="text"
className="border rounded text-xl px-2 py-1 mr-2 w-40 placeholder:text-center"
placeholder="입력"
value={yearFilter.경과년도}
onChange={e => {
// 숫자만 입력 허용
const val = e.target.value.replace(/[^0-9]/g, '');
setYearFilter({ ...yearFilter, 경과년도: val });
}}
/>
</div>
</div>
<div>
{/* 하천명 */}
<label className="font-semibold text-xl mr-2">하천명</label>
<input className="border rounded text-xl px-2 py-1 mr-4" value={변수1} onChange={e => set변수1(e.target.value)} />
{/* 계획방류량 */}
<label className="font-semibold text-xl mr-2">계획방류량</label>
<input
type="text"
className="border rounded text-xl px-2 py-1 mr-8 w-40 placeholder:text-center"
placeholder="입력"
value={시작1}
onChange={e => {
// 숫자만 입력 허용
const val = e.target.value.replace(/[^0-9]/g, '');
set시작1(val);
}}
/>
<span className="font-semibold text-xl mx-2">~</span>
<input
type="text"
className="border rounded text-xl px-2 py-1 mr-8 w-40 placeholder:text-center"
placeholder="입력"
value={종료1}
onChange={e => {
// 숫자만 입력 허용
const val = e.target.value.replace(/[^0-9]/g, '');
set종료1(val);
}}
/>
</div>
</div>
)}
{/* 조회 버튼 중앙 정렬 */}
<div className="flex justify-center">
<Button className="w-40 text-base py-2" onClick={handleSearch}>조회</Button>
</div>
</div>
)}
{/* 운영정보 탭 */}
{activeTab === 'operation' && (
<div className="border rounded p-6 mb-4 w-full h-[22vh] overflow-y-auto">
<div className="flex items-center mb-6">
<label className="w-24 font-semibold text-xl">업무구분</label>
<select
className="border rounded px-3 py-2 text-xl mr-8"
value={workType}
onChange={e => setWorkType(e.target.value)}
style={{ minWidth: 140 }}
>
{WORK_TYPES.map(w => (
<option key={w.key} value={w.key}>{w.label}</option>
))}
</select>
{/* 설치년도 */}
<div className="flex items-center mb-2">
<label className="font-semibold text-xl mr-2">설치년도</label>
{/* 시작년도 */}
<select
className="border rounded text-xl px-2 py-1 mr-1"
value={yearFilter.시작}
onChange={e => setYearFilter({ ...yearFilter, 시작: e.target.value })}
>
<option value="">년</option>
{Array.from({ length: today.getFullYear() - 1900 + 1 }, (_, i) => {
const y = (today.getFullYear() - i).toString();
if (yearFilter.종료 && parseInt(y) > parseInt(yearFilter.종료)) return null;
return <option key={y} value={y}>{y}</option>;
})}
</select>
<span className="font-semibold text-xl mx-2">~</span>
{/* 종료년도 */}
<select
className="border rounded text-xl px-2 py-1 mr-8"
value={yearFilter.종료}
onChange={e => setYearFilter({ ...yearFilter, 종료: e.target.value })}
>
<option value="">년</option>
{Array.from({ length: today.getFullYear() - 1900 + 1 }, (_, i) => {
const y = (today.getFullYear() - i).toString();
// 시작년이 선택된 경우, 종료년은 시작년 이상만 표시
if (yearFilter.시작 && parseInt(y) < parseInt(yearFilter.시작)) return null;
return <option key={y} value={y}>{y}</option>;
})}
</select>
{/* 경과년도 */}
<label className="font-semibold text-xl mr-2">경과년도</label>
<input
type="text"
className="border rounded text-xl px-2 py-1 mr-2 w-40 placeholder:text-center"
placeholder="입력"
value={yearFilter.경과년도}
onChange={e => {
// 숫자만 입력 허용
const val = e.target.value.replace(/[^0-9]/g, '');
setYearFilter({ ...yearFilter, 경과년도: val });
}}
/>
</div>
</div>
{/* 조회 버튼 중앙 정렬 */}
<div className="flex justify-center">
<Button className="w-40 text-base py-2" onClick={handleSearch}>조회</Button>
</div>
</div>
)}
{/* 하수도정비기본계획보고서 탭 */}
{activeTab === 'report' && (
<div className="border rounded p-6 mb-4 w-full h-[22vh] overflow-y-auto">
{/* 시도, 시군구, 하수처리구역 한 줄 */}
<div className="flex items-center mb-6">
<label className="font-semibold text-xl mr-2">시도</label>
<select className="border rounded text-xl px-2 py-1 mr-8" value={시도} onChange={e => set시도(parseInt(e.target.value))}>
{sidoList.map(opt => (
<option key={opt.id} value={opt.id}>{opt.name}</option>
))}
</select>
<label className="font-semibold text-xl mr-2">시군구</label>
<select className="border rounded text-xl px-2 py-1 mr-8" value={시군구} onChange={e => set시군구(parseInt(e.target.value))}>
{selectedSigunguList.map(opt => (
<option key={opt.id} value={opt.id}>{opt.name}</option>
))}
</select>
<label className="font-semibold text-xl mr-2">하수처리구역</label>
<select className="border rounded px-2 text-xl py-1 mr-2" value={처리구역} onChange={e => set처리구역(e.target.value)}>
{처리구역List.map(opt => (
<option key={opt} value={opt}>{opt}</option>
))}
</select>
<input className="border rounded text-xl px-2 py-1 w-24 mr-8" value={변수1} placeholder="입력" />
<label className="font-semibold text-xl mr-2">승인년도</label>
{/* 시작일자 */}
<select
className="border rounded text-xl px-2 py-1 mr-1"
value={yearFilter.시작}
onChange={e => setYearFilter({ ...yearFilter, 시작: e.target.value })}
>
<option value="">년</option>
{Array.from({ length: today.getFullYear() - 1900 + 1 }, (_, i) => {
const y = (today.getFullYear() - i).toString();
if (yearFilter.종료 && parseInt(y) > parseInt(yearFilter.종료)) return null;
return <option key={y} value={y}>{y}</option>;
})}
</select>
<span className="font-semibold text-xl mx-2">~</span>
{/* 종료일자 */}
<select
className="border rounded text-xl px-2 py-1 mr-8"
value={yearFilter.종료}
onChange={e => setYearFilter({ ...yearFilter, 종료: e.target.value })}
>
<option value="">년</option>
{Array.from({ length: today.getFullYear() - 1900 + 1 }, (_, i) => {
const y = (today.getFullYear() - i).toString();
// 시작년이 선택된 경우, 종료년은 시작년 이상만 표시
if (yearFilter.시작 && parseInt(y) < parseInt(yearFilter.시작)) return null;
return <option key={y} value={y}>{y}</option>;
})}
</select>
</div>
{/* 조회 버튼 중앙 정렬 */}
<div className="flex justify-center">
<Button className="w-40 text-base py-2" onClick={handleSearch}>조회</Button>
</div>
</div>
)}
</UITab2>
{/* 📋 결과 리스트 */}
< div
className="border rounded space-y-2 w-full h-[60vh] overflow-auto"
onWheel={e => {
// 세로 스크롤이 최상단/최하단에 도달했을 때 가로 스크롤이 막히는 현상을 해결
const container = e.currentTarget;
const isShift = e.shiftKey;
if (!isShift) return;
// 세로 스크롤 위치
const atTop = container.scrollTop === 0;
const atBottom = Math.ceil(container.scrollTop + container.clientHeight) >= container.scrollHeight;
// 세로 스크롤이 최상단/최하단에 도달한 경우에만 가로 스크롤 수동 처리
if ((atTop && e.deltaY < 0) || (atBottom && e.deltaY > 0)) {
container.scrollLeft += e.deltaY;
e.preventDefault();
}
}}
>
{/* 선택된 탭의 label */}
< div className="m-2 text-2xl font-bold mb-2" >
{TABS.find(tab => tab.key === activeTab)?.label}
{/* 조회 결과 라벨 및 건수 표시 */}
{
(() => {
// 조회 건수 변수 선언
const resultCount = tableData.length;
// 탭별 라벨
const label =
activeTab === 'basic'
? BASIC_TYPES.find(type => type.key === basicType)?.label
: activeTab === 'operation'
? WORK_TYPES.find(type => type.key === workType)?.label
: activeTab === 'report'
? '하수도정비기본계획보고서'
: TABS.find(tab => tab.key === activeTab)?.label;
return (
<div className="text-lg font-semibold mb-6">
▶ {label} 조회 결과
<span className="ml-2 text-base text-gray-500">({resultCount.toLocaleString()}건)</span>
</div>
);
})()
}
<UiTable2
headName=''
tableData={tableData}
headlist={dynamicHeadlist}
pageSize={tableData.length < 10 ? tableData.length : 10}
total={tableData.length}
children={undefined}
cellClick={handleCellClick}
onPageChange={setCurPage}
resetPageNo={false}
cur={curPage}
/>
{/* FeatureClickEventPopup 표시 */}
{
popupFeature && (
<FeatureClickEventPopup
featureData={popupFeature}
onClose={() => setPopupFeature(null)}
/>
)
}
</div >
</div>
</>
);
return (
<UiModal
open={open}
onOpenChange={onOpenChange}
bodyClass={`
h-[100vh]
w-[60vw]
max-w-none
max-h-none
overflow-hidden
z-[200]
`}
body={renderBody()}
headerTitle="검색"
// 필요시 footerContent 등 추가
/>
);
}
export default React.memo(SearchModal);