"use client"; import { create } from "zustand"; import { devtools, persist } from "zustand/middleware"; import { clearTabCache } from "@/lib/tabStateCache"; // --- 타입 정의 --- export type AppMode = "user" | "admin"; export interface Tab { id: string; type: "screen" | "admin"; title: string; screenId?: number; menuObjid?: number; adminUrl?: string; } interface ModeTabData { tabs: Tab[]; activeTabId: string | null; } interface TabState { mode: AppMode; user: ModeTabData; admin: ModeTabData; refreshKeys: Record; setMode: (mode: AppMode) => void; openTab: (tab: Omit, insertIndex?: number) => void; closeTab: (tabId: string) => void; switchTab: (tabId: string) => void; refreshTab: (tabId: string) => void; closeOtherTabs: (tabId: string) => void; closeTabsToLeft: (tabId: string) => void; closeTabsToRight: (tabId: string) => void; closeAllTabs: () => void; updateTabOrder: (fromIndex: number, toIndex: number) => void; } // --- 헬퍼 함수 --- function generateTabId(tab: Omit): string { if (tab.type === "screen" && tab.screenId != null) { return `tab-screen-${tab.screenId}-${tab.menuObjid ?? 0}`; } if (tab.type === "admin" && tab.adminUrl) { return `tab-admin-${tab.adminUrl.replace(/[^a-zA-Z0-9]/g, "-")}`; } return `tab-${Date.now()}`; } function findDuplicateTab(tabs: Tab[], newTab: Omit): Tab | undefined { if (newTab.type === "screen" && newTab.screenId != null) { return tabs.find( (t) => t.type === "screen" && t.screenId === newTab.screenId && t.menuObjid === newTab.menuObjid, ); } if (newTab.type === "admin" && newTab.adminUrl) { return tabs.find((t) => t.type === "admin" && t.adminUrl === newTab.adminUrl); } return undefined; } function getNextActiveTabId(tabs: Tab[], closedTabId: string, currentActiveId: string | null): string | null { if (currentActiveId !== closedTabId) return currentActiveId; const idx = tabs.findIndex((t) => t.id === closedTabId); if (idx === -1) return null; const remaining = tabs.filter((t) => t.id !== closedTabId); if (remaining.length === 0) return null; if (idx > 0) return remaining[Math.min(idx - 1, remaining.length - 1)].id; return remaining[0].id; } // 현재 모드의 데이터 키 반환 function modeKey(state: TabState): AppMode { return state.mode; } // --- 셀렉터 (컴포넌트에서 사용) --- export function selectTabs(state: TabState): Tab[] { return state[state.mode].tabs; } export function selectActiveTabId(state: TabState): string | null { return state[state.mode].activeTabId; } // --- Store --- const EMPTY_MODE: ModeTabData = { tabs: [], activeTabId: null }; export const useTabStore = create()( devtools( persist( (set, get) => ({ mode: "user" as AppMode, user: { ...EMPTY_MODE }, admin: { ...EMPTY_MODE }, refreshKeys: {}, setMode: (mode) => { set({ mode }); }, openTab: (tabData, insertIndex) => { const mk = modeKey(get()); const modeData = get()[mk]; const existing = findDuplicateTab(modeData.tabs, tabData); if (existing) { set({ [mk]: { ...modeData, activeTabId: existing.id } }); return; } const id = generateTabId(tabData); const newTab: Tab = { ...tabData, id }; const newTabs = [...modeData.tabs]; if (insertIndex != null && insertIndex >= 0 && insertIndex <= newTabs.length) { newTabs.splice(insertIndex, 0, newTab); } else { newTabs.push(newTab); } set({ [mk]: { tabs: newTabs, activeTabId: id } }); }, closeTab: (tabId) => { clearTabCache(tabId); const mk = modeKey(get()); const modeData = get()[mk]; const nextActive = getNextActiveTabId(modeData.tabs, tabId, modeData.activeTabId); const newTabs = modeData.tabs.filter((t) => t.id !== tabId); const { [tabId]: _, ...restKeys } = get().refreshKeys; set({ [mk]: { tabs: newTabs, activeTabId: nextActive }, refreshKeys: restKeys }); }, switchTab: (tabId) => { const mk = modeKey(get()); const modeData = get()[mk]; set({ [mk]: { ...modeData, activeTabId: tabId } }); }, refreshTab: (tabId) => { set((state) => ({ refreshKeys: { ...state.refreshKeys, [tabId]: (state.refreshKeys[tabId] || 0) + 1 }, })); }, closeOtherTabs: (tabId) => { const mk = modeKey(get()); const modeData = get()[mk]; modeData.tabs.filter((t) => t.id !== tabId).forEach((t) => clearTabCache(t.id)); set({ [mk]: { tabs: modeData.tabs.filter((t) => t.id === tabId), activeTabId: tabId } }); }, closeTabsToLeft: (tabId) => { const mk = modeKey(get()); const modeData = get()[mk]; const idx = modeData.tabs.findIndex((t) => t.id === tabId); if (idx === -1) return; modeData.tabs.slice(0, idx).forEach((t) => clearTabCache(t.id)); set({ [mk]: { tabs: modeData.tabs.slice(idx), activeTabId: tabId } }); }, closeTabsToRight: (tabId) => { const mk = modeKey(get()); const modeData = get()[mk]; const idx = modeData.tabs.findIndex((t) => t.id === tabId); if (idx === -1) return; modeData.tabs.slice(idx + 1).forEach((t) => clearTabCache(t.id)); set({ [mk]: { tabs: modeData.tabs.slice(0, idx + 1), activeTabId: tabId } }); }, closeAllTabs: () => { const mk = modeKey(get()); const modeData = get()[mk]; modeData.tabs.forEach((t) => clearTabCache(t.id)); set({ [mk]: { tabs: [], activeTabId: null } }); }, updateTabOrder: (fromIndex, toIndex) => { const mk = modeKey(get()); const modeData = get()[mk]; const newTabs = [...modeData.tabs]; const [moved] = newTabs.splice(fromIndex, 1); newTabs.splice(toIndex, 0, moved); set({ [mk]: { ...modeData, tabs: newTabs } }); }, }), { name: "erp-tab-store", storage: { getItem: (name) => { if (typeof window === "undefined") return null; const raw = sessionStorage.getItem(name); return raw ? JSON.parse(raw) : null; }, setItem: (name, value) => { if (typeof window === "undefined") return; sessionStorage.setItem(name, JSON.stringify(value)); }, removeItem: (name) => { if (typeof window === "undefined") return; sessionStorage.removeItem(name); }, }, partialize: (state) => ({ mode: state.mode, user: state.user, admin: state.admin, }) as unknown as TabState, }, ), { name: "TabStore" }, ), );