'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 { Divider } from 'primereact/divider'; import { useRouter } from 'next/navigation'; interface ChantierDashboard { id: string; nom: string; client: string; statut: string; avancement: number; budget: number; coutReel: number; dateDebut: Date; dateFinPrevue: Date; nombreEmployes: number; nombrePhases: number; phasesTerminees: number; retard: number; priorite: string; localisation: string; } const DashboardChantiers = () => { const toast = useRef(null); const router = useRouter(); const [chantiers, setChantiers] = useState([]); const [loading, setLoading] = useState(true); const [selectedPeriod, setSelectedPeriod] = useState('30'); const [selectedStatut, setSelectedStatut] = useState(''); const [dateRange, setDateRange] = useState([]); const [chartData, setChartData] = useState({}); const [chartOptions, setChartOptions] = useState({}); const periodOptions = [ { label: '7 derniers jours', value: '7' }, { label: '30 derniers jours', value: '30' }, { label: '3 derniers mois', value: '90' }, { label: '6 derniers mois', value: '180' }, { label: 'Cette année', value: '365' } ]; const statutOptions = [ { label: 'Tous les statuts', value: '' }, { label: 'Planifié', value: 'PLANIFIE' }, { label: 'En cours', value: 'EN_COURS' }, { label: 'En pause', value: 'EN_PAUSE' }, { label: 'Terminé', value: 'TERMINE' }, { label: 'Annulé', value: 'ANNULE' } ]; useEffect(() => { loadChantiers(); initCharts(); }, [selectedPeriod, selectedStatut, dateRange]); const loadChantiers = async () => { try { setLoading(true); // TODO: Remplacer par un vrai appel API // const response = await chantierService.getDashboardData({ // period: selectedPeriod, // statut: selectedStatut, // dateRange // }); // Données simulées pour la démonstration const mockChantiers: ChantierDashboard[] = [ { id: '1', nom: 'Résidence Les Jardins', client: 'Immobilière Moderne', statut: 'EN_COURS', avancement: 75, budget: 850000, coutReel: 620000, dateDebut: new Date('2024-03-15'), dateFinPrevue: new Date('2024-12-20'), nombreEmployes: 12, nombrePhases: 8, phasesTerminees: 6, retard: 0, priorite: 'HAUTE', localisation: 'Paris 15ème' }, { id: '2', nom: 'Centre Commercial Atlantis', client: 'Groupe Retail Plus', statut: 'EN_COURS', avancement: 45, budget: 2500000, coutReel: 1200000, dateDebut: new Date('2024-01-10'), dateFinPrevue: new Date('2025-06-30'), nombreEmployes: 25, nombrePhases: 12, phasesTerminees: 5, retard: 15, priorite: 'CRITIQUE', localisation: 'Nanterre' }, { id: '3', nom: 'Rénovation Hôtel Luxe', client: 'Hôtels Prestige', statut: 'EN_PAUSE', avancement: 30, budget: 1200000, coutReel: 380000, dateDebut: new Date('2024-02-01'), dateFinPrevue: new Date('2024-11-15'), nombreEmployes: 8, nombrePhases: 10, phasesTerminees: 3, retard: 45, priorite: 'MOYENNE', localisation: 'Lyon' }, { id: '4', nom: 'Usine Pharmaceutique', client: 'PharmaFrance', statut: 'PLANIFIE', avancement: 0, budget: 3200000, coutReel: 0, dateDebut: new Date('2025-02-01'), dateFinPrevue: new Date('2026-08-30'), nombreEmployes: 0, nombrePhases: 15, phasesTerminees: 0, retard: 0, priorite: 'HAUTE', localisation: 'Marseille' }, { id: '5', nom: 'Pont Autoroutier A86', client: 'Ministère des Transports', statut: 'TERMINE', avancement: 100, budget: 4500000, coutReel: 4350000, dateDebut: new Date('2023-06-01'), dateFinPrevue: new Date('2024-10-31'), nombreEmployes: 0, nombrePhases: 20, phasesTerminees: 20, retard: -10, priorite: 'CRITIQUE', localisation: 'Île-de-France' } ]; // Filtrer selon les critères sélectionnés let filteredChantiers = mockChantiers; if (selectedStatut) { filteredChantiers = filteredChantiers.filter(c => c.statut === selectedStatut); } setChantiers(filteredChantiers); } catch (error) { console.error('Erreur lors du chargement des chantiers:', error); toast.current?.show({ severity: 'error', summary: 'Erreur', detail: 'Impossible de charger les données des chantiers' }); } finally { setLoading(false); } }; const initCharts = () => { const documentStyle = getComputedStyle(document.documentElement); const textColor = documentStyle.getPropertyValue('--text-color'); const textColorSecondary = documentStyle.getPropertyValue('--text-color-secondary'); const surfaceBorder = documentStyle.getPropertyValue('--surface-border'); // Graphique de répartition par statut const statutData = { labels: ['En cours', 'Planifié', 'En pause', 'Terminé', 'Annulé'], datasets: [ { data: [ chantiers.filter(c => c.statut === 'EN_COURS').length, chantiers.filter(c => c.statut === 'PLANIFIE').length, chantiers.filter(c => c.statut === 'EN_PAUSE').length, chantiers.filter(c => c.statut === 'TERMINE').length, chantiers.filter(c => c.statut === 'ANNULE').length ], backgroundColor: [ documentStyle.getPropertyValue('--green-500'), documentStyle.getPropertyValue('--blue-500'), documentStyle.getPropertyValue('--orange-500'), documentStyle.getPropertyValue('--cyan-500'), documentStyle.getPropertyValue('--red-500') ], hoverBackgroundColor: [ documentStyle.getPropertyValue('--green-400'), documentStyle.getPropertyValue('--blue-400'), documentStyle.getPropertyValue('--orange-400'), documentStyle.getPropertyValue('--cyan-400'), documentStyle.getPropertyValue('--red-400') ] } ] }; const options = { plugins: { legend: { labels: { usePointStyle: true, color: textColor } } } }; setChartData(statutData); setChartOptions(options); }; const getStatutSeverity = (statut: string) => { switch (statut) { case 'EN_COURS': return 'success'; case 'PLANIFIE': return 'info'; case 'EN_PAUSE': return 'warning'; case 'TERMINE': return 'success'; case 'ANNULE': 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 statutBodyTemplate = (rowData: ChantierDashboard) => ( ); const prioriteBodyTemplate = (rowData: ChantierDashboard) => ( ); const avancementBodyTemplate = (rowData: ChantierDashboard) => (
{rowData.avancement}%
); const budgetBodyTemplate = (rowData: ChantierDashboard) => { const pourcentageUtilise = (rowData.coutReel / rowData.budget) * 100; const couleur = pourcentageUtilise > 90 ? 'text-red-500' : pourcentageUtilise > 75 ? 'text-orange-500' : 'text-green-500'; return (
{new Intl.NumberFormat('fr-FR', { style: 'currency', currency: 'EUR', notation: 'compact' }).format(rowData.budget)}
{pourcentageUtilise.toFixed(1)}% utilisé
); }; const retardBodyTemplate = (rowData: ChantierDashboard) => { if (rowData.retard === 0) { return ; } else if (rowData.retard > 0) { return ; } else { return ; } }; const actionBodyTemplate = (rowData: ChantierDashboard) => (
); // Calculs des métriques const totalBudget = chantiers.reduce((sum, c) => sum + c.budget, 0); const totalCoutReel = chantiers.reduce((sum, c) => sum + c.coutReel, 0); const chantiersEnRetard = chantiers.filter(c => c.retard > 0).length; const avancementMoyen = chantiers.length > 0 ? chantiers.reduce((sum, c) => sum + c.avancement, 0) / chantiers.length : 0; return (
{/* En-tête avec filtres */}

Dashboard Chantiers

setSelectedPeriod(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 Chantiers
{chantiers.length}
Budget Total
{new Intl.NumberFormat('fr-FR', { style: 'currency', currency: 'EUR', notation: 'compact' }).format(totalBudget)}
En Retard
{chantiersEnRetard}
Avancement Moyen
{avancementMoyen.toFixed(1)}%
{/* Graphique de répartition */}
Répartition par Statut
{/* Indicateurs de performance */}
Indicateurs de Performance
{((totalBudget - totalCoutReel) / totalBudget * 100).toFixed(1)}%
Économies réalisées
{chantiers.filter(c => c.statut === 'EN_COURS').length}
Chantiers actifs
{chantiers.reduce((sum, c) => sum + c.nombreEmployes, 0)}
Employés mobilisés
{(chantiersEnRetard / chantiers.length * 100).toFixed(1)}%
Taux de retard
{/* Tableau des chantiers */}
Liste des Chantiers ({chantiers.length})
c.statut === 'EN_COURS').length} actifs`} severity="success" />
); }; export default DashboardChantiers;