'use client'; import React, { useState, useRef, useEffect, useContext } from 'react'; // Imports Atlantis React - Collection complète des composants avancés import { Button } from 'primereact/button'; import { Chart } from 'primereact/chart'; import { Card } from 'primereact/card'; import { ProgressBar } from 'primereact/progressbar'; import { Badge } from 'primereact/badge'; import { Tag } from 'primereact/tag'; import { Knob } from 'primereact/knob'; import { Column } from 'primereact/column'; import { DataTable } from 'primereact/datatable'; import { Dropdown } from 'primereact/dropdown'; import { Message } from 'primereact/message'; import { Avatar } from 'primereact/avatar'; import { AvatarGroup } from 'primereact/avatargroup'; import { Divider } from 'primereact/divider'; import { Chip } from 'primereact/chip'; import { OverlayPanel } from 'primereact/overlaypanel'; import { Toast } from 'primereact/toast'; import { ConfirmDialog } from 'primereact/confirmdialog'; import { Dialog } from 'primereact/dialog'; import { SplitButton } from 'primereact/splitbutton'; import { Terminal } from 'primereact/terminal'; import { Inplace, InplaceDisplay, InplaceContent } from 'primereact/inplace'; import { Rating } from 'primereact/rating'; import { ColorPicker } from 'primereact/colorpicker'; import { LayoutContext } from '../../../layout/context/layoutcontext'; import { ChartData, ChartOptions } from 'chart.js'; import { useDashboard, ChantierActif } from '../../../hooks/useDashboard'; import { ProgressSpinner } from 'primereact/progressspinner'; import { Panel } from 'primereact/panel'; import { Skeleton } from 'primereact/skeleton'; import { Ripple } from 'primereact/ripple'; import StatsCard from '../../../components/dashboard/StatsCard'; // Interface ChantierActif importée depuis useDashboard interface KPIData { chantiersActifs: number; chantiersEnRetard: number; caRealise: number; caObjectif: number; margeGlobale: number; effectifsSurSite: number; satisfactionClient: number; } const DashboardBTP = () => { const { layoutConfig } = useContext(LayoutContext); const [periode, setPeriode] = useState('mois'); // executive, operational, analytical const [fullScreenChart, setFullScreenChart] = useState(false); const [selectedMetric, setSelectedMetric] = useState('ca'); const [autoRefresh, setAutoRefresh] = useState(true); const [refreshInterval, setRefreshInterval] = useState(30); const toast = useRef(null); const overlayPanel = useRef(null); const terminalRef = useRef(null); // Hook pour les données du dashboard const { metrics, chantiersActifs, activitesRecentes, tachesUrgentes, loading, error, refresh, changePeriode } = useDashboard(periode as 'semaine' | 'mois' | 'trimestre' | 'annee'); // Calculs réels basés sur les données backend const calculerIndicateursPerformance = () => { if (!chantiersActifs || chantiersActifs.length === 0) { return [ { label: 'Chantiers à l\'heure', value: 0, color: '#10b981' }, { label: 'Chantiers en retard', value: 0, color: '#ef4444' } ]; } const totalChantiers = chantiersActifs.length; const chantiersEnRetard = chantiersActifs.filter(c => c.statut === 'EN_RETARD').length; const chantiersALHeure = totalChantiers - chantiersEnRetard; const tauxALHeure = Math.round((chantiersALHeure / totalChantiers) * 100); const tauxRetard = Math.round((chantiersEnRetard / totalChantiers) * 100); return [ { label: 'Chantiers à l\'heure', value: tauxALHeure, color: '#10b981' }, { label: 'Chantiers en retard', value: tauxRetard, color: '#ef4444' } ]; }; const performanceMetersCalculated = calculerIndicateursPerformance(); // Auto-refresh effect useEffect(() => { let interval: NodeJS.Timeout; if (autoRefresh) { interval = setInterval(() => { refresh(); }, refreshInterval * 1000); } return () => { if (interval) clearInterval(interval); }; }, [autoRefresh, refreshInterval, refresh]); // Fonctions utilitaires avancées const exportDashboard = () => { toast.current?.show({ severity: 'info', summary: 'Export en cours', detail: 'Génération du rapport dashboard complet...', life: 3000 }); }; const openSupportDialog = () => { toast.current?.show({ severity: 'info', summary: 'Support Technique', detail: 'Redirection vers le centre d\'aide...', life: 3000 }); }; const toggleFullScreen = () => { if (!document.fullscreenElement) { document.documentElement.requestFullscreen(); } else { document.exitFullscreen(); } }; const exportToPDF = () => { toast.current?.show({ severity: 'success', summary: 'Export PDF', detail: 'Génération du rapport PDF en cours...', life: 3000 }); }; const exportToExcel = () => { toast.current?.show({ severity: 'success', summary: 'Export Excel', detail: 'Téléchargement du fichier Excel...', life: 3000 }); }; const syncToCloud = () => { toast.current?.show({ severity: 'info', summary: 'Synchronisation', detail: 'Sauvegarde cloud en cours...', life: 3000 }); }; const openDeveloperTools = () => { if (terminalRef.current) { toast.current?.show({ severity: 'info', summary: 'Mode Développeur', detail: 'Console de développement activée', life: 3000 }); } }; const handlePeriodeChange = (value: string) => { setPeriode(value); changePeriode(value as 'semaine' | 'mois' | 'trimestre' | 'annee'); }; // Affichage de chargement amélioré if (loading && !metrics) { return (

