120 lines
3.5 KiB
TypeScript
120 lines
3.5 KiB
TypeScript
|
|
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<string, any>;
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface WorkspaceWidgetConfig {
|
||
|
|
id: string;
|
||
|
|
widget_key: string;
|
||
|
|
position_x: number;
|
||
|
|
position_y: number;
|
||
|
|
width: number;
|
||
|
|
height: number;
|
||
|
|
config: Record<string, any>;
|
||
|
|
}
|
||
|
|
|
||
|
|
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<string, any> }[];
|
||
|
|
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<string>;
|
||
|
|
hasWorkspaces: () => boolean;
|
||
|
|
// Reset
|
||
|
|
reset: () => void;
|
||
|
|
}
|
||
|
|
|
||
|
|
const SESSION_STORAGE_KEY = 'leocrm-active-workspace';
|
||
|
|
|
||
|
|
export const useWorkspaceStore = create<WorkspaceStoreState>()(
|
||
|
|
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;
|
||
|
|
// 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;
|
||
|
|
return new Set(ctx?.modules?.map(m => m.module_key) || []);
|
||
|
|
},
|
||
|
|
|
||
|
|
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 }),
|
||
|
|
}
|
||
|
|
)
|
||
|
|
);
|