import { create } from 'zustand'; import { persist, createJSONStorage } from 'zustand/middleware'; import { setActiveWorkspaceId as setApiClientWorkspaceId } from '@/api/client'; export interface WorkspaceModuleConfig { module_key: string; is_visible: boolean; menu_order: number; config: Record; } export interface WorkspaceWidgetConfig { id: string; widget_key: string; position_x: number; position_y: number; width: number; height: number; config: Record; } export interface WorkspaceInfo { id: string; name: string; icon: string; description: string | null; is_default: boolean; is_active: boolean; role?: 'member' | 'manager' | 'admin'; is_user_default?: boolean; modules?: WorkspaceModuleConfig[]; } export interface WorkspaceContextState { workspace_id: string | null; name?: string; icon?: string; role?: string; modules: { module_key: string; menu_order: number; config: Record; is_visible?: boolean }[]; widgets: WorkspaceWidgetConfig[]; error?: string; } interface WorkspaceStoreState { // Active workspace ID (per-tab via sessionStorage) activeWorkspaceId: string | null; // Cached context context: WorkspaceContextState | null; // Available workspaces for the user myWorkspaces: WorkspaceInfo[]; // Loading states isLoading: boolean; // Actions setActiveWorkspace: (id: string | null) => void; setContext: (ctx: WorkspaceContextState | null) => void; setMyWorkspaces: (workspaces: WorkspaceInfo[]) => void; setLoading: (loading: boolean) => void; // Helpers isModuleVisible: (moduleKey: string, isSystemAdmin?: boolean) => boolean; visibleModuleKeys: () => Set; getModuleConfig: (moduleKey: string) => Record; hasWorkspaces: () => boolean; // Reset reset: () => void; } const SESSION_STORAGE_KEY = 'leocrm-active-workspace'; export const useWorkspaceStore = create()( persist( (set, get) => ({ activeWorkspaceId: sessionStorage.getItem(SESSION_STORAGE_KEY), context: null, myWorkspaces: [], isLoading: false, setActiveWorkspace: (id) => { if (id) { sessionStorage.setItem(SESSION_STORAGE_KEY, id); } else { sessionStorage.removeItem(SESSION_STORAGE_KEY); } setApiClientWorkspaceId(id); set({ activeWorkspaceId: id, context: null }); }, setContext: (ctx) => set({ context: ctx }), setMyWorkspaces: (workspaces) => set({ myWorkspaces: workspaces }), setLoading: (loading) => set({ isLoading: loading }), isModuleVisible: (moduleKey: string, isSystemAdmin?: boolean) => { // System admins see all modules regardless of workspace if (isSystemAdmin) return true; // While permissions are loading, show nothing (fail-closed) if (isSystemAdmin === undefined) return false; // If no workspace context, show all (backward compatible) const ctx = get().context; if (!ctx?.workspace_id || !ctx?.modules?.length) return true; return get().visibleModuleKeys().has(moduleKey); }, visibleModuleKeys: () => { const ctx = get().context; // ARCH-004: respect is_visible=false from workspace module config. // Modules without the flag stay visible (backward compatible). return new Set( (ctx?.modules ?? []) .filter(m => m.is_visible !== false) .map(m => m.module_key) ); }, getModuleConfig: (moduleKey: string) => { // N3: scope defaults (e.g. default_saved_view_id, default_view) come // from the active workspace context; no context = no defaults. const ctx = get().context; const mod = (ctx?.modules ?? []).find(m => m.module_key === moduleKey); return mod?.config ?? {}; }, hasWorkspaces: () => get().myWorkspaces.length > 0, reset: () => { sessionStorage.removeItem(SESSION_STORAGE_KEY); set({ activeWorkspaceId: null, context: null, myWorkspaces: [], isLoading: false }); }, }), { name: 'leocrm-workspace-store', storage: createJSONStorage(() => sessionStorage), partialize: (state) => ({ activeWorkspaceId: state.activeWorkspaceId }), } ) );