import { create } from 'zustand'; import { persist, createJSONStorage } from 'zustand/middleware'; import { GLOBAL_USER_ROLES, type GlobalUserRole } from '../types/roles'; export interface User { id: string; email: string; full_name: string; // Updated to match backend is_active: boolean; is_verified: boolean; created_at: string; last_login?: string; phone?: string; language?: string; timezone?: string; avatar?: string; // User avatar image URL tenant_id?: string; role?: GlobalUserRole; } export interface AuthState { // State user: User | null; token: string | null; refreshToken: string | null; isAuthenticated: boolean; isLoading: boolean; error: string | null; // Actions login: (email: string, password: string) => Promise; register: (userData: { email: string; password: string; full_name: string; tenant_name?: string }) => Promise; logout: () => void; refreshAuth: () => Promise; updateUser: (updates: Partial) => void; clearError: () => void; setLoading: (loading: boolean) => void; // Permission helpers hasPermission: (permission: string) => boolean; hasRole: (role: string) => boolean; canAccess: (resource: string, action: string) => boolean; } import { authService, apiClient } from '../api'; export const useAuthStore = create()( persist( (set, get) => ({ // Initial state user: null, token: null, refreshToken: null, isAuthenticated: false, isLoading: false, error: null, // Actions login: async (email: string, password: string) => { try { set({ isLoading: true, error: null }); const response = await authService.login({ email, password }); if (response && response.access_token) { // Set the auth tokens on the API client immediately apiClient.setAuthToken(response.access_token); if (response.refresh_token) { apiClient.setRefreshToken(response.refresh_token); } set({ user: response.user || null, token: response.access_token, refreshToken: response.refresh_token || null, isAuthenticated: true, isLoading: false, error: null, }); } else { throw new Error('Login failed'); } } catch (error) { set({ user: null, token: null, refreshToken: null, isAuthenticated: false, isLoading: false, error: error instanceof Error ? error.message : 'Error de autenticación', }); throw error; } }, register: async (userData: { email: string; password: string; full_name: string; tenant_name?: string }) => { try { set({ isLoading: true, error: null }); const response = await authService.register(userData); if (response && response.access_token) { // Set the auth tokens on the API client immediately apiClient.setAuthToken(response.access_token); if (response.refresh_token) { apiClient.setRefreshToken(response.refresh_token); } set({ user: response.user || null, token: response.access_token, refreshToken: response.refresh_token || null, isAuthenticated: true, isLoading: false, error: null, }); } else { throw new Error('Registration failed'); } } catch (error) { set({ user: null, token: null, refreshToken: null, isAuthenticated: false, isLoading: false, error: error instanceof Error ? error.message : 'Error de registro', }); throw error; } }, logout: () => { // Clear the auth tokens from API client apiClient.setAuthToken(null); apiClient.setRefreshToken(null); apiClient.setTenantId(null); set({ user: null, token: null, refreshToken: null, isAuthenticated: false, isLoading: false, error: null, }); }, refreshAuth: async () => { try { const { refreshToken } = get(); if (!refreshToken) { throw new Error('No refresh token available'); } set({ isLoading: true }); const response = await authService.refreshToken(refreshToken); if (response && response.access_token) { // Set the auth tokens on the API client immediately apiClient.setAuthToken(response.access_token); if (response.refresh_token) { apiClient.setRefreshToken(response.refresh_token); } set({ token: response.access_token, refreshToken: response.refresh_token || refreshToken, isLoading: false, error: null, }); } else { throw new Error('Token refresh failed'); } } catch (error) { set({ user: null, token: null, refreshToken: null, isAuthenticated: false, isLoading: false, error: error instanceof Error ? error.message : 'Error al renovar sesión', }); throw error; } }, updateUser: (updates: Partial) => { const { user } = get(); if (user) { set({ user: { ...user, ...updates }, }); } }, clearError: () => { set({ error: null }); }, setLoading: (loading: boolean) => { set({ isLoading: loading }); }, // Permission helpers - Global user permissions only hasPermission: (_permission: string): boolean => { const { user } = get(); if (!user || !user.is_active) return false; // Super admin and admin have all global permissions if (user.role === GLOBAL_USER_ROLES.SUPER_ADMIN || user.role === GLOBAL_USER_ROLES.ADMIN) { return true; } // Manager has limited permissions if (user.role === GLOBAL_USER_ROLES.MANAGER) { return ['user_management', 'system_settings'].includes(_permission); } // Regular users have basic permissions return false; }, hasRole: (role: string): boolean => { const { user } = get(); return user?.role === role; }, canAccess: (resource: string, action: string): boolean => { const { user } = get(); if (!user || !user.is_active) return false; // Global role-based access control (system-wide) switch (user.role) { case GLOBAL_USER_ROLES.SUPER_ADMIN: case GLOBAL_USER_ROLES.ADMIN: return true; case GLOBAL_USER_ROLES.MANAGER: return ['users', 'system'].includes(resource); case GLOBAL_USER_ROLES.USER: return action === 'read'; default: return false; } }, }), { name: 'auth-storage', storage: createJSONStorage(() => localStorage), partialize: (state) => ({ user: state.user, token: state.token, refreshToken: state.refreshToken, isAuthenticated: state.isAuthenticated, }), onRehydrateStorage: () => (state) => { // Initialize API client with stored tokens when store rehydrates if (state?.token) { // Use direct import to avoid timing issues apiClient.setAuthToken(state.token); if (state.refreshToken) { apiClient.setRefreshToken(state.refreshToken); } if (state.user?.tenant_id) { apiClient.setTenantId(state.user.tenant_id); } } }, } ) ); // Selectors for common use cases export const useAuthUser = () => useAuthStore((state) => state.user); export const useIsAuthenticated = () => useAuthStore((state) => state.isAuthenticated); export const useAuthLoading = () => useAuthStore((state) => state.isLoading); export const useAuthError = () => useAuthStore((state) => state.error); export const usePermissions = () => useAuthStore((state) => ({ hasPermission: state.hasPermission, hasRole: state.hasRole, canAccess: state.canAccess, })); // Hook for auth actions export const useAuthActions = () => useAuthStore((state) => ({ login: state.login, register: state.register, logout: state.logout, refreshAuth: state.refreshAuth, updateUser: state.updateUser, clearError: state.clearError, setLoading: state.setLoading, }));