import { create } from 'zustand'; import { persist, createJSONStorage } from 'zustand/middleware'; import { tenantService, TenantResponse } from '../services/api/tenant.service'; import { useAuthUser } from './auth.store'; export interface TenantState { // State currentTenant: TenantResponse | null; availableTenants: TenantResponse[] | null; isLoading: boolean; error: string | null; // Actions setCurrentTenant: (tenant: TenantResponse) => void; switchTenant: (tenantId: string) => Promise; loadUserTenants: () => Promise; clearTenants: () => void; clearError: () => void; setLoading: (loading: boolean) => void; // Permission helpers (migrated from BakeryContext) hasPermission: (permission: string) => boolean; canAccess: (resource: string, action: string) => boolean; } export const useTenantStore = create()( persist( (set, get) => ({ // Initial state currentTenant: null, availableTenants: null, isLoading: false, error: null, // Actions setCurrentTenant: (tenant: TenantResponse) => { set({ currentTenant: tenant }); // Update API client with new tenant ID tenantService.setCurrentTenant(tenant); }, switchTenant: async (tenantId: string): Promise => { try { set({ isLoading: true, error: null }); const { availableTenants } = get(); // Find tenant in available tenants first const targetTenant = availableTenants?.find(t => t.id === tenantId); if (!targetTenant) { throw new Error('Tenant not found in available tenants'); } // Switch tenant using service const response = await tenantService.switchTenant(tenantId); if (response.success && response.data?.tenant) { get().setCurrentTenant(response.data.tenant); set({ isLoading: false }); return true; } else { throw new Error(response.error || 'Failed to switch tenant'); } } catch (error) { set({ isLoading: false, error: error instanceof Error ? error.message : 'Failed to switch tenant', }); return false; } }, loadUserTenants: async (): Promise => { try { set({ isLoading: true, error: null }); // Get current user to determine user ID const user = useAuthUser.getState?.() || JSON.parse(localStorage.getItem('auth-storage') || '{}')?.state?.user; if (!user?.id) { throw new Error('User not authenticated'); } const response = await tenantService.getUserTenants(user.id); if (response.success && response.data) { const tenants = Array.isArray(response.data) ? response.data : [response.data]; set({ availableTenants: tenants, isLoading: false }); // If no current tenant is set, set the first one as current const { currentTenant } = get(); if (!currentTenant && tenants.length > 0) { get().setCurrentTenant(tenants[0]); } } else { throw new Error(response.error || 'Failed to load user tenants'); } } catch (error) { set({ isLoading: false, error: error instanceof Error ? error.message : 'Failed to load tenants', }); } }, clearTenants: () => { set({ currentTenant: null, availableTenants: null, error: null, }); tenantService.clearCurrentTenant(); }, clearError: () => { set({ error: null }); }, setLoading: (loading: boolean) => { set({ isLoading: loading }); }, // Permission helpers (migrated from BakeryContext) hasPermission: (permission: string): boolean => { const { currentTenant } = get(); if (!currentTenant) return false; // Get user to determine role within this tenant const user = useAuthUser.getState?.() || JSON.parse(localStorage.getItem('auth-storage') || '{}')?.state?.user; // Admin role has all permissions if (user?.role === 'admin') return true; // TODO: Implement proper tenant-based permissions // For now, use basic role-based permissions switch (user?.role) { case 'admin': return true; case 'manager': return ['inventory', 'production', 'sales', 'reports'].some(resource => permission.startsWith(resource) ); case 'baker': return ['production', 'inventory'].some(resource => permission.startsWith(resource) ) && !permission.includes(':delete'); case 'staff': return ['inventory', 'sales'].some(resource => permission.startsWith(resource) ) && permission.includes(':read'); default: return false; } }, canAccess: (resource: string, action: string): boolean => { const { hasPermission } = get(); // Check specific permission if (hasPermission(`${resource}:${action}`)) return true; // Check wildcard permissions if (hasPermission(`${resource}:*`)) return true; return false; }, }), { name: 'tenant-storage', storage: createJSONStorage(() => localStorage), partialize: (state) => ({ currentTenant: state.currentTenant, availableTenants: state.availableTenants, }), onRehydrateStorage: () => (state) => { // Initialize API client with stored tenant when store rehydrates if (state?.currentTenant) { import('../services/api/client').then(({ apiClient }) => { apiClient.setTenantId(state.currentTenant!.id); }); } }, } ) ); // Selectors for common use cases export const useCurrentTenant = () => useTenantStore((state) => state.currentTenant); export const useAvailableTenants = () => useTenantStore((state) => state.availableTenants); export const useTenantLoading = () => useTenantStore((state) => state.isLoading); export const useTenantError = () => useTenantStore((state) => state.error); // Hook for tenant actions export const useTenantActions = () => useTenantStore((state) => ({ setCurrentTenant: state.setCurrentTenant, switchTenant: state.switchTenant, loadUserTenants: state.loadUserTenants, clearTenants: state.clearTenants, clearError: state.clearError, setLoading: state.setLoading, })); // Hook for tenant permissions (replaces useBakeryPermissions) export const useTenantPermissions = () => useTenantStore((state) => ({ hasPermission: state.hasPermission, canAccess: state.canAccess, })); // Combined hook for convenience export const useTenant = () => { const currentTenant = useCurrentTenant(); const availableTenants = useAvailableTenants(); const isLoading = useTenantLoading(); const error = useTenantError(); const actions = useTenantActions(); const permissions = useTenantPermissions(); return { currentTenant, availableTenants, isLoading, error, ...actions, ...permissions, }; };