Files
bakery-ia/frontend/src/stores/auth.store.ts
Claude 7741dd8067 Fix frontend build TypeScript errors
Fixed multiple TypeScript type errors that were preventing the build from working properly:

1. Fixed infinite query type issue in forecasting.ts by excluding 'select' from options
2. Fixed Card variant type errors by changing contentPadding="default" to contentPadding="md"
3. Fixed router export issues by removing non-existent exports (ROUTE_CONFIGS, getRoutesForRole, etc.)
4. Fixed router readonly array type issues by updating RouteConfig interface
5. Fixed ProtectedRoute requiredRoles prop issue by removing invalid prop usage
6. Fixed auth store User type compatibility by allowing null for tenant_id
7. Fixed missing useToasts export from ui.store by removing from exports
8. Fixed permissions utility boolean type issues by wrapping expressions in Boolean()

The frontend build now completes successfully.
2025-11-06 18:39:20 +00:00

319 lines
9.3 KiB
TypeScript

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 | null;
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<void>;
register: (userData: {
email: string;
password: string;
full_name: string;
tenant_name?: string;
subscription_plan?: string;
use_trial?: boolean;
payment_method_id?: string;
}) => Promise<void>;
logout: () => void;
refreshAuth: () => Promise<void>;
updateUser: (updates: Partial<User>) => 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<AuthState>()(
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;
subscription_plan?: string;
use_trial?: boolean;
payment_method_id?: 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);
// Clear tenant store to remove cached tenant data
// Import dynamically to avoid circular dependencies
import('./tenant.store').then(({ useTenantStore }) => {
useTenantStore.getState().clearTenants();
}).catch(err => {
console.warn('Failed to clear tenant store on logout:', err);
});
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<User>) => {
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,
}));