AeThex-Connect/packages/core/state/slices/authSlice.ts

157 lines
4 KiB
TypeScript

/**
* Authentication State Slice
* Manages user authentication state
*/
import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit';
export interface User {
id: string;
email: string;
name: string;
avatar?: string;
verifiedDomain?: string;
isPremium: boolean;
createdAt: string;
}
interface AuthState {
user: User | null;
token: string | null;
isAuthenticated: boolean;
loading: boolean;
error: string | null;
}
const initialState: AuthState = {
user: null,
token: null,
isAuthenticated: false,
loading: false,
error: null,
};
// Async thunks
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 }),
});
if (!response.ok) {
const error = await response.json();
return rejectWithValue(error.message);
}
const data = await response.json();
return data;
} catch (error: any) {
return rejectWithValue(error.message);
}
}
);
export const register = createAsyncThunk(
'auth/register',
async ({ email, password, name }: { email: string; password: string; name: string }, { rejectWithValue }) => {
try {
const response = await fetch('/api/auth/register', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password, name }),
});
if (!response.ok) {
const error = await response.json();
return rejectWithValue(error.message);
}
const data = await response.json();
return data;
} catch (error: any) {
return rejectWithValue(error.message);
}
}
);
export const logout = createAsyncThunk(
'auth/logout',
async () => {
// Clear token from storage
localStorage.removeItem('token');
}
);
// Slice
const authSlice = createSlice({
name: 'auth',
initialState,
reducers: {
setUser: (state, action: PayloadAction<User>) => {
state.user = action.payload;
state.isAuthenticated = true;
},
setToken: (state, action: PayloadAction<string>) => {
state.token = action.payload;
localStorage.setItem('token', action.payload);
},
clearError: (state) => {
state.error = null;
},
updateUser: (state, action: PayloadAction<Partial<User>>) => {
if (state.user) {
state.user = { ...state.user, ...action.payload };
}
},
},
extraReducers: (builder) => {
// Login
builder.addCase(login.pending, (state) => {
state.loading = true;
state.error = null;
});
builder.addCase(login.fulfilled, (state, action) => {
state.loading = false;
state.user = action.payload.user;
state.token = action.payload.token;
state.isAuthenticated = true;
localStorage.setItem('token', action.payload.token);
});
builder.addCase(login.rejected, (state, action) => {
state.loading = false;
state.error = action.payload as string;
});
// Register
builder.addCase(register.pending, (state) => {
state.loading = true;
state.error = null;
});
builder.addCase(register.fulfilled, (state, action) => {
state.loading = false;
state.user = action.payload.user;
state.token = action.payload.token;
state.isAuthenticated = true;
localStorage.setItem('token', action.payload.token);
});
builder.addCase(register.rejected, (state, action) => {
state.loading = false;
state.error = action.payload as string;
});
// Logout
builder.addCase(logout.fulfilled, (state) => {
state.user = null;
state.token = null;
state.isAuthenticated = false;
state.error = null;
});
},
});
export const { setUser, setToken, clearError, updateUser } = authSlice.actions;
export default authSlice.reducer;