import { createSlice, PayloadAction } from '@reduxjs/toolkit'; import { updateAxiosToken, clearAxiosToken, updateAxiosSchoolCode, clearAxiosSchoolCode, } from '@/lib/api/student/axiosConfig'; import { StudentUser } from '@/lib/api/student/functions'; // Define the student auth state interface // Store complete user data from API response interface StudentAuthState { isAuthenticated: boolean; user: StudentUser | null; // Store complete user object from API token: string | null; schoolCode: string | null; // Store school code for API headers loading: boolean; error: string | null; } // LocalStorage keys for auth persistence const AUTH_STORAGE_KEY = 'student_auth_state'; // Helper function to save auth state to localStorage const saveAuthToLocalStorage = ( user: StudentUser | null, token: string | null, schoolCode: string | null ) => { if (typeof window !== 'undefined') { if (user && token && schoolCode) { localStorage.setItem( AUTH_STORAGE_KEY, JSON.stringify({ user, token, schoolCode }) ); } else { localStorage.removeItem(AUTH_STORAGE_KEY); } } }; // Helper function to load auth state from localStorage // This should only be called on the client side after mount export const loadAuthFromLocalStorage = (): { user: StudentUser | null; token: string | null; schoolCode: string | null; } | null => { if (typeof window !== 'undefined') { try { const stored = localStorage.getItem(AUTH_STORAGE_KEY); if (stored) { return JSON.parse(stored); } } catch { // Use console.log to prevent error overlay in development localStorage.removeItem(AUTH_STORAGE_KEY); } } return null; }; // Initial state - start with unauthenticated to match server render // This prevents hydration mismatch between server and client // Auth state will be rehydrated from localStorage after client mount const initialState: StudentAuthState = { isAuthenticated: false, user: null, token: null, schoolCode: null, loading: false, error: null, }; // Create the student auth slice const studentAuthSlice = createSlice({ name: 'studentAuth', initialState, reducers: { // Rehydrate auth state from localStorage after client mount // This action should be dispatched once after the app mounts on the client // It prevents hydration mismatch by loading persisted state after initial render rehydrateAuth: (state) => { const storedAuth = loadAuthFromLocalStorage(); if (storedAuth?.token && storedAuth?.user) { state.isAuthenticated = true; state.user = storedAuth.user; state.token = storedAuth.token; state.schoolCode = storedAuth.schoolCode; // Update axios config with restored auth data updateAxiosToken(storedAuth.token); if (storedAuth.schoolCode) { updateAxiosSchoolCode(storedAuth.schoolCode); } } }, // Set loading state setLoading: (state, action: PayloadAction<boolean>) => { state.loading = action.payload; }, // Set error message setError: (state, action: PayloadAction<string | null>) => { state.error = action.payload; }, // Login success loginSuccess: ( state, action: PayloadAction<{ user: StudentAuthState['user']; token: string }> ) => { state.isAuthenticated = true; state.user = action.payload.user; state.token = action.payload.token; // Extract school code from user data state.schoolCode = action.payload.user?.school?.code || null; state.loading = false; state.error = null; // Save auth state to localStorage for persistence saveAuthToLocalStorage( action.payload.user, action.payload.token, state.schoolCode ); // Update axios token and school code for future requests updateAxiosToken(action.payload.token); updateAxiosSchoolCode(state.schoolCode); }, // Logout logout: (state) => { state.isAuthenticated = false; state.user = null; state.token = null; state.schoolCode = null; state.loading = false; state.error = null; // Clear auth state from localStorage saveAuthToLocalStorage(null, null, null); // Clear axios token and school code clearAxiosToken(); clearAxiosSchoolCode(); }, // Clear error clearError: (state) => { state.error = null; }, // Login action (for handling login process) loginStart: (state) => { state.loading = true; state.error = null; }, // Login failure loginFailure: (state, action: PayloadAction<string>) => { state.loading = false; state.error = action.payload; state.isAuthenticated = false; state.user = null; state.token = null; state.schoolCode = null; // Clear axios token and school code on failure clearAxiosToken(); clearAxiosSchoolCode(); }, // Update user profile data without affecting token or auth status updateUserProfile: (state, action: PayloadAction<StudentAuthState['user']>) => { state.user = action.payload; // Also update school code if it changed if (action.payload?.school?.code) { state.schoolCode = action.payload.school.code; saveAuthToLocalStorage( action.payload, state.token, action.payload.school.code ); } else { saveAuthToLocalStorage(action.payload, state.token, state.schoolCode); } }, }, }); // Export actions export const { rehydrateAuth, setLoading, setError, loginSuccess, logout, clearError, loginStart, loginFailure, updateUserProfile, } = studentAuthSlice.actions; // Export reducer export default studentAuthSlice.reducer;