Files
frontend/apps/wr/app/map/mapSearchBar.tsx
T

371 lines
14 KiB
TypeScript

"use client"
import { Button, Input, Label } from "@common/components/ui";
import React, { useCallback, useEffect, useRef, useState, useMemo } from "react";
import { SearchFormLeft, type EnvPartSelectDataType } from "@common/business_components/ui/search_form_left";
import { SearchFormRight } from "@common/business_components/ui/search_form_right";
import type { SelectDataType } from "@common/business_components/ui/search_input_select";
import type { ReturnType } from "@common/business_components/ui/search_base_form";
import { MapSearchFormLeft } from "./mapSearchForm";
import { useWrStore } from "@/app/store";
import { btnClass } from "@/app/store/common/code";
import { SelectBox } from "@common/business_components/ui/select";
import { SearchBaseForm } from "@common/business_components";
type SearchBaseType = {
searchType: string;
selectSidoData?: SelectDataType[] | [];
selectSigunData?: SelectDataType[] | [];
selectRegionData?: SelectDataType[] | [];
selectUnitWaterShedData?: SelectDataType[] | [];
selectMiddleRegionData?: SelectDataType[] | [];
selectEnvPartData?: EnvPartSelectDataType[] | [];
queryClick: (returnType: ReturnType) => void;
initClick: () => void;
onFeatureSelect: () => void;
btnClass?: string;
ref?: React.RefObject<ReturnType>;
isAdmin?: boolean;
leftClassName?: string;
rightClassName?: string;
showQueryButton?: boolean;
showInitButton?: boolean;
showApplyButton?: boolean;
}
const API_URL = `${process.env.NEXT_PUBLIC_SEWERAGE_API_URL}`;
const YEARS = Array.from({ length: 100 }, (_, i) => `${2025 - i}`);
interface SearchModalProps {
open: boolean;
onOpenChange: (open: boolean) => void;
onFeatureSelect: (row: any) => void;
}
interface SelectedDataTypeWithSggNm {
text: string;
val: string;
sggNm: string;
}
export function MapSearchBar({
searchType,
selectSidoData,
selectSigunData,
selectRegionData,
selectUnitWaterShedData,
selectMiddleRegionData,
selectEnvPartData,
btnClass,
queryClick,
initClick,
onFeatureSelect,
isAdmin = false,
leftClassName,
rightClassName,
showQueryButton = true,
showInitButton = true,
showApplyButton = true
}: SearchBaseType) {
// 로딩바
const [isLoading, setIsLoading] = useState(false);
// 상세 정보 조회를 위한 상태 추가
const [popupFeature, setPopupFeature] = useState<any>(null);
const { code } = useWrStore((state) => state);
const [isChHead, setIsChHead] = useState(false);
const ref = useRef<ReturnType>({} as ReturnType);
// 전체 목록
const [partValue, setPartValue] = useState<string>("A");
const [sidoValue, setSidoValue] = useState<string>("0");
const [sigunValue, setSigunValue] = useState<string>("0");
const [regionValue, setRegionValue] = useState<string>("0");
const [untitWaterShedValue, setUntitWaterShedValue] = useState<string>("0");
const [middleRegionValue, setMiddleRegionValue] = useState<string>("0");
const [envPartValue, setEnvPartValue] = useState<string>("0");
const [resetTrigger, setResetTrigger] = useState(false);
const [sidoText, setSidoText] = useState<string>("0");
const [sigunText, setSigunText] = useState<string>("0");
// 공급처 후보지 조회 필터 목록
const [selectedSido, setSelectedSido] = useState<SelectDataType>({ text: "전체", val: '0' });
const [selectedSigun, setSelectedSigun] = useState<SelectDataType>({ text: "전체", val: '0' });
const [selectedFclt, setSelectedFclt] = useState<SelectedDataTypeWithSggNm>({ text: "전체", val: '0', sggNm: '전체' }); // 시설명 선택 상태
// 전체 시설
let allFclt: SelectedDataTypeWithSggNm[] = [{ text: "전체", val: '0', sggNm: '전체' }];
// 우리동네가뭄지도 필터 목록
const [selectedDong, setSelectedDong] = useState<SelectedDataTypeWithSggNm>({ text: "전체", val: '0', sggNm: '전체' }); // 시설명 선택 상태 // 행정동
const [allSido, setAllSido] = useState<SelectDataType[]>([
{ text: "전체", val: '0' },
...(code?.cityCd || [])
]);
const [allRegion, setAllRegion] = useState<SelectDataType[]>([
{ text: "전체", val: '0' },
...(code?.rvbsnCd || [])
]);
const filteredSigunData = useMemo(() => {
if (!selectedSido || selectedSido.val === '0' || !selectedSido.val) return [{ text: "전체", val: '0' }];
return [
{ text: "전체", val: '0' },
...code.sggCd.filter(item => item.val.substring(0, 2) === selectedSido.val.substring(0, 2))
];
}, [selectedSido]);
const filteredFcltData = useMemo(() => {
if (!selectedSigun || selectedSigun.val === '0' || !selectedSigun.val) return [{ text: "전체", val: '0', sggNm: '전체' }];
return [
{ text: "전체", val: '0', sggNm: '전체' },
...allFclt.filter(item => item.sggNm === selectedSido.text)
];
}, [selectedSigun]);
const filteredDong = useMemo(() => {
if (!selectedSigun || selectedSigun.val === '0' || !selectedSigun.val) return [{ text: "전체", val: '0', sggNm: '전체' }];
return [
{ text: "전체", val: '0', sggNm: '전체' },
// ...code.dong.filter(item => item.sggNm === selectedSido.text)
];
}, [selectedSigun]);
const queryVal = () => {
if (searchType == '공급처 후보지 조회') {
} else if (searchType == '우리동네가뭄지도보기') {
} else if (searchType == '지도보기') {
console.log("조회버튼 클릭");
queryClick({
partValue: partValue,
sidoValue: sidoValue,
sigunValue: sigunValue,
regionValue: regionValue,
unitWaterShedValue: untitWaterShedValue,
middleRegionValue: middleRegionValue,
envPartValue: envPartValue,
sidoText: selectedSido.text,
sigunText: selectedSigun.text,
});
}
}
const initVal = useCallback(() => {
setPartValue("A");
setSidoValue("0");
setSigunValue("0");
setRegionValue("0");
setUntitWaterShedValue("0");
setMiddleRegionValue("0");
setEnvPartValue("0");
setSidoText("전체");
setSigunText("전체");
setSelectedSido({ text: "전체", val: '0' });
setSelectedSigun({ text: "전체", val: '0' });
setSelectedFclt({ text: "전체", val: '0', sggNm: '전체' });
setSelectedDong({ text: "전체", val: '0', sggNm: '전체' });
setResetTrigger(true);
initClick(); // 부모로부터 받은 초기화 함수 호출
}, [initClick]);
const applyVal = useCallback(() => {
onFeatureSelect
}, [onFeatureSelect]);
useEffect(() => {
if (resetTrigger) {
setResetTrigger(false);
}
}, [resetTrigger]);
// 시설 조회
useEffect(() => {
const fetchData = async () => {
setIsLoading(true);
if (!selectedSigun || selectedSigun.val === '0' || !selectedSigun.val) {
allFclt = ([{ text: "전체", val: '0', sggNm: '전체' }]);
setIsLoading(false);
return;
}
try {
const queryParamsFacility = new URLSearchParams({
lyrTypeNm: 'tb_il_swage_prcs_fclt',
page: '0',
size: '100000',
sort: 'string',
});
const cqlFiltersFacility: string[] = [];
if (selectedSigun.text && selectedSigun.val !== '0') {
cqlFiltersFacility.push(`sgg_nm ILIKE '%${selectedSigun.text}%'`);
}
if (cqlFiltersFacility.length > 0) {
queryParamsFacility.append('cqlFiter', cqlFiltersFacility.join(' and '));
}
console.log("BaseLayout API URL for Facilities:", `v1/fclts?${queryParamsFacility.toString()}`);
const facilityRes = await fetch(`${API_URL}/v1/fclts?${queryParamsFacility.toString()}`, {
method: "GET",
headers: { accept: "application/json" }
}).then(res => res.json());
const facilities = (facilityRes.data || []).map((item: any) => ({
text: item.swtrmtFcltNm,
val: item.fid,
sggNm: item.sggNm
}));
allFclt = (facilities);
} catch (error) {
console.error("BaseLayout: 지역/시설 데이터 조회 중 오류 발생:", error);
allFclt = ([{ text: "전체", val: '0', sggNm: '전체' }]);
} finally {
setIsLoading(false);
}
};
fetchData();
}, [selectedSigun]);
useEffect(() => {
if (ref && ref.current) {
const customRef = ref.current;
customRef.partValue = partValue;
customRef.sidoValue = sidoValue;
customRef.sigunValue = sigunValue;
customRef.regionValue = regionValue;
customRef.unitWaterShedValue = untitWaterShedValue;
customRef.middleRegionValue = middleRegionValue;
customRef.envPartValue = envPartValue;
customRef.sidoText = selectedSido.text;
customRef.sigunText = selectedSigun.text;
}
}, [partValue, sidoValue, sigunValue, regionValue, untitWaterShedValue, middleRegionValue, envPartValue, ref]);
return (
<div className="absolute left-1/2 -translate-x-1/2 bg-white flex items-center justify-center p-4 rounded-md shadow-lg w-auto min-w-fit">
<div className={leftClassName ? leftClassName : `flex items-center justify-center space-x-2 `}>
<div className="flex gap-8 items-center">
{searchType == "공급처 후보지 조회" && (
<>
<SelectBox
className="flex items-center"
label={'시도'}
selectArray={allSido}
onSelectValue={(val) => {
const selected = code.cityCd.find(item => (item.val === val));
setSelectedSido(selected || { text: "전체", val: '0' });
}}
value={selectedSido.val}
selectClass={'w-32'}
/>
<SelectBox
className="flex items-center"
label={'시군구'}
selectArray={filteredSigunData}
onSelectValue={(val) => {
const selected = filteredSigunData.find(item => (item.val === val));
setSelectedSigun(selected || { text: "전체", val: '0' });
}}
value={selectedSigun.val}
selectClass={'w-32'}
/>
<SelectBox
className="flex items-center"
label={'시설명'}
selectArray={filteredFcltData}
onSelectValue={(val) => {
const selected = allFclt.find(item => (item.val === val));
setSelectedFclt(selected || { text: "전체", val: '0', sggNm: '전체' });
}}
value={selectedFclt.val}
selectClass={'w-32'}
/>
<Label className="w-auto text-xl text-gray-600 whitespace-nowrap">건물명</Label>
<div className="w-44">
<Input className="mr-4" />
</div>
</>
)}
{searchType == "우리동네가뭄지도보기" && (
<>
<SelectBox
className="flex items-center"
label={'시도'}
selectArray={allSido}
onSelectValue={(val) => {
const selected = code.cityCd.find(item => (item.val === val));
setSelectedSido(selected || { text: "전체", val: '0' });
}}
value={selectedSido.val}
selectClass={'w-32'}
/>
<SelectBox
className="flex items-center"
label={'시군구'}
selectArray={filteredSigunData}
onSelectValue={(val) => {
const selected = filteredSigunData.find(item => (item.val === val));
setSelectedSigun(selected || { text: "전체", val: '0' });
}}
value={selectedSigun.val}
selectClass={'w-32'}
/>
<SelectBox
className="flex items-center mr-8"
label={'행정동'}
selectArray={filteredDong}
onSelectValue={(val) => {
const selected = filteredDong.find(item => (item.val === val));
setSelectedDong(selected || { text: "전체", val: '0', sggNm: '전체' });
}}
value={selectedDong.val}
selectClass={'w-32'}
/>
</>
)}
{searchType == "지도보기" && (
<div className="flex items-center gap-2 w-[800px]">
<div className="flex min-w-0"> {/* SearchFormLeft 래퍼 */}
<MapSearchFormLeft
isShowPart={true}
selectSidoData={allSido}
selectSigunData={filteredSigunData}
selectRegionData={allRegion}
selectUnitWaterShedData={selectUnitWaterShedData}
selectMiddleRegionData={selectMiddleRegionData}
selectEnvPartData={selectEnvPartData}
setRtnPartValue={setPartValue}
setRtnSidoValue={setSidoValue}
setRtnSigunValue={setSigunValue}
setRtnRegionValue={setRegionValue}
setRtnUnitWaterShedValue={setUntitWaterShedValue}
setRtnMiddleRegionValue={setMiddleRegionValue}
setRtnEnvPartValue={setEnvPartValue}
setRtnEnvPartSidoValue={setSidoValue}
setRtnEnvPartSigunValue={setSigunValue}
setRtnSidoText={setSidoText}
setRtnSigunText={setSigunText}
resetTrigger={resetTrigger}
isAdmin={isAdmin}
children={undefined}
/>
</div>
<Label className="w-auto text-xl text-gray-600 whitespace-nowrap">시설명</Label>
<div className="w-44">
<Input className="mr-4" />
</div>
</div>
)}
</div>
{/* 버튼 부분 */}
<div className="flex items-center justify-center gap-4">
{showQueryButton && <Button className={btnClass} onClick={queryVal}>조회</Button>}
{showInitButton && <Button className={btnClass} onClick={initVal}>초기화</Button>}
{showApplyButton && <Button className={btnClass} onClick={applyVal}>적용</Button>}
</div>
</div>
</div>
)
}