314a98c1ad
- Détection des profils incomplets (société/campus/service/date d'entrée/validateur) avec alerte Dashboard et section dédiée dans Équipe - Formulaire d'édition des collaborateurs (date d'entrée, rôle, validateur N+1, société, campus, service, type de contrat) avec combobox de recherche pour le validateur - Correction du mapping poste (colonne description au lieu de fonction) et du filtre des validateurs (variantes de rôle: Validateur/Validatrice, Directeur/Directrice, President) - Fix du proxy Vite vers le backend Docker Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
162 lines
6.8 KiB
TypeScript
162 lines
6.8 KiB
TypeScript
import { useState, useEffect } from "react";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
|
import { Calendar, LogIn } from "lucide-react";
|
|
import { useNavigate } from "react-router-dom";
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
|
import { toast } from "sonner";
|
|
import { Badge } from "@/components/ui/badge";
|
|
|
|
interface User {
|
|
id: number;
|
|
email: string;
|
|
nom: string;
|
|
prenom: string;
|
|
role: string;
|
|
service: string;
|
|
}
|
|
|
|
const LoginDev = () => {
|
|
const navigate = useNavigate();
|
|
const [users, setUsers] = useState<User[]>([]);
|
|
const [selectedEmail, setSelectedEmail] = useState("");
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
useEffect(() => {
|
|
chargerUtilisateurs();
|
|
}, []);
|
|
|
|
const chargerUtilisateurs = async () => {
|
|
try {
|
|
const response = await fetch('/api/users-dev');
|
|
const data = await response.json();
|
|
setUsers(data);
|
|
} catch (error) {
|
|
toast.error("Erreur lors du chargement des utilisateurs");
|
|
}
|
|
};
|
|
|
|
const handleLogin = async () => {
|
|
if (!selectedEmail) {
|
|
toast.error("Veuillez sélectionner un utilisateur");
|
|
return;
|
|
}
|
|
|
|
setLoading(true);
|
|
try {
|
|
const response = await fetch('/api/login-dev', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ email: selectedEmail })
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (response.ok) {
|
|
localStorage.setItem('token', data.token);
|
|
localStorage.setItem('user', JSON.stringify(data.user));
|
|
toast.success(`Bienvenue ${data.user.prenom} ${data.user.nom} !`);
|
|
navigate('/dashboard');
|
|
} else {
|
|
toast.error(data.error || "Erreur de connexion");
|
|
}
|
|
} catch (error) {
|
|
toast.error("Erreur lors de la connexion");
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const getRoleBadge = (role: string) => {
|
|
switch (role) {
|
|
case 'Admin':
|
|
return <Badge variant="destructive">{role}</Badge>;
|
|
case 'RH':
|
|
return <Badge variant="default">{role}</Badge>;
|
|
case 'Validateur':
|
|
return <Badge variant="secondary">{role}</Badge>;
|
|
default:
|
|
return <Badge variant="outline">{role}</Badge>;
|
|
}
|
|
};
|
|
|
|
const selectedUser = users.find(u => u.email === selectedEmail);
|
|
|
|
return (
|
|
<div className="min-h-screen bg-gradient-subtle flex items-center justify-center p-4">
|
|
<Card className="w-full max-w-md shadow-elegant border-0">
|
|
<CardHeader className="space-y-4">
|
|
<div className="flex justify-center">
|
|
<div className="w-16 h-16 bg-gradient-primary rounded-2xl flex items-center justify-center">
|
|
<Calendar className="w-8 h-8 text-primary-foreground" />
|
|
</div>
|
|
</div>
|
|
<div className="text-center">
|
|
<CardTitle className="text-2xl">RH Suivi - Mode Dev</CardTitle>
|
|
<CardDescription>
|
|
Sélectionnez un utilisateur pour vous connecter
|
|
</CardDescription>
|
|
</div>
|
|
</CardHeader>
|
|
<CardContent className="space-y-6">
|
|
<div className="space-y-2">
|
|
<label className="text-sm font-medium">Utilisateur</label>
|
|
<Select value={selectedEmail} onValueChange={setSelectedEmail}>
|
|
<SelectTrigger>
|
|
<SelectValue placeholder="Choisir un utilisateur..." />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
{users.map(user => (
|
|
<SelectItem key={user.id} value={user.email}>
|
|
<div className="flex items-center gap-2">
|
|
<span>{user.prenom} {user.nom}</span>
|
|
<span className="text-xs text-muted-foreground">({user.role})</span>
|
|
</div>
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
|
|
{selectedUser && (
|
|
<Card className="bg-muted/50 border-0">
|
|
<CardContent className="p-4 space-y-2">
|
|
<div className="flex items-center justify-between">
|
|
<span className="text-sm text-muted-foreground">Nom</span>
|
|
<span className="font-medium">{selectedUser.prenom} {selectedUser.nom}</span>
|
|
</div>
|
|
<div className="flex items-center justify-between">
|
|
<span className="text-sm text-muted-foreground">Email</span>
|
|
<span className="text-sm">{selectedUser.email}</span>
|
|
</div>
|
|
<div className="flex items-center justify-between">
|
|
<span className="text-sm text-muted-foreground">Rôle</span>
|
|
{getRoleBadge(selectedUser.role)}
|
|
</div>
|
|
<div className="flex items-center justify-between">
|
|
<span className="text-sm text-muted-foreground">Service</span>
|
|
<span className="text-sm">{selectedUser.service || 'Non assigné'}</span>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
)}
|
|
|
|
<Button
|
|
onClick={handleLogin}
|
|
disabled={loading || !selectedEmail}
|
|
className="w-full bg-gradient-primary hover:opacity-90 transition-smooth"
|
|
>
|
|
<LogIn className="w-4 h-4 mr-2" />
|
|
{loading ? 'Connexion...' : 'Se connecter'}
|
|
</Button>
|
|
|
|
<p className="text-xs text-center text-muted-foreground pt-2 border-t">
|
|
Version 2.0.0
|
|
</p>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default LoginDev; |