Ajout page login non fonctionnelle

This commit is contained in:
2025-09-16 10:55:23 +02:00
parent 83e2b786c7
commit ad695b82f7
12 changed files with 3076 additions and 784 deletions

View File

@@ -1,12 +1,71 @@
import React from 'react';
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
import { AuthProvider, useAuth } from './context/AuthContext';
import Login from './components/Login';
import RHDashboard from './components/RHDashboard';
// Composant pour protéger les routes
const ProtectedRoute = ({ children }) => {
const { isAuthorized } = useAuth();
return isAuthorized ? children : <Navigate to="/login" replace />;
};
// Composant pour rediriger si déjà connecté
const PublicRoute = ({ children }) => {
const { isAuthorized } = useAuth();
return !isAuthorized ? children : <Navigate to="/dashboard" replace />;
};
function AppContent() {
return (
<div className="min-h-screen bg-gray-50">
<Routes>
{/* Route de login - accessible seulement si non connecté */}
<Route
path="/login"
element={
<PublicRoute>
<Login />
</PublicRoute>
}
/>
{/* Route du dashboard - accessible seulement si connecté */}
<Route
path="/dashboard"
element={
<ProtectedRoute>
<RHDashboard />
</ProtectedRoute>
}
/>
{/* Route par défaut - redirige vers login ou dashboard selon l'état */}
<Route
path="/"
element={<Navigate to="/login" replace />}
/>
{/* Route catch-all pour les URLs non trouvées */}
<Route
path="*"
element={<Navigate to="/login" replace />}
/>
</Routes>
</div>
);
}
function App() {
return (
<div className="min-h-screen bg-gray-50">
<RHDashboard />
</div>
);
return (
<AuthProvider>
<Router>
<AppContent />
</Router>
</AuthProvider>
);
}
export default App;

View File

