import { createSlice, createAsyncThunk, type PayloadAction } from '@reduxjs/toolkit'; import { authService, type LoginRequest, type LoginResponse, type LoginError, type GeneralError, type Permission } from '@/services/auth-service'; interface User { id: string; email: string; first_name: string; last_name: string; } interface AuthState { user: User | null; tenantId: string | null; // tenant: Tenant | null; roles: string[]; permissions: Permission[]; accessToken: string | null; refreshToken: string | null; tokenType: string | null; expiresIn: number | null; expiresAt: string | null; isAuthenticated: boolean; isLoading: boolean; error: string | null; } const initialState: AuthState = { user: null, tenantId: null, // tenant: null, roles: [], permissions: [], accessToken: null, refreshToken: null, tokenType: null, expiresIn: null, expiresAt: null, isAuthenticated: false, isLoading: false, error: null, }; // Async thunk for login export const loginAsync = createAsyncThunk< { data: LoginResponse['data']; message?: string }, LoginRequest, { rejectValue: LoginError } >('auth/login', async (credentials, { rejectWithValue }) => { try { const response = await authService.login(credentials); if (response.success) { return { data: response.data, message: response.message }; } return rejectWithValue(response as unknown as LoginError); } catch (error: any) { if (error.response?.data) { return rejectWithValue(error.response.data as LoginError); } return rejectWithValue({ success: false, error: { code: 'UNKNOWN_ERROR', message: error.message || 'An unexpected error occurred', }, } as GeneralError); } }); // Async thunk for logout export const logoutAsync = createAsyncThunk<{ message?: string }, void, { rejectValue: { message?: string } }>('auth/logout', async (_, { rejectWithValue }) => { try { const response = await authService.logout(); return { message: response.message }; } catch (error: any) { // Even if API call fails, we should still logout locally const errorData = error?.response?.data || { message: 'Logout failed' }; return rejectWithValue({ message: errorData.message }); } }); const authSlice = createSlice({ name: 'auth', initialState, reducers: { logout: (state) => { state.user = null; state.tenantId = null; // state.tenant = null; state.roles = []; state.permissions = []; state.accessToken = null; state.refreshToken = null; state.tokenType = null; state.expiresIn = null; state.expiresAt = null; state.isAuthenticated = false; state.error = null; }, clearError: (state) => { state.error = null; }, }, extraReducers: (builder) => { builder .addCase(loginAsync.pending, (state) => { state.isLoading = true; state.error = null; }) .addCase(loginAsync.fulfilled, (state, action: PayloadAction<{ data: LoginResponse['data']; message?: string }>) => { state.isLoading = false; state.user = action.payload.data.user; state.tenantId = action.payload.data.tenant_id; // state.tenant = action.payload.data.tenant; state.roles = action.payload.data.roles; state.permissions = action.payload.data.permissions || []; state.accessToken = action.payload.data.access_token; state.refreshToken = action.payload.data.refresh_token; state.tokenType = action.payload.data.token_type; state.expiresIn = action.payload.data.expires_in; state.expiresAt = action.payload.data.expires_at; state.isAuthenticated = true; state.error = null; }) .addCase(loginAsync.rejected, (state, action) => { state.isLoading = false; state.isAuthenticated = false; if (action.payload) { const error = action.payload; if ('error' in error && typeof error.error === 'object' && 'message' in error.error) { // General error state.error = error.error.message; } else { // Validation error or other state.error = 'Validation failed'; } } else { state.error = action.error.message || 'Login failed'; } }) .addCase(logoutAsync.pending, (state) => { state.isLoading = true; }) .addCase(logoutAsync.fulfilled, (state) => { // Reset to initial state state.user = null; state.tenantId = null; // state.tenant = null; state.roles = []; state.permissions = []; state.accessToken = null; state.refreshToken = null; state.tokenType = null; state.expiresIn = null; state.expiresAt = null; state.isAuthenticated = false; state.isLoading = false; state.error = null; }) .addCase(logoutAsync.rejected, (state) => { // Even if API call fails, clear local state state.user = null; state.tenantId = null; // state.tenant = null; state.roles = []; state.permissions = []; state.accessToken = null; state.refreshToken = null; state.tokenType = null; state.expiresIn = null; state.expiresAt = null; state.isAuthenticated = false; state.isLoading = false; state.error = null; }); }, }); export const { logout, clearError } = authSlice.actions; export default authSlice.reducer;