import { useState, useEffect } from "react"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Calendar, Users, Clock, CheckCircle, XCircle, AlertCircle, Plus, LogOut, FileSpreadsheet, RefreshCw, Edit, ClipboardList, FileText } from "lucide-react"; import { useNavigate } from "react-router-dom"; import { toast } from "sonner"; import { useSSE } from "@/hooks/useSSE"; import { PublicClientApplication } from "@azure/msal-browser"; // Configuration MSAL const msalConfig = { auth: { clientId: "4bb4cc24-bac3-427c-b02c-5d14fc67b561", authority: "https://login.microsoftonline.com/9840a2a0-6ae1-4688-b03d-d2ec291be0f9", redirectUri: "https://mygta-rh.ensup-adm.net", }, cache: { cacheLocation: "sessionStorage", storeAuthStateInCookie: false, }, }; interface Stats { enAttente: number; valideeCeMois: number; nombreEquipes: number; } interface Demande { Id: number; nomEmploye: string; typesConge: string; DateDebut: string; Statut: string; } const Dashboard = () => { const navigate = useNavigate(); const [stats, setStats] = useState({ enAttente: 0, valideeCeMois: 0, nombreEquipes: 0 }); const [recentRequests, setRecentRequests] = useState([]); const [user, setUser] = useState(null); const [token, setToken] = useState(''); const [isLoading, setIsLoading] = useState(true); // ✅ Chargement initial de l'utilisateur useEffect(() => { const userData = localStorage.getItem('user'); const userToken = localStorage.getItem('token'); if (!userData || !userToken) { toast.error("Session expirée, reconnexion nécessaire"); navigate('/'); return; } try { const parsedUser = JSON.parse(userData); setUser(parsedUser); setToken(userToken); } catch (error) { console.error('Erreur parsing user:', error); toast.error("Erreur lors de la récupération des données utilisateur"); navigate('/'); } }, [navigate]); // ✅ Fonction de chargement des données const chargerDonnees = async () => { if (!token) { console.log('⏳ Pas de token, attente...'); return; } console.log('📥 Chargement des données du dashboard...'); setIsLoading(true); try { // Charger les stats const respStats = await fetch('/api/stats', { headers: { 'Authorization': `Bearer ${token}` } }); if (!respStats.ok) { throw new Error(`Erreur stats: ${respStats.status}`); } const dataStats = await respStats.json(); console.log('📊 Stats reçues:', dataStats); setStats(dataStats); // Charger les demandes récentes const respDemandes = await fetch('/api/demandes?limit=5', { headers: { 'Authorization': `Bearer ${token}` } }); if (!respDemandes.ok) { throw new Error(`Erreur demandes: ${respDemandes.status}`); } const dataDemandes = await respDemandes.json(); console.log('📋 Demandes reçues:', dataDemandes.length); setRecentRequests(dataDemandes); } catch (error: any) { console.error("❌ Erreur chargement données:", error); toast.error("Erreur lors du chargement des données: " + error.message); } finally { setIsLoading(false); } }; // ✅ Charger les données quand le token est disponible useEffect(() => { if (token) { chargerDonnees(); } }, [token]); // 🔔 CONNEXION SSE useSSE((event) => { console.log('🔔 Événement SSE reçu dans Dashboard:', event.type); switch (event.type) { case 'connected': console.log('✅ Dashboard connecté au serveur temps réel'); toast.success("Connexion temps réel établie", { duration: 2000 }); break; case 'demande-created': console.log('🆕 Nouvelle demande créée'); toast.info("Nouvelle demande de congé", { duration: 3000 }); if (token) chargerDonnees(); break; case 'demande-updated': console.log('🔄 Demande mise à jour'); if (token) chargerDonnees(); break; case 'demande-validated': console.log('✅ Demande validée/refusée'); toast.info("Une demande a été traitée", { duration: 3000 }); if (token) chargerDonnees(); break; // 🆕 GESTION DES ANNULATIONS case 'demande-cancelled': console.log('🚫 Demande annulée'); toast.warning("Une demande a été annulée", { description: "La liste a été mise à jour", duration: 4000 }); if (token) chargerDonnees(); break; case 'demande-deleted': console.log('🗑️ Demande supprimée'); if (token) chargerDonnees(); break; case 'demande-list-updated': console.log('📋 Liste des demandes mise à jour'); if (token) chargerDonnees(); break; case 'heartbeat': // Heartbeat pour garder la connexion break; default: console.log('❓ Type d\'événement inconnu:', event.type); } }); const handleLogout = async () => { try { const msalInstance = new PublicClientApplication(msalConfig); await msalInstance.initialize(); const accounts = msalInstance.getAllAccounts(); if (accounts.length > 0) { // Déconnexion popup au lieu de redirect await msalInstance.logoutPopup({ account: accounts[0], mainWindowRedirectUri: "https://mygta-rh.ensup-adm.net" }); } // Nettoyer tout le stockage localStorage.removeItem('token'); localStorage.removeItem('user'); sessionStorage.clear(); toast.success("Déconnexion réussie"); navigate("/"); } catch (error) { console.error("Erreur déconnexion:", error); // Forcer la déconnexion locale même en cas d'erreur localStorage.removeItem('token'); localStorage.removeItem('user'); sessionStorage.clear(); toast.success("Déconnexion réussie"); navigate("/"); } }; // ✅ FONCTION MISE À JOUR AVEC LE STATUT "ANNULÉE" const getStatusBadge = (status: string) => { switch (status) { case "En attente": return En attente ; case "Validée": return Validée ; case "Refusée": return Refusée ; case "Annulée": return Annulée ; default: return {status} ; } }; const formatDate = (dateString: string) => { if (!dateString) return ''; try { return new Date(dateString).toLocaleDateString('fr-FR', { day: '2-digit', month: '2-digit', year: 'numeric' }); } catch (error) { console.error('Erreur formatage date:', error); return dateString; } }; const statsCards = [ { title: "Demandes en attente", value: stats.enAttente, icon: Clock, color: "text-warning", bg: "bg-warning/10", action: () => navigate("/validation") }, { title: "Validées ce mois", value: stats.valideeCeMois, icon: CheckCircle, color: "text-success", bg: "bg-success/10", action: () => navigate("/validation") }, { title: "Total équipes", value: stats.nombreEquipes, icon: Users, color: "text-primary", bg: "bg-primary/10", action: () => navigate("/teams") }, ]; const actionCards = [ { title: "Saisie manuelle", description: "Enregistrer une demande", icon: Plus, color: "bg-[#7e5aa2]", action: () => navigate("/saisie-manuelle") }, { title: "Validation", description: "Valider les demandes", icon: CheckCircle, color: "bg-[#7e5aa2]", // Mauve GTARH comme les autres action: () => navigate("/validation") }, { title: "Récupération", description: "Gérer les samedis travaillés", icon: Clock, color: "bg-purple-600", action: () => navigate("/gestion-recuperation") }, { title: "Export paie", description: "Générer un rapport", icon: FileSpreadsheet, color: "bg-blue-600", action: () => navigate("/export-paie") }, { title: "Compteurs", description: "Gérer les soldes", icon: RefreshCw, color: "bg-purple-600", action: () => navigate("/compteurs") }, { title: "Équipes", description: "Voir toutes les équipes", icon: Users, color: "bg-indigo-600", action: () => navigate("/teams") }, { title: "Historique", description: "Consulter l'historique", icon: ClipboardList, color: "bg-orange-600", action: () => navigate("/historique") }, { title: "Comptes-Rendus RH", description: "Gérer les forfaits jour", icon: FileText, color: "bg-cyan-600", action: () => navigate("/compte-rendu-rh") } ]; const handleDeleteRequest = async (id: number) => { if (!confirm("Voulez-vous vraiment supprimer cette demande ?")) return; if (!token) { toast.error("Utilisateur non authentifié"); return; } try { console.log('🗑️ Suppression demande:', id); const resp = await fetch(`/api/demandes/${id}`, { method: "DELETE", headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json', }, }); const data = await resp.json(); if (resp.ok) { toast.success("Demande supprimée avec succès"); // Le SSE rechargera automatiquement, mais on peut forcer un rechargement immédiat await chargerDonnees(); } else { toast.error(data.error || "Erreur lors de la suppression"); } } catch (error: any) { console.error("❌ Erreur suppression:", error); toast.error("Erreur lors de la suppression: " + error.message); } }; // ✅ Affichage du loader pendant le chargement initial if (isLoading && !user) { return (

Chargement du tableau de bord...

); } return (

GTARH

{user && (

{user.prenom} {user.nom} - {user.role}

)}
{/* Stats Cards */}
{statsCards.map((stat, index) => (

{stat.title}

{stat.value}

))}
{/* Action Cards */}
{actionCards.map((action, index) => (

{action.title}

{action.description}

))}
{/* Recent Requests */}
Demandes récentes Les dernières demandes soumises
{isLoading && recentRequests.length === 0 ? (

Chargement des demandes...

) : recentRequests.length > 0 ? (
{recentRequests.map((request) => (
navigate(`/modification-demande/${request.Id}`)} >

{request.nomEmploye}

{request.typesConge}

À partir du {formatDate(request.DateDebut)}

{getStatusBadge(request.Statut)} {/* Bouton Edit */} {/* Bouton Supprimer */}
))}
) : (

Aucune demande récente

)}
); }; export default Dashboard;