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

352 lines
11 KiB
TypeScript

"use client";
import { SelectBox, type SelectDataType } from "@common/business_components/ui";
import { Button } from "@common/components/ui";
import { useCallback, useEffect, useState } from "react";
import SearchInputText from "./SearchInputText";
const FACILITY_TYPE_URL_MAP: Record<string, string> = {
tb_il_swage_prcs_fclt:
"/geoserver/hasudo/ows?service=WFS&version=1.0.0&request=GetFeature&typeName=hasudo:tb_il_swage_prcs_fclt&outputFormat=application/json&propertyName=swtrmt_fclt_cd,swtrmt_fclt_nm&cql_filter=",
tb_wr_bdst_ruse_fclt:
"/reuse-service/reuseFc/pubDetailList/{year}/{sggCd}/1/1000/",
tb_wr_rnwtr_utztn_fclt:
"/reuse-service/reuseFc/rainyInfoList/1/1000/{sggCd}/{fcltyNm}",
tb_wr_rswtr_fclt:
"/reuse-service/reuseFc/waterreuseFc/1/1000/{sggCd}/{fcltyNm}",
tb_wr_htdfl_fclt:
"/reuse-service/drain/facilityList/1/1000/{sggCd}/{fcltyNm}",
tb_wr_ruse_fclt_dmd_co:
"/reuse-service/reuseFc/request/1/1000/{sggCd}/{fcltyNm}",
tb_wr_tmpr_dmd_co:
"/reuse-service/reuseFc/request/temp/1/1000/{sidoNm} {sggNm}/{fcltyNm}",
};
interface SearchBarProps {
gisMaps: React.RefObject<any>;
facilityTypes: { text: string; val: string }[];
onFeatureFocus: any;
}
export function SearchBar({
gisMaps,
facilityTypes,
onFeatureFocus,
}: SearchBarProps) {
console.log('gisMaps ', gisMaps)
console.log('onFeatureFocus ', onFeatureFocus)
const [sidoValue, setSidoValue] = useState("0");
const [sigunValue, setSigunValue] = useState("0");
const [facilityType, setFacilityType] = useState(
facilityTypes?.length === 1 && facilityTypes[0] ? facilityTypes[0].val : "0"
);
const [fcltyNm, setFcltyNm] = useState("");
const [searchList, setSearchList] = useState<{ fnm: string; fid: string }[]>(
[]
);
const [sidoData, setSidoData] = useState<SelectDataType[]>([
{ text: "시도", val: "0" },
]);
const [sigunData, setSigunData] = useState<SelectDataType[]>([
{ text: "시군구", val: "0" },
]);
const [filteredSigunData, setFilteredSigunData] = useState<SelectDataType[]>([
{ text: "시군구", val: "0" },
]);
const fetchSido = async () => {
const res = await fetch("/cms-service/api/v1/code/cache/ADSTDSTRCD/Y");
const json = await res.json();
const sidoList = json
.filter((item: any) => item.lwpoCd?.length === 2)
.map((item: any) => ({ val: item.lwpoCd, text: item.lwpoCdNm }));
const sigunList = json
.filter((item: any) => item.lwpoCd?.length > 2)
.map((item: any) => ({ val: item.lwpoCd, text: item.lwpoCdNm }));
setSidoData([{ text: "시도", val: "0" }, ...sidoList]);
setSigunData(sigunList);
};
const initializeSigunData = useCallback(
(sidoCd: string) => {
if (sidoCd === "0") {
setFilteredSigunData([{ text: "시도", val: "0" }]);
setSigunValue("0");
return;
}
const prefix = sidoCd.substring(0, 2);
const filtered = sigunData.filter((item) => item.val.startsWith(prefix));
setFilteredSigunData([{ text: "시군구", val: "0" }, ...filtered]);
setSigunValue("0");
},
[sigunData]
);
useEffect(() => {
fetchSido();
}, []);
const onSelectValue = useCallback(
(val: string, type: string) => {
if (type === "ctpvCd") {
setSidoValue(val);
initializeSigunData(val);
setSearchList([]);
setFcltyNm("");
} else if (type === "sggCd") {
setSigunValue(val);
setSearchList([]);
setFcltyNm("");
} else if (type === "facilityType") {
setFacilityType(val);
setSearchList([]);
setFcltyNm("");
}
},
[initializeSigunData]
);
const handleSearch = async () => {
if (!fcltyNm) return alert("시설명을 입력하세요.");
const sidoNm = sidoData.find((item) => item.val === sidoValue)?.text || "";
const sggNm = sigunData.find((item) => item.val === sigunValue)?.text || "";
const type = facilityType;
const baseUrlTemplate = FACILITY_TYPE_URL_MAP[type];
if (!baseUrlTemplate) return;
let url = baseUrlTemplate;
const year = new Date().getFullYear().toString();
url = url
.replace("{year}", year)
.replace("{sggCd}", sigunValue)
.replace("{sidoNm}", encodeURIComponent(sidoNm))
.replace("{sggNm}", encodeURIComponent(sggNm))
.replace("{fcltyNm}", encodeURIComponent(fcltyNm));
let list: SelectDataType[] = [];
if (type === "tb_il_swage_prcs_fclt") {
const cqlFilter = `ctpv_nm='${sidoNm}' and sgg_nm='${sggNm}' and swtrmt_fclt_nm like '%${fcltyNm}%'`;
url += encodeURIComponent(cqlFilter);
const res = await fetch(url);
const json = await res.json();
list = json.features.map((f: any) => ({
text: f.properties.swtrmt_fclt_nm ?? "(이름없음)",
val: f.id,
}));
} else if (
type === "tb_wr_bdst_ruse_fclt" // 재이용시설
) {
//재이용에서 참조하는 하수처리시설 목록
const procRes = await fetch("/reuse-service/reuseFc/pubFc");
const procJson = await procRes.json();
const procData = procJson.data;
const procResult = procData
.filter((code: string) => code.slice(0, 5) === sigunValue)
.map((code: string) => {
return code;
});
//처리시설명으로 처리시설코드 조회
let procUrl2 =
"/operation-service/prcsFclt/ruseInfo/1/1000/A/{sggCd}/0/1/SW/{fcltyNm}";
procUrl2 = procUrl2
.replace("{sggCd}", sigunValue)
.replace("{fcltyNm}", encodeURIComponent(fcltyNm));
const procRes2 = await fetch(procUrl2, {
method: "POST",
headers: {
accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify(procResult),
});
const procJson2 = await procRes2.json();
const procResult2 = (procJson2.data || []).map((item: any) => item.fid);
//최종 처리시설코드로 재이용시설조회
const res = await fetch(url, {
method: "POST",
headers: {
accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify(procResult2),
});
const json = await res.json();
list = (json.data || []).map((item: any) => ({
text: item.ruseUtztnFcltNm ?? "(이름없음)",
val: item.pbnstRuseFcltId,
}));
} else if (
type === "tb_wr_ruse_fclt_dmd_co" //수요처
) {
const res = await fetch(url);
const json = await res.json();
list = (json.data || []).map((item: any) => ({
text: item.dmdCoNm ?? "(이름없음)",
val: item.dmdInfoId,
}));
} else if (
type === "tb_wr_tmpr_dmd_co" //잠재수요처
) {
// GET 방식
const res = await fetch(url);
const json = await res.json();
list = (json.data || []).map((item: any) => ({
text: item.dmdCoNm ?? "(이름없음)",
val: item.fctryMngSn,
}));
} else {
// GET 방식
const res = await fetch(url);
const json = await res.json();
list = (json.data || []).map((item: any) => ({
text: item.bdstNm ?? "(이름없음)",
val: item.bdstInfoSn,
}));
}
if (list.length === 0) {
alert("검색 결과가 없습니다.");
setSearchList([]);
setFcltyNm("");
return;
}
const newList = list.map((item: any) => {
const code = item.val;
const name = item.text;
return { fnm: name, fid: code };
});
setSearchList(newList);
};
const moveFocus = (feature: any) => {
console.log('feature ' ,feature)
// console.log('searchbar:moveFocus')
// const map = gisMaps.current;
// if (!map) {
// console.warn("지도가 초기화되지 않았습니다.");
// return;
// }
// // 항상 같은 select 객체 사용
// const select = GisApp.Module.select as any;
// if (!select) return;
// // 1. 기존 선택 초기화
// select.resetSelectedFeature();
// // 2. data가 OpenLayers Feature 인스턴스인지 확인
// let foundFeature: any = null;
// let featureId = data.fid;
// const layerName = data.layerNm;
// map
// .getLayers()
// .getArray()
// .forEach((layer: any) => {
// const source = layer.getSource?.();
// if (source?.getFeatureById) {
// const feature = source.getFeatureById(String(featureId).trim());
// if (feature) foundFeature = feature;
// }
// });
// // 3. 피처 선택
// if (foundFeature) {
// const geometry = foundFeature.getGeometry();
// if (geometry) {
// const extent = geometry.getExtent();
// map.getView().fit(extent, {
// duration: 300,
// padding: [50, 50, 50, 50],
// maxZoom: 16,
// constrainResolution: true,
// nearest: true,
// });
// }
// // select.selectFeature(foundFeature, true);
// } else {
// console.warn("OpenLayers Feature를 찾을 수 없습니다:", data);
// }
};
return (
<div className="grid grid-cols-4 gap-4">
<div className="col-span-4">
<div className="card card-border flex">
<div className="col-span-10 flex flex-row">
{facilityTypes.length > 1 && (
<SelectBox
labelClass="w-[120px]"
className="mr-5 w-[150px]"
selectClass="rounded-lg bg-white"
label=""
selectArray={[
{ text: "시설 구분", val: "0" },
...facilityTypes,
]}
onSelectValue={(val) =>
onSelectValue(val.toString(), "facilityType")
}
value={facilityType}
/>
)}
<SelectBox
labelClass="w-[40px]"
className="mr-5 w-[150px]"
selectClass="rounded-lg bg-white"
label=""
selectArray={sidoData}
onSelectValue={(val) => onSelectValue(val.toString(), "ctpvCd")}
value={sidoValue}
/>
<SelectBox
labelClass="w-[65px]"
className="mr-5 w-[150px]"
selectClass="rounded-lg bg-white"
label=""
selectArray={filteredSigunData}
onSelectValue={(val) => onSelectValue(val.toString(), "sggCd")}
value={sigunValue}
/>
<SearchInputText
initValue={fcltyNm}
setValue={(val: string = "") => {
setFcltyNm(val);
}}
setRtnData={(selected: { fnm: string; fid: string }) => {
// 선택된 항목이 있을 경우 onFeatureSelect 실행
const type = facilityType;
moveFocus({
layerNm: type,
...selected,
});
}}
searchList={searchList}
onEnter={handleSearch}
className="mr-5 w-[150px]"
/>
</div>
<div className="flex flex-row space-x-2">
<Button
color="primary"
className="mx-2 mt-1"
size="sm"
onClick={handleSearch}
>
조회
</Button>
</div>
</div>
</div>
</div>
);
}