Files
frontend/apps/sn/app/map/SearchModal.tsx
T
swcho 39628cd12f feat:
- 분석 대상지역 선택 기능 구현 (API 연동)
- SearchModal.tsx 주석 삭제
2025-06-02 16:09:19 +09:00

1129 lines
43 KiB
TypeScript

import FeatureClickEventPopup from './featureClickEventPopup';
import React, { useState } from 'react';
import axios from 'axios';
import { UiModal, UiTable } from '@common/business_components/ui';
import { Button, Input, Label } from '@common/components';
import { UiTable2 } from '@common/business_components/ui/table/index-2';
import { UITab2 } from '@common/business_components/ui/tab/index-2';
import { SelectBox2 } from '@common/business_components/ui/select/index-2';
import { YearRangeSelectBox } from '@/components/ui/YearRangeSelectBox';
import { SidoSigunguSelectBox } from '@/components/ui/SidoSigunguSelectBox';
import { SwtrmtFcltCol, SwtrmtPrcsZoneCol } from './components/common/table';
import LoadingOverlay from '@/components/ui/LoadingBar';
const API_URL = `${process.env.NEXT_PUBLIC_SEWERAGE_API_URL}`;
// 탭
const TABS = [
{ val: "basic", text: "기본정보" },
{ val: "operation", text: "운영정보" },
{ val: "report", text: "하수도정비기본계획보고서" },
];
// 기본정보 '구분' 선택 목록
const BASIC_TYPES = [
{ val: "tb_ms_swage_ppl", text: "하수관로" },
{ val: "tb_il_swage_prcs_fclt", text: "하수처리시설" },
{ val: "tb_ms_swage_prcs_zone", text: "하수처리구역" },
{ val: "tb_ms_swage_mh", text: "하수맨홀" },
{ val: "tb_ms_swage_psmtn_1", text: "오수펌프" },
{ val: "tb_ms_swage_psmtn_2", text: "빗물펌프" },
{ val: "tb_ms_otfl", text: "우수토실" },
];
// 운영정보 '구분' 선택 목록
const WORK_TYPES = [
{ val: "maintenance", text: "보수 및 갱신" },
{ val: "cleaning", text: "청소 및 준설" },
{ val: "inspection", text: "점검 및 진단" },
];
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 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 처리구역List = [
{ val: "0", text: "선택" },
{ val: "a", text: "A" },
{ val: "b", text: "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]?.val ?? "basic");
const [workType, setWorkType] = useState(WORK_TYPES[0]?.val ?? "");
const [showTable, setShowTable] = useState(false);
const [tableData, setTableData] = useState<any[]>([]);
const [tableCount, setTableCount] = useState<number>(0);
const [curPage, setCurPage] = useState(1);
const [basicType, setBasicType] = useState(BASIC_TYPES[0]?.val ?? "");
const [시도, set시도] = useState(0);
const [시군구, set시군구] = useState(0);
const [처리구역, set처리구역] = useState("");
// 날짜 상태 선언
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 [yearFilter, setYearFilter] = useState({
시작: "",
종료: "",
경과년도: "",
});
const [가동개시_시작_월, set가동개시_시작_월] = useState('');
const [가동개시_종료_월, set가동개시_종료_월] = useState('');
const [가동개시_시작_일, set가동개시_시작_일] = useState('');
const [가동개시_종료_일, set가동개시_종료_일] = useState('');
// 로딩바
const [isLoading, setIsLoading] = useState(false);
// 상세 정보 조회를 위한 상태 추가
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 = SwtrmtPrcsZoneCol; // default 값
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 = SwtrmtFcltCol; // 하수처리시설
else if (basicType === 'tb_ms_swage_prcs_zone') dynamicHeadlist = SwtrmtPrcsZoneCol; // 하수처리구역
else if (basicType === 'tb_ms_swage_mh') dynamicHeadlist = swageMhTableHeadlist; // 하수맨홀
// 조회 버튼 클릭 시 1페이지로 이동하도록 수정
const handleSearch = () => {
setCurPage(1); // 페이지를 1로 초기화
fetchFilteredData();
setShowTable(true);
};
// cellClick 함수 타입 지정
const handleCellClick = (index?: number, headId?: string) => {
if (typeof index === "number") {
onFeatureSelect(tableData[index % 10]); // 10은 page size 하드 코딩한 숫자
onOpenChange(false); // SearchModal 닫기
}
};
// 구분(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시도(0);
set시군구(0);
set처리구역("");
setYearFilter({
시작: "",
종료: "",
경과년도: "",
});
setShowTable(false);
setTableCount(0);
setTableData([]);
// queryParams는 fetchFilteredData 내에서 매번 새로 생성되므로 별도 상태 초기화는 필요 없음
}, [activeTab, basicType]);
// 페이지 변경 시마다 데이터 fetch
React.useEffect(() => {
// showTable이 true일 때만 fetch (최초 렌더 방지)
if (showTable) {
fetchFilteredData(); // 실제 API 사용 시
// fetchDummyData(); // 더미 데이터 사용 시
}
}, [curPage]);
// 하수관로용 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 = async () => {
setIsLoading(true); // 로딩 시작
try {
const queryParams = new URLSearchParams({
lyrTypeNm: basicType,
page: `${curPage - 1}`,
size: '10',
sort: 'string',
});
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();
}
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);
setTableData(res.data.data || []);
setTableCount(res.data.count || []);
console.log('tableCount 상태 업데이트 후:', res.data.count); // tableCount 로그 추가
setShowTable(true);
} catch (err) {
console.error('API ERROR:', err);
setTableData([]);
} finally {
setIsLoading(false); // 로딩 종료
}
};
const renderBody = () => (
<>
{/* 탭 영역 */}
<UITab2
defaultValue={TABS[0]?.val}
tabList={TABS.map((tab) => ({
value: tab.val,
label: tab.text,
onClick: () => setActiveTab(tab.val),
}))}
>
{/* 각각의 탭 영역 */}
{/* 기본정보 탭 */}
{activeTab === 'basic' && (
<div className="relative border rounded-lg p-6 mt-4 w-full h-[23vh]">
{/* 구분 */}
<div className="mb-3 flex items-center justify-between gap-4">
<div className="flex items-center">
<SelectBox2
className="w-[150px] mr-3"
selectClass="rounded-lg bg-white"
label="구분"
selectArray={BASIC_TYPES}
onSelectValue={(value: string | number) =>
setBasicType(String(value))
}
value={basicType}
/>
</div>
</div>
{/* 분기처리: 하수관로 */}
{basicType === "tb_ms_swage_ppl" && (
<div className="mb-6">
<div className="mb-2 flex items-center">
{/* 설치년도 */}
<YearRangeSelectBox
start={yearFilter.시작}
end={yearFilter.종료}
onChange={(range) =>
setYearFilter({
...yearFilter,
시작: range.start,
종료: range.end,
})
}
startLabel="설치년도"
endLabel=""
/>
{/* 경과년도 */}
<div className="flex items-center">
<Label className="mr-2 w-auto text-xl text-gray-600">
경과년도
</Label>
<Input
className='!text-xl w-[70px] placeholder:text-center mr-8'
placeholder="입력"
value={yearFilter.경과년도}
onChange={(e) => {
// 숫자만 입력 허용
const val = e.target.value.replace(/[^0-9]/g, "");
setYearFilter({ ...yearFilter, 경과년도: val });
}}
/>
</div>
{/* 용도코드 */}
<Label className="mr-2 w-auto text-xl text-gray-600">
용도코드
</Label>
<Input
className="mr-8 w-[100px] !text-xl placeholder:text-center"
placeholder="입력"
value={변수1}
onChange={(e) => {
// 숫자만 입력 허용
const val = e.target.value.replace(/[^0-9]/g, "");
set변수1(val);
}}
/>
{/* 재질코드 */}
<Label className="mr-2 w-auto text-xl text-gray-600">
재질코드
</Label>
<Input
className="mr-8 w-[100px] !text-xl 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="mr-2 w-auto text-xl text-gray-600">
행정읍면동코드
</Label>
<Input
className="mr-8 w-[100px] !text-xl placeholder:text-center"
placeholder="입력"
value={변수3}
onChange={(e) => {
// 숫자만 입력 허용
const val = e.target.value.replace(/[^0-9]/g, "");
set변수3(val);
}}
/>
{/* 관경 */}
<Label className="mr-2 w-auto text-xl text-gray-600">
관경(mm)
</Label>
<Input
className="w-[100px] !text-xl placeholder:text-center"
placeholder="입력"
value={시작1}
onChange={(e) => {
// 숫자만 입력 허용
const val = e.target.value.replace(/[^0-9]/g, "");
set시작1(val);
}}
/>
<span className="mx-2 text-xl font-semibold">~</span>
<Input
className="mr-8 w-[100px] !text-xl placeholder:text-center"
placeholder="입력"
value={종료1}
onChange={(e) => {
// 숫자만 입력 허용
const val = e.target.value.replace(/[^0-9]/g, "");
set종료1(val);
}}
/>
{/* 연장 */}
<Label className="mr-2 w-auto text-xl text-gray-600">
연장(m)
</Label>
<Input
className="w-[100px] !text-xl placeholder:text-center"
placeholder="입력"
value={시작2}
onChange={(e) => {
// 숫자만 입력 허용
const val = e.target.value.replace(/[^0-9]/g, "");
set시작2(val);
}}
/>
<span className="mx-2 text-xl font-semibold">~</span>
<Input
className="mr-8 w-[100px] !text-xl placeholder:text-center"
placeholder="입력"
value={종료2}
onChange={(e) => {
// 숫자만 입력 허용
const val = e.target.value.replace(/[^0-9]/g, "");
set종료2(val);
}}
/>
</div>
</div>
)}
{/* 분기처리: 하수처리시설 */}
{basicType === "tb_il_swage_prcs_fclt" && (
<div className="mb-6">
{/* 가동개시일자 */}
<div className="mb-2 flex items-center">
<YearRangeSelectBox
start={yearFilter.시작}
end={yearFilter.종료}
onChange={(range) =>
setYearFilter({
...yearFilter,
시작: range.start,
종료: range.end,
})
}
startLabel="가동개시일자"
endLabel=" "
/>
{/* 시설용량 */}
<Label className="mr-2 w-auto text-xl text-gray-600">
시설용량
</Label>
<Input
className="w-[100px] !text-xl placeholder:text-center"
placeholder="입력"
value={시작1}
onChange={(e) => {
// 숫자만 입력 허용
const val = e.target.value.replace(/[^0-9]/g, "");
set시작1(val);
}}
/>
<span className="mx-2 text-xl font-semibold">~</span>
<Input
className="mr-8 w-[100px] !text-xl placeholder:text-center"
placeholder="입력"
value={종료1}
onChange={(e) => {
// 숫자만 입력 허용
const val = e.target.value.replace(/[^0-9]/g, "");
set종료1(val);
}}
/>
{/* 하수처리시설명 */}
<Label className="mr-2 w-auto text-xl text-gray-600">
하수처리시설명
</Label>
<Input
className="mr-8 w-[100px] !text-xl placeholder:text-center"
placeholder="입력"
value={변수1}
onChange={(e) => {
set변수1(e.target.value);
}}
/>
</div>
</div>
)}
{/* 분기처리: 하수처리구역 */}
{basicType === "tb_ms_swage_prcs_zone" && (
<div className="mb-3">
<div className="mb-2 flex items-center">
<SidoSigunguSelectBox
sido={시도}
sigungu={시군구}
onSidoChange={(sidoId) => {
set시도(sidoId);
set시군구(0);
}}
onSigunguChange={set시군구}
/>
<Label className="mr-2 w-auto text-xl text-gray-600">
하수처리구역명
</Label>
<Input
className="mr-8 w-[100px] !text-xl placeholder:text-center"
placeholder="입력"
value={변수1}
onChange={(e) => {
set변수1(e.target.value);
}}
/>
<Label className="mr-2 w-auto text-xl text-gray-600">
하수처리시설명
</Label>
<Input
className='!text-xl w-[120px] placeholder:text-center mr-8'
placeholder="입력"
value={변수2}
onChange={(e) => {
set변수2(e.target.value);
}}
/>
</div>
</div>
)}
{/* 분기처리: 하수맨홀 */}
{basicType === "tb_ms_swage_mh" && (
<div className="mb-3">
<div className="mb-2 flex items-center">
{/* 설치년도 */}
<YearRangeSelectBox
start={yearFilter.시작}
end={yearFilter.종료}
onChange={(range) =>
setYearFilter({
...yearFilter,
시작: range.start,
종료: range.end,
})
}
startLabel="설치년도"
endLabel=""
/>
{/* 경과년도 */}
<Label className="mr-2 w-auto text-xl text-gray-600">
경과년도
</Label>
<Input
className="mr-8 w-[100px] !text-xl 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="mr-2 w-auto text-xl text-gray-600">
용도코드
</Label>
<Input
className="mr-8 w-[100px] !text-xl placeholder:text-center"
placeholder="입력"
value={변수1}
onChange={(e) => {
// 숫자만 입력 허용
const val = e.target.value.replace(/[^0-9]/g, "");
set변수1(val);
}}
/>
{/* 하수맨홀 구경 */}
<Label className="mr-2 w-auto text-xl text-gray-600">
구경(mm)
</Label>
<Input
className="w-[100px] !text-xl placeholder:text-center"
placeholder="입력"
value={시작1}
onChange={(e) => {
// 숫자만 입력 허용
const val = e.target.value.replace(/[^0-9]/g, "");
set시작1(val);
}}
/>
<span className="mx-2 text-xl font-semibold">~</span>
<Input
className="mr-8 w-[100px] !text-xl 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">
<YearRangeSelectBox
start={yearFilter.시작}
end={yearFilter.종료}
onChange={range => setYearFilter({ ...yearFilter, 시작: range.start, 종료: range.end })}
startLabel="설치년도"
endLabel=""
/>
{/* 경과년도 */}
<Label className="w-auto text-xl text-gray-600 mr-2">경과년도</Label>
<Input
className='!text-xl w-[100px] placeholder:text-center mr-8'
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="mr-2 w-auto text-xl text-gray-600">
일일처리용량
</Label>
<Input
className="w-[100px] !text-xl placeholder:text-center"
placeholder="입력"
value={시작1}
onChange={(e) => {
// 숫자만 입력 허용
const val = e.target.value.replace(/[^0-9]/g, "");
set시작1(val);
}}
/>
<span className="mx-2 text-xl font-semibold">~</span>
<Input
className="mr-8 w-[100px] !text-xl placeholder:text-center"
placeholder="입력"
value={종료1}
onChange={(e) => {
// 숫자만 입력 허용
const val = e.target.value.replace(/[^0-9]/g, "");
set종료1(val);
}}
/>
{/* 우수양수능력 - 우수맨홀 */}
<Label className="mr-2 w-auto text-xl text-gray-600">
우수양수능력
</Label>
<Input
className="w-[100px] !text-xl placeholder:text-center"
placeholder="입력"
value={시작2}
onChange={(e) => {
// 숫자만 입력 허용
const val = e.target.value.replace(/[^0-9]/g, "");
set시작2(val);
}}
/>
<span className="mx-2 text-xl font-semibold">~</span>
<Input
className="mr-8 w-[100px] !text-xl 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">
<YearRangeSelectBox
start={yearFilter.시작}
end={yearFilter.종료}
onChange={range => setYearFilter({ ...yearFilter, 시작: range.start, 종료: range.end })}
startLabel="설치년도"
endLabel=""
/>
{/* 경과년도 */}
<Label className="w-auto text-xl text-gray-600 mr-2">경과년도</Label>
<Input
className='!text-xl w-[100px] placeholder:text-center mr-8'
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="mr-2 w-auto text-xl text-gray-600">
하천명
</Label>
<Input
className="mr-8 w-[100px] !text-xl placeholder:text-center"
placeholder="입력"
value={변수1}
onChange={(e) => {
set변수1(e.target.value);
}}
/>
{/* 계획방류량 */}
<Label className="mr-2 w-auto text-xl text-gray-600">
계획방류량
</Label>
<Input
className="w-[100px] !text-xl placeholder:text-center"
placeholder="입력"
value={시작1}
onChange={(e) => {
// 숫자만 입력 허용
const val = e.target.value.replace(/[^0-9]/g, "");
set시작1(val);
}}
/>
<span className="mx-2 text-xl font-semibold">~</span>
<Input
className="mr-8 w-[100px] !text-xl placeholder:text-center"
placeholder="입력"
value={종료1}
onChange={(e) => {
// 숫자만 입력 허용
const val = e.target.value.replace(/[^0-9]/g, "");
set종료1(val);
}}
/>
</div>
</div>
)}
{/* 조회 버튼 중앙 정렬 */}
<div className="absolute bottom-4 right-4 mr-2">
<Button className="text-base py-2" onClick={handleSearch}>조회</Button>
</div>
</div>
)}
{/* 운영정보 탭 */}
{activeTab === 'operation' && (
<div className="relative flex flex-col border rounded-lg p-6 mb-4 w-full h-[23vh]">
<div className="flex items-center mb-6">
<SelectBox2
className="w-60 mr-8"
selectClass="rounded-lg bg-white text-xl"
label="업무구분"
selectArray={WORK_TYPES.map((w) => ({
val: w.val,
text: w.text,
}))}
onSelectValue={(val) => setWorkType(String(val))}
value={workType}
/>
{/* 설치년도 */}
<YearRangeSelectBox
start={yearFilter.시작}
end={yearFilter.종료}
onChange={(range) =>
setYearFilter({
...yearFilter,
시작: range.start,
종료: range.end,
})
}
startLabel="설치년도"
endLabel=""
/>
<div className="flex items-center">
{/* 경과년도 */}
<Label className="mr-2 w-auto text-xl text-gray-600">
경과년도
</Label>
<Input
className="mr-8 w-[100px] !text-xl placeholder:text-center"
placeholder="입력"
value={yearFilter.경과년도}
onChange={(e) => {
// 숫자만 입력 허용
const val = e.target.value.replace(/[^0-9]/g, "");
setYearFilter({ ...yearFilter, 경과년도: val });
}}
/>
</div>
</div>
{/* 조회 버튼 중앙 정렬 */}
<div className="absolute bottom-4 right-4 mr-2">
<Button className="text-base py-2" onClick={handleSearch}>조회</Button>
</div>
</div>
)}
{/* 하수도정비기본계획보고서 탭 */}
{activeTab === 'report' && (
<div className="relative flex flex-col border rounded-lg p-6 mb-4 w-full h-[23vh]">
{/* 시도, 시군구, 하수처리구역 한 줄 */}
<div className="mb-6 flex items-center">
<SidoSigunguSelectBox
sido={시도}
sigungu={시군구}
onSidoChange={(sidoId) => {
set시도(sidoId);
set시군구(0);
}}
onSigunguChange={set시군구}
/>
</div>
<div className="flex justify-between items-center mb-6">
<div className="flex items-center gap-4">
<SelectBox2
className="w-auto mr-2"
selectClass="rounded-lg bg-white text-xl"
label="하수처리구역"
selectArray={처리구역List.map(opt => ({ val: opt.val, text: opt.text }))}
onSelectValue={val => set처리구역(String(val))}
value={처리구역}
/>
<Input
className='!text-xl w-[100px] placeholder:text-center mr-8'
placeholder="입력"
value={변수1}
onChange={(e) => {
// 숫자만 입력 허용
const val = e.target.value.replace(/[^0-9]/g, '');
set변수1(val);
}}
/>
<YearRangeSelectBox
start={yearFilter.시작}
end={yearFilter.종료}
onChange={range => setYearFilter({ ...yearFilter, 시작: range.start, 종료: range.end })}
startLabel="승인년도"
endLabel=""
/>
</div>
{/* 조회 버튼 중앙 정렬 */}
<div className="absolute bottom-4 right-4 mr-2">
<Button className="text-base py-2" onClick={handleSearch}>조회</Button>
</div>
</div>
</div>
)}
</UITab2>
{/* 📋 결과 리스트 */}
<div className="h-[60vh] w-full space-y-2 overflow-hidden rounded-lg border">
{/* 테이블 영역 (가로/세로 스크롤 가능) */}
<div className="flex-1 px-2 pb-2">
<LoadingOverlay isLoading={isLoading}/>
<UiTable2
headName={
(() => {
const resultCount = tableData.length;
const label =
activeTab === "basic"
? BASIC_TYPES.find((type) => type.val === basicType)?.text
: activeTab === "operation"
? WORK_TYPES.find((type) => type.val === workType)?.text
: activeTab === "report"
? "하수도정비기본계획보고서"
: TABS.find((tab) => tab.val === activeTab)?.text;
return (
<>
{label} 조회 결과 ({tableCount})건
</>
);
})()
}
tableData={tableData}
headlist={dynamicHeadlist}
pageSize={tableData.length < 10 ? tableData.length : 10}
total={tableCount}
children={undefined}
cellClick={handleCellClick}
onPageChange={setCurPage}
resetPageNo={false}
cur={curPage}
/>
</div>
</div>
</>
);
return (
<UiModal
open={open}
onOpenChange={onOpenChange}
bodyClass={`
h-[100vh]
w-[60vw]
max-w-none
max-h-none
overflow-visible
z-[101]
`}
body={renderBody()}
/>
);
};
export default React.memo(SearchModal);