Files
frontend/apps/sn/app/store/useOnsemiroAddrStore.ts
T

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.lclgvrnmntNm,
item.sido,
item.gugun,
item.cityNm,
item.cityName,
item.sggNm,
item.sggName,
item.korNm,
].find((value) => typeof value === "string" && value.trim());
const valueCandidate = [
item.val,
item.code,
item.id,
item.govAreaCd,
item.govAreaCode,
item.lclgvrnmntCd,
item.adstDstrCd,
item.cityCd,
item.cityCode,
item.sggCd,
item.sggCode,
].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;