Files
frontend/apps/dm/app/store/common/index.tsx
T

513 lines
14 KiB
TypeScript

/**====================================
* 보편적으로 쓰는 store 공간
====================================**/
import { fetchType, getFetch } from '@/components/ui/api';
/**
* STORE - TYPE
*/
// selectBox
export type SelectDataType = {
text: string;
val: string;
}
export interface MenuItem {
menuCd: number;
menuKornNm: string;
menuEngNm: string;
parentId: number | null;
sortSeq: number;
iconNm?: string;
children: MenuItem[] | null;
levelNo: number;
urlPath: string;
menuLnkgTypeCd: string;
menuLnkgSn: number;
menuPath: string;
qrcd: string;
useYn: string;
otptYn: string;
newWndwYn: string;
requiredUrlPath: boolean;
}
// grid header
export type TableUpperProps = {
id: string;
title: string;
upName?: string;
upSequnce?: number;
align? : string;
row? : number;
rowSpan?: number;
colSpan?: number;
}
// 시도 데이터
export type sidoDataType = {
adstDstrCd: number,
sido: string,
}
// 시군구 데이터
export type sigunDataType = {
adstDstrCd: number,
gugun: string
}
// 권역 데이터
export type regionDataType = {
rvbsnCd: string,
rvbsn: string,
}
// 단위유역 데이터
export type unitWaterShedDataType = {
rvbsnCd: string,
lgbsn: string,
}
// 중권역 데이터
export type middleRegionDataType = {
rvbsnCd: string,
mdmbsn: string,
}
// 환경청 데이터
export type envPartDataType = {
lclgvrnmntCd: string,
lclgvrnmntNm: string,
adstDliStrCd: string,
adstDstrCd: string,
}
export type envPartSelectDataType = SelectDataType & {
adstDliStrCd: string,
}
/**
* STORE - DATA
*/
// 시도 데이터
let selectSidoData: SelectDataType[] = [
{ text: "전체", val: '0' }
];
// 시군구 데이터
let selectSigunData: SelectDataType[] = [
{ text: "전체", val: '0' }
];
// 권역 데이터
const selectRegionData: SelectDataType[] = [
{ text: "전체", val: '0' }
];
// 단위유역 데이터
const selectUnitWaterShedData: SelectDataType[] = [
];
// 환경청 데이터
let selectEnvPartData: envPartSelectDataType[] = [
{ text: "전체", val: '0', adstDliStrCd: '' }
];
// 환경청 내 시도 데이터
const selectEnvPartSidoData: SelectDataType[] = [
{ text: "전체", val: '0' }
];
// 환경청 내 시군구 데이터
const selectEnvPartSigunData: SelectDataType[] = [
{ text: "전체", val: '0' }
];
// 구분 데이터
// const selectPartData:SelectDataType[] = [
// { text: "행정별", val: '0' },
// { text: "유역별", val: '01' },
// { text: "환경청별", val: '02' },
// ];
// 상향 하향 데이터
const selectUpdownData:SelectDataType[] = [
{ text: "전체", val: '0' },
{ text: "500이하", val: '499' },
{ text: "500이상", val: '500' },
];
// 운영 상태 데이터
const selectOperationData:SelectDataType[] = [
{ text: "전체", val: '0' },
{ text: "가동중", val: '1' },
{ text: "재가동", val: '2' },
{ text: "가동중지", val: '3' },
{ text: "시설폐쇄", val: '4' },
];
// 시설 구분 데이터
const selectFacilityPartData:SelectDataType[] = [
{ text: "전체", val: '0' },
{ text: "공공하수처리시설", val: 'SW' },
{ text: "오수처리장", val: 'SS' },
{ text: "개인하수처리시설", val: 'PB' },
{ text: "분뇨처리장", val: 'NS' },
{ text: "폐수처리장", val: 'LW' },
{ text: "침출수처리장", val: 'LC' },
{ text: "기타기초시설", val: 'ET' },
];
// 년도 데이터
const getYearData = () => {
const year = new Date().getFullYear();
const year1 = year - 1;
const year2 = year - 2;
const year3 = year - 3;
const year4 = year - 4;
const year5 = year - 5;
return [
{ text: year1 + "년", val: year1.toString() },
{ text: year2 + "년", val: year2.toString() },
{ text: year3 + "년", val: year3.toString() },
{ text: year4 + "년", val: year4.toString() },
{ text: year5 + "년", val: year5.toString() },
]
}
const selectSearchYear:SelectDataType[] = getYearData();
// 유량 기간 구분
const selectFlowTerm : SelectDataType[] = [
{ text: "연간", val: '0' },
{ text: "하절기", val: '01' },
{ text: "동절기", val: '02' },
{ text: "청천시", val: '03' },
{ text: "강우시", val: '04' },
];
// 유량 기간 구분
const selectFlowPanel : SelectDataType[] = [
{ text: "전체", val: '0'},
{ text: "3mm미만", val: '1'},
{ text: "3~10mm미만", val: '2'},
{ text: "10~20mm미만", val: '3'},
{ text: "20~30mm미만", val: '4'},
{ text: "30~40mm미만", val: '5'},
{ text: "40~50mm미만", val: '6'},
{ text: "50~60mm미만", val: '7'},
{ text: "60mm이상", val: '8'},
];
// 중권역 데이터
const selectMiddleRegionData: SelectDataType[] = [
{ text: "전체", val: '0' }
];
/**
* STORE - API
*/
/** API 선택 목록의 빈 값과 중복 값을 제거하고 전체 항목을 한 번만 추가한다. */
function normalizeSelectData<T extends SelectDataType>(items: T[], allItem: T): T[] {
const uniqueItems = new Map<string, T>();
items.forEach((item) => {
const val = String(item.val ?? '').trim();
if (!val || val === '0' || uniqueItems.has(val)) return;
uniqueItems.set(val, { ...item, val });
});
return [allItem, ...uniqueItems.values()];
}
// 시도 API 호출 함수 추가
const fetchSidoData = async (): Promise<SelectDataType[]> => {
try {
const res = await getFetch(fetchType.FO, '/addr/city');
const data = res.data;
selectSidoData = normalizeSelectData(
data.map((item: sidoDataType) => ({
text: item.sido || '',
val: String(item.adstDstrCd) || ''
})),
{ text: "전체", val: '0' }
);
return selectSidoData;
} catch (error) {
console.error('시도 데이터를 가져오는데 실패했습니다:', error);
return selectSidoData;
}
};
// 시군구 API 호출 함수 추가
const fetchSigunData = async (): Promise<SelectDataType[]> => {
try {
const res = await getFetch(fetchType.FO, '/addr/sgg');
const data = res.data;
selectSigunData = normalizeSelectData(
data.map((item: sigunDataType) => ({
text: item.gugun || '',
val: String(item.adstDstrCd) || ''
})),
{ text: "전체", val: '0' }
);
return selectSigunData;
} catch (error) {
console.error('시군구 데이터를 가져오는데 실패했습니다:', error);
return selectSigunData;
}
};
// 권역 API 호출 함수 추가
const fetchRegionData = async (): Promise<SelectDataType[]> => {
try {
const res = await getFetch(fetchType.FO, '/addr/sarea');
const data = res.data;
return normalizeSelectData(
data.map((item: regionDataType) => ({
text: item.rvbsn || '',
val: String(item.rvbsnCd) || ''
})),
{ text: "전체", val: '0' }
);
} catch (error) {
console.error('권역 데이터를 가져오는데 실패했습니다:', error);
return selectRegionData;
}
};
// 중권역 API 호출 함수 추가
const fetchMiddleRegionData = async (): Promise<SelectDataType[]> => {
try {
const res = await getFetch(fetchType.FO, '/addr/midArea');
const data = res.data;
return normalizeSelectData(
data.map((item: middleRegionDataType) => ({
text: item.mdmbsn || '',
val: String(item.rvbsnCd) || ''
})),
{ text: "전체", val: '0' }
);
} catch (error) {
console.error('중권역 데이터를 가져오는데 실패했습니다:', error);
return selectMiddleRegionData;
}
};
// 단위유역 API 호출 함수 추가
const fetchUnitWaterShedData = async (): Promise<SelectDataType[]> => {
try {
const res = await getFetch(fetchType.FO, '/addr/largeArea');
const data = res.data;
return normalizeSelectData(
data.map((item: unitWaterShedDataType) => ({
text: item.lgbsn || '',
val: String(item.rvbsnCd) || ''
})),
{ text: "전체", val: '0' }
);
} catch (error) {
console.error('단위유역 데이터를 가져오는데 실패했습니다:', error);
return selectUnitWaterShedData;
}
};
// 환경청 데이터
const fetchEnvPartData = async (): Promise<envPartSelectDataType[]> => {
try {
const res = await getFetch(fetchType.FO, '/addr/govArea');
const data = res.data;
selectEnvPartData = normalizeSelectData(
data.map((item: envPartDataType) => ({
text: item.lclgvrnmntNm || '',
val: String(item.lclgvrnmntCd) || '',
adstDliStrCd: item.adstDliStrCd || ''
})),
{ text: "전체", val: '0', adstDliStrCd: '' }
);
return selectEnvPartData;
} catch (error) {
console.error('환경청 데이터를 가져오는데 실패했습니다:', error);
return selectEnvPartData;
}
};
function sortMenus(items: MenuItem[]): MenuItem[] {
return [...items]
.sort((a, b) => a.sortSeq - b.sortSeq)
.map((item) => ({
...item,
children: item.children ? sortMenus(item.children) : [],
}));
}
function isVisible(item: MenuItem): boolean {
return item.useYn !== "N" && item.otptYn !== "N";
}
function normalizeMenus(items: MenuItem[]): MenuItem[] {
return sortMenus(items)
.filter(isVisible)
.map((item) => ({
...item,
children: item.children ? normalizeMenus(item.children) : [],
}));
}
export const REUSE_SITE_ID = process.env.NEXT_PUBLIC_SITE_ID || "DM";
const anonymousMenuCache = new Map<string, MenuItem[]>();
const anonymousMenuInFlight = new Map<string, Promise<MenuItem[]>>();
export async function fetchAnonymousMenu(siteId = REUSE_SITE_ID): Promise<MenuItem[]> {
const cached = anonymousMenuCache.get(siteId);
if (cached) return cached;
const inFlight = anonymousMenuInFlight.get(siteId);
if (inFlight) return inFlight;
const request = (async () => {
const response = await fetch(`/cms-service/api/v1/menu/anonymous/user/${siteId}`, {
method: "GET",
headers: { "Content-Type": "application/json" },
cache: "no-store",
});
if (!response.ok) {
throw new Error(`Failed to load menu: ${response.status}`);
}
const data = (await response.json()) as MenuItem[];
const normalized = Array.isArray(data) ? normalizeMenus(data) : [];
anonymousMenuCache.set(siteId, normalized);
return normalized;
})();
anonymousMenuInFlight.set(siteId, request);
try {
return await request;
} finally {
anonymousMenuInFlight.delete(siteId);
}
}
function isPathMatched(pathname: string, urlPath: string) {
if (!urlPath) return false;
return pathname === urlPath || pathname.startsWith(`${urlPath}/`);
}
function hasMatchedPathInTree(menu: MenuItem, pathname: string): boolean {
if (isPathMatched(pathname, menu.urlPath)) return true;
return (menu.children ?? []).some((child) => hasMatchedPathInTree(child, pathname));
}
export function findActiveTopMenu(menus: MenuItem[], pathname: string): MenuItem | null {
const level1Menus = menus.filter((menu) => menu.levelNo === 1 || menu.parentId === null);
const matched = level1Menus.filter((menu) => hasMatchedPathInTree(menu, pathname));
return matched[0] ?? level1Menus[0] ?? null;
}
export function hasAnyChild(item: MenuItem): boolean {
return Array.isArray(item.children) && item.children.length > 0;
}
export function isMenuActive(pathname: string, menu: MenuItem): boolean {
return isPathMatched(pathname, menu.urlPath);
}
/**====================================
* Store - expaort
====================================*/
export type CommonAction = {
initializeCommonData: () => Promise<void>;
getMenuAuth: (menuCd:number) => Promise<void>;
fetchAnonymousMenu: (siteId?: string) => Promise<MenuItem[]>;
}
export type CommonType = {
Common: {
isInit: boolean;
selectSidoData: SelectDataType[];
selectSigunData: SelectDataType[];
selectUpdownData: SelectDataType[];
selectOperationData: SelectDataType[];
selectFacilityPartData: SelectDataType[];
selectSearchYear: SelectDataType[];
selectUnitWaterShedData: SelectDataType[];
selectRegionData: SelectDataType[];
selectMiddleRegionData: SelectDataType[];
selectEnvPartSidoData: SelectDataType[];
selectEnvPartSigunData: SelectDataType[];
selectFlowTerm: SelectDataType[];
selectFlowPanel: SelectDataType[];
selectEnvPartData: envPartSelectDataType[];
btnClass: string;
}
}
export const CommonState:CommonType = {
Common: {
isInit: false,
selectSidoData: selectSidoData,
selectSigunData: selectSigunData,
selectUpdownData: selectUpdownData,
selectOperationData: selectOperationData,
selectFacilityPartData: selectFacilityPartData,
selectSearchYear: selectSearchYear,
selectRegionData: selectRegionData,
selectUnitWaterShedData: selectUnitWaterShedData,
selectMiddleRegionData: selectMiddleRegionData,
selectEnvPartSidoData : selectEnvPartSidoData,
selectEnvPartSigunData : selectEnvPartSigunData,
selectFlowTerm: selectFlowTerm,
selectFlowPanel: selectFlowPanel,
selectEnvPartData : selectEnvPartData,
btnClass: "p-4 m-2 text-white rounded-sm text-lg font-medium"
}
}
export const CommonReducer:(set:any)=>CommonAction=(set: any) => {
return {
getMenuAuth : async (menuCd : number) =>{
const response = await getFetch(
"local",
`/cms-service/api/v1/menu-authrts/pageinfo/${menuCd}`
);
set((state: CommonType) => {
return {
...state,
auth: {
mngAuthrtYn: response[0].mngAuthrtYn,
inqAuthrtYn: response[0].mngAuthrtYn,
regAuthrtYn: response[0].mngAuthrtYn,
mdfcnAuthrtYn: response[0].mngAuthrtYn,
delAuthrtYn: response[0].mngAuthrtYn,
},
};
});
},
// 시도 변경 이벤트
initializeCommonData: async () => {
const promiseSet = await Promise.all([fetchSidoData(), fetchSigunData(), fetchRegionData(), fetchUnitWaterShedData(), fetchMiddleRegionData(), fetchEnvPartData()]);
const [sidoData, sggData, regionData, unitData, midData, envData] = promiseSet;
set((state: CommonType) => ({
Common: {
...state.Common,
selectSidoData: sidoData,
selectSigunData: sggData,
selectRegionData: regionData,
selectMiddleRegionData: midData,
selectUnitWaterShedData: unitData,
selectEnvPartData: envData
}
})
)},
fetchAnonymousMenu: async (siteId = REUSE_SITE_ID) => fetchAnonymousMenu(siteId),
}
}
export type CommonStore = CommonType & CommonAction;