'use client'; import React, { useState, useRef, useEffect } from 'react'; import { DataTable, DataTableExpandedRows } from 'primereact/datatable'; import { Column } from 'primereact/column'; import { Button } from 'primereact/button'; import { Tag } from 'primereact/tag'; import { ProgressBar } from 'primereact/progressbar'; import { Badge } from 'primereact/badge'; import { Toast } from 'primereact/toast'; import { ConfirmDialog, confirmDialog } from 'primereact/confirmdialog'; import { Menu } from 'primereact/menu'; import { TabView, TabPanel } from 'primereact/tabview'; import { ActionButtonGroup, ViewButton, EditButton, DeleteButton, StartButton, CompleteButton, ProgressButton, BudgetPlanButton, BudgetTrackButton } from '../ui/ActionButton'; import { PhaseChantier, StatutPhase } from '../../types/btp-extended'; import phaseService from '../../services/phaseService'; import materielPhaseService from '../../services/materielPhaseService'; import fournisseurPhaseService from '../../services/fournisseurPhaseService'; export interface PhasesTableProps { // Données phases: PhaseChantier[]; loading?: boolean; chantierId?: string; // Affichage showStats?: boolean; showChantierColumn?: boolean; showSubPhases?: boolean; showBudget?: boolean; showExpansion?: boolean; showGlobalFilter?: boolean; // Actions disponibles actions?: Array<'view' | 'edit' | 'delete' | 'start' | 'complete' | 'progress' | 'budget-plan' | 'budget-track' | 'all'>; // Callbacks onRefresh?: () => void; onPhaseSelect?: (phase: PhaseChantier) => void; onPhaseEdit?: (phase: PhaseChantier) => void; onPhaseDelete?: (phaseId: string) => void; onPhaseStart?: (phaseId: string) => void; onPhaseProgress?: (phase: PhaseChantier) => void; onPhaseBudgetPlan?: (phase: PhaseChantier) => void; onPhaseBudgetTrack?: (phase: PhaseChantier) => void; onSubPhaseAdd?: (parentPhase: PhaseChantier) => void; // Configuration rows?: number; emptyMessage?: string; className?: string; globalFilter?: string; } const PhasesTable: React.FC = ({ phases, loading = false, chantierId, showStats = false, showChantierColumn = false, showSubPhases = true, showBudget = true, showExpansion = true, showGlobalFilter = false, actions = ['all'], onRefresh, onPhaseSelect, onPhaseEdit, onPhaseDelete, onPhaseStart, onPhaseProgress, onPhaseBudgetPlan, onPhaseBudgetTrack, onSubPhaseAdd, rows = 15, emptyMessage = "Aucune phase trouvée", className = "p-datatable-lg", globalFilter = '' }) => { const toast = useRef(null); const [expandedRows, setExpandedRows] = useState(undefined); const [materielsPhase, setMaterielsPhase] = useState([]); const [fournisseursPhase, setFournisseursPhase] = useState([]); // Déterminer quelles actions afficher const shouldShowAction = (action: string) => { return actions.includes('all') || actions.includes(action as any); }; // Templates de colonnes const statutBodyTemplate = (rowData: PhaseChantier) => { const severityMap: Record = { 'PLANIFIEE': 'secondary', 'EN_ATTENTE': 'warning', 'EN_COURS': 'info', 'SUSPENDUE': 'warning', 'TERMINEE': 'success', 'ANNULEE': 'danger' }; return ; }; const avancementBodyTemplate = (rowData: PhaseChantier) => { const progress = rowData.pourcentageAvancement || 0; const color = progress === 100 ? 'var(--green-500)' : progress >= 50 ? 'var(--blue-500)' : 'var(--orange-500)'; return (
{progress}%
); }; const dateBodyTemplate = (rowData: PhaseChantier, field: keyof PhaseChantier) => { const date = rowData[field] as string; if (!date) return -; const dateObj = new Date(date); const isOverdue = field === 'dateFinPrevue' && dateObj < new Date() && rowData.statut !== 'TERMINEE'; return ( {dateObj.toLocaleDateString('fr-FR')} ); }; const prioriteBodyTemplate = (rowData: PhaseChantier) => { const severityMap: Record = { 'FAIBLE': 'secondary', 'MOYENNE': 'info', 'ELEVEE': 'warning', 'CRITIQUE': 'danger' }; return rowData.priorite ? : null; }; const budgetBodyTemplate = (rowData: PhaseChantier) => { return ( {new Intl.NumberFormat('fr-FR', { style: 'currency', currency: 'EUR', minimumFractionDigits: 0 }).format(rowData.budgetPrevu || 0)} ); }; const coutReelBodyTemplate = (rowData: PhaseChantier) => { const cout = rowData.coutReel || 0; const budget = rowData.budgetPrevu || 0; const depassement = cout > budget; return ( {new Intl.NumberFormat('fr-FR', { style: 'currency', currency: 'EUR', minimumFractionDigits: 0 }).format(cout)} {depassement && ( )} ); }; const chantierBodyTemplate = (rowData: PhaseChantier) => { return rowData.chantier?.nom || '-'; }; const phaseNameBodyTemplate = (rowData: PhaseChantier) => { return (
{showSubPhases && ( )}
{rowData.nom} {rowData.description && ( {rowData.description.length > 60 ? rowData.description.substring(0, 60) + '...' : rowData.description } )}
{rowData.critique && ( )}
); }; // Chargement du matériel et fournisseurs pour l'expansion const loadMaterielPhase = async (phaseId: string) => { try { const materiels = await materielPhaseService.getByPhase(phaseId); setMaterielsPhase(materiels); } catch (error) { console.error('Erreur lors du chargement du matériel:', error); } }; const loadFournisseursPhase = async (phaseId: string) => { try { const fournisseurs = await fournisseurPhaseService.getByPhase(phaseId); setFournisseursPhase(fournisseurs); } catch (error) { console.error('Erreur lors du chargement des fournisseurs:', error); } }; // Template d'expansion const rowExpansionTemplate = (data: PhaseChantier) => { if (data.phaseParent || !showSubPhases) return null; const sousPhases = phases.filter(p => p.phaseParent === data.id); return (
Sous-phases de "{data.nom}" ({sousPhases.length})
{onSubPhaseAdd && (
{sousPhases.length > 0 ? ( (
{rowData.nom} {rowData.critique && ( )}
)} /> dateBodyTemplate(rowData, 'dateDebutPrevue')} /> dateBodyTemplate(rowData, 'dateFinPrevue')} /> {showBudget && ( <> )} ( {shouldShowAction('view') && onPhaseSelect && ( onPhaseSelect(rowData)} /> )} {shouldShowAction('edit') && onPhaseEdit && ( onPhaseEdit(rowData)} /> )} {shouldShowAction('start') && onPhaseStart && ( onPhaseStart(rowData.id!)} /> )} {shouldShowAction('delete') && onPhaseDelete && ( onPhaseDelete(rowData.id!)} /> )} )} />
) : (

Aucune sous-phase définie pour cette phase.

)}
); }; // Template des actions principales const actionBodyTemplate = (rowData: PhaseChantier) => { const handleDelete = () => { confirmDialog({ message: `Êtes-vous sûr de vouloir supprimer la phase "${rowData.nom}" ?`, header: 'Confirmer la suppression', icon: 'pi pi-exclamation-triangle', acceptClassName: 'p-button-danger', acceptLabel: 'Supprimer', rejectLabel: 'Annuler', accept: async () => { try { await phaseService.delete(rowData.id!); if (onRefresh) onRefresh(); toast.current?.show({ severity: 'success', summary: 'Suppression réussie', detail: 'La phase a été supprimée', life: 3000 }); } catch (error) { console.error('Erreur:', error); toast.current?.show({ severity: 'error', summary: 'Erreur', detail: 'Impossible de supprimer la phase', life: 5000 }); } } }); }; return ( {shouldShowAction('view') && onPhaseSelect && ( onPhaseSelect(rowData)} /> )} {shouldShowAction('edit') && onPhaseEdit && ( onPhaseEdit(rowData)} /> )} {shouldShowAction('start') && onPhaseStart && ( onPhaseStart(rowData.id!)} /> )} {shouldShowAction('progress') && onPhaseProgress && ( onPhaseProgress(rowData)} /> )} {shouldShowAction('budget-plan') && onPhaseBudgetPlan && ( onPhaseBudgetPlan(rowData)} /> )} {shouldShowAction('budget-track') && onPhaseBudgetTrack && ( onPhaseBudgetTrack(rowData)} /> )} {shouldShowAction('delete') && (onPhaseDelete || true) && ( onPhaseDelete ? onPhaseDelete(rowData.id!) : handleDelete()} /> )} ); }; // Style des lignes const phaseRowClassName = (rowData: PhaseChantier) => { let className = ''; if (rowData.phaseParent) { className += ' bg-surface-card border-left-4 border-surface-300'; } else { className += ' bg-surface-ground border-left-4 border-primary font-semibold'; } if (rowData.critique) { className += ' border-red-500'; } return className; }; // Filtrer les phases principales seulement si subPhases est activé const displayPhases = showSubPhases ? phases.filter(p => !p.phaseParent) : phases; return ( <> setExpandedRows(e.data as any) : undefined} rowExpansionTemplate={showExpansion ? rowExpansionTemplate : undefined} rowClassName={phaseRowClassName} > {showExpansion && showSubPhases && } {showChantierColumn && ( )} dateBodyTemplate(rowData, 'dateDebutPrevue')} sortable style={{ width: '10rem' }} /> dateBodyTemplate(rowData, 'dateFinPrevue')} sortable style={{ width: '10rem' }} /> ( {rowData.dureeEstimeeHeures || 0}h )} /> {showBudget && ( <> )} ); }; export default PhasesTable;