/**==================================== * 보편적으로 쓰는 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 호출 함수 추가 const fetchSidoData = async (): Promise => { try { const res = await getFetch(fetchType.FO, '/addr/city'); const data = res.data; selectSidoData = [ { text: "전체", val: '0'}, ...data.map((item: sidoDataType) => ({ text: item.sido || '', val: String(item.adstDstrCd) || '' })) ]; return selectSidoData; } catch (error) { console.error('시도 데이터를 가져오는데 실패했습니다:', error); return selectSidoData; } }; // 시군구 API 호출 함수 추가 const fetchSigunData = async (): Promise => { try { const res = await getFetch(fetchType.FO, '/addr/sgg'); const data = res.data; selectSigunData = [ { text: "전체", val: '0' }, ...data .map((item: sigunDataType) => ({ text: item.gugun || '', val: String(item.adstDstrCd) || '' })) ]; return selectSigunData; } catch (error) { console.error('시군구 데이터를 가져오는데 실패했습니다:', error); return selectSigunData; } }; // 권역 API 호출 함수 추가 const fetchRegionData = async (): Promise => { try { const res = await getFetch(fetchType.FO, '/addr/sarea'); const data = res.data; return [ { text: "전체", val: '0'}, ...data.map((item: regionDataType) => ({ text: item.rvbsn || '', val: String(item.rvbsnCd) || '' })) ]; } catch (error) { console.error('권역 데이터를 가져오는데 실패했습니다:', error); return selectRegionData; } }; // 중권역 API 호출 함수 추가 const fetchMiddleRegionData = async (): Promise => { try { const res = await getFetch(fetchType.FO, '/addr/midArea'); const data = res.data; return [ { text: "전체", val: '0' }, ...data .map((item: middleRegionDataType) => ({ text: item.mdmbsn || '', val: String(item.rvbsnCd) || '' })) ]; } catch (error) { console.error('중권역 데이터를 가져오는데 실패했습니다:', error); return selectMiddleRegionData; } }; // 단위유역 API 호출 함수 추가 const fetchUnitWaterShedData = async (): Promise => { try { const res = await getFetch(fetchType.FO, '/addr/largeArea'); const data = res.data; return [ { text: "전체", val: '0' }, ...data .map((item: unitWaterShedDataType) => ({ text: item.lgbsn || '', val: String(item.rvbsnCd) || '' })) ]; } catch (error) { console.error('단위유역 데이터를 가져오는데 실패했습니다:', error); return selectUnitWaterShedData; } }; // 환경청 데이터 const fetchEnvPartData = async (): Promise => { try { const res = await getFetch(fetchType.FO, '/addr/govArea'); const data = res.data; selectEnvPartData = [ { text: "전체", val: '0', adstDliStrCd: '' }, ...data .map((item: envPartDataType) => ({ text: item.lclgvrnmntNm || '', val: String(item.lclgvrnmntCd) || '', adstDliStrCd: item.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(); const anonymousMenuInFlight = new Map>(); export async function fetchAnonymousMenu(siteId = REUSE_SITE_ID): Promise { 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; getMenuAuth: (menuCd:number) => Promise; fetchAnonymousMenu: (siteId?: string) => Promise; } 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;