import { createSlice, createAsyncThunk, PayloadAction } from "@reduxjs/toolkit"; // Define the initial state for the authentication slice interface AuthState { isLoggedIn: boolean; authMessage: string; status: "idle" | "loading" | "succeeded" | "failed"; error: string | null; } const initialState: AuthState = { isLoggedIn: false, authMessage: "", status: "idle", error: null, }; // Async Thunk for Login // This handles the API call to your Next.js login Route Handler export const login = createAsyncThunk( "auth/login", async ( { email, password }: { email: string; password: string }, { rejectWithValue } ) => { try { const response = await fetch("/api/auth/login", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ email, password }), }); const data = await response.json(); if (!response.ok) { // If the server responded with an error status (e.g., 401, 400, 500) return rejectWithValue(data.message || "Login failed"); } // On successful login, the backend sets the HTTP-only cookie. // We'll set a client-side flag (like localStorage) for immediate UI updates, // though the primary source of truth for auth is the HTTP-only cookie. if (typeof window !== "undefined") { // Ensure localStorage access is client-side localStorage.setItem("userToken", "mock-authenticated"); // For client-side state sync } return data.message || "Login successful"; } catch (error: any) { // Handle network errors or other unexpected issues return rejectWithValue(error.message || "Network error during login"); } } ); // Async Thunk for Logout // This handles the API call to your Next.js logout Route Handler export const logout = createAsyncThunk( "auth/logout", async (_, { rejectWithValue }) => { try { const response = await fetch("/api/auth/logout", { method: "DELETE", }); const data = await response.json(); if (!response.ok) { // If the server responded with an error status return rejectWithValue(data.message || "Logout failed"); } if (typeof window !== "undefined") { // Ensure localStorage access is client-side localStorage.removeItem("userToken"); // Clear client-side flag } return data.message || "Logged out successfully"; } catch (error: any) { // Handle network errors return rejectWithValue(error.message || "Network error during logout"); } } ); // Create the authentication slice const authSlice = createSlice({ name: "auth", initialState, reducers: { // Reducer to set an authentication message (e.g., from UI actions) setAuthMessage: (state, action: PayloadAction) => { state.authMessage = action.payload; }, // Reducer to clear the authentication message clearAuthMessage: (state) => { state.authMessage = ""; }, // Reducer to initialize login status from client-side storage (e.g., on app load) // This is useful for cases where middleware might not redirect immediately, // or for client-side rendering of protected content based on initial state. initializeAuth: (state) => { if (typeof window !== "undefined") { // Ensure this runs only on the client const userToken = localStorage.getItem("userToken"); state.isLoggedIn = userToken === "mock-authenticated"; } }, }, extraReducers: (builder) => { builder // Login Thunk Reducers .addCase(login.pending, (state) => { state.status = "loading"; state.error = null; state.authMessage = "Attempting login..."; }) .addCase(login.fulfilled, (state, action) => { state.status = "succeeded"; state.isLoggedIn = true; state.authMessage = action.payload; }) .addCase(login.rejected, (state, action) => { state.status = "failed"; state.isLoggedIn = false; state.error = action.payload as string; state.authMessage = action.payload as string; // Display error message }) // Logout Thunk Reducers .addCase(logout.pending, (state) => { state.status = "loading"; state.error = null; state.authMessage = "Logging out..."; }) .addCase(logout.fulfilled, (state, action) => { state.status = "succeeded"; state.isLoggedIn = false; state.authMessage = action.payload; }) .addCase(logout.rejected, (state, action) => { state.status = "failed"; state.isLoggedIn = true; // Stay logged in if logout failed state.error = action.payload as string; state.authMessage = action.payload as string; // Display error message }); }, }); export const { setAuthMessage, clearAuthMessage, initializeAuth } = authSlice.actions; export default authSlice.reducer;