'use client'; import React, { useState, useEffect, useRef } from 'react'; import { Card } from 'primereact/card'; import { Chart } from 'primereact/chart'; import { DataTable } from 'primereact/datatable'; import { Column } from 'primereact/column'; import { Tag } from 'primereact/tag'; import { ProgressBar } from 'primereact/progressbar'; 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 { useRouter } from 'next/navigation'; interface MaintenanceDashboard { id: string; materiel: string; type: string; statut: string; priorite: string; dateCreation: Date; datePrevue: Date; dateRealisation?: Date; technicien: string; coutEstime: number; coutReel: number; dureeEstimee: number; dureeReelle?: number; description: string; localisation: string; } const DashboardMaintenance = () => { const toast = useRef(null); const router = useRouter(); const [maintenances, setMaintenances] = useState([]); const [loading, setLoading] = useState(true); const [selectedType, setSelectedType] = useState(''); const [selectedStatut, setSelectedStatut] = useState(''); const [dateRange, setDateRange] = useState([]); const [chartData, setChartData] = useState({}); const [chartOptions, setChartOptions] = useState({}); const [timelineEvents, setTimelineEvents] = useState([]); const typeOptions = [ { label: 'Tous les types', value: '' }, { label: 'Préventive', value: 'PREVENTIVE' }, { label: 'Corrective', value: 'CORRECTIVE' }, { label: 'Prédictive', value: 'PREDICTIVE' }, { label: 'Urgente', value: 'URGENTE' } ]; const statutOptions = [ { label: 'Tous les statuts', value: '' }, { label: 'Planifiée', value: 'PLANIFIEE' }, { label: 'En cours', value: 'EN_COURS' }, { label: 'Terminée', value: 'TERMINEE' }, { label: 'Reportée', value: 'REPORTEE' }, { label: 'Annulée', value: 'ANNULEE' } ]; useEffect(() => { loadMaintenances(); initCharts(); initTimeline(); }, [selectedType, selectedStatut, dateRange]); const loadMaintenances = async () => { try { setLoading(true); // TODO: Remplacer par un vrai appel API // const response = await maintenanceService.getDashboardData({ // type: selectedType, // statut: selectedStatut, // dateRange // }); // Données simulées pour la démonstration const mockMaintenances: MaintenanceDashboard[] = [ { id: '1', materiel: 'Grue mobile Liebherr LTM 1050', type: 'PREVENTIVE', statut: 'PLANIFIEE', priorite: 'MOYENNE', dateCreation: new Date('2024-12-15'), datePrevue: new Date('2025-01-15'), technicien: 'Jean Dupont', coutEstime: 1200, coutReel: 0, dureeEstimee: 4, description: 'Révision générale et contrôle sécurité', localisation: 'Atelier Central' }, { id: '2', materiel: 'Pelleteuse CAT 320D', type: 'CORRECTIVE', statut: 'EN_COURS', priorite: 'HAUTE', dateCreation: new Date('2024-12-20'), datePrevue: new Date('2024-12-28'), dateRealisation: new Date('2024-12-28'), technicien: 'Marie Martin', coutEstime: 800, coutReel: 950, dureeEstimee: 2, dureeReelle: 3, description: 'Réparation système hydraulique', localisation: 'Chantier Résidence Les Jardins' }, { id: '3', materiel: 'Bétonnière Schwing S36X', type: 'URGENTE', statut: 'TERMINEE', priorite: 'CRITIQUE', dateCreation: new Date('2024-12-18'), datePrevue: new Date('2024-12-19'), dateRealisation: new Date('2024-12-19'), technicien: 'Pierre Leroy', coutEstime: 1500, coutReel: 1650, dureeEstimee: 6, dureeReelle: 8, description: 'Remplacement pompe défaillante', localisation: 'Chantier Centre Commercial' }, { id: '4', materiel: 'Compacteur Bomag BW 213', type: 'PREDICTIVE', statut: 'PLANIFIEE', priorite: 'BASSE', dateCreation: new Date('2024-12-22'), datePrevue: new Date('2025-02-01'), technicien: 'Luc Bernard', coutEstime: 600, coutReel: 0, dureeEstimee: 3, description: 'Analyse vibratoire et diagnostic', localisation: 'Atelier Central' }, { id: '5', materiel: 'Nacelle Genie Z-45/25J', type: 'PREVENTIVE', statut: 'REPORTEE', priorite: 'MOYENNE', dateCreation: new Date('2024-12-10'), datePrevue: new Date('2024-12-25'), technicien: 'Sophie Dubois', coutEstime: 400, coutReel: 0, dureeEstimee: 2, description: 'Contrôle sécurité annuel', localisation: 'Atelier Central' } ]; // Filtrer selon les critères sélectionnés let filteredMaintenances = mockMaintenances; if (selectedType) { filteredMaintenances = filteredMaintenances.filter(m => m.type === selectedType); } if (selectedStatut) { filteredMaintenances = filteredMaintenances.filter(m => m.statut === selectedStatut); } setMaintenances(filteredMaintenances); } catch (error) { console.error('Erreur lors du chargement des maintenances:', error); toast.current?.show({ severity: 'error', summary: 'Erreur', detail: 'Impossible de charger les données de maintenance' }); } finally { setLoading(false); } }; const initCharts = () => { const documentStyle = getComputedStyle(document.documentElement); // Graphique de répartition par type const typeData = { labels: ['Préventive', 'Corrective', 'Prédictive', 'Urgente'], datasets: [ { data: [ maintenances.filter(m => m.type === 'PREVENTIVE').length, maintenances.filter(m => m.type === 'CORRECTIVE').length, maintenances.filter(m => m.type === 'PREDICTIVE').length, maintenances.filter(m => m.type === 'URGENTE').length ], backgroundColor: [ documentStyle.getPropertyValue('--green-500'), documentStyle.getPropertyValue('--orange-500'), documentStyle.getPropertyValue('--blue-500'), documentStyle.getPropertyValue('--red-500') ] } ] }; const options = { plugins: { legend: { labels: { usePointStyle: true, color: documentStyle.getPropertyValue('--text-color') } } } }; setChartData(typeData); setChartOptions(options); }; const initTimeline = () => { const events = maintenances .filter(m => m.statut === 'PLANIFIEE' || m.statut === 'EN_COURS') .sort((a, b) => a.datePrevue.getTime() - b.datePrevue.getTime()) .slice(0, 5) .map(m => ({ status: m.priorite === 'CRITIQUE' ? 'danger' : m.priorite === 'HAUTE' ? 'warning' : 'info', date: m.datePrevue.toLocaleDateString('fr-FR'), icon: m.type === 'URGENTE' ? 'pi pi-exclamation-triangle' : 'pi pi-wrench', color: m.priorite === 'CRITIQUE' ? '#e74c3c' : m.priorite === 'HAUTE' ? '#f39c12' : '#3498db', materiel: m.materiel, type: m.type, technicien: m.technicien })); setTimelineEvents(events); }; const getStatutSeverity = (statut: string) => { switch (statut) { case 'PLANIFIEE': return 'info'; case 'EN_COURS': return 'warning'; case 'TERMINEE': return 'success'; case 'REPORTEE': return 'warning'; case 'ANNULEE': return 'danger'; default: return 'info'; } }; const getPrioriteSeverity = (priorite: string) => { switch (priorite) { case 'CRITIQUE': return 'danger'; case 'HAUTE': return 'warning'; case 'MOYENNE': return 'info'; case 'BASSE': return 'success'; default: return 'info'; } }; const getTypeSeverity = (type: string) => { switch (type) { case 'URGENTE': return 'danger'; case 'CORRECTIVE': return 'warning'; case 'PREVENTIVE': return 'success'; case 'PREDICTIVE': return 'info'; default: return 'info'; } }; const statutBodyTemplate = (rowData: MaintenanceDashboard) => ( ); const prioriteBodyTemplate = (rowData: MaintenanceDashboard) => ( ); const typeBodyTemplate = (rowData: MaintenanceDashboard) => ( ); const coutBodyTemplate = (rowData: MaintenanceDashboard) => { const ecart = rowData.coutReel - rowData.coutEstime; const couleur = ecart > 0 ? 'text-red-500' : ecart < 0 ? 'text-green-500' : 'text-gray-500'; return (
{new Intl.NumberFormat('fr-FR', { style: 'currency', currency: 'EUR' }).format(rowData.coutEstime)}
{rowData.coutReel > 0 && (
Réel: {new Intl.NumberFormat('fr-FR', { style: 'currency', currency: 'EUR' }).format(rowData.coutReel)}
)}
); }; const dureeBodyTemplate = (rowData: MaintenanceDashboard) => (
{rowData.dureeEstimee}h
{rowData.dureeReelle && (
rowData.dureeEstimee ? 'text-red-500' : 'text-green-500'}`}> Réel: {rowData.dureeReelle}h
)}
); const actionBodyTemplate = (rowData: MaintenanceDashboard) => (
); // Calculs des métriques const totalCoutEstime = maintenances.reduce((sum, m) => sum + m.coutEstime, 0); const totalCoutReel = maintenances.reduce((sum, m) => sum + (m.coutReel || 0), 0); const maintenancesUrgentes = maintenances.filter(m => m.priorite === 'CRITIQUE' || m.type === 'URGENTE').length; const tauxRealisation = maintenances.length > 0 ? (maintenances.filter(m => m.statut === 'TERMINEE').length / maintenances.length) * 100 : 0; return (
{/* En-tête avec filtres */}

Dashboard Maintenance

setSelectedType(e.value)} className="w-full md:w-14rem" />
setSelectedStatut(e.value)} className="w-full md:w-14rem" />
setDateRange(e.value as Date[])} selectionMode="range" readOnlyInput hideOnRangeSelection className="w-full md:w-14rem" placeholder="Sélectionner une période" />
{/* Métriques principales */}
Total Maintenances
{maintenances.length}
Coût Total
{new Intl.NumberFormat('fr-FR', { style: 'currency', currency: 'EUR', notation: 'compact' }).format(totalCoutEstime)}
Urgentes
{maintenancesUrgentes}
Taux Réalisation
{tauxRealisation.toFixed(1)}%
{/* Graphique et Timeline */}
Répartition par Type
Prochaines Maintenances
} content={(item) => (

Type: {item.type}
Technicien: {item.technicien}

)} />
{/* Tableau des maintenances */}
Liste des Maintenances ({maintenances.length})
m.statut === 'EN_COURS').length} en cours`} severity="warning" />
rowData.datePrevue.toLocaleDateString('fr-FR')} sortable />
); }; export default DashboardMaintenance;