'use client'; /** * Page principale de planification budgétaire * Vue d'ensemble de tous les budgets avec possibilité de planification détaillée */ import React, { useState, useRef, useEffect, useContext } from 'react'; import { LayoutContext } from '../../../../layout/context/layoutcontext'; import { Card } from 'primereact/card'; import { DataTable } from 'primereact/datatable'; import { Column } from 'primereact/column'; import { Button } from 'primereact/button'; import { Tag } from 'primereact/tag'; import { ProgressBar } from 'primereact/progressbar'; import { Toolbar } from 'primereact/toolbar'; import { InputText } from 'primereact/inputtext'; import { Dropdown } from 'primereact/dropdown'; import { Calendar } from 'primereact/calendar'; import { Toast } from 'primereact/toast'; import { Chart } from 'primereact/chart'; import { Divider } from 'primereact/divider'; import { TabView, TabPanel } from 'primereact/tabview'; import { Panel } from 'primereact/panel'; import BudgetPlanningDialog from '../../../../components/phases/BudgetPlanningDialog'; interface BudgetChantier { id: string; chantierNom: string; client: string; dateDebut: string; dateFin: string; budgetTotal: number; budgetPlanifie: number; avancementPlanification: number; nombrePhases: number; nombrePhasesPlanifiees: number; statut: 'NON_PLANIFIE' | 'EN_COURS_PLANIFICATION' | 'PLANIFIE' | 'VALIDE'; priorite: 'HAUTE' | 'MOYENNE' | 'BASSE'; responsable: string; derniereMaj: string; } const BudgetPlanificationPage = () => { const { layoutConfig, setLayoutConfig, layoutState, setLayoutState } = useContext(LayoutContext); const toast = useRef(null); // États const [budgets, setBudgets] = useState([]); const [loading, setLoading] = useState(true); const [globalFilter, setGlobalFilter] = useState(''); const [selectedBudget, setSelectedBudget] = useState(null); const [showPlanningDialog, setShowPlanningDialog] = useState(false); const [activeIndex, setActiveIndex] = useState(0); // Filtres const [statusFilter, setStatusFilter] = useState(''); const [prioriteFilter, setPrioriteFilter] = useState(''); const [dateFilter, setDateFilter] = useState(null); // Options pour les filtres const statusOptions = [ { label: 'Tous les statuts', value: '' }, { label: 'Non planifié', value: 'NON_PLANIFIE' }, { label: 'En cours', value: 'EN_COURS_PLANIFICATION' }, { label: 'Planifié', value: 'PLANIFIE' }, { label: 'Validé', value: 'VALIDE' } ]; const prioriteOptions = [ { label: 'Toutes priorités', value: '' }, { label: 'Haute', value: 'HAUTE' }, { label: 'Moyenne', value: 'MOYENNE' }, { label: 'Basse', value: 'BASSE' } ]; // Charger les données useEffect(() => { loadBudgets(); }, []); const loadBudgets = async () => { try { setLoading(true); // Simuler des données de budget (en production, ceci viendrait de l'API) const budgetsSimules: BudgetChantier[] = [ { id: '1', chantierNom: 'Villa Moderne Bordeaux', client: 'Jean Dupont', dateDebut: '2025-03-01', dateFin: '2025-08-30', budgetTotal: 250000, budgetPlanifie: 180000, avancementPlanification: 72, nombrePhases: 8, nombrePhasesPlanifiees: 6, statut: 'EN_COURS_PLANIFICATION', priorite: 'HAUTE', responsable: 'Marie Martin', derniereMaj: '2025-01-28' }, { id: '2', chantierNom: 'Extension Maison Lyon', client: 'Sophie Lambert', dateDebut: '2025-02-15', dateFin: '2025-06-15', budgetTotal: 120000, budgetPlanifie: 120000, avancementPlanification: 100, nombrePhases: 5, nombrePhasesPlanifiees: 5, statut: 'PLANIFIE', priorite: 'MOYENNE', responsable: 'Pierre Dubois', derniereMaj: '2025-01-25' }, { id: '3', chantierNom: 'Rénovation Appartement Paris', client: 'Michel Robert', dateDebut: '2025-04-01', dateFin: '2025-07-31', budgetTotal: 85000, budgetPlanifie: 0, avancementPlanification: 0, nombrePhases: 6, nombrePhasesPlanifiees: 0, statut: 'NON_PLANIFIE', priorite: 'HAUTE', responsable: 'Anne Legrand', derniereMaj: '2025-01-20' } ]; setBudgets(budgetsSimules); } catch (error) { console.error('Erreur lors du chargement des budgets:', error); toast.current?.show({ severity: 'error', summary: 'Erreur', detail: 'Impossible de charger les budgets', life: 3000 }); } finally { setLoading(false); } }; // Templates pour le DataTable const statutTemplate = (rowData: BudgetChantier) => { const severityMap = { 'NON_PLANIFIE': 'danger', 'EN_COURS_PLANIFICATION': 'warning', 'PLANIFIE': 'info', 'VALIDE': 'success' } as const; const labelMap = { 'NON_PLANIFIE': 'Non planifié', 'EN_COURS_PLANIFICATION': 'En cours', 'PLANIFIE': 'Planifié', 'VALIDE': 'Validé' }; return ; }; const prioriteTemplate = (rowData: BudgetChantier) => { const severityMap = { 'HAUTE': 'danger', 'MOYENNE': 'warning', 'BASSE': 'info' } as const; return ; }; const budgetTemplate = (rowData: BudgetChantier) => { const pourcentagePlanifie = rowData.budgetTotal > 0 ? (rowData.budgetPlanifie / rowData.budgetTotal) * 100 : 0; return (
{new Intl.NumberFormat('fr-FR', { style: 'currency', currency: 'EUR' }).format(rowData.budgetPlanifie)} {new Intl.NumberFormat('fr-FR', { style: 'currency', currency: 'EUR' }).format(rowData.budgetTotal)}
{pourcentagePlanifie.toFixed(0)}% planifié
); }; const avancementTemplate = (rowData: BudgetChantier) => { return (
Phases planifiées {rowData.nombrePhasesPlanifiees}/{rowData.nombrePhases}
{rowData.avancementPlanification}% avancement
); }; const dateTemplate = (rowData: BudgetChantier, field: keyof BudgetChantier) => { const date = rowData[field] as string; return new Date(date).toLocaleDateString('fr-FR'); }; const actionsTemplate = (rowData: BudgetChantier) => { return (
); }; // Fonctions d'action const validerBudget = async (budgetId: string) => { try { // Appel API pour valider le budget setBudgets(budgets.map(b => b.id === budgetId ? { ...b, statut: 'VALIDE' as const } : b )); toast.current?.show({ severity: 'success', summary: 'Budget validé', detail: 'Le budget a été validé avec succès', life: 3000 }); } catch (error) { toast.current?.show({ severity: 'error', summary: 'Erreur', detail: 'Impossible de valider le budget', life: 3000 }); } }; const handleSaveBudgetPlanning = async (budgetData: any) => { if (!selectedBudget) return; try { // Mettre à jour le budget avec les nouvelles données setBudgets(budgets.map(b => b.id === selectedBudget.id ? { ...b, budgetPlanifie: budgetData.prixVenteCalcule, statut: 'PLANIFIE' as const, avancementPlanification: 100, derniereMaj: new Date().toISOString().split('T')[0] } : b )); toast.current?.show({ severity: 'success', summary: 'Budget planifié', detail: `Le budget du chantier "${selectedBudget.chantierNom}" a été mis à jour`, life: 3000 }); } catch (error) { toast.current?.show({ severity: 'error', summary: 'Erreur', detail: 'Impossible de sauvegarder la planification', life: 3000 }); } }; // Filtrer les données const filteredBudgets = budgets.filter(budget => { let matches = true; if (globalFilter) { const search = globalFilter.toLowerCase(); matches = matches && ( budget.chantierNom.toLowerCase().includes(search) || budget.client.toLowerCase().includes(search) || budget.responsable.toLowerCase().includes(search) ); } if (statusFilter) { matches = matches && budget.statut === statusFilter; } if (prioriteFilter) { matches = matches && budget.priorite === prioriteFilter; } return matches; }); // Statistiques const stats = { totalBudgets: budgets.length, budgetsNonPlanifies: budgets.filter(b => b.statut === 'NON_PLANIFIE').length, budgetsEnCours: budgets.filter(b => b.statut === 'EN_COURS_PLANIFICATION').length, budgetsPlanifies: budgets.filter(b => b.statut === 'PLANIFIE').length, budgetsValides: budgets.filter(b => b.statut === 'VALIDE').length, montantTotalBudget: budgets.reduce((sum, b) => sum + b.budgetTotal, 0), montantTotalPlanifie: budgets.reduce((sum, b) => sum + b.budgetPlanifie, 0) }; // Données pour le graphique const chartData = { labels: ['Non planifié', 'En cours', 'Planifié', 'Validé'], datasets: [ { data: [ stats.budgetsNonPlanifies, stats.budgetsEnCours, stats.budgetsPlanifies, stats.budgetsValides ], backgroundColor: ['#dc3545', '#ffc107', '#007ad9', '#22c55e'], borderWidth: 0 } ] }; // Template de la toolbar const toolbarStartTemplate = (
Planification Budgétaire
); const toolbarEndTemplate = (
setGlobalFilter(e.target.value)} placeholder="Rechercher..." /> setStatusFilter(e.value)} placeholder="Statut" className="w-10rem" /> setPrioriteFilter(e.value)} placeholder="Priorité" className="w-10rem" />
); return (
setActiveIndex(e.index)}> {/* Statistiques générales */}
Budget Total
{new Intl.NumberFormat('fr-FR', { style: 'currency', currency: 'EUR', minimumFractionDigits: 0 }).format(stats.montantTotalBudget)}
Budget Planifié
{new Intl.NumberFormat('fr-FR', { style: 'currency', currency: 'EUR', minimumFractionDigits: 0 }).format(stats.montantTotalPlanifie)}
En Attente
{stats.budgetsNonPlanifies + stats.budgetsEnCours}
chantiers
Taux Planification
{stats.totalBudgets > 0 ? (((stats.budgetsPlanifies + stats.budgetsValides) / stats.totalBudgets) * 100).toFixed(0) : 0}%
{/* Graphique de répartition */}

{stats.budgetsNonPlanifies} chantiers n'ont pas encore de budget planifié.

{stats.budgetsNonPlanifies > 0 && (
dateTemplate(rowData, 'dateDebut')} sortable style={{ width: '8rem' }} /> dateTemplate(rowData, 'dateFin')} sortable style={{ width: '8rem' }} />
{/* Dialog de planification budgétaire */} setShowPlanningDialog(false)} phase={selectedBudget ? { id: parseInt(selectedBudget.id), nom: selectedBudget.chantierNom, budgetPrevu: selectedBudget.budgetTotal } as any : null} onSave={handleSaveBudgetPlanning} />
); }; export default BudgetPlanificationPage;