151 lines
4.1 KiB
TypeScript
151 lines
4.1 KiB
TypeScript
"use client";
|
|
|
|
import { create } from "zustand";
|
|
|
|
import { getRawJson } from "@/components/lib/api";
|
|
|
|
import type { SelectDataType } from "@common/business_components";
|
|
|
|
type OnsemiroAddrStoreState = {
|
|
govArea: SelectDataType[];
|
|
city: SelectDataType[];
|
|
sgg: SelectDataType[];
|
|
fetchGovArea: () => Promise<void>;
|
|
fetchCity: () => Promise<void>;
|
|
fetchSgg: () => Promise<void>;
|
|
};
|
|
|
|
/** 시도 SelectBox에 적용할 행정구역 표준 표시 순서다. */
|
|
const CITY_SORT_ORDER = [
|
|
"서울특별시",
|
|
"부산광역시",
|
|
"대구광역시",
|
|
"인천광역시",
|
|
"광주광역시",
|
|
"대전광역시",
|
|
"울산광역시",
|
|
"세종특별자치시",
|
|
"경기도",
|
|
"강원특별자치도",
|
|
"충청북도",
|
|
"충청남도",
|
|
"전북특별자치도",
|
|
"전라남도",
|
|
"경상북도",
|
|
"경상남도",
|
|
"제주특별자치도",
|
|
];
|
|
|
|
/** 공통 응답 래퍼와 배열 응답을 동일한 목록으로 정규화한다. */
|
|
const toResponseList = (response: unknown) => {
|
|
if (Array.isArray(response)) return response;
|
|
if (response && typeof response === "object") {
|
|
const data = (response as { data?: unknown }).data;
|
|
if (Array.isArray(data)) return data;
|
|
}
|
|
return [];
|
|
};
|
|
|
|
/** ONSEMIRO 주소 원본 필드를 공통 SelectBox 옵션으로 변환한다. */
|
|
const toSelectOption = (item: Record<string, unknown>) => {
|
|
const textCandidate = [
|
|
item.text,
|
|
item.name,
|
|
item.label,
|
|
item.govAreaNm,
|
|
item.govAreaName,
|
|
item.gugun,
|
|
item.sggNm,
|
|
item.sggName,
|
|
item.lclgvrnmntNm,
|
|
item.sido,
|
|
item.cityNm,
|
|
item.cityName,
|
|
item.korNm,
|
|
].find((value) => typeof value === "string" && value.trim());
|
|
|
|
const valueCandidate = [
|
|
item.val,
|
|
item.code,
|
|
item.id,
|
|
item.govAreaCd,
|
|
item.govAreaCode,
|
|
item.adstDstrCd,
|
|
item.sggCd,
|
|
item.sggCode,
|
|
item.lclgvrnmntCd,
|
|
item.cityCd,
|
|
item.cityCode,
|
|
].find(
|
|
(value) => value !== null && value !== undefined && String(value).trim(),
|
|
);
|
|
|
|
if (!textCandidate || valueCandidate === undefined) return null;
|
|
|
|
return {
|
|
text: String(textCandidate).trim(),
|
|
val: String(valueCandidate).trim(),
|
|
};
|
|
};
|
|
|
|
/** 시도 옵션을 표준 순서로 정렬하고 나머지는 한글명으로 정렬한다. */
|
|
const compareCityOptions = (left: SelectDataType, right: SelectDataType) => {
|
|
const leftIndex = CITY_SORT_ORDER.indexOf(left.text);
|
|
const rightIndex = CITY_SORT_ORDER.indexOf(right.text);
|
|
|
|
if (leftIndex !== -1 && rightIndex !== -1) return leftIndex - rightIndex;
|
|
if (leftIndex !== -1) return -1;
|
|
if (rightIndex !== -1) return 1;
|
|
return left.text.localeCompare(right.text, "ko");
|
|
};
|
|
|
|
/** 주소 API를 호출해 공통 SelectBox 옵션 목록을 만든다. */
|
|
const loadSelectOptions = async (url: string) => {
|
|
const response = await getRawJson(url);
|
|
return toResponseList(response)
|
|
.map((item) =>
|
|
item && typeof item === "object"
|
|
? toSelectOption(item as Record<string, unknown>)
|
|
: null,
|
|
)
|
|
.filter((item): item is SelectDataType => Boolean(item));
|
|
};
|
|
|
|
/** ONSEMIRO 환경청·시도·시군구 선택목록을 공유하는 상태 저장소다. */
|
|
const useOnsemiroAddrStore = create<OnsemiroAddrStoreState>((set) => ({
|
|
govArea: [],
|
|
city: [],
|
|
sgg: [],
|
|
/** 환경청 선택목록을 조회해 한글명 순으로 저장한다. */
|
|
fetchGovArea: async () => {
|
|
const govArea = await loadSelectOptions(
|
|
"/sewerage-service/api/v1/onsemiro/addr/govArea",
|
|
);
|
|
set({
|
|
govArea: govArea.sort((left, right) =>
|
|
left.text.localeCompare(right.text, "ko"),
|
|
),
|
|
});
|
|
},
|
|
/** 시도 선택목록을 조회해 표준 행정구역 순서로 저장한다. */
|
|
fetchCity: async () => {
|
|
const city = await loadSelectOptions(
|
|
"/sewerage-service/api/v1/onsemiro/addr/city",
|
|
);
|
|
set({
|
|
city: city.sort(compareCityOptions),
|
|
});
|
|
},
|
|
/** 시군구 선택목록을 조회해 한글명 순으로 저장한다. */
|
|
fetchSgg: async () => {
|
|
const sgg = await loadSelectOptions(
|
|
"/sewerage-service/api/v1/onsemiro/addr/sgg",
|
|
);
|
|
set({
|
|
sgg: sgg.sort((left, right) => left.text.localeCompare(right.text, "ko")),
|
|
});
|
|
},
|
|
}));
|
|
|
|
export default useOnsemiroAddrStore;
|