455 lines
17 KiB
TypeScript
455 lines
17 KiB
TypeScript
"use client"
|
|
|
|
import { Button, Input, Label } from "@common/components/ui";
|
|
import React, { useCallback, useEffect, useRef, useState, useMemo } from "react";
|
|
import { useForm } from "react-hook-form";
|
|
import { MapSearchFormLeft } from "./mapSearchForm";
|
|
import { useWrStore } from "@/app/store";
|
|
import { btnClass } from "@/app/store/common/code";
|
|
import { SearchBaseForm } from "@common/business_components";
|
|
import { FormUi, SelectBox, SearchFormLeft, SearchFormRight, Form, FormField } from "@common/business_components/ui";
|
|
import type { ReturnType, SelectDataType, EnvPartSelectDataType } from "@common/business_components/ui";
|
|
import SearchInputText from "@/components/ui/searchInput/searchInput";
|
|
import { zodResolver } from "@hookform/resolvers/zod";
|
|
|
|
|
|
// 건물 검색 타입 정의
|
|
interface BuildingInfo {
|
|
bdstInfoPk: number;
|
|
ctpvCd: number;
|
|
sggCd: number;
|
|
daddr: string;
|
|
bdstNm: string;
|
|
frstArchPrmsnDt: string;
|
|
archArea: number;
|
|
ctpvNm: string | null;
|
|
sggNm: string | null;
|
|
lat: number | null;
|
|
lot: number | null;
|
|
regDt: string;
|
|
rgtr: string | null;
|
|
mdfcnDt: string;
|
|
mdfr: string | null;
|
|
}
|
|
|
|
type SearchBaseType = {
|
|
searchType: string;
|
|
selectSidoData?: SelectDataType[] | [];
|
|
selectSigunData?: SelectDataType[] | [];
|
|
selectRegionData?: SelectDataType[] | [];
|
|
selectUnitWaterShedData?: SelectDataType[] | [];
|
|
selectMiddleRegionData?: SelectDataType[] | [];
|
|
selectEnvPartData?: EnvPartSelectDataType[] | [];
|
|
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,
|
|
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 [selectedBuilding, setSelectedBuilding] = useState<BuildingInfo | null>(null);
|
|
const [buildingSearchText, setBuildingSearchText] = useState("");
|
|
const [buildingList, setBuildingList] = useState<BuildingInfo[]>([]);
|
|
|
|
// 전체 목록
|
|
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 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]);
|
|
|
|
// 건물 검색 API 호출 함수
|
|
const searchBuildings = async (searchText: string) => {
|
|
if (!searchText.trim()) {
|
|
setBuildingList([]);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const flg = "41"; // 적절한 플래그 값으로 설정
|
|
const sigunCode = selectedSigun.val !== '0' ? selectedSigun.val : '';
|
|
const apiEndpoint = `${API_URL}/v1/reuseFc/building/${flg}/${sigunCode}/${encodeURIComponent(searchText)}`;
|
|
|
|
const response = await fetch(apiEndpoint, {
|
|
method: "GET",
|
|
headers: {
|
|
accept: "application/json",
|
|
"Content-Type": "application/json"
|
|
}
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`API 호출 실패: ${response.status}`);
|
|
}
|
|
|
|
const result = await response.json();
|
|
|
|
if (result.status === 200 && Array.isArray(result.data)) {
|
|
// 건물 검색 결과를 demandCompList 형태로 변환
|
|
const convertedList = result.data.map((building: BuildingInfo) => ({
|
|
dmdInfoPk: String(building.bdstInfoPk),
|
|
dmdCoNm: building.bdstNm,
|
|
daddr: building.daddr,
|
|
useUsgCd: "",
|
|
splyCpct: 0,
|
|
splyFmYn: "Y",
|
|
// 추가 필드들...
|
|
}));
|
|
setBuildingList(result.data);
|
|
// 글로벌 상태에도 업데이트 (SearchInputText가 사용할 수 있도록)
|
|
// getDemandCompList를 직접 호출하는 대신 상태를 직접 설정
|
|
const { setBuildingSearchResults } = useWrStore.getState();
|
|
if (setBuildingSearchResults) {
|
|
setBuildingSearchResults(convertedList);
|
|
}
|
|
} else {
|
|
setBuildingList([]);
|
|
}
|
|
} catch (error) {
|
|
console.error("건물 검색 중 오류 발생:", error);
|
|
setBuildingList([]);
|
|
}
|
|
};
|
|
|
|
// 조회 함수
|
|
const queryVal = () => {
|
|
console.log("조회버튼 클릭");
|
|
if (searchType == '공급처 후보지 조회') {
|
|
// queryClick({
|
|
// partValue: partValue,
|
|
// sidoValue: sidoValue,
|
|
// sigunValue: sigunValue,
|
|
// regionValue: regionValue,
|
|
// unitWaterShedValue: untitWaterShedValue,
|
|
// middleRegionValue: middleRegionValue,
|
|
// envPartValue: envPartValue,
|
|
// sidoText: selectedSido.text,
|
|
// sigunText: selectedSigun.text,
|
|
// });
|
|
} else if (searchType == '우리동네가뭄지도보기') {
|
|
} else if (searchType == '지도보기') {
|
|
|
|
}
|
|
}
|
|
|
|
// 초기화 함수
|
|
const initVal = () => {
|
|
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);
|
|
};
|
|
|
|
// 적용 버튼
|
|
const applyVal = () => {
|
|
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={code.cityCd}
|
|
onSelectValue={(val) => {
|
|
const selected = code.cityCd.find(item => (item.val === val));
|
|
setSelectedSido(selected || { text: "전체", val: '0' });
|
|
setSelectedSigun({ 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' });
|
|
setSelectedFclt({ text: "전체", val: '0', sggNm: '전체' });
|
|
}}
|
|
value={selectedSigun.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> */}
|
|
<div className="flex items-center">
|
|
<Label className="w-20 text-xl text-gray-600">시설명</Label>
|
|
<div className="w-64">
|
|
<SearchInputText
|
|
label="시설명"
|
|
initValue={buildingSearchText}
|
|
setValue={(searchText) => {
|
|
setBuildingSearchText(searchText || "");
|
|
if (searchText) {
|
|
searchBuildings(searchText);
|
|
}
|
|
}}
|
|
setRtnData={(buildingData) => {
|
|
if (buildingData && buildingData.length > 0 && buildingData[0]) {
|
|
// 선택된 건물 정보를 찾아서 설정
|
|
const selectedBuildingInfo = buildingList.find(building =>
|
|
building.bdstNm === buildingData[0].dmdCoNm
|
|
);
|
|
setSelectedBuilding(selectedBuildingInfo || null);
|
|
console.log("선택된 시설:", selectedBuildingInfo);
|
|
}
|
|
}}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</>
|
|
)}
|
|
{searchType == "우리동네가뭄지도보기" && (
|
|
<>
|
|
<SelectBox
|
|
className="flex items-center"
|
|
label={'시도'}
|
|
selectArray={code.cityCd}
|
|
onSelectValue={(val) => {
|
|
const selected = code.cityCd.find(item => (item.val === val));
|
|
setSelectedSido(selected || { text: "전체", val: '0' });
|
|
setSelectedSigun({ text: "전체", val: '0' });
|
|
}}
|
|
value={selectedSido.val}
|
|
selectClass={'w-44'}
|
|
/>
|
|
<SelectBox
|
|
className="flex items-center"
|
|
label={'시군구'}
|
|
selectArray={filteredSigunData}
|
|
onSelectValue={(val) => {
|
|
const selected = filteredSigunData.find(item => (item.val === val));
|
|
setSelectedSigun(selected || { text: "전체", val: '0' });
|
|
setSelectedDong({ text: "전체", val: '0', sggNm: '전체' });
|
|
}}
|
|
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={code.cityCd}
|
|
selectSigunData={filteredSigunData}
|
|
selectRegionData={code.rvbsnCd}
|
|
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>
|
|
)
|
|
}
|