@bwg-ui/core 1.1.8 → 1.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +0,0 @@
1
- {"version":3,"file":"menuViewStore-BJak0NMm.cjs","sources":["../../src/stores/favoriteStore.ts","../../src/stores/menuModelStore.ts","../../src/stores/menuViewStore.ts"],"sourcesContent":["import { create } from \"zustand\";\r\nimport { callService } from \"../utils/apiUtils\";\r\nimport { getServiceCode } from \"../utils/serviceConfig\";\r\nimport type { MenuItem } from \"./menuModelStore\";\r\nimport { isLocal } from \"@/utils\";\r\n\r\n// 즐겨찾기 메뉴 타입 정의\r\nexport interface FavoriteMenuItem extends MenuItem {\r\n addedAt: string; // 즐겨찾기 추가 시간\r\n userId: string; // 사용자 ID\r\n}\r\n\r\n// 즐겨찾기 스토어 타입 정의\r\nexport interface FavoriteStore {\r\n favorites: FavoriteMenuItem[];\r\n isLoading: boolean;\r\n error: string | null;\r\n\r\n // 즐겨찾기 메뉴 가져오기\r\n fetchFavorites: (params: { crprCd: string; userId: string }) => Promise<void>;\r\n\r\n // 즐겨찾기 추가\r\n addFavorite: (menuItem: MenuItem, userId: string) => Promise<void>;\r\n\r\n // 즐겨찾기 제거\r\n removeFavorite: (menuId: string, userId: string) => Promise<void>;\r\n\r\n // 즐겨찾기 여부 확인\r\n isFavorite: (menuId: string) => boolean;\r\n\r\n // 즐겨찾기 초기화\r\n clearFavorites: () => void;\r\n}\r\n\r\n// 즐겨찾기 스토어 생성\r\nexport const useFavoriteStore = create<FavoriteStore>((set, get) => ({\r\n favorites: [],\r\n isLoading: false,\r\n error: null,\r\n\r\n // 즐겨찾기 메뉴 가져오기\r\n fetchFavorites: async ({ crprCd, userId }) => {\r\n console.log(\"즐겨찾기 메뉴 요청:\", { crprCd, userId });\r\n set({ isLoading: true, error: null });\r\n\r\n try {\r\n // 서버에서 즐겨찾기 메뉴 가져오기\r\n const data = await callService(getServiceCode(\"AUTH_BMRK\"), {\r\n crprCd,\r\n userId,\r\n });\r\n\r\n let favoritesList: FavoriteMenuItem[] = [];\r\n\r\n if (data?.favorites && Array.isArray(data.favorites)) {\r\n favoritesList = data.favorites;\r\n } else if (Array.isArray(data)) {\r\n favoritesList = data;\r\n } else {\r\n console.warn(\"즐겨찾기 데이터가 비어있거나 예상과 다른 구조입니다.\");\r\n favoritesList = [];\r\n }\r\n\r\n console.log(\"즐겨찾기 메뉴 로드 완료:\", favoritesList);\r\n\r\n // 개발 모드에서 테스트용 즐겨찾기 데이터 추가\r\n if (isLocal && favoritesList.length === 0) {\r\n const testFavorites: FavoriteMenuItem[] = [\r\n {\r\n crprCd: \"100\",\r\n menuGbCd: \"CMPRGRP\",\r\n menuPrntId: \"FAV001\",\r\n menuId: \"FAV_TEST001\",\r\n menuNm: \"API 테스트\",\r\n scrnId: \"TEST001\",\r\n menuNo: 1,\r\n scrnPath: \"/dev/ApiTest\",\r\n menuLvl: 3,\r\n rootMenu: \"FAV001\",\r\n addedAt: new Date().toISOString(),\r\n userId,\r\n },\r\n {\r\n crprCd: \"100\",\r\n menuGbCd: \"CMPRGRP\",\r\n menuPrntId: \"FAV001\",\r\n menuId: \"FAV_TEST002\",\r\n menuNm: \"프로젝트 개요\",\r\n scrnId: \"DOCS001\",\r\n menuNo: 2,\r\n scrnPath: \"/docs/ProjectOverview\",\r\n menuLvl: 3,\r\n rootMenu: \"FAV001\",\r\n addedAt: new Date().toISOString(),\r\n userId,\r\n },\r\n ];\r\n set({ favorites: testFavorites, isLoading: false, error: null });\r\n console.log(\"개발 모드: 테스트용 즐겨찾기 데이터 추가\");\r\n } else {\r\n set({ favorites: favoritesList, isLoading: false, error: null });\r\n }\r\n } catch (error) {\r\n console.error(\"즐겨찾기 메뉴 로드 실패:\", error);\r\n set({\r\n error: error instanceof Error ? error.message : \"즐겨찾기 로드 실패\",\r\n isLoading: false,\r\n });\r\n }\r\n },\r\n\r\n // 즐겨찾기 추가\r\n addFavorite: async (menuItem: MenuItem, userId: string) => {\r\n const { favorites } = get();\r\n\r\n // 이미 즐겨찾기에 있는지 확인\r\n if (favorites.some((fav) => fav.menuId === menuItem.menuId)) {\r\n console.log(\"이미 즐겨찾기에 추가된 메뉴입니다:\", menuItem.menuNm);\r\n return;\r\n }\r\n\r\n try {\r\n // 서버에 즐겨찾기 추가 요청\r\n await callService(getServiceCode(\"AUTH_BMRK\"), {\r\n action: \"add\",\r\n crprCd: menuItem.crprCd,\r\n userId,\r\n menuId: menuItem.menuId,\r\n menuNm: menuItem.menuNm,\r\n scrnPath: menuItem.scrnPath,\r\n });\r\n\r\n // 로컬 상태 업데이트\r\n const newFavorite: FavoriteMenuItem = {\r\n ...menuItem,\r\n addedAt: new Date().toISOString(),\r\n userId,\r\n };\r\n\r\n set({ favorites: [...favorites, newFavorite] });\r\n console.log(\"즐겨찾기 추가 완료:\", menuItem.menuNm);\r\n } catch (error) {\r\n console.error(\"즐겨찾기 추가 실패:\", error);\r\n throw error;\r\n }\r\n },\r\n\r\n // 즐겨찾기 제거\r\n removeFavorite: async (menuId: string, userId: string) => {\r\n const { favorites } = get();\r\n\r\n try {\r\n // 서버에 즐겨찾기 제거 요청\r\n await callService(getServiceCode(\"AUTH_BMRK\"), {\r\n action: \"remove\",\r\n crprCd: \"100\", // 기본값\r\n userId,\r\n menuId,\r\n });\r\n\r\n // 로컬 상태 업데이트\r\n const updatedFavorites = favorites.filter((fav) => fav.menuId !== menuId);\r\n set({ favorites: updatedFavorites });\r\n console.log(\"즐겨찾기 제거 완료:\", menuId);\r\n } catch (error) {\r\n console.error(\"즐겨찾기 제거 실패:\", error);\r\n throw error;\r\n }\r\n },\r\n\r\n // 즐겨찾기 여부 확인\r\n isFavorite: (menuId: string) => {\r\n const { favorites } = get();\r\n return favorites.some((fav) => fav.menuId === menuId);\r\n },\r\n\r\n // 즐겨찾기 초기화\r\n clearFavorites: () => {\r\n set({ favorites: [], isLoading: false, error: null });\r\n },\r\n}));\r\n","import { create } from \"zustand\";\r\n// 필요에 맞게 경로 조정\r\nimport { callService } from \"@/utils/apiUtils\";\r\nimport { getServiceCode } from \"@/utils/serviceConfig\";\r\n\r\n/* ──── Constants ──── */\r\nconst MENU_CONSTANTS = {\r\n DEFAULT_ACTIVE_MENU: \"1\",\r\n DEFAULT_PRNT_GBCD: 1,\r\n COMPANY_CODE: \"100\",\r\n MENU_PARENT_ROOT: \"-\",\r\n MENU_TYPE: { MAIN: \"CMPRGRM\", SUB: \"CMPRGRS\", PROGRAM: \"CMPRGRP\" } as const,\r\n} as const;\r\n\r\n/* ──── Types (이 파일 전용) ──── */\r\nexport interface MenuItem {\r\n crprCd: string;\r\n menuGbCd: string;\r\n menuPrntId: string;\r\n menuId: string;\r\n menuNm: string;\r\n menuNo?: number;\r\n scrnId?: string;\r\n scrnPath?: string;\r\n prsnInfoYn?: \"Y\" | \"N\";\r\n rootMenu: string;\r\n iconCd?: string;\r\n children?: MenuItem[];\r\n}\r\n\r\nexport type FetchMenuParams = {\r\n crprCd: string;\r\n userId: string;\r\n prntGbcd: number;\r\n};\r\n\r\nexport interface MenuApiResponse {\r\n menus?: MenuItem[];\r\n [k: string]: any;\r\n}\r\n\r\n/* 런타임 타입가드 */\r\nconst isMenuItem = (v: any): v is MenuItem =>\r\n v &&\r\n typeof v === \"object\" &&\r\n typeof v.menuId === \"string\" &&\r\n typeof v.menuNm === \"string\" &&\r\n typeof v.crprCd === \"string\";\r\n\r\nconst isMenuItemArray = (v: any): v is MenuItem[] =>\r\n Array.isArray(v) && v.every(isMenuItem);\r\n\r\n/* ──── State Shape ──── */\r\ntype MenuModelState = {\r\n // 📊 State (상태)\r\n // 계층형 메뉴 트리 구조 (부모-자식 관계)\r\n menuList: MenuItem[];\r\n // 평면화된 메뉴 리스트 (검색/조회용)\r\n flatMenuList: MenuItem[];\r\n // 메뉴 데이터 로딩 상태\r\n isLoading: boolean;\r\n // 에러 메시지\r\n error: string | null;\r\n // 마지막 조회 파라미터 (중복 요청 방지)\r\n _lastFetchParams: FetchMenuParams | null;\r\n\r\n // 🔧 Actions (액션 함수들)\r\n // 서버에서 메뉴 데이터 조회\r\n fetchMenu: (p: FetchMenuParams) => Promise<void>;\r\n // 메뉴 데이터 초기화\r\n clearMenu: () => void;\r\n // 메뉴 ID로 특정 메뉴 찾기\r\n findMenuById: (menuId: string) => MenuItem | null;\r\n};\r\n\r\n/* ──── Local Memoization Cache ──── */\r\nlet _lastFlat: MenuItem[] = [];\r\nlet _cachedTree: MenuItem[] = [];\r\n\r\n/* ──── Helpers ──── */\r\nconst parseMenuResponse = (data: unknown): MenuItem[] => {\r\n if (data && typeof data === \"object\") {\r\n const r = data as MenuApiResponse;\r\n if (r.menus && isMenuItemArray(r.menus)) return r.menus;\r\n if (isMenuItemArray(data)) return data;\r\n }\r\n console.warn(\"⚠️ 메뉴 응답이 비어있거나 예상과 다릅니다.\");\r\n return [];\r\n};\r\n\r\nconst sameParams = (a: FetchMenuParams, b: FetchMenuParams | null) =>\r\n !!b &&\r\n a.crprCd === b.crprCd &&\r\n a.userId === b.userId &&\r\n (a.prntGbcd ?? MENU_CONSTANTS.DEFAULT_PRNT_GBCD) ===\r\n (b.prntGbcd ?? MENU_CONSTANTS.DEFAULT_PRNT_GBCD);\r\n\r\nconst buildHierarchy = (flat: MenuItem[]): MenuItem[] => {\r\n if (\r\n _lastFlat.length === flat.length &&\r\n _lastFlat.every(\r\n (x, i) =>\r\n x.menuId === flat[i]?.menuId && x.menuPrntId === flat[i]?.menuPrntId\r\n )\r\n )\r\n return _cachedTree;\r\n\r\n if (!flat.length) return [];\r\n\r\n const map = new Map<string, MenuItem>();\r\n const roots: MenuItem[] = [];\r\n\r\n flat.forEach((m) => m?.menuId && map.set(m.menuId, { ...m, children: [] }));\r\n flat.forEach((m) => {\r\n const cur = map.get(m.menuId);\r\n if (!cur) return;\r\n if (m.menuPrntId && m.menuPrntId !== \"\" && m.menuPrntId !== \"-\") {\r\n const p = map.get(m.menuPrntId);\r\n p?.children?.push(cur);\r\n } else {\r\n roots.push(cur);\r\n }\r\n });\r\n\r\n const sortRec = (arr: MenuItem[]): MenuItem[] =>\r\n arr\r\n .sort((a, b) => (a?.menuNo ?? 0) - (b?.menuNo ?? 0))\r\n .map((x) => ({\r\n ...x,\r\n children:\r\n x.children && x.children.length ? sortRec(x.children) : undefined,\r\n }));\r\n\r\n const out = sortRec(roots);\r\n _lastFlat = [...flat];\r\n _cachedTree = out;\r\n return out;\r\n};\r\n\r\n/* ──── Store ──── */\r\nexport const useMenuModelStore = create<MenuModelState>((set, get) => ({\r\n // 📊 초기 상태값\r\n menuList: [],\r\n flatMenuList: [],\r\n isLoading: false,\r\n error: null,\r\n _lastFetchParams: null,\r\n\r\n // 🔧 메뉴 모델 조회\r\n // 서버에서 사용자별 메뉴 권한 데이터를 가져와서 트리/플랫 구조로 저장\r\n fetchMenu: async ({ crprCd, userId, prntGbcd }: FetchMenuParams) => {\r\n const p = {\r\n crprCd,\r\n userId,\r\n prntGbcd: prntGbcd ?? MENU_CONSTANTS.DEFAULT_PRNT_GBCD,\r\n };\r\n // 중복 요청 방지\r\n if (sameParams(p, get()._lastFetchParams)) {\r\n console.log(\"✅ 중복 fetch 차단\", p);\r\n return;\r\n }\r\n set({ isLoading: true, error: null, _lastFetchParams: p });\r\n try {\r\n // API 호출하여 메뉴 데이터 가져오기\r\n const data = await callService(getServiceCode(\"AUTH_MENU\"), p);\r\n const flat = parseMenuResponse(data); // 응답 데이터 파싱\r\n const tree = buildHierarchy(flat); // 계층 구조 생성\r\n set({\r\n flatMenuList: flat, // 평면 리스트 저장\r\n menuList: tree, // 트리 구조 저장\r\n isLoading: false,\r\n error: null,\r\n });\r\n } catch (e: any) {\r\n set({ isLoading: false, error: e?.message ?? \"메뉴 로드 실패\" });\r\n }\r\n },\r\n\r\n // 🔧 메뉴 모델 초기화\r\n // 로그아웃 시나 사용자 변경 시 메뉴 모델 클리어\r\n clearMenu: () => {\r\n set({\r\n menuList: [],\r\n flatMenuList: [],\r\n error: null,\r\n _lastFetchParams: null,\r\n });\r\n },\r\n\r\n // 🔧 메뉴 ID로 메뉴 아이템 찾기\r\n // URL 파라미터나 프로그래밍 방식으로 특정 메뉴를 찾을 때 사용\r\n findMenuById: (menuId: string) => {\r\n const { flatMenuList } = get();\r\n return flatMenuList.find((m) => m.menuId === menuId) ?? null;\r\n },\r\n}));\r\n","import { create } from \"zustand\";\r\nimport React from \"react\";\r\nimport { MenuItem, useMenuModelStore } from \"./menuModelStore\";\r\nimport { message } from \"antd\";\r\n\r\n/* ──── Constants (UI 측에도 독립적으로 존재) ──── */\r\nconst MENU_CONSTANTS = {\r\n DEFAULT_ACTIVE_MENU: \"1\",\r\n MAX_TABS: 15,\r\n} as const;\r\n\r\n/* ──── Types (이 파일 전용) ──── */\r\nexport interface TabItem {\r\n key: string; // menuId\r\n label: string; // menuNm\r\n gubun: \"M\" | \"C\"; // menuId or componentPath\r\n menuItem: MenuItem;\r\n\r\n closable: boolean;\r\n}\r\n\r\n/* ──── State Shape ──── */\r\ntype MenuViewState = {\r\n // 📊 UI State (화면 상태)\r\n // 현재 활성화된 메뉴 ID\r\n activeMenuId: string | undefined | null;\r\n // 현재 활성화된 메뉴 정보\r\n activeMenuItem: MenuItem | null;\r\n // 열린 탭 목록\r\n tabs: TabItem[];\r\n // 사이드바 접힘/펼침 상태\r\n sidebarCollapsed: boolean;\r\n // 최대 탭 개수\r\n maxTabs: number;\r\n tabProtectFlag: Record<string, boolean>;\r\n tabParams: Record<string, Record<string, any>>;\r\n\r\n /* 🛠 UI Actions (UI 액션 함수들) */\r\n // 메뉴 아이템으로 탭 추가\r\n openTabFromMenu: (menuItem: MenuItem, params?: Record<string, any>) => void;\r\n // 메뉴 ID로 탭 추가 (URL 파라미터 처리용)\r\n openTabByMenuId: (menuId: string, params?: Record<string, any>) => void;\r\n // 컴포넌트 ID로 탭 추가\r\n openTabByComponentId: (\r\n cpntId: string,\r\n cpntNm?: string,\r\n params?: Record<string, any>\r\n ) => void;\r\n // 활성 탭 변경\r\n focusTab: (tabKey: string) => void;\r\n // 탭 제거\r\n closeTab: (tabKey: string) => void;\r\n // 모든 탭 제거\r\n closeAllTabs: () => void;\r\n // 사이드바 토글\r\n toggleSidebar: () => void;\r\n // 탭 순서 변경\r\n reorderTabs: (keys: string[]) => void;\r\n // 개인정보 포함여부에 따른 탭 잠금 Flag 설정\r\n setProtectFlagForKey: (key: string) => void;\r\n // 개인정보 포함여부에 따른 탭 잠금 해제\r\n clearProtectFlagForKey: (key: string) => void;\r\n setTabParams: (key: string, params: Record<string, any>) => void;\r\n};\r\n\r\n/* ──── Store ──── */\r\nexport const useMenuViewStore = create<MenuViewState>((set, get) => ({\r\n // 📊 초기 상태값\r\n activeMenuId: null,\r\n activeMenuItem: null,\r\n tabs: [],\r\n sidebarCollapsed: false,\r\n maxTabs: MENU_CONSTANTS.MAX_TABS,\r\n tabProtectFlag: {},\r\n tabParams: {},\r\n // 🔧 탭 추가 (메뉴 아이템 객체로)\r\n // 사이드바에서 메뉴 클릭 시 호출되는 메인 함수\r\n openTabFromMenu: (menuItem, params) => {\r\n const { tabs, activeMenuId } = get();\r\n const exists = tabs.find((t) => t.key === menuItem.menuId);\r\n\r\n // 새 탭 생성\r\n if (!exists) {\r\n // 2. 최대 탭 개수 확인\r\n if (tabs.length > MENU_CONSTANTS.MAX_TABS) {\r\n console.warn(\"❌ 최대 탭 개수를 초과했습니다.\");\r\n message.warning(\r\n `최대 ${MENU_CONSTANTS.MAX_TABS}개의 탭만 열 수 있습니다.\\n기존 탭을 닫고 다시 시도해주세요.`\r\n );\r\n return;\r\n }\r\n const newTab: TabItem = {\r\n key: menuItem.menuId,\r\n label: menuItem.menuNm,\r\n gubun: \"M\",\r\n menuItem: menuItem as MenuItem,\r\n closable: true,\r\n };\r\n set({\r\n tabs: [...tabs, newTab], // 탭 목록에 추가\r\n activeMenuId: menuItem.menuId, // 새 탭을 활성화\r\n activeMenuItem: menuItem, // 현재 프로그램으로 설정\r\n });\r\n get().setTabParams(menuItem.menuId, params || {});\r\n console.log(\"✅ 새 탭 추가 - activeMenuItem:\", menuItem);\r\n\r\n // 개인정보 메뉴만 Protect\r\n if (menuItem?.prsnInfoYn === \"Y\") {\r\n get().setProtectFlagForKey(menuItem.menuId);\r\n }\r\n } else if (activeMenuId !== menuItem.menuId) {\r\n // 이미 존재하는 탭이면 활성화만\r\n set({ activeMenuId: menuItem.menuId, activeMenuItem: menuItem });\r\n console.log(\"✅ 기존 탭 활성화 - activeMenuItem:\", menuItem);\r\n get().setTabParams(menuItem.menuId, params || {});\r\n // 개인정보 메뉴만 Protect\r\n if (menuItem?.prsnInfoYn === \"Y\") {\r\n get().setProtectFlagForKey(menuItem.menuId);\r\n }\r\n } else {\r\n console.warn(\"❌ 유효하지 않은 메뉴정보\", menuItem.menuId);\r\n message.warning(\"유효하지 않은 메뉴정보입니다.\");\r\n }\r\n },\r\n // 🔧 탭 추가 (메뉴 ID로)\r\n // URL 파라미터로 메뉴 열기 시 사용 (예: ?menuId=CM000301)\r\n openTabByMenuId: (menuId, params) => {\r\n const target = useMenuModelStore.getState().findMenuById(menuId);\r\n if (!target) {\r\n console.warn(\"❌ 메뉴 ID를 찾을 수 없음:\", menuId);\r\n message.warning(\"메뉴 ID를 찾을 수 없습니다.\");\r\n } else {\r\n get().openTabFromMenu(target, params); // 찾은 메뉴로 탭 추가\r\n }\r\n },\r\n openTabByComponentId: (cpntId, cpntNm, params) => {\r\n const { tabs, activeMenuId } = get();\r\n const exists = tabs.find((t) => t.key === cpntId);\r\n const menuItem = {\r\n crprCd: \"\",\r\n menuNm: cpntNm || cpntId,\r\n menuId: cpntId,\r\n scrnId: cpntId,\r\n scrnPath: cpntId,\r\n menuNo: undefined,\r\n menuLvl: 0,\r\n prsnInfoYn: \"N\" as const,\r\n rootMenu: \"\",\r\n iconCd: \"\",\r\n menuGbCd: \"\",\r\n menuPrntId: \"\",\r\n children: [],\r\n };\r\n // 새 탭 생성\r\n if (!exists) {\r\n const newTab: TabItem = {\r\n key: cpntId,\r\n gubun: \"C\",\r\n label: cpntNm || cpntId,\r\n menuItem,\r\n closable: true,\r\n };\r\n\r\n set({\r\n tabs: [...tabs, newTab], // 탭 목록에 추가\r\n activeMenuId: cpntId, // 새 탭을 활성화\r\n activeMenuItem: menuItem, // 현재 프로그램으로 설정\r\n });\r\n get().setTabParams(cpntId, params || {});\r\n } else if (activeMenuId !== menuItem.menuId) {\r\n // 이미 존재하는 탭이면 활성화만\r\n set({ activeMenuId: menuItem.menuId, activeMenuItem: menuItem });\r\n get().setTabParams(menuItem.menuId, params || {});\r\n } else {\r\n console.warn(\"❌ 유효하지 않은 메뉴정보\", cpntId);\r\n message.warning(\"유효하지 않은 메뉴정보입니다.\");\r\n return;\r\n }\r\n },\r\n // 🔧 탭 제거\r\n // X 버튼 클릭 시 탭을 닫고 관련 상태도 정리\r\n closeTab: (tabKey) => {\r\n const { tabs, activeMenuId } = get();\r\n const removed = tabs.find((t) => t.key === tabKey);\r\n const nextTabs = tabs.filter((t) => t.key !== tabKey);\r\n\r\n // 닫힌 탭이 현재 활성 탭이면 다음 탭으로 이동\r\n let nextActive = activeMenuId;\r\n if (activeMenuId === tabKey) {\r\n const idx = tabs.findIndex((t) => t.key === tabKey);\r\n nextActive = nextTabs.length\r\n ? nextTabs[Math.min(idx, nextTabs.length - 1)]?.key ?? null\r\n : null;\r\n }\r\n\r\n set({\r\n tabs: nextTabs,\r\n });\r\n\r\n if (nextActive) get().focusTab(nextActive);\r\n },\r\n // 🔧 활성 탭 변경\r\n // 탭 헤더 클릭 시 해당 탭으로 전환\r\n focusTab: (tabKey) => {\r\n const { tabs } = get();\r\n const target = tabs.find((t) => t.key === tabKey);\r\n\r\n if (target) {\r\n if (target.menuItem.prsnInfoYn === \"Y\") {\r\n set({\r\n activeMenuId: tabKey,\r\n activeMenuItem: target.menuItem,\r\n });\r\n console.log(\r\n \"✅ 탭 포커스 (개인정보) - activeMenuItem:\",\r\n target.menuItem\r\n );\r\n get().setProtectFlagForKey(tabKey);\r\n } else {\r\n set({\r\n activeMenuId: tabKey,\r\n activeMenuItem: target.menuItem,\r\n });\r\n console.log(\"✅ 탭 포커스 (일반) - activeMenuItem:\", target.menuItem);\r\n get().clearProtectFlagForKey(tabKey);\r\n }\r\n } else {\r\n set({ activeMenuId: tabKey, activeMenuItem: null });\r\n console.warn(\"❌ 탭을 찾을 수 없음:\", tabKey);\r\n }\r\n },\r\n\r\n // 🔧 모든 탭 제거\r\n // 로그아웃이나 전체 초기화 시 사용\r\n closeAllTabs: () =>\r\n set({ tabs: [], activeMenuId: null, activeMenuItem: null }),\r\n\r\n // 🔧 사이드바 토글\r\n // 햄버거 메뉴 클릭 시 사이드바 접기/펼치기\r\n toggleSidebar: () => set((s) => ({ sidebarCollapsed: !s.sidebarCollapsed })),\r\n\r\n // useMenuViewStore.ts\r\n reorderTabs: (keys: string[]) =>\r\n set((state) => {\r\n const map = new Map(state.tabs.map((t) => [t.key, t]));\r\n return { tabs: keys.map((k) => map.get(k)!).filter(Boolean) };\r\n }),\r\n // 개인정보 포함여부에 따른 탭 잠금 Flag 설정\r\n setProtectFlagForKey: (key: string) =>\r\n set((s) => ({\r\n tabProtectFlag: { ...s.tabProtectFlag, [key]: true },\r\n })),\r\n // 개인정보 포함여부에 따른 탭 잠금 해제\r\n clearProtectFlagForKey: (key: string) =>\r\n set((s) => {\r\n const newFlags = { ...s.tabProtectFlag };\r\n delete newFlags[key];\r\n return { tabProtectFlag: newFlags };\r\n }),\r\n setTabParams: (key, params) =>\r\n set((state) => {\r\n const prev = state.tabParams[key] || {};\r\n const next = { ...(params || {}) }; // 새 객체 (불변 보장)\r\n\r\n // shallow equal이면 스킵(불필요 렌더 방지)\r\n const isShallowEqual =\r\n Object.keys(prev).length === Object.keys(next).length &&\r\n Object.keys(next).every((k) => prev[k] === next[k]);\r\n if (isShallowEqual) return state;\r\n\r\n return {\r\n tabParams: { ...state.tabParams, [key]: next },\r\n };\r\n }),\r\n}));\r\n"],"names":["useFavoriteStore","create","set","get","crprCd","userId","data","callService","getServiceCode","favoritesList","isLocal","testFavorites","error","menuItem","favorites","fav","newFavorite","menuId","updatedFavorites","MENU_CONSTANTS","isMenuItem","v","isMenuItemArray","_lastFlat","_cachedTree","parseMenuResponse","r","sameParams","a","b","buildHierarchy","flat","x","i","map","roots","m","cur","sortRec","arr","out","useMenuModelStore","prntGbcd","p","tree","e","flatMenuList","useMenuViewStore","params","tabs","activeMenuId","t","message","newTab","target","cpntId","cpntNm","exists","tabKey","nextTabs","nextActive","idx","s","keys","state","k","key","newFlags","prev","next"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"menuViewStore-BwR3vgPM.js","sources":["../../src/stores/favoriteStore.ts","../../src/stores/menuModelStore.ts","../../src/stores/menuViewStore.ts"],"sourcesContent":["import { create } from \"zustand\";\r\nimport { callService } from \"../utils/apiUtils\";\r\nimport { getServiceCode } from \"../utils/serviceConfig\";\r\nimport type { MenuItem } from \"./menuModelStore\";\r\nimport { isLocal } from \"@/utils\";\r\n\r\n// 즐겨찾기 메뉴 타입 정의\r\nexport interface FavoriteMenuItem extends MenuItem {\r\n addedAt: string; // 즐겨찾기 추가 시간\r\n userId: string; // 사용자 ID\r\n}\r\n\r\n// 즐겨찾기 스토어 타입 정의\r\nexport interface FavoriteStore {\r\n favorites: FavoriteMenuItem[];\r\n isLoading: boolean;\r\n error: string | null;\r\n\r\n // 즐겨찾기 메뉴 가져오기\r\n fetchFavorites: (params: { crprCd: string; userId: string }) => Promise<void>;\r\n\r\n // 즐겨찾기 추가\r\n addFavorite: (menuItem: MenuItem, userId: string) => Promise<void>;\r\n\r\n // 즐겨찾기 제거\r\n removeFavorite: (menuId: string, userId: string) => Promise<void>;\r\n\r\n // 즐겨찾기 여부 확인\r\n isFavorite: (menuId: string) => boolean;\r\n\r\n // 즐겨찾기 초기화\r\n clearFavorites: () => void;\r\n}\r\n\r\n// 즐겨찾기 스토어 생성\r\nexport const useFavoriteStore = create<FavoriteStore>((set, get) => ({\r\n favorites: [],\r\n isLoading: false,\r\n error: null,\r\n\r\n // 즐겨찾기 메뉴 가져오기\r\n fetchFavorites: async ({ crprCd, userId }) => {\r\n console.log(\"즐겨찾기 메뉴 요청:\", { crprCd, userId });\r\n set({ isLoading: true, error: null });\r\n\r\n try {\r\n // 서버에서 즐겨찾기 메뉴 가져오기\r\n const data = await callService(getServiceCode(\"AUTH_BMRK\"), {\r\n crprCd,\r\n userId,\r\n });\r\n\r\n let favoritesList: FavoriteMenuItem[] = [];\r\n\r\n if (data?.favorites && Array.isArray(data.favorites)) {\r\n favoritesList = data.favorites;\r\n } else if (Array.isArray(data)) {\r\n favoritesList = data;\r\n } else {\r\n console.warn(\"즐겨찾기 데이터가 비어있거나 예상과 다른 구조입니다.\");\r\n favoritesList = [];\r\n }\r\n\r\n console.log(\"즐겨찾기 메뉴 로드 완료:\", favoritesList);\r\n\r\n // 개발 모드에서 테스트용 즐겨찾기 데이터 추가\r\n if (isLocal && favoritesList.length === 0) {\r\n const testFavorites: FavoriteMenuItem[] = [\r\n {\r\n crprCd: \"100\",\r\n menuGbCd: \"CMPRGRP\",\r\n menuPrntId: \"FAV001\",\r\n menuId: \"FAV_TEST001\",\r\n menuNm: \"API 테스트\",\r\n scrnId: \"TEST001\",\r\n menuNo: 1,\r\n scrnPath: \"/dev/ApiTest\",\r\n menuLvl: 3,\r\n rootMenu: \"FAV001\",\r\n addedAt: new Date().toISOString(),\r\n userId,\r\n },\r\n {\r\n crprCd: \"100\",\r\n menuGbCd: \"CMPRGRP\",\r\n menuPrntId: \"FAV001\",\r\n menuId: \"FAV_TEST002\",\r\n menuNm: \"프로젝트 개요\",\r\n scrnId: \"DOCS001\",\r\n menuNo: 2,\r\n scrnPath: \"/docs/ProjectOverview\",\r\n menuLvl: 3,\r\n rootMenu: \"FAV001\",\r\n addedAt: new Date().toISOString(),\r\n userId,\r\n },\r\n ];\r\n set({ favorites: testFavorites, isLoading: false, error: null });\r\n console.log(\"개발 모드: 테스트용 즐겨찾기 데이터 추가\");\r\n } else {\r\n set({ favorites: favoritesList, isLoading: false, error: null });\r\n }\r\n } catch (error) {\r\n console.error(\"즐겨찾기 메뉴 로드 실패:\", error);\r\n set({\r\n error: error instanceof Error ? error.message : \"즐겨찾기 로드 실패\",\r\n isLoading: false,\r\n });\r\n }\r\n },\r\n\r\n // 즐겨찾기 추가\r\n addFavorite: async (menuItem: MenuItem, userId: string) => {\r\n const { favorites } = get();\r\n\r\n // 이미 즐겨찾기에 있는지 확인\r\n if (favorites.some((fav) => fav.menuId === menuItem.menuId)) {\r\n console.log(\"이미 즐겨찾기에 추가된 메뉴입니다:\", menuItem.menuNm);\r\n return;\r\n }\r\n\r\n try {\r\n // 서버에 즐겨찾기 추가 요청\r\n await callService(getServiceCode(\"AUTH_BMRK\"), {\r\n action: \"add\",\r\n crprCd: menuItem.crprCd,\r\n userId,\r\n menuId: menuItem.menuId,\r\n menuNm: menuItem.menuNm,\r\n scrnPath: menuItem.scrnPath,\r\n });\r\n\r\n // 로컬 상태 업데이트\r\n const newFavorite: FavoriteMenuItem = {\r\n ...menuItem,\r\n addedAt: new Date().toISOString(),\r\n userId,\r\n };\r\n\r\n set({ favorites: [...favorites, newFavorite] });\r\n console.log(\"즐겨찾기 추가 완료:\", menuItem.menuNm);\r\n } catch (error) {\r\n console.error(\"즐겨찾기 추가 실패:\", error);\r\n throw error;\r\n }\r\n },\r\n\r\n // 즐겨찾기 제거\r\n removeFavorite: async (menuId: string, userId: string) => {\r\n const { favorites } = get();\r\n\r\n try {\r\n // 서버에 즐겨찾기 제거 요청\r\n await callService(getServiceCode(\"AUTH_BMRK\"), {\r\n action: \"remove\",\r\n crprCd: \"100\", // 기본값\r\n userId,\r\n menuId,\r\n });\r\n\r\n // 로컬 상태 업데이트\r\n const updatedFavorites = favorites.filter((fav) => fav.menuId !== menuId);\r\n set({ favorites: updatedFavorites });\r\n console.log(\"즐겨찾기 제거 완료:\", menuId);\r\n } catch (error) {\r\n console.error(\"즐겨찾기 제거 실패:\", error);\r\n throw error;\r\n }\r\n },\r\n\r\n // 즐겨찾기 여부 확인\r\n isFavorite: (menuId: string) => {\r\n const { favorites } = get();\r\n return favorites.some((fav) => fav.menuId === menuId);\r\n },\r\n\r\n // 즐겨찾기 초기화\r\n clearFavorites: () => {\r\n set({ favorites: [], isLoading: false, error: null });\r\n },\r\n}));\r\n","import { create } from \"zustand\";\r\n// 필요에 맞게 경로 조정\r\nimport { callService } from \"@/utils/apiUtils\";\r\nimport { getServiceCode } from \"@/utils/serviceConfig\";\r\n\r\n/* ──── Constants ──── */\r\nconst MENU_CONSTANTS = {\r\n DEFAULT_ACTIVE_MENU: \"1\",\r\n DEFAULT_PRNT_GBCD: 1,\r\n COMPANY_CODE: \"100\",\r\n MENU_PARENT_ROOT: \"-\",\r\n MENU_TYPE: { MAIN: \"CMPRGRM\", SUB: \"CMPRGRS\", PROGRAM: \"CMPRGRP\" } as const,\r\n} as const;\r\n\r\n/* ──── Types (이 파일 전용) ──── */\r\nexport interface MenuItem {\r\n crprCd: string;\r\n menuGbCd: string;\r\n menuPrntId: string;\r\n menuId: string;\r\n menuNm: string;\r\n menuNo?: number;\r\n scrnId?: string;\r\n scrnPath?: string;\r\n prsnInfoYn?: \"Y\" | \"N\";\r\n rootMenu: string;\r\n iconCd?: string;\r\n children?: MenuItem[];\r\n}\r\n\r\nexport type FetchMenuParams = {\r\n crprCd: string;\r\n userId: string;\r\n prntGbcd: number;\r\n};\r\n\r\nexport interface MenuApiResponse {\r\n menus?: MenuItem[];\r\n [k: string]: any;\r\n}\r\n\r\n/* 런타임 타입가드 */\r\nconst isMenuItem = (v: any): v is MenuItem =>\r\n v &&\r\n typeof v === \"object\" &&\r\n typeof v.menuId === \"string\" &&\r\n typeof v.menuNm === \"string\" &&\r\n typeof v.crprCd === \"string\";\r\n\r\nconst isMenuItemArray = (v: any): v is MenuItem[] =>\r\n Array.isArray(v) && v.every(isMenuItem);\r\n\r\n/* ──── State Shape ──── */\r\ntype MenuModelState = {\r\n // 📊 State (상태)\r\n // 계층형 메뉴 트리 구조 (부모-자식 관계)\r\n menuList: MenuItem[];\r\n // 평면화된 메뉴 리스트 (검색/조회용)\r\n flatMenuList: MenuItem[];\r\n // 메뉴 데이터 로딩 상태\r\n isLoading: boolean;\r\n // 에러 메시지\r\n error: string | null;\r\n // 마지막 조회 파라미터 (중복 요청 방지)\r\n _lastFetchParams: FetchMenuParams | null;\r\n\r\n // 🔧 Actions (액션 함수들)\r\n // 서버에서 메뉴 데이터 조회\r\n fetchMenu: (p: FetchMenuParams) => Promise<void>;\r\n // 메뉴 데이터 초기화\r\n clearMenu: () => void;\r\n // 메뉴 ID로 특정 메뉴 찾기\r\n findMenuById: (menuId: string) => MenuItem | null;\r\n};\r\n\r\n/* ──── Local Memoization Cache ──── */\r\nlet _lastFlat: MenuItem[] = [];\r\nlet _cachedTree: MenuItem[] = [];\r\n\r\n/* ──── Helpers ──── */\r\nconst parseMenuResponse = (data: unknown): MenuItem[] => {\r\n if (data && typeof data === \"object\") {\r\n const r = data as MenuApiResponse;\r\n if (r.menus && isMenuItemArray(r.menus)) return r.menus;\r\n if (isMenuItemArray(data)) return data;\r\n }\r\n console.warn(\"⚠️ 메뉴 응답이 비어있거나 예상과 다릅니다.\");\r\n return [];\r\n};\r\n\r\nconst sameParams = (a: FetchMenuParams, b: FetchMenuParams | null) =>\r\n !!b &&\r\n a.crprCd === b.crprCd &&\r\n a.userId === b.userId &&\r\n (a.prntGbcd ?? MENU_CONSTANTS.DEFAULT_PRNT_GBCD) ===\r\n (b.prntGbcd ?? MENU_CONSTANTS.DEFAULT_PRNT_GBCD);\r\n\r\nconst buildHierarchy = (flat: MenuItem[]): MenuItem[] => {\r\n if (\r\n _lastFlat.length === flat.length &&\r\n _lastFlat.every(\r\n (x, i) =>\r\n x.menuId === flat[i]?.menuId && x.menuPrntId === flat[i]?.menuPrntId\r\n )\r\n )\r\n return _cachedTree;\r\n\r\n if (!flat.length) return [];\r\n\r\n const map = new Map<string, MenuItem>();\r\n const roots: MenuItem[] = [];\r\n\r\n flat.forEach((m) => m?.menuId && map.set(m.menuId, { ...m, children: [] }));\r\n flat.forEach((m) => {\r\n const cur = map.get(m.menuId);\r\n if (!cur) return;\r\n if (m.menuPrntId && m.menuPrntId !== \"\" && m.menuPrntId !== \"-\") {\r\n const p = map.get(m.menuPrntId);\r\n p?.children?.push(cur);\r\n } else {\r\n roots.push(cur);\r\n }\r\n });\r\n\r\n const sortRec = (arr: MenuItem[]): MenuItem[] =>\r\n arr\r\n .sort((a, b) => (a?.menuNo ?? 0) - (b?.menuNo ?? 0))\r\n .map((x) => ({\r\n ...x,\r\n children:\r\n x.children && x.children.length ? sortRec(x.children) : undefined,\r\n }));\r\n\r\n const out = sortRec(roots);\r\n _lastFlat = [...flat];\r\n _cachedTree = out;\r\n return out;\r\n};\r\n\r\n/* ──── Store ──── */\r\nexport const useMenuModelStore = create<MenuModelState>((set, get) => ({\r\n // 📊 초기 상태값\r\n menuList: [],\r\n flatMenuList: [],\r\n isLoading: false,\r\n error: null,\r\n _lastFetchParams: null,\r\n\r\n // 🔧 메뉴 모델 조회\r\n // 서버에서 사용자별 메뉴 권한 데이터를 가져와서 트리/플랫 구조로 저장\r\n fetchMenu: async ({ crprCd, userId, prntGbcd }: FetchMenuParams) => {\r\n const p = {\r\n crprCd,\r\n userId,\r\n prntGbcd: prntGbcd ?? MENU_CONSTANTS.DEFAULT_PRNT_GBCD,\r\n };\r\n // 중복 요청 방지\r\n if (sameParams(p, get()._lastFetchParams)) {\r\n console.log(\"✅ 중복 fetch 차단\", p);\r\n return;\r\n }\r\n set({ isLoading: true, error: null, _lastFetchParams: p });\r\n try {\r\n // API 호출하여 메뉴 데이터 가져오기\r\n const data = await callService(getServiceCode(\"AUTH_MENU\"), p);\r\n const flat = parseMenuResponse(data); // 응답 데이터 파싱\r\n const tree = buildHierarchy(flat); // 계층 구조 생성\r\n set({\r\n flatMenuList: flat, // 평면 리스트 저장\r\n menuList: tree, // 트리 구조 저장\r\n isLoading: false,\r\n error: null,\r\n });\r\n } catch (e: any) {\r\n set({ isLoading: false, error: e?.message ?? \"메뉴 로드 실패\" });\r\n }\r\n },\r\n\r\n // 🔧 메뉴 모델 초기화\r\n // 로그아웃 시나 사용자 변경 시 메뉴 모델 클리어\r\n clearMenu: () => {\r\n set({\r\n menuList: [],\r\n flatMenuList: [],\r\n error: null,\r\n _lastFetchParams: null,\r\n });\r\n },\r\n\r\n // 🔧 메뉴 ID로 메뉴 아이템 찾기\r\n // URL 파라미터나 프로그래밍 방식으로 특정 메뉴를 찾을 때 사용\r\n findMenuById: (menuId: string) => {\r\n const { flatMenuList } = get();\r\n return flatMenuList.find((m) => m.menuId === menuId) ?? null;\r\n },\r\n}));\r\n","import { create } from \"zustand\";\r\nimport React from \"react\";\r\nimport { MenuItem, useMenuModelStore } from \"./menuModelStore\";\r\nimport { message } from \"antd\";\r\n\r\n/* ──── Constants (UI 측에도 독립적으로 존재) ──── */\r\nconst MENU_CONSTANTS = {\r\n DEFAULT_ACTIVE_MENU: \"1\",\r\n MAX_TABS: 15,\r\n} as const;\r\n\r\n/* ──── Types (이 파일 전용) ──── */\r\nexport interface TabItem {\r\n key: string; // menuId\r\n label: string; // menuNm\r\n gubun: \"M\" | \"C\"; // menuId or componentPath\r\n menuItem: MenuItem;\r\n\r\n closable: boolean;\r\n}\r\n\r\n/* ──── State Shape ──── */\r\ntype MenuViewState = {\r\n // 📊 UI State (화면 상태)\r\n // 현재 활성화된 메뉴 ID\r\n activeMenuId: string | undefined | null;\r\n // 현재 활성화된 메뉴 정보\r\n activeMenuItem: MenuItem | null;\r\n // 열린 탭 목록\r\n tabs: TabItem[];\r\n // 사이드바 접힘/펼침 상태\r\n sidebarCollapsed: boolean;\r\n // 최대 탭 개수\r\n maxTabs: number;\r\n tabProtectFlag: Record<string, boolean>;\r\n tabParams: Record<string, Record<string, any>>;\r\n\r\n /* 🛠 UI Actions (UI 액션 함수들) */\r\n // 메뉴 아이템으로 탭 추가\r\n openTabFromMenu: (menuItem: MenuItem, params?: Record<string, any>) => void;\r\n // 메뉴 ID로 탭 추가 (URL 파라미터 처리용)\r\n openTabByMenuId: (menuId: string, params?: Record<string, any>) => void;\r\n // 컴포넌트 ID로 탭 추가\r\n openTabByComponentId: (\r\n cpntId: string,\r\n cpntNm?: string,\r\n params?: Record<string, any>\r\n ) => void;\r\n // 활성 탭 변경\r\n focusTab: (tabKey: string) => void;\r\n // 탭 제거\r\n closeTab: (tabKey: string) => void;\r\n // 모든 탭 제거\r\n closeAllTabs: () => void;\r\n // 사이드바 토글\r\n toggleSidebar: () => void;\r\n // 탭 순서 변경\r\n reorderTabs: (keys: string[]) => void;\r\n // 개인정보 포함여부에 따른 탭 잠금 Flag 설정\r\n setProtectFlagForKey: (key: string) => void;\r\n // 개인정보 포함여부에 따른 탭 잠금 해제\r\n clearProtectFlagForKey: (key: string) => void;\r\n setTabParams: (key: string, params: Record<string, any>) => void;\r\n};\r\n\r\n/* ──── Store ──── */\r\nexport const useMenuViewStore = create<MenuViewState>((set, get) => ({\r\n // 📊 초기 상태값\r\n activeMenuId: null,\r\n activeMenuItem: null,\r\n tabs: [],\r\n sidebarCollapsed: false,\r\n maxTabs: MENU_CONSTANTS.MAX_TABS,\r\n tabProtectFlag: {},\r\n tabParams: {},\r\n // 🔧 탭 추가 (메뉴 아이템 객체로)\r\n // 사이드바에서 메뉴 클릭 시 호출되는 메인 함수\r\n openTabFromMenu: (menuItem, params) => {\r\n const { tabs, activeMenuId } = get();\r\n const exists = tabs.find((t) => t.key === menuItem.menuId);\r\n\r\n // 새 탭 생성\r\n if (!exists) {\r\n // 2. 최대 탭 개수 확인\r\n if (tabs.length > MENU_CONSTANTS.MAX_TABS) {\r\n console.warn(\"❌ 최대 탭 개수를 초과했습니다.\");\r\n message.warning(\r\n `최대 ${MENU_CONSTANTS.MAX_TABS}개의 탭만 열 수 있습니다.\\n기존 탭을 닫고 다시 시도해주세요.`\r\n );\r\n return;\r\n }\r\n const newTab: TabItem = {\r\n key: menuItem.menuId,\r\n label: menuItem.menuNm,\r\n gubun: \"M\",\r\n menuItem: menuItem as MenuItem,\r\n closable: true,\r\n };\r\n set({\r\n tabs: [...tabs, newTab], // 탭 목록에 추가\r\n activeMenuId: menuItem.menuId, // 새 탭을 활성화\r\n activeMenuItem: menuItem, // 현재 프로그램으로 설정\r\n });\r\n get().setTabParams(menuItem.menuId, params || {});\r\n console.log(\"✅ 새 탭 추가 - activeMenuItem:\", menuItem);\r\n\r\n // 개인정보 메뉴만 Protect\r\n if (menuItem?.prsnInfoYn === \"Y\") {\r\n get().setProtectFlagForKey(menuItem.menuId);\r\n }\r\n } else if (activeMenuId !== menuItem.menuId) {\r\n // 이미 존재하는 탭이면 활성화만\r\n set({ activeMenuId: menuItem.menuId, activeMenuItem: menuItem });\r\n console.log(\"✅ 기존 탭 활성화 - activeMenuItem:\", menuItem);\r\n get().setTabParams(menuItem.menuId, params || {});\r\n // 개인정보 메뉴만 Protect\r\n if (menuItem?.prsnInfoYn === \"Y\") {\r\n get().setProtectFlagForKey(menuItem.menuId);\r\n }\r\n } else {\r\n console.warn(\"❌ 유효하지 않은 메뉴정보\", menuItem.menuId);\r\n message.warning(\"유효하지 않은 메뉴정보입니다.\");\r\n }\r\n },\r\n // 🔧 탭 추가 (메뉴 ID로)\r\n // URL 파라미터로 메뉴 열기 시 사용 (예: ?menuId=CM000301)\r\n openTabByMenuId: (menuId, params) => {\r\n const target = useMenuModelStore.getState().findMenuById(menuId);\r\n if (!target) {\r\n console.warn(\"❌ 메뉴 ID를 찾을 수 없음:\", menuId);\r\n message.warning(\"메뉴 ID를 찾을 수 없습니다.\");\r\n } else {\r\n get().openTabFromMenu(target, params); // 찾은 메뉴로 탭 추가\r\n }\r\n },\r\n openTabByComponentId: (cpntId, cpntNm, params) => {\r\n const { tabs, activeMenuId } = get();\r\n const exists = tabs.find((t) => t.key === cpntId);\r\n const menuItem = {\r\n crprCd: \"\",\r\n menuNm: cpntNm || cpntId,\r\n menuId: cpntId,\r\n scrnId: cpntId,\r\n scrnPath: cpntId,\r\n menuNo: undefined,\r\n menuLvl: 0,\r\n prsnInfoYn: \"N\" as const,\r\n rootMenu: \"\",\r\n iconCd: \"\",\r\n menuGbCd: \"\",\r\n menuPrntId: \"\",\r\n children: [],\r\n };\r\n // 새 탭 생성\r\n if (!exists) {\r\n const newTab: TabItem = {\r\n key: cpntId,\r\n gubun: \"C\",\r\n label: cpntNm || cpntId,\r\n menuItem,\r\n closable: true,\r\n };\r\n\r\n set({\r\n tabs: [...tabs, newTab], // 탭 목록에 추가\r\n activeMenuId: cpntId, // 새 탭을 활성화\r\n activeMenuItem: menuItem, // 현재 프로그램으로 설정\r\n });\r\n get().setTabParams(cpntId, params || {});\r\n } else if (activeMenuId !== menuItem.menuId) {\r\n // 이미 존재하는 탭이면 활성화만\r\n set({ activeMenuId: menuItem.menuId, activeMenuItem: menuItem });\r\n get().setTabParams(menuItem.menuId, params || {});\r\n } else {\r\n console.warn(\"❌ 유효하지 않은 메뉴정보\", cpntId);\r\n message.warning(\"유효하지 않은 메뉴정보입니다.\");\r\n return;\r\n }\r\n },\r\n // 🔧 탭 제거\r\n // X 버튼 클릭 시 탭을 닫고 관련 상태도 정리\r\n closeTab: (tabKey) => {\r\n const { tabs, activeMenuId } = get();\r\n const removed = tabs.find((t) => t.key === tabKey);\r\n const nextTabs = tabs.filter((t) => t.key !== tabKey);\r\n\r\n // 닫힌 탭이 현재 활성 탭이면 다음 탭으로 이동\r\n let nextActive = activeMenuId;\r\n if (activeMenuId === tabKey) {\r\n const idx = tabs.findIndex((t) => t.key === tabKey);\r\n nextActive = nextTabs.length\r\n ? nextTabs[Math.min(idx, nextTabs.length - 1)]?.key ?? null\r\n : null;\r\n }\r\n\r\n set({\r\n tabs: nextTabs,\r\n });\r\n\r\n if (nextActive) get().focusTab(nextActive);\r\n },\r\n // 🔧 활성 탭 변경\r\n // 탭 헤더 클릭 시 해당 탭으로 전환\r\n focusTab: (tabKey) => {\r\n const { tabs } = get();\r\n const target = tabs.find((t) => t.key === tabKey);\r\n\r\n if (target) {\r\n if (target.menuItem.prsnInfoYn === \"Y\") {\r\n set({\r\n activeMenuId: tabKey,\r\n activeMenuItem: target.menuItem,\r\n });\r\n console.log(\r\n \"✅ 탭 포커스 (개인정보) - activeMenuItem:\",\r\n target.menuItem\r\n );\r\n get().setProtectFlagForKey(tabKey);\r\n } else {\r\n set({\r\n activeMenuId: tabKey,\r\n activeMenuItem: target.menuItem,\r\n });\r\n console.log(\"✅ 탭 포커스 (일반) - activeMenuItem:\", target.menuItem);\r\n get().clearProtectFlagForKey(tabKey);\r\n }\r\n } else {\r\n set({ activeMenuId: tabKey, activeMenuItem: null });\r\n console.warn(\"❌ 탭을 찾을 수 없음:\", tabKey);\r\n }\r\n },\r\n\r\n // 🔧 모든 탭 제거\r\n // 로그아웃이나 전체 초기화 시 사용\r\n closeAllTabs: () =>\r\n set({ tabs: [], activeMenuId: null, activeMenuItem: null }),\r\n\r\n // 🔧 사이드바 토글\r\n // 햄버거 메뉴 클릭 시 사이드바 접기/펼치기\r\n toggleSidebar: () => set((s) => ({ sidebarCollapsed: !s.sidebarCollapsed })),\r\n\r\n // useMenuViewStore.ts\r\n reorderTabs: (keys: string[]) =>\r\n set((state) => {\r\n const map = new Map(state.tabs.map((t) => [t.key, t]));\r\n return { tabs: keys.map((k) => map.get(k)!).filter(Boolean) };\r\n }),\r\n // 개인정보 포함여부에 따른 탭 잠금 Flag 설정\r\n setProtectFlagForKey: (key: string) =>\r\n set((s) => ({\r\n tabProtectFlag: { ...s.tabProtectFlag, [key]: true },\r\n })),\r\n // 개인정보 포함여부에 따른 탭 잠금 해제\r\n clearProtectFlagForKey: (key: string) =>\r\n set((s) => {\r\n const newFlags = { ...s.tabProtectFlag };\r\n delete newFlags[key];\r\n return { tabProtectFlag: newFlags };\r\n }),\r\n setTabParams: (key, params) =>\r\n set((state) => {\r\n const prev = state.tabParams[key] || {};\r\n const next = { ...(params || {}) }; // 새 객체 (불변 보장)\r\n\r\n // shallow equal이면 스킵(불필요 렌더 방지)\r\n const isShallowEqual =\r\n Object.keys(prev).length === Object.keys(next).length &&\r\n Object.keys(next).every((k) => prev[k] === next[k]);\r\n if (isShallowEqual) return state;\r\n\r\n return {\r\n tabParams: { ...state.tabParams, [key]: next },\r\n };\r\n }),\r\n}));\r\n"],"names":["useFavoriteStore","create","set","get","crprCd","userId","data","callService","getServiceCode","favoritesList","isLocal","testFavorites","error","menuItem","favorites","fav","newFavorite","menuId","updatedFavorites","MENU_CONSTANTS","isMenuItem","v","isMenuItemArray","_lastFlat","_cachedTree","parseMenuResponse","r","sameParams","a","b","buildHierarchy","flat","x","i","map","roots","m","cur","sortRec","arr","out","useMenuModelStore","prntGbcd","p","tree","e","flatMenuList","useMenuViewStore","params","tabs","activeMenuId","t","message","newTab","target","cpntId","cpntNm","exists","tabKey","nextTabs","nextActive","idx","s","keys","state","k","key","newFlags","prev","next"],"mappings":"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"}