'use client'; import React, { useState, useEffect, useRef } from 'react'; import { Card } from 'primereact/card'; import { DataTable } from 'primereact/datatable'; import { Column } from 'primereact/column'; import { Tag } from 'primereact/tag'; import { Button } from 'primereact/button'; import { Toast } from 'primereact/toast'; import { Dropdown } from 'primereact/dropdown'; import { Calendar } from 'primereact/calendar'; import { Badge } from 'primereact/badge'; import { Timeline } from 'primereact/timeline'; import { Message } from 'primereact/message'; import { Divider } from 'primereact/divider'; import { ProgressBar } from 'primereact/progressbar'; import { useRouter } from 'next/navigation'; interface Alerte { id: string; titre: string; description: string; type: string; severite: string; statut: string; dateCreation: Date; dateEcheance?: Date; source: string; entite: string; entiteId: string; actions: string[]; responsable?: string; lu: boolean; archive: boolean; } const DashboardAlertes = () => { const toast = useRef(null); const router = useRouter(); const [alertes, setAlertes] = useState([]); const [loading, setLoading] = useState(true); const [selectedType, setSelectedType] = useState(''); const [selectedSeverite, setSelectedSeverite] = useState(''); const [selectedStatut, setSelectedStatut] = useState(''); const [showArchived, setShowArchived] = useState(false); const [timelineEvents, setTimelineEvents] = useState([]); const typeOptions = [ { label: 'Tous les types', value: '' }, { label: 'Sécurité', value: 'SECURITE' }, { label: 'Maintenance', value: 'MAINTENANCE' }, { label: 'Budget', value: 'BUDGET' }, { label: 'Planning', value: 'PLANNING' }, { label: 'Qualité', value: 'QUALITE' }, { label: 'Ressources', value: 'RESSOURCES' }, { label: 'Conformité', value: 'CONFORMITE' }, { label: 'Système', value: 'SYSTEME' } ]; const severiteOptions = [ { label: 'Toutes les sévérités', value: '' }, { label: 'Critique', value: 'CRITIQUE' }, { label: 'Élevée', value: 'ELEVEE' }, { label: 'Moyenne', value: 'MOYENNE' }, { label: 'Faible', value: 'FAIBLE' }, { label: 'Info', value: 'INFO' } ]; const statutOptions = [ { label: 'Tous les statuts', value: '' }, { label: 'Nouvelle', value: 'NOUVELLE' }, { label: 'En cours', value: 'EN_COURS' }, { label: 'Résolue', value: 'RESOLUE' }, { label: 'Fermée', value: 'FERMEE' }, { label: 'Ignorée', value: 'IGNOREE' } ]; useEffect(() => { loadAlertes(); initTimeline(); }, [selectedType, selectedSeverite, selectedStatut, showArchived]); const loadAlertes = async () => { try { setLoading(true); // TODO: Remplacer par un vrai appel API // const response = await alerteService.getDashboardData({ // type: selectedType, // severite: selectedSeverite, // statut: selectedStatut, // archived: showArchived // }); // Données simulées pour la démonstration const mockAlertes: Alerte[] = [ { id: '1', titre: 'Maintenance urgente requise', description: 'La pelleteuse CAT 320D présente des signes de défaillance hydraulique', type: 'MAINTENANCE', severite: 'CRITIQUE', statut: 'NOUVELLE', dateCreation: new Date('2024-12-28T10:30:00'), dateEcheance: new Date('2024-12-29T17:00:00'), source: 'Système de monitoring', entite: 'Matériel', entiteId: 'MAT-002', actions: ['Arrêt immédiat', 'Inspection technique', 'Réparation'], responsable: 'Marie Martin', lu: false, archive: false }, { id: '2', titre: 'Dépassement budget chantier', description: 'Le chantier Résidence Les Jardins dépasse le budget prévu de 15%', type: 'BUDGET', severite: 'ELEVEE', statut: 'EN_COURS', dateCreation: new Date('2024-12-27T14:15:00'), dateEcheance: new Date('2024-12-30T12:00:00'), source: 'Contrôle de gestion', entite: 'Chantier', entiteId: 'CHT-001', actions: ['Analyse des coûts', 'Révision budget', 'Validation client'], responsable: 'Jean Dupont', lu: true, archive: false }, { id: '3', titre: 'Retard de livraison matériaux', description: 'Livraison d\'acier reportée de 3 jours pour le Centre Commercial Atlantis', type: 'PLANNING', severite: 'MOYENNE', statut: 'EN_COURS', dateCreation: new Date('2024-12-26T09:45:00'), dateEcheance: new Date('2025-01-02T08:00:00'), source: 'Fournisseur', entite: 'Chantier', entiteId: 'CHT-002', actions: ['Replanification', 'Contact fournisseur', 'Solutions alternatives'], responsable: 'Pierre Leroy', lu: true, archive: false }, { id: '4', titre: 'Certification CACES expirée', description: 'La certification CACES de Luc Bernard expire dans 7 jours', type: 'CONFORMITE', severite: 'ELEVEE', statut: 'NOUVELLE', dateCreation: new Date('2024-12-25T16:20:00'), dateEcheance: new Date('2025-01-01T23:59:00'), source: 'Gestion RH', entite: 'Employé', entiteId: 'EMP-004', actions: ['Planifier formation', 'Restriction temporaire', 'Renouvellement'], responsable: 'Sophie Dubois', lu: false, archive: false }, { id: '5', titre: 'Incident sécurité mineur', description: 'Chute d\'un ouvrier sans blessure grave sur le chantier Hôtel Luxe', type: 'SECURITE', severite: 'MOYENNE', statut: 'RESOLUE', dateCreation: new Date('2024-12-24T11:30:00'), source: 'Chef de chantier', entite: 'Chantier', entiteId: 'CHT-003', actions: ['Rapport incident', 'Analyse causes', 'Mesures préventives'], responsable: 'Marc Rousseau', lu: true, archive: false }, { id: '6', titre: 'Surcharge serveur de monitoring', description: 'Le serveur de monitoring des équipements approche 90% de capacité', type: 'SYSTEME', severite: 'FAIBLE', statut: 'EN_COURS', dateCreation: new Date('2024-12-23T08:15:00'), source: 'Système automatique', entite: 'Infrastructure', entiteId: 'SYS-001', actions: ['Optimisation', 'Augmentation capacité', 'Surveillance'], responsable: 'Admin Système', lu: true, archive: false } ]; // Filtrer selon les critères sélectionnés let filteredAlertes = mockAlertes; if (selectedType) { filteredAlertes = filteredAlertes.filter(a => a.type === selectedType); } if (selectedSeverite) { filteredAlertes = filteredAlertes.filter(a => a.severite === selectedSeverite); } if (selectedStatut) { filteredAlertes = filteredAlertes.filter(a => a.statut === selectedStatut); } if (!showArchived) { filteredAlertes = filteredAlertes.filter(a => !a.archive); } setAlertes(filteredAlertes); } catch (error) { console.error('Erreur lors du chargement des alertes:', error); toast.current?.show({ severity: 'error', summary: 'Erreur', detail: 'Impossible de charger les alertes' }); } finally { setLoading(false); } }; const initTimeline = () => { const recentAlertes = alertes .filter(a => a.severite === 'CRITIQUE' || a.severite === 'ELEVEE') .sort((a, b) => b.dateCreation.getTime() - a.dateCreation.getTime()) .slice(0, 5) .map(a => ({ status: a.severite === 'CRITIQUE' ? 'danger' : 'warning', date: a.dateCreation.toLocaleDateString('fr-FR'), time: a.dateCreation.toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' }), icon: getTypeIcon(a.type), color: getSeveriteColor(a.severite), titre: a.titre, description: a.description, statut: a.statut })); setTimelineEvents(recentAlertes); }; const getTypeIcon = (type: string) => { switch (type) { case 'SECURITE': return 'pi pi-shield'; case 'MAINTENANCE': return 'pi pi-wrench'; case 'BUDGET': return 'pi pi-euro'; case 'PLANNING': return 'pi pi-calendar'; case 'QUALITE': return 'pi pi-star'; case 'RESSOURCES': return 'pi pi-users'; case 'CONFORMITE': return 'pi pi-verified'; case 'SYSTEME': return 'pi pi-server'; default: return 'pi pi-info-circle'; } }; const getSeveriteColor = (severite: string) => { switch (severite) { case 'CRITIQUE': return '#dc2626'; case 'ELEVEE': return '#ea580c'; case 'MOYENNE': return '#d97706'; case 'FAIBLE': return '#65a30d'; case 'INFO': return '#2563eb'; default: return '#6b7280'; } }; const getSeveriteSeverity = (severite: string) => { switch (severite) { case 'CRITIQUE': return 'danger'; case 'ELEVEE': return 'warning'; case 'MOYENNE': return 'info'; case 'FAIBLE': return 'success'; case 'INFO': return 'info'; default: return 'secondary'; } }; const getStatutSeverity = (statut: string) => { switch (statut) { case 'NOUVELLE': return 'danger'; case 'EN_COURS': return 'warning'; case 'RESOLUE': return 'success'; case 'FERMEE': return 'secondary'; case 'IGNOREE': return 'secondary'; default: return 'info'; } }; const getTypeSeverity = (type: string) => { switch (type) { case 'SECURITE': return 'danger'; case 'MAINTENANCE': return 'warning'; case 'BUDGET': return 'info'; case 'PLANNING': return 'info'; case 'QUALITE': return 'success'; case 'RESSOURCES': return 'info'; case 'CONFORMITE': return 'warning'; case 'SYSTEME': return 'secondary'; default: return 'info'; } }; const severiteBodyTemplate = (rowData: Alerte) => ( ); const statutBodyTemplate = (rowData: Alerte) => ( ); const typeBodyTemplate = (rowData: Alerte) => (
); const titreBodyTemplate = (rowData: Alerte) => (
{!rowData.lu && (
)}
{rowData.titre}
{rowData.description.substring(0, 80)}...
); const echeanceBodyTemplate = (rowData: Alerte) => { if (!rowData.dateEcheance) return -; const now = new Date(); const echeance = rowData.dateEcheance; const diffHours = (echeance.getTime() - now.getTime()) / (1000 * 60 * 60); let severity = 'info'; if (diffHours < 0) severity = 'danger'; else if (diffHours < 24) severity = 'warning'; else if (diffHours < 72) severity = 'info'; return (
{echeance.toLocaleDateString('fr-FR')}
); }; const actionsBodyTemplate = (rowData: Alerte) => (
{rowData.actions.slice(0, 2).map((action, index) => ( ))} {rowData.actions.length > 2 && ( )}
); const actionButtonsTemplate = (rowData: Alerte) => (
{!rowData.lu && (
); const handleMarkAsRead = (alerte: Alerte) => { // TODO: Appel API pour marquer comme lu toast.current?.show({ severity: 'success', summary: 'Alerte marquée comme lue', detail: alerte.titre, life: 3000 }); loadAlertes(); }; const handleTreatAlert = (alerte: Alerte) => { router.push(`/alertes/${alerte.id}/traiter`); }; const handleResolveAlert = (alerte: Alerte) => { // TODO: Appel API pour résoudre toast.current?.show({ severity: 'success', summary: 'Alerte résolue', detail: alerte.titre, life: 3000 }); loadAlertes(); }; const handleIgnoreAlert = (alerte: Alerte) => { // TODO: Appel API pour ignorer toast.current?.show({ severity: 'info', summary: 'Alerte ignorée', detail: alerte.titre, life: 3000 }); loadAlertes(); }; // Calculs des métriques const alertesCritiques = alertes.filter(a => a.severite === 'CRITIQUE').length; const alertesNonLues = alertes.filter(a => !a.lu).length; const alertesEchues = alertes.filter(a => a.dateEcheance && a.dateEcheance < new Date()).length; const alertesEnCours = alertes.filter(a => a.statut === 'EN_COURS').length; return (
{/* En-tête avec filtres */}

Dashboard Alertes

setSelectedType(e.value)} className="w-full md:w-14rem" />
setSelectedSeverite(e.value)} className="w-full md:w-14rem" />
setSelectedStatut(e.value)} className="w-full md:w-14rem" />
setShowArchived(e.target.checked)} className="mr-2" />
{/* Métriques principales */}
Critiques
{alertesCritiques}
Non lues
{alertesNonLues}
Échues
{alertesEchues}
En cours
{alertesEnCours}
{/* Alertes critiques en cours */} {alertesCritiques > 0 && (
)} {/* Timeline des alertes récentes */}
Alertes Récentes (Critiques & Élevées)
} content={(item) => (

{item.description}

)} />
{/* Répartition par type */}
Répartition par Type
{typeOptions.slice(1).map((type, index) => { const count = alertes.filter(a => a.type === type.value).length; const percentage = alertes.length > 0 ? (count / alertes.length) * 100 : 0; return (
{type.label}
); })}
{/* Tableau des alertes */}
Liste des Alertes ({alertes.length})
rowData.dateCreation.toLocaleDateString('fr-FR')} sortable />
); }; export default DashboardAlertes;