Files
frontend/apps/sn/app/components/ui/ctpvSggPrcsFcltSelectBox.tsx
T

168 lines
5.4 KiB
TypeScript

/**
* 📄 CtpvSggPrcsFcltSelectBox Component
* 시도/시군구/하수처리시설 선택 컴포넌트
*
* - 시도 선택 및 시군구 필터링 기능
* - 시군구 선택 시 하수처리시설 목록 조회
* - 선택 값 변경 시 콜백 함수 호출
* - 로딩 상태 관리 및 에러 핸들링
* - Zustand를 통한 시도/시군구 데이터 관리
* - CQL 필터를 통한 시설물 검색 조건 설정
*
* 🔗 사용 API
* - 시설물 조회: GET /v1/fclts (sewerage-service)
*/
import React, { useState, useEffect } from "react";
import { SelectBox } from '@common/business_components/ui'
import { Label } from '@common/components';
import useStore from '../../store/useStore';
import { getData } from '@/components/lib/api/index';
interface ApiSelectOption {
text: string;
val: string;
}
interface CtpvSggPrcsFcltSelectBoxProps {
sido: string;
sigungu: string;
sggName?: string;
onSidoChange: (id: string) => void;
onSigunguChange: (id: string) => void;
setCtpvName?: (name: string) => void;
setSggName?: (name: string) => void;
className?: string;
selectedSwagePrcsFclt?: string;
onSwagePrcsFcltChange?: (value: string) => void;
setSwtrmtFcltNm?: (name: string) => void;
setIsLoading?: (isLoading: boolean) => void;
}
const defaultOption: ApiSelectOption = { val: '0', text: '전체' };
export function CtpvSggPrcsFcltSelectBox({
sido,
sigungu,
sggName,
onSidoChange,
setCtpvName,
onSigunguChange,
setSggName,
className = "",
selectedSwagePrcsFclt,
onSwagePrcsFcltChange,
setSwtrmtFcltNm,
setIsLoading,
}: CtpvSggPrcsFcltSelectBoxProps) {
const [prcsFcltOptions, setPrcsFcltOptions] = useState<ApiSelectOption[]>([]);
const { ctpv, sgg } = useStore();
const sidoStr = String(sido ?? '');
const sidoPrefix = sidoStr.slice(0, 2);
useEffect(() => {
(async () => {
onSidoChange("0");
onSigunguChange("0");
if (setCtpvName && setSggName) {
setCtpvName("");
setSggName("");
}
if (setSwtrmtFcltNm) setSwtrmtFcltNm("");
})();
}, []);
useEffect(() => {
onSwagePrcsFcltChange?.('0');
const fetchData = async () => {
if (!sggName) {
setPrcsFcltOptions([]);
if (setIsLoading) setIsLoading(false);
return;
}
if (setIsLoading) setIsLoading(true);
try {
const queryParamsFacility = new URLSearchParams({
lyrTypeNm: 'tb_il_swage_prcs_fclt',
page: '0',
size: '100000', // 요청하신 URL에 맞게 50으로 변경 가능
sort: 'string',
});
const cqlFiltersFacility: string[] = [];
if (sggName) cqlFiltersFacility.push(`sgg_nm ILIKE '%${sggName}%'`);
if (cqlFiltersFacility.length > 0) queryParamsFacility.append('cqlFiter', cqlFiltersFacility.join(' and '));
const facilityRes = await getData(`v1/fclts?${queryParamsFacility.toString()}`);
const facilities = (facilityRes.data || [])
.map((item: any) => ({
text: item.swtrmtFcltNm,
val: item.fid
}))
.sort((a: ApiSelectOption, b: ApiSelectOption) => a.text.localeCompare(b.text, 'ko'));
setPrcsFcltOptions(facilities);
} catch (error) {
console.error("BaseLayout: 지역/시설 데이터 조회 중 오류 발생:", error);
setPrcsFcltOptions([]);
} finally {
if (setIsLoading) setIsLoading(false);
}
};
fetchData();
}, [sggName]);
return (
<div className={`flex items-center justify-center ${className} mr-8`}>
<Label className="text-xl text-gray-600 font-medium whitespace-nowrap mr-2">시도</Label>
<SelectBox
className="w-48 mr-8"
selectClass="rounded-lg bg-white"
label=""
selectArray={[defaultOption, ...ctpv]}
value={sido}
onSelectValue={val => {
onSidoChange(String(val));
if (setCtpvName) setCtpvName(ctpv.find(opt => String(opt.val) === String(val))?.text || "");
if (setSwtrmtFcltNm) setSwtrmtFcltNm("");
}}
selectContentClass="z-[101]"
/>
<Label className="text-xl text-gray-600 font-medium whitespace-nowrap mr-2">시군구</Label>
<SelectBox
className="w-48"
selectClass="w-48 rounded-lg bg-white truncate"
label=""
selectArray={[defaultOption, ...sgg.filter(opt => opt.val.startsWith(sidoPrefix))]}
value={sigungu}
onSelectValue={val => {
onSigunguChange(String(val))
if (setSggName) setSggName(sgg.find(opt => String(opt.val) === String(val))?.text || "");
if (setSwtrmtFcltNm) setSwtrmtFcltNm("")
}}
selectContentClass="z-[101]"
/>
{selectedSwagePrcsFclt && onSwagePrcsFcltChange && (
<>
<Label className="text-xl text-gray-600 font-medium whitespace-nowrap ml-8 mr-4">하수처리시설명</Label>
<SelectBox
className="w-[160px]"
selectClass="rounded-lg bg-white truncate"
label=""
selectArray={[defaultOption, ...prcsFcltOptions]}
value={selectedSwagePrcsFclt}
onSelectValue={(val) => {
onSwagePrcsFcltChange?.(String(val))
if (setSwtrmtFcltNm) setSwtrmtFcltNm(prcsFcltOptions.find(opt => String(opt.val) === String(val))?.text || "")
}}
selectContentClass="z-[101]"
/>
</>
)}
</div>
);
}