299 lines
10 KiB
TypeScript
299 lines
10 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";
|
|
|
|
const geoserverUrl =
|
|
"/geoserver/hasudo/ows?service=WFS&version=1.0.0&request=GetFeature&outputFormat=application/json";
|
|
|
|
const FACILITY_TYPE_URL_MAP: Record<string, string> = {
|
|
tb_il_swage_prcs_fclt: geoserverUrl, //하수처리시설
|
|
tb_wr_bdst_ruse_fclt:
|
|
"/reuse-service/reuseFc/pubDetailList/{year}/{sggCd}/1/1000", //재이용시설 post
|
|
tb_wr_rnwtr_utztn_fclt: "/reuse-service/reuseFc/rainyInfoList/1/1000/{sggCd}", //빗물이용시설 get
|
|
tb_wr_rswtr_fclt: "/reuse-service/reuseFc/waterreuseFc/1/1000/{sggCd}", //중수도시설 get
|
|
tb_wr_htdfl_fclt: "/reuse-service/drain/facilityList/1/1000/{sggCd}", //온배수시설 get
|
|
tb_wr_ruse_fclt_dmd_co: "/reuse-service/reuseFc/request/1/1000/{sggCd}", //수요처 get
|
|
tb_wr_tmpr_dmd_co:
|
|
"/reuse-service/reuseFc/request/temp/1/1000/{sidoNm} {sggNm}", //잠재수요처 get
|
|
};
|
|
|
|
interface SearchBarProps {
|
|
onFeatureSelect: (feature: any) => void;
|
|
gisMaps: React.RefObject<any>;
|
|
facilityTypes: { text: string; val: string }[];
|
|
}
|
|
|
|
export function SearchBar({
|
|
onFeatureSelect,
|
|
gisMaps,
|
|
facilityTypes,
|
|
}: SearchBarProps) {
|
|
const [sidoValue, setSidoValue] = useState("0");
|
|
const [sigunValue, setSigunValue] = useState("0");
|
|
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 [facilityData, setFacilityData] = useState<SelectDataType[]>([
|
|
{ text: "선택", val: "0" },
|
|
]);
|
|
const [facilityValue, setFacilityValue] = useState("0");
|
|
|
|
const [facilityType, setFacilityType] = useState(
|
|
facilityTypes?.length === 1 && facilityTypes[0] ? facilityTypes[0].val : "0"
|
|
);
|
|
|
|
//시도, 시군구 조회
|
|
const fetchSido = async () => {
|
|
const url = `/cms-service/api/v1/code/cache/ADSTDSTRCD/Y`;
|
|
const res = await fetch(url);
|
|
const json = await res.json();
|
|
const sidoList = json
|
|
.filter(
|
|
(item: any) =>
|
|
typeof item.lwpoCd === "string" && item.lwpoCd.length === 2
|
|
)
|
|
.map((feature: any) => ({
|
|
val: feature.lwpoCd,
|
|
text: feature.lwpoCdNm,
|
|
}));
|
|
setSidoData([{ text: "선택", val: "0" }, ...sidoList]);
|
|
|
|
const sigunList = json
|
|
.filter(
|
|
(item: any) =>
|
|
typeof item.lwpoCd === "string" && item.lwpoCd.length !== 2
|
|
)
|
|
.map((feature: any) => ({
|
|
val: feature.lwpoCd,
|
|
text: feature.lwpoCdNm,
|
|
}));
|
|
setSigunData(sigunList);
|
|
};
|
|
|
|
//시설명 콤보박스 조회
|
|
const fetchFacilitiesBySggNm = async (
|
|
sggCd: string,
|
|
sggNm: string,
|
|
sidoNm: string,
|
|
currentFacilityType?: string
|
|
) => {
|
|
const type = currentFacilityType ?? facilityType;
|
|
if (!type || type === "0") return;
|
|
|
|
const baseUrlTemplate = FACILITY_TYPE_URL_MAP[type];
|
|
if (!baseUrlTemplate) return;
|
|
|
|
const year = new Date().getFullYear().toString();
|
|
let url = baseUrlTemplate
|
|
.replace("{year}", year)
|
|
.replace("{sggCd}", sggCd)
|
|
.replace("{sidoNm}", encodeURIComponent(sidoNm))
|
|
.replace("{sggNm}", encodeURIComponent(sggNm));
|
|
|
|
let list: SelectDataType[] = [];
|
|
|
|
if (
|
|
type === "tb_il_swage_prcs_fclt" //하수처리시설
|
|
) {
|
|
const cqlFilter = `ctpv_nm='${encodeURIComponent(sidoNm)}' AND sgg_nm='${encodeURIComponent(sggNm)}'`;
|
|
url = `${url}&typeName=hasudo:${type}&cql_filter=${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 paramsRes = await fetch("/reuse-service/reuseFc/pubFc");
|
|
const paramsJson = await paramsRes.json();
|
|
const params = paramsJson.data;
|
|
|
|
const res = await fetch(url, {
|
|
method: "POST",
|
|
headers: {
|
|
accept: "application/json",
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify(params),
|
|
});
|
|
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,
|
|
}));
|
|
}
|
|
|
|
setFacilityData([{ text: "선택", val: "0" }, ...list]);
|
|
setFacilityValue("0");
|
|
};
|
|
|
|
//시도, 시군구 초기 데이터 조회
|
|
useEffect(() => {
|
|
fetchSido();
|
|
}, []);
|
|
|
|
//시군구 데이터 초기화
|
|
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]
|
|
);
|
|
|
|
//콤보박스 선택시 연관 콤보박스 처리
|
|
const onSelectValue = useCallback(
|
|
(val: string, type: string) => {
|
|
if (type === "ctpvCd") {
|
|
setSidoValue(val);
|
|
initializeSigunData(val);
|
|
setFacilityData([{ text: "선택", val: "0" }]);
|
|
setFacilityValue("0");
|
|
} else if (type === "sggCd") {
|
|
setSigunValue(val);
|
|
const sidoNm =
|
|
sidoData.find((item) => item.val === sidoValue)?.text || "";
|
|
const sggNm = sigunData.find((item) => item.val === val)?.text || "";
|
|
const sggCd = sigunData.find((item) => item.val === val)?.val || "";
|
|
if (sggNm && sidoNm)
|
|
fetchFacilitiesBySggNm(sggCd, sggNm, sidoNm, facilityType);
|
|
} else if (type === "facilityType") {
|
|
setFacilityType(val);
|
|
setFacilityValue("0");
|
|
setFacilityData([{ text: "선택", val: "0" }]);
|
|
const sidoNm =
|
|
sidoData.find((item) => item.val === sidoValue)?.text || "";
|
|
const sggNm =
|
|
sigunData.find((item) => item.val === sigunValue)?.text || "";
|
|
const sggCd =
|
|
sigunData.find((item) => item.val === sigunValue)?.val || "";
|
|
if (sggNm && sidoNm) fetchFacilitiesBySggNm(sggCd, sggNm, sidoNm, val);
|
|
} else if (type === "fcltCd") {
|
|
setFacilityValue(val);
|
|
}
|
|
},
|
|
[initializeSigunData, sigunData, sigunValue, sidoData, sidoValue]
|
|
);
|
|
|
|
//조회 버튼 클릭
|
|
const handleSearch = () => {
|
|
if (!gisMaps.current || facilityValue === "0") return;
|
|
const layers = gisMaps.current.getLayers().getArray();
|
|
for (const layer of layers) {
|
|
const source = layer.getSource?.();
|
|
const feature = source?.getFeatureById?.(facilityValue);
|
|
if (feature) {
|
|
onFeatureSelect(feature);
|
|
return;
|
|
}
|
|
}
|
|
alert("해당 처리시설을 지도에서 찾을 수 없습니다.");
|
|
};
|
|
|
|
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-10 w-[190px]"
|
|
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-10 w-[150px]"
|
|
selectClass="rounded-lg bg-white"
|
|
label="시도"
|
|
selectArray={sidoData}
|
|
onSelectValue={(val) => onSelectValue(val.toString(), "ctpvCd")}
|
|
value={sidoValue}
|
|
/>
|
|
<SelectBox
|
|
labelClass="w-[65px]"
|
|
className="mr-10 w-[150px]"
|
|
selectClass="rounded-lg bg-white"
|
|
label="시군구"
|
|
selectArray={filteredSigunData}
|
|
onSelectValue={(val) => onSelectValue(val.toString(), "sggCd")}
|
|
value={sigunValue}
|
|
/>
|
|
<SelectBox
|
|
labelClass="w-[60px]"
|
|
className="mr-3 w-[180px]"
|
|
selectClass="rounded-lg bg-white"
|
|
label="시설명"
|
|
selectArray={facilityData}
|
|
onSelectValue={(val) => onSelectValue(val.toString(), "fcltCd")}
|
|
value={facilityValue}
|
|
/>
|
|
</div>
|
|
<div className="flex flex-row space-x-2">
|
|
<Button
|
|
color="primary"
|
|
className="mx-2 mt-1 ml-5"
|
|
size={"sm"}
|
|
onClick={handleSearch}
|
|
>
|
|
조회
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|