215 lines
6.6 KiB
TypeScript
215 lines
6.6 KiB
TypeScript
'use client';
|
|
|
|
import React, { createContext, useContext, useState, useEffect, useCallback } from 'react';
|
|
import { useRouter } from 'next/navigation';
|
|
import { User, LoginCredentials, RegisterData, AuthResponse } from '@/types';
|
|
import { authService } from '@/services/auth.service';
|
|
import { setToken, removeToken, getToken, storage } from '@/utils/helpers';
|
|
import { STORAGE_KEYS, ROUTES } from '@/utils/constants';
|
|
import toast from 'react-hot-toast';
|
|
|
|
interface AuthContextType {
|
|
user: User | null;
|
|
isLoading: boolean;
|
|
isAuthenticated: boolean;
|
|
login: (credentials: LoginCredentials) => Promise<void>;
|
|
register: (data: RegisterData) => Promise<void>;
|
|
logout: () => Promise<void>;
|
|
googleLogin: (token: string) => Promise<void>;
|
|
facebookLogin: (token: string) => Promise<void>;
|
|
refreshUser: () => Promise<void>;
|
|
}
|
|
|
|
export const AuthContext = createContext<AuthContextType | undefined>(undefined);
|
|
|
|
export const useAuth = () => {
|
|
const context = useContext(AuthContext);
|
|
if (!context) {
|
|
throw new Error('useAuth must be used within an AuthProvider');
|
|
}
|
|
return context;
|
|
};
|
|
|
|
interface AuthProviderProps {
|
|
children: React.ReactNode;
|
|
}
|
|
|
|
export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
|
|
const [user, setUser] = useState<User | null>(null);
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
const router = useRouter();
|
|
|
|
const loadUser = useCallback(async () => {
|
|
const token = getToken();
|
|
if (!token) {
|
|
setIsLoading(false);
|
|
return;
|
|
}
|
|
|
|
// Try to load cached user first for faster initial render
|
|
const cachedUser = storage.get(STORAGE_KEYS.USER);
|
|
if (cachedUser) {
|
|
try {
|
|
const parsedUser = JSON.parse(cachedUser);
|
|
setUser(parsedUser);
|
|
setIsLoading(false);
|
|
|
|
// Refresh user data in background (don't block UI)
|
|
authService.getCurrentUser()
|
|
.then(userData => {
|
|
setUser(userData);
|
|
storage.set(STORAGE_KEYS.USER, JSON.stringify(userData));
|
|
})
|
|
.catch(error => {
|
|
console.error('Error refreshing user:', error);
|
|
// If refresh fails, keep cached user
|
|
});
|
|
return;
|
|
} catch (e) {
|
|
// Invalid cached data, continue to fetch
|
|
}
|
|
}
|
|
|
|
try {
|
|
const userData = await authService.getCurrentUser();
|
|
setUser(userData);
|
|
storage.set(STORAGE_KEYS.USER, JSON.stringify(userData));
|
|
} catch (error) {
|
|
console.error('Error loading user:', error);
|
|
removeToken();
|
|
storage.remove(STORAGE_KEYS.USER);
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
loadUser();
|
|
}, [loadUser]);
|
|
|
|
const login = async (credentials: LoginCredentials) => {
|
|
try {
|
|
const response: AuthResponse = await authService.login(credentials);
|
|
setToken(response.token);
|
|
setUser(response.user);
|
|
storage.set(STORAGE_KEYS.USER, JSON.stringify(response.user));
|
|
toast.success('Connexion réussie !');
|
|
|
|
// Redirect based on role (support both uppercase and lowercase)
|
|
// Use replace() to remove login page from history
|
|
const role = response.user.role.toUpperCase();
|
|
if (role === 'ADMIN') {
|
|
router.replace(ROUTES.ADMIN_DASHBOARD);
|
|
} else if (role === 'EMPLOYEE') {
|
|
router.replace(ROUTES.EMPLOYEE_DASHBOARD);
|
|
} else {
|
|
router.replace(ROUTES.CLIENT_DASHBOARD);
|
|
}
|
|
} catch (error: any) {
|
|
toast.error(error.message || 'Erreur lors de la connexion');
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
const register = async (data: RegisterData) => {
|
|
try {
|
|
const response: AuthResponse = await authService.register(data);
|
|
setToken(response.token);
|
|
setUser(response.user);
|
|
storage.set(STORAGE_KEYS.USER, JSON.stringify(response.user));
|
|
toast.success('Inscription réussie !');
|
|
router.push(ROUTES.CLIENT_DASHBOARD);
|
|
} catch (error: any) {
|
|
toast.error(error.message || 'Erreur lors de l\'inscription');
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
const logout = async () => {
|
|
try {
|
|
await authService.logout();
|
|
} catch (error) {
|
|
console.error('Error during logout:', error);
|
|
} finally {
|
|
removeToken();
|
|
storage.remove(STORAGE_KEYS.USER);
|
|
setUser(null);
|
|
toast.success('Déconnexion réussie');
|
|
router.push(ROUTES.LOGIN);
|
|
}
|
|
};
|
|
|
|
const googleLogin = async (token: string) => {
|
|
try {
|
|
const response: AuthResponse = await authService.googleLogin(token);
|
|
setToken(response.token);
|
|
setUser(response.user);
|
|
storage.set(STORAGE_KEYS.USER, JSON.stringify(response.user));
|
|
toast.success('Connexion avec Google réussie !');
|
|
|
|
// Redirect based on role (support both uppercase and lowercase)
|
|
// Use replace() to remove login page from history
|
|
const role = response.user.role.toUpperCase();
|
|
if (role === 'ADMIN') {
|
|
router.replace(ROUTES.ADMIN_DASHBOARD);
|
|
} else if (role === 'EMPLOYEE') {
|
|
router.replace(ROUTES.EMPLOYEE_DASHBOARD);
|
|
} else {
|
|
router.replace(ROUTES.CLIENT_DASHBOARD);
|
|
}
|
|
} catch (error: any) {
|
|
toast.error(error.message || 'Erreur lors de la connexion avec Google');
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
const facebookLogin = async (token: string) => {
|
|
try {
|
|
const response: AuthResponse = await authService.facebookLogin(token);
|
|
setToken(response.token);
|
|
setUser(response.user);
|
|
storage.set(STORAGE_KEYS.USER, JSON.stringify(response.user));
|
|
toast.success('Connexion avec Facebook réussie !');
|
|
|
|
// Redirect based on role (support both uppercase and lowercase)
|
|
// Use replace() to remove login page from history
|
|
const role = response.user.role.toUpperCase();
|
|
if (role === 'ADMIN') {
|
|
router.replace(ROUTES.ADMIN_DASHBOARD);
|
|
} else if (role === 'EMPLOYEE') {
|
|
router.replace(ROUTES.EMPLOYEE_DASHBOARD);
|
|
} else {
|
|
router.replace(ROUTES.CLIENT_DASHBOARD);
|
|
}
|
|
} catch (error: any) {
|
|
toast.error(error.message || 'Erreur lors de la connexion avec Facebook');
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
const refreshUser = async () => {
|
|
try {
|
|
const userData = await authService.getCurrentUser();
|
|
setUser(userData);
|
|
storage.set(STORAGE_KEYS.USER, JSON.stringify(userData));
|
|
} catch (error) {
|
|
console.error('Error refreshing user:', error);
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
const value: AuthContextType = {
|
|
user,
|
|
isLoading,
|
|
isAuthenticated: !!user,
|
|
login,
|
|
register,
|
|
logout,
|
|
googleLogin,
|
|
facebookLogin,
|
|
refreshUser,
|
|
};
|
|
|
|
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
|
|
};
|