'use client'; import React, { useState, useEffect, useRef } from 'react'; import { Card } from 'primereact/card'; import FullCalendar from '@fullcalendar/react'; 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 { Divider } from 'primereact/divider'; import { useRouter } from 'next/navigation'; import dayGridPlugin from '@fullcalendar/daygrid'; import timeGridPlugin from '@fullcalendar/timegrid'; import interactionPlugin from '@fullcalendar/interaction'; import frLocale from '@fullcalendar/core/locales/fr'; import RoleProtectedPage from '@/components/RoleProtectedPage'; interface PlanningEvent { id: string; title: string; start: Date; end: Date; type: string; chantier: string; ressources: string[]; statut: string; priorite: string; description: string; responsable: string; color?: string; } interface ConflitPlanning { id: string; type: string; ressource: string; evenements: string[]; dateDebut: Date; dateFin: Date; severite: string; } const DashboardPlanningContent = () => { const toast = useRef(null); const router = useRouter(); const [events, setEvents] = useState([]); const [conflits, setConflits] = useState([]); const [loading, setLoading] = useState(true); const [selectedView, setSelectedView] = useState('month'); const [selectedType, setSelectedType] = useState(''); const [selectedChantier, setSelectedChantier] = useState(''); const [dateRange, setDateRange] = useState([]); const [calendarOptions, setCalendarOptions] = useState({}); const [timelineEvents, setTimelineEvents] = useState([]); const viewOptions = [ { label: 'Mois', value: 'month' }, { label: 'Semaine', value: 'week' }, { label: 'Jour', value: 'day' }, { label: 'Liste', value: 'list' } ]; const typeOptions = [ { label: 'Tous les types', value: '' }, { label: 'Chantier', value: 'CHANTIER' }, { label: 'Maintenance', value: 'MAINTENANCE' }, { label: 'Formation', value: 'FORMATION' }, { label: 'Réunion', value: 'REUNION' }, { label: 'Livraison', value: 'LIVRAISON' } ]; const chantierOptions = [ { label: 'Tous les chantiers', value: '' }, { label: 'Résidence Les Jardins', value: 'jardins' }, { label: 'Centre Commercial Atlantis', value: 'atlantis' }, { label: 'Rénovation Hôtel Luxe', value: 'hotel' }, { label: 'Usine Pharmaceutique', value: 'usine' } ]; useEffect(() => { loadPlanning(); initCalendar(); initTimeline(); }, [selectedView, selectedType, selectedChantier, dateRange]); const loadPlanning = async () => { try { setLoading(true); // TODO: Remplacer par un vrai appel API // const response = await planningService.getDashboardData({ // view: selectedView, // type: selectedType, // chantier: selectedChantier, // dateRange // }); // Données simulées pour la démonstration const mockEvents: PlanningEvent[] = [ { id: '1', title: 'Coulage dalle béton', start: new Date('2025-01-02T08:00:00'), end: new Date('2025-01-02T17:00:00'), type: 'CHANTIER', chantier: 'Résidence Les Jardins', ressources: ['Équipe Gros Œuvre', 'Bétonnière S36X', 'Grue LTM 1050'], statut: 'PLANIFIE', priorite: 'HAUTE', description: 'Coulage de la dalle du niveau R+1', responsable: 'Jean Dupont', color: '#10b981' }, { id: '2', title: 'Maintenance préventive', start: new Date('2025-01-03T09:00:00'), end: new Date('2025-01-03T12:00:00'), type: 'MAINTENANCE', chantier: 'Atelier Central', ressources: ['Pelleteuse CAT 320D'], statut: 'PLANIFIE', priorite: 'MOYENNE', description: 'Révision système hydraulique', responsable: 'Marie Martin', color: '#f59e0b' }, { id: '3', title: 'Formation CACES', start: new Date('2025-01-06T08:00:00'), end: new Date('2025-01-08T17:00:00'), type: 'FORMATION', chantier: 'Centre de Formation', ressources: ['Pierre Leroy', 'Luc Bernard'], statut: 'CONFIRME', priorite: 'MOYENNE', description: 'Formation CACES R482 - Engins de chantier', responsable: 'Sophie Dubois', color: '#3b82f6' }, { id: '4', title: 'Livraison matériaux', start: new Date('2025-01-07T14:00:00'), end: new Date('2025-01-07T16:00:00'), type: 'LIVRAISON', chantier: 'Centre Commercial Atlantis', ressources: ['Camion benne Volvo'], statut: 'PLANIFIE', priorite: 'HAUTE', description: 'Livraison acier pour structure', responsable: 'Marc Rousseau', color: '#8b5cf6' }, { id: '5', title: 'Réunion chantier', start: new Date('2025-01-09T10:00:00'), end: new Date('2025-01-09T11:30:00'), type: 'REUNION', chantier: 'Résidence Les Jardins', ressources: ['Équipe complète'], statut: 'CONFIRME', priorite: 'MOYENNE', description: 'Point hebdomadaire équipe', responsable: 'Jean Dupont', color: '#06b6d4' } ]; const mockConflits: ConflitPlanning[] = [ { id: '1', type: 'RESSOURCE_DOUBLE', ressource: 'Grue LTM 1050', evenements: ['Coulage dalle béton', 'Montage charpente'], dateDebut: new Date('2025-01-02T08:00:00'), dateFin: new Date('2025-01-02T17:00:00'), severite: 'HAUTE' }, { id: '2', type: 'EMPLOYE_INDISPONIBLE', ressource: 'Pierre Leroy', evenements: ['Formation CACES'], dateDebut: new Date('2025-01-06T08:00:00'), dateFin: new Date('2025-01-08T17:00:00'), severite: 'MOYENNE' } ]; // Filtrer selon les critères sélectionnés let filteredEvents = mockEvents; if (selectedType) { filteredEvents = filteredEvents.filter(e => e.type === selectedType); } if (selectedChantier) { filteredEvents = filteredEvents.filter(e => e.chantier.toLowerCase().includes(selectedChantier)); } setEvents(filteredEvents); setConflits(mockConflits); } catch (error) { console.error('Erreur lors du chargement du planning:', error); toast.current?.show({ severity: 'error', summary: 'Erreur', detail: 'Impossible de charger les données du planning' }); } finally { setLoading(false); } }; const initCalendar = () => { const calendarEvents = events.map(event => ({ id: event.id, title: event.title, start: event.start, end: event.end, backgroundColor: event.color, borderColor: event.color, textColor: '#ffffff', extendedProps: { type: event.type, chantier: event.chantier, responsable: event.responsable, description: event.description, ressources: event.ressources } })); const options = { plugins: [dayGridPlugin, timeGridPlugin, interactionPlugin], headerToolbar: { left: 'prev,next today', center: 'title', right: 'dayGridMonth,timeGridWeek,timeGridDay' }, initialView: selectedView === 'month' ? 'dayGridMonth' : selectedView === 'week' ? 'timeGridWeek' : 'timeGridDay', locale: frLocale, events: calendarEvents, editable: true, selectable: true, selectMirror: true, dayMaxEvents: true, weekends: true, eventClick: (info: any) => { const event = events.find(e => e.id === info.event.id); if (event) { handleEventClick(event); } }, select: (info: any) => { handleDateSelect(info.start, info.end); }, eventDrop: (info: any) => { handleEventDrop(info); }, eventResize: (info: any) => { handleEventResize(info); } }; setCalendarOptions(options); }; const initTimeline = () => { const upcomingEvents = events .filter(e => e.start > new Date()) .sort((a, b) => a.start.getTime() - b.start.getTime()) .slice(0, 5) .map(e => ({ status: e.priorite === 'HAUTE' ? 'danger' : e.priorite === 'MOYENNE' ? 'warning' : 'info', date: e.start.toLocaleDateString('fr-FR'), time: e.start.toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' }), icon: getTypeIcon(e.type), color: e.color, title: e.title, chantier: e.chantier, responsable: e.responsable })); setTimelineEvents(upcomingEvents); }; const getTypeIcon = (type: string) => { switch (type) { case 'CHANTIER': return 'pi pi-building'; case 'MAINTENANCE': return 'pi pi-wrench'; case 'FORMATION': return 'pi pi-graduation-cap'; case 'REUNION': return 'pi pi-users'; case 'LIVRAISON': return 'pi pi-truck'; default: return 'pi pi-calendar'; } }; const getStatutSeverity = (statut: string) => { switch (statut) { case 'PLANIFIE': return 'info'; case 'CONFIRME': return 'success'; case 'EN_COURS': return 'warning'; case 'TERMINE': return 'success'; case 'ANNULE': return 'danger'; default: return 'info'; } }; const getPrioriteSeverity = (priorite: string) => { switch (priorite) { case 'HAUTE': return 'danger'; case 'MOYENNE': return 'warning'; case 'BASSE': return 'info'; default: return 'info'; } }; const getSeveriteSeverity = (severite: string) => { switch (severite) { case 'HAUTE': return 'danger'; case 'MOYENNE': return 'warning'; case 'BASSE': return 'info'; default: return 'info'; } }; const handleEventClick = (event: PlanningEvent) => { toast.current?.show({ severity: 'info', summary: event.title, detail: `${event.chantier} - ${event.responsable}`, life: 3000 }); }; const handleDateSelect = (start: Date, end: Date) => { router.push(`/planning/nouveau?start=${start.toISOString()}&end=${end.toISOString()}`); }; const handleEventDrop = (info: any) => { toast.current?.show({ severity: 'success', summary: 'Événement déplacé', detail: `${info.event.title} déplacé au ${info.event.start.toLocaleDateString('fr-FR')}`, life: 3000 }); }; const handleEventResize = (info: any) => { toast.current?.show({ severity: 'success', summary: 'Événement redimensionné', detail: `Durée de ${info.event.title} modifiée`, life: 3000 }); }; const statutBodyTemplate = (rowData: PlanningEvent) => ( ); const prioriteBodyTemplate = (rowData: PlanningEvent) => ( ); const typeBodyTemplate = (rowData: PlanningEvent) => (
{rowData.type}
); const ressourcesBodyTemplate = (rowData: PlanningEvent) => (
{rowData.ressources.slice(0, 2).map((ressource, index) => ( ))} {rowData.ressources.length > 2 && ( )}
); const conflitTypeBodyTemplate = (rowData: ConflitPlanning) => (
{rowData.type.replace('_', ' ')}
); const conflitSeveriteBodyTemplate = (rowData: ConflitPlanning) => ( ); const actionBodyTemplate = (rowData: PlanningEvent) => (
); const conflitActionBodyTemplate = (rowData: ConflitPlanning) => (
); // Calculs des métriques const evenementsAujourdhui = events.filter(e => e.start.toDateString() === new Date().toDateString() ).length; const evenementsSemaine = events.filter(e => { const today = new Date(); const weekStart = new Date(today.setDate(today.getDate() - today.getDay())); const weekEnd = new Date(today.setDate(today.getDate() - today.getDay() + 6)); return e.start >= weekStart && e.start <= weekEnd; }).length; const conflitsActifs = conflits.filter(c => c.severite === 'HAUTE').length; return (
{/* En-tête avec filtres */}

Dashboard Planning

setSelectedView(e.value)} className="w-full md:w-10rem" />
setSelectedType(e.value)} className="w-full md:w-14rem" />
setSelectedChantier(e.value)} className="w-full md:w-16rem" />
setDateRange(e.value as Date[])} selectionMode="range" readOnlyInput className="w-full md:w-14rem" placeholder="Sélectionner une période" />
{/* Métriques principales */}
Aujourd'hui
{evenementsAujourdhui}
Cette semaine
{evenementsSemaine}
Conflits actifs
{conflitsActifs}
Total événements
{events.length}
{/* Calendrier */} {selectedView !== 'list' && (
Calendrier
)} {/* Timeline des prochains événements */}
Prochains Événements
} content={(item) => (

Chantier: {item.chantier}
Responsable: {item.responsable}

)} />
{/* Conflits de planning */} {conflits.length > 0 && (
Conflits de Planning
rowData.evenements.join(', ')} /> rowData.dateDebut.toLocaleDateString('fr-FR')} /> rowData.dateFin.toLocaleDateString('fr-FR')} />
)} {/* Liste des événements */}
Liste des Événements ({events.length})
e.statut === 'CONFIRME').length} confirmés`} severity="success" />
rowData.start.toLocaleString('fr-FR')} sortable /> rowData.end.toLocaleString('fr-FR')} sortable />
); }; const DashboardPlanning = () => { return ( ); }; export default DashboardPlanning;