Chargement du tableau de bord

Récupération des données en temps réel...

); } // Affichage d'erreur sophistiqué if (error) { return (

Impossible de charger les données

{error}

); } // Calculs KPIs étendus avec données réelles backend const chiffreAffaires = metrics?.chiffreAffaires || 0; const coutReel = metrics?.coutReel || 0; const objectifCA = metrics?.objectifCA || 0; const kpis: KPIData = { chantiersActifs: metrics?.chantiersActifs || 0, chantiersEnRetard: metrics?.chantiersEnRetard || 0, caRealise: chiffreAffaires, caObjectif: objectifCA, margeGlobale: chiffreAffaires > 0 ? ((chiffreAffaires - coutReel) / chiffreAffaires * 100) : 0, effectifsSurSite: metrics?.totalEquipes || 0, satisfactionClient: metrics?.satisfactionClient || 0 }; const chantiersData = chantiersActifs || []; const periodeOptions = [ { label: 'Cette semaine', value: 'semaine', icon: 'pi pi-calendar' }, { label: 'Ce mois', value: 'mois', icon: 'pi pi-calendar' }, { label: 'Ce trimestre', value: 'trimestre', icon: 'pi pi-calendar' }, { label: 'Cette année', value: 'annee', icon: 'pi pi-calendar' } ]; // Données graphiques avancées const getAdvancedChartData = (): ChartData => { const documentStyle = typeof window !== 'undefined' ? getComputedStyle(document.documentElement) : null; const months = ['Jan', 'Fév', 'Mar', 'Avr', 'Mai', 'Jun', 'Jul', 'Aoû', 'Sep', 'Oct', 'Nov', 'Déc']; return { labels: months, datasets: [ { label: 'CA Réalisé', data: new Array(12).fill(0), // Données réelles du backend backgroundColor: 'rgba(54, 162, 235, 0.2)', borderColor: 'rgba(54, 162, 235, 1)', borderWidth: 3, fill: true, tension: 0.4, type: 'line' as const }, { label: 'Objectif', data: new Array(12).fill(0), // Données réelles du backend backgroundColor: 'rgba(255, 99, 132, 0.2)', borderColor: 'rgba(255, 99, 132, 1)', borderWidth: 2, borderDash: [5, 5], fill: false, type: 'line' as const }, { label: 'Marge', data: new Array(12).fill(0), // Données réelles du backend backgroundColor: 'rgba(75, 192, 192, 0.6)', borderColor: 'rgba(75, 192, 192, 1)', type: 'bar' as const } ] }; }; const chartOptions: ChartOptions = { responsive: true, maintainAspectRatio: false, interaction: { mode: 'index' as const, intersect: false, }, plugins: { legend: { position: 'top' as const, labels: { usePointStyle: true, padding: 20, font: { family: 'Inter, sans-serif', size: 13, weight: '500' } } }, tooltip: { backgroundColor: 'rgba(0, 0, 0, 0.8)', titleColor: 'white', bodyColor: 'white', borderColor: 'rgba(255, 255, 255, 0.2)', borderWidth: 1 } }, scales: { y: { beginAtZero: true, grid: { color: 'rgba(160, 167, 181, 0.3)' }, ticks: { font: { family: 'Inter, sans-serif' } } }, x: { grid: { color: 'rgba(160, 167, 181, 0.3)' }, ticks: { font: { family: 'Inter, sans-serif' } } } } }; // Templates de données avancés const advancedAvancementTemplate = (rowData: ChantierActif) => { const getProgressColor = (value: number) => { if (value >= 80) return 'success'; if (value >= 50) return 'info'; if (value >= 25) return 'warning'; return 'danger'; }; return (
); }; const advancedChantierTemplate = (rowData: ChantierActif) => { return (
{rowData.nom}
{typeof rowData.client === 'string' ? rowData.client : rowData.client?.nom || 'Non spécifié'}
); }; const advancedClientTemplate = (rowData: ChantierActif) => { const clientName = typeof rowData.client === 'string' ? rowData.client : rowData.client?.nom || 'Client non défini'; return (
{clientName}
); }; const advancedStatutTemplate = (rowData: ChantierActif) => { const getStatutConfig = (statut: string) => { switch (statut) { case 'EN_COURS': return { severity: 'success', icon: 'pi pi-play', label: 'En cours' }; case 'EN_RETARD': return { severity: 'danger', icon: 'pi pi-exclamation-triangle', label: 'En retard' }; default: return { severity: 'info', icon: 'pi pi-clock', label: 'Planifié' }; } }; const config = getStatutConfig(rowData.statut); return (
); }; const advancedEquipeTemplate = (rowData: ChantierActif) => { if (!rowData.equipe) return Non assignée; return (
{Array.from({ length: Math.min(4, rowData.equipe.nombreMembres) }).map((_, index) => ( ))} {rowData.equipe.nombreMembres > 4 && ( )}
{rowData.equipe.nom}
{rowData.equipe.nombreMembres} personnes
); }; const advancedProgressTemplate = (rowData: ChantierActif) => { try { const progression = rowData?.avancement || 0; return (
{progression}%
); } catch (error) { console.error('Erreur template progression:', error, rowData); return N/A; } }; const advancedBudgetTemplate = (rowData: ChantierActif) => { try { if (!rowData?.budget) { return N/A; } return (
{new Intl.NumberFormat('fr-FR', { style: 'currency', currency: 'EUR', notation: 'compact' }).format(rowData.budget)}
); } catch (error) { console.error('Erreur template budget:', error, rowData); return Erreur; } }; // Timeline événements avec style ultra-moderne const evenementsRecents = (activitesRecentes || []).map(activite => ({ status: activite.titre, date: new Date(activite.date).toLocaleDateString('fr-FR'), time: new Date(activite.date).toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' }), icon: getIconForType(activite.type), color: getColorForStatut(activite.statut), description: activite.description, user: activite.utilisateur, category: activite.type })); function getIconForType(type: string) { const icons: Record = { 'CHANTIER': 'pi pi-building', 'MAINTENANCE': 'pi pi-wrench', 'DOCUMENT': 'pi pi-file-pdf', 'EQUIPE': 'pi pi-users', 'FINANCE': 'pi pi-euro', 'ALERTE': 'pi pi-bell' }; return icons[type] || 'pi pi-circle'; } function getColorForStatut(statut: string) { const colors: Record = { 'SUCCESS': '#10b981', 'WARNING': '#f59e0b', 'ERROR': '#ef4444', 'INFO': '#3b82f6' }; return colors[statut] || '#6b7280'; } // Template du header optimisé et compact const headerTemplate = () => (
Dashboard BTP Xpress
Pilotage intelligent et temps réel
handlePeriodeChange(e.value)} className="w-9rem" panelClassName="border-round shadow-4" />
); return (
{/* Composants de dialogue et overlay */} {/* Dialog plein écran pour graphiques */} setFullScreenChart(false)} maximizable style={{ width: '95vw', height: '90vh' }} header="Analyse Graphique Détaillée" > {/* Header style Atlantis */} {headerTemplate()} {/* Alertes avancées avec Actions */} {kpis.chantiersEnRetard > 0 && (
{kpis.chantiersEnRetard} chantier(s) nécessitent une attention
Action immédiate requise pour respecter les délais contractuels
} />
)} {/* KPIs Section ultra-moderne */}
{/* KPI Chantiers Actifs - Version Premium */}
LIVE
Chantiers Actifs
{loading ? : ( {kpis.chantiersActifs} Détail: {kpis.chantiersActifs} projets )}
0 ? 'p-chip-danger' : 'p-chip-success'}`} /> {kpis.chantiersEnRetard > 0 && ( )}
{/* KPI CA Réalisé - Version Ultra */}
CA Réalisé / Objectif
Détails du CA

Chiffre d'affaires calculé en temps réel basé sur les facturations et acomptes reçus.

{loading ? ( ) : (
0 ? Math.min(100, (kpis.caRealise / kpis.caObjectif) * 100) : 0} size={120} strokeWidth={8} valueColor="#10b981" rangeColor="#e5e7eb" textColor="#374151" valueTemplate="{value}%" className="shadow-2" />
)}
{new Intl.NumberFormat('fr-FR', { style: 'currency', currency: 'EUR', notation: 'compact' }).format(kpis.caRealise)}
sur {new Intl.NumberFormat('fr-FR', { style: 'currency', currency: 'EUR', notation: 'compact' }).format(kpis.caObjectif || 0)}
Indicateurs de Performance
{performanceMetersCalculated.map((meter, index) => (
{meter.label} {meter.value}%
))}
{/* KPI Marge - Version Analytique */}
{}} style={{width: '20px', height: '20px'}} className="border-round opacity-50" />
Marge Globale
{loading ? ( ) : (
)}
{Number.isFinite(kpis.margeGlobale) ? kpis.margeGlobale.toFixed(1) : '0.0'}%
de rentabilité
{/* KPI Équipes - Version Interactive */}
Équipes Actives
{loading ? : ( {kpis.effectifsSurSite} )}
{kpis.effectifsSurSite > 0 ? 'équipes déployées' : 'Aucune équipe active'}
{kpis.effectifsSurSite > 0 ? `${kpis.effectifsSurSite} employés actifs` : 'Aucun employé actif'}
{/* Section principale avec layout optimisé */}
{/* Graphique principal */}

Performance Financière

{loading && (
)}
{/* Activités en Direct - Layout compact */}

Activités Récentes

{evenementsRecents.length > 0 ? (
{evenementsRecents.slice(0, 6).map((item, index) => (
{item.status} {item.time}

{item.description}

{item.user || 'Système'}
))}
) : (
Aucune activité récente
)}
{/* Section tableau des chantiers */}

Chantiers Actifs

(
{/* Terminal de développement (caché) */} ); }; export default DashboardBTP;