@@ -0,0 +1,129 @@
import React, { useState, useEffect } from 'react';
import { useAuth } from '../context/AuthContext';
import { useNavigate } from 'react-router-dom';
import { AlertTriangle, Users } from 'lucide-react';
const Login = () => {
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState('');
const navigate = useNavigate();
const { loginWithO365, isAuthorized } = useAuth();
// Redirection si déjà connecté
useEffect(() => {
if (isAuthorized) {
navigate('/dashboard');
}
}, [isAuthorized]);
const handleO365Login = async () => {
setIsLoading(true);
setError('');
try {
const success = await loginWithO365();
if (!success) {
setError("Erreur lors de la connexion Office 365");
setIsLoading(false);
return;
}
// Récupération du token
const token = localStorage.getItem("o365_token");
// Simulation synchronisation groupes
const data = { success: true };
console.log("Résultat syncGroups :", data);
if (!data.success) {
setError("Erreur de synchronisation des groupes : " + data.message);
setIsLoading(false);
return;
}
// Redirection vers dashboard
navigate('/dashboard');
} catch (err) {
console.error('Erreur O365:', err);
if (err.message?.includes('non autorisé') || err.message?.includes('Accès refusé')) {
setError('Accès refusé : Vous devez être membre d\'un groupe autorisé dans votre organisation.');
} else if (err.message?.includes('AADSTS')) {
setError('Erreur d\'authentification Azure AD. Contactez votre administrateur.');
} else {
setError(err.message || "Erreur lors de la connexion Office 365");
}
}
setIsLoading(false);
};
return (
<div className="min-h-screen bg-gray-100 flex items-center justify-center p-4">
<div className="max-w-md w-full">
<div className="bg-white rounded-lg shadow-md p-8">
{/* En-tête */}
<div className="text-center mb-8">
<div className="w-16 h-16 bg-[#7e5aa2] rounded-lg flex items-center justify-center mx-auto mb-4">
<Users className="w-8 h-8 text-white" />
</div>
<h1 className="text-2xl font-bold text-gray-900 mb-2">GTF - Espace RH</h1>
</div>
{/* Bouton de connexion Office 365 */}
<button
onClick={handleO365Login}
disabled={isLoading}
className="w-full bg-[#7e5aa2] hover:bg-[#6d4a91] text-white py-3 px-4 rounded-lg font-medium transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center space-x-2"
>
{isLoading ? (
<>
<div className="w-5 h-5 border-2 border-white border-t-transparent rounded-full animate-spin"></div>
<span>Connexion...</span>
</>
) : (
<>
<svg className="w-5 h-5" viewBox="0 0 24 24" fill="currentColor">
<path d="M23.5 12c0-.813-.069-1.613-.2-2.4H12v4.54h6.458c-.28 1.488-1.13 2.75-2.41 3.6v2.988h3.908c2.28-2.1 3.594-5.194 3.594-8.728z" />
<path d="M12 24c3.24 0 5.956-1.075 7.944-2.906l-3.908-2.988c-1.075.725-2.456 1.156-4.036 1.156-3.106 0-5.744-2.1-6.681-4.919H1.294v3.081C3.281 21.394 7.344 24 12 24z" />
<path d="M5.319 14.343c-.238-.725-.375-1.494-.375-2.343s.138-1.619.375-2.344V6.575H1.294C.469 8.225 0 10.044 0 12s.469 3.775 1.294 5.425l4.025-3.082z" />
<path d="M12 4.781c1.75 0 3.319.6 4.556 1.781l3.419-3.419C17.944 1.194 15.231 0 12 0 7.344 0 3.281 2.606 1.294 6.575l4.025 3.081C6.256 6.881 8.894 4.781 12 4.781z" />
</svg>
<span>Se connecter avec Office 365</span>
</>
)}
</button>
{/* Message d'erreur */}
{error && (
<div className="mt-6 p-4 bg-red-50 border border-red-200 rounded-lg">
<div className="flex items-start space-x-2">
<AlertTriangle className="w-5 h-5 text-red-500 flex-shrink-0 mt-0.5" />
<div>
<p className="text-red-800 font-medium text-sm">
{error.includes('Accès refusé') ? 'Accès refusé' : 'Erreur de connexion'}
</p>
<p className="text-red-700 text-xs mt-1">{error}</p>
{error.includes('groupe autorisé') && (
<p className="text-red-700 text-xs mt-2">
Contactez votre administrateur pour être ajouté aux groupes appropriés.
</p>
)}
</div>
</div>
</div>
)}
{/* Note */}
</div>
</div>
</div>
);
};
export default Login;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,120 @@
import React, { createContext, useContext, useState, useEffect } from 'react';
const AuthContext = createContext();
export const useAuth = () => {
const context = useContext(AuthContext);
if (!context) {
throw new Error('useAuth must be used within an AuthProvider');
}
return context;
};
export const AuthProvider = ({ children }) => {
const [isAuthorized, setIsAuthorized] = useState(false);
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
// Vérifier l'état d'authentification au chargement
useEffect(() => {
const token = localStorage.getItem('token') || localStorage.getItem('o365_token');
const userData = localStorage.getItem('user');
if (token && userData) {
setUser(JSON.parse(userData));
setIsAuthorized(true);
}
setLoading(false);
}, []);
// Connexion classique (email/password)
const login = async (email, password) => {
try {
const response = await fetch('http://localhost:3002/api/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ email, password }),
});
if (!response.ok) {
throw new Error('Identifiants incorrects');
}
const data = await response.json();
localStorage.setItem('token', data.token);
localStorage.setItem('user', JSON.stringify(data.user));
setUser(data.user);
setIsAuthorized(true);
return true;
} catch (error) {
console.error('Erreur de connexion:', error);
throw error;
}
};
// Connexion Office 365
const loginWithO365 = async () => {
try {
// Simuler l'authentification Office 365
// Remplacez cette partie par votre vraie logique O365
const mockUser = {
id: '1',
email: 'user@office365.com',
name: 'Utilisateur O365',
role: 'rh'
};
const mockToken = 'mock-o365-token';
localStorage.setItem('o365_token', mockToken);
localStorage.setItem('user', JSON.stringify(mockUser));
setUser(mockUser);
setIsAuthorized(true);
return true;
} catch (error) {
console.error('Erreur connexion O365:', error);
throw error;
}
};
// Déconnexion
const logout = () => {
localStorage.removeItem('token');
localStorage.removeItem('o365_token');
localStorage.removeItem('user');
setUser(null);
setIsAuthorized(false);
};
const value = {
isAuthorized,
user,
login,
loginWithO365,
logout,
loading
};
if (loading) {
return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600"></div>
</div>
);
}
return (
<AuthContext.Provider value={value}>
{children}
</AuthContext.Provider>
);
};