'use client'; import React, { useState, useEffect, useRef } from 'react'; import { Card } from 'primereact/card'; import { Button } from 'primereact/button'; import { Calendar } from 'primereact/calendar'; import { Chart } from 'primereact/chart'; import { DataTable } from 'primereact/datatable'; import { Column } from 'primereact/column'; import { Toast } from 'primereact/toast'; import { Toolbar } from 'primereact/toolbar'; import { Dropdown } from 'primereact/dropdown'; import { TabView, TabPanel } from 'primereact/tabview'; import { Knob } from 'primereact/knob'; import { ProgressBar } from 'primereact/progressbar'; import { Tag } from 'primereact/tag'; interface CAData { periode: string; chiffreAffaires: number; objectif: number; factures: number; devis: number; croissance: number; tauxReussite: number; } interface CADetail { id: string; date: Date; client: string; chantier: string; montant: number; type: 'FACTURE' | 'DEVIS_ACCEPTE' | 'AVENANT'; statut: 'PAYE' | 'EN_ATTENTE' | 'RETARD'; mode: 'VIREMENT' | 'CHEQUE' | 'ESPECES' | 'CARTE'; } const ChiffreAffairesPage = () => { const [loading, setLoading] = useState(true); const [caData, setCaData] = useState([]); const [caDetails, setCaDetails] = useState([]); const [dateDebut, setDateDebut] = useState(new Date(new Date().getFullYear(), 0, 1)); const [dateFin, setDateFin] = useState(new Date()); const [selectedPeriod, setSelectedPeriod] = useState('annee'); const [selectedView, setSelectedView] = useState('mensuel'); const [globalFilter, setGlobalFilter] = useState(''); const [activeIndex, setActiveIndex] = useState(0); const toast = useRef(null); const dt = useRef>(null); const periodOptions = [ { label: 'Cette semaine', value: 'semaine' }, { label: 'Ce mois', value: 'mois' }, { label: 'Ce trimestre', value: 'trimestre' }, { label: 'Cette année', value: 'annee' }, { label: 'Personnalisé', value: 'custom' } ]; const viewOptions = [ { label: 'Vue Mensuelle', value: 'mensuel' }, { label: 'Vue Trimestrielle', value: 'trimestriel' }, { label: 'Vue Annuelle', value: 'annuel' } ]; useEffect(() => { loadCAData(); }, [dateDebut, dateFin, selectedView]); const loadCAData = async () => { try { setLoading(true); // Données mockées const mockCAData: CAData[] = [ { periode: 'Janvier 2024', chiffreAffaires: 145000, objectif: 150000, factures: 12, devis: 8, croissance: 12.5, tauxReussite: 75 }, { periode: 'Février 2024', chiffreAffaires: 165000, objectif: 160000, factures: 15, devis: 10, croissance: 13.8, tauxReussite: 82 }, { periode: 'Mars 2024', chiffreAffaires: 180000, objectif: 170000, factures: 18, devis: 12, croissance: 9.1, tauxReussite: 85 }, { periode: 'Avril 2024', chiffreAffaires: 175000, objectif: 175000, factures: 16, devis: 11, croissance: -2.8, tauxReussite: 78 }, { periode: 'Mai 2024', chiffreAffaires: 195000, objectif: 180000, factures: 20, devis: 14, croissance: 11.4, tauxReussite: 88 }, { periode: 'Juin 2024', chiffreAffaires: 210000, objectif: 185000, factures: 22, devis: 16, croissance: 7.7, tauxReussite: 90 } ]; const mockCADetails: CADetail[] = [ { id: '1', date: new Date('2024-06-15'), client: 'Kouassi Jean', chantier: 'Résidence Les Palmiers', montant: 25000, type: 'FACTURE', statut: 'PAYE', mode: 'VIREMENT' }, { id: '2', date: new Date('2024-06-10'), client: 'Traoré Fatou', chantier: 'Immeuble Commercial', montant: 35000, type: 'FACTURE', statut: 'EN_ATTENTE', mode: 'VIREMENT' }, { id: '3', date: new Date('2024-06-08'), client: 'Diabaté Mamadou', chantier: 'Villa Moderne', montant: 18000, type: 'DEVIS_ACCEPTE', statut: 'PAYE', mode: 'CHEQUE' }, { id: '4', date: new Date('2024-06-05'), client: 'Koné Mariame', chantier: 'Rénovation Bureau', montant: 12000, type: 'FACTURE', statut: 'RETARD', mode: 'VIREMENT' }, { id: '5', date: new Date('2024-06-02'), client: 'Ouattara Ibrahim', chantier: 'Garage Automobile', montant: 28000, type: 'AVENANT', statut: 'PAYE', mode: 'VIREMENT' } ]; setCaData(mockCAData); setCaDetails(mockCADetails); } catch (error) { console.error('Erreur lors du chargement:', error); toast.current?.show({ severity: 'error', summary: 'Erreur', detail: 'Impossible de charger les données du chiffre d\'affaires', life: 3000 }); } finally { setLoading(false); } }; const onPeriodChange = (e: any) => { setSelectedPeriod(e.value); const now = new Date(); let debut = new Date(); switch (e.value) { case 'semaine': debut = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 7); break; case 'mois': debut = new Date(now.getFullYear(), now.getMonth(), 1); break; case 'trimestre': debut = new Date(now.getFullYear(), Math.floor(now.getMonth() / 3) * 3, 1); break; case 'annee': debut = new Date(now.getFullYear(), 0, 1); break; default: return; } setDateDebut(debut); setDateFin(now); }; const exportPDF = () => { toast.current?.show({ severity: 'info', summary: 'Export PDF', detail: 'Génération du rapport PDF...', life: 3000 }); }; const exportExcel = () => { toast.current?.show({ severity: 'info', summary: 'Export Excel', detail: 'Génération du rapport Excel...', life: 3000 }); }; const leftToolbarTemplate = () => { return (
setSelectedView(e.value)} placeholder="Vue" /> {selectedPeriod === 'custom' && ( <> setDateDebut(e.value || new Date())} dateFormat="dd/mm/yy" placeholder="Date début" /> setDateFin(e.value || new Date())} dateFormat="dd/mm/yy" placeholder="Date fin" /> )}
); }; const rightToolbarTemplate = () => { return (
); }; // Calculs pour les indicateurs const totalCA = caData.reduce((sum, item) => sum + item.chiffreAffaires, 0); const totalObjectif = caData.reduce((sum, item) => sum + item.objectif, 0); const totalFactures = caData.reduce((sum, item) => sum + item.factures, 0); const moyenneCroissance = caData.length > 0 ? caData.reduce((sum, item) => sum + item.croissance, 0) / caData.length : 0; const tauxAtteinte = totalObjectif > 0 ? (totalCA / totalObjectif) * 100 : 0; // Données pour les graphiques const chartData = { labels: caData.map(item => item.periode), datasets: [ { label: 'Chiffre d\'affaires', data: caData.map(item => item.chiffreAffaires), borderColor: '#3B82F6', backgroundColor: 'rgba(59, 130, 246, 0.1)', tension: 0.4, fill: true }, { label: 'Objectif', data: caData.map(item => item.objectif), borderColor: '#EF4444', backgroundColor: 'rgba(239, 68, 68, 0.1)', borderDash: [5, 5], tension: 0.4, fill: false } ] }; const barChartData = { labels: caData.map(item => item.periode), datasets: [ { label: 'Factures', data: caData.map(item => item.factures), backgroundColor: '#10B981', borderColor: '#047857', borderWidth: 1 }, { label: 'Devis', data: caData.map(item => item.devis), backgroundColor: '#F59E0B', borderColor: '#D97706', borderWidth: 1 } ] }; const chartOptions = { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'bottom' as const } }, scales: { y: { beginAtZero: true, ticks: { callback: function(value: any) { return new Intl.NumberFormat('fr-FR', { style: 'currency', currency: 'EUR', minimumFractionDigits: 0 }).format(value); } } } } }; const formatCurrency = (amount: number) => { return new Intl.NumberFormat('fr-FR', { style: 'currency', currency: 'EUR' }).format(amount); }; const typeBodyTemplate = (rowData: CADetail) => { let severity: "success" | "warning" | "danger" | "info" = 'info'; let label = rowData.type; switch (rowData.type) { case 'FACTURE': severity = 'success'; label = 'Facture'; break; case 'DEVIS_ACCEPTE': severity = 'info'; label = 'Devis Accepté'; break; case 'AVENANT': severity = 'warning'; label = 'Avenant'; break; } return ; }; const statutBodyTemplate = (rowData: CADetail) => { let severity: "success" | "warning" | "danger" = 'success'; let label = rowData.statut; switch (rowData.statut) { case 'PAYE': severity = 'success'; label = 'Payé'; break; case 'EN_ATTENTE': severity = 'warning'; label = 'En attente'; break; case 'RETARD': severity = 'danger'; label = 'En retard'; break; } return ; }; const montantBodyTemplate = (rowData: CADetail) => { return formatCurrency(rowData.montant); }; const dateBodyTemplate = (rowData: CADetail) => { return rowData.date.toLocaleDateString('fr-FR'); }; const header = (
Détail des Transactions
setGlobalFilter(e.target.value)} className="p-inputtext p-component" />
); return (
setActiveIndex(e.index)}>
{/* Indicateurs principaux */}
{formatCurrency(totalCA)}
Chiffre d'Affaires
{formatCurrency(totalObjectif)}
Objectif
{totalFactures}
Factures
{moyenneCroissance.toFixed(1)}%
Croissance Moyenne
{/* Taux d'atteinte objectif */}
= 100 ? "#10B981" : tauxAtteinte >= 80 ? "#F59E0B" : "#EF4444"} rangeColor="#E5E7EB" textColor="#374151" />
{tauxAtteinte.toFixed(1)}% de l'objectif atteint
{/* Performance mensuelle */}
{caData.slice(-3).map((month, index) => (
{month.periode} = 0 ? 'text-green-500' : 'text-red-500'}`}> {month.croissance >= 0 ? '+' : ''}{month.croissance.toFixed(1)}%
{formatCurrency(month.chiffreAffaires)} Obj: {formatCurrency(month.objectif)}
))}
{/* Graphique évolution */}
{/* Graphique factures/devis */}
formatCurrency(rowData.chiffreAffaires)} sortable /> formatCurrency(rowData.objectif)} sortable /> { const ecart = rowData.chiffreAffaires - rowData.objectif; return ( = 0 ? 'text-green-500' : 'text-red-500'}> {formatCurrency(ecart)} ); }} sortable /> { const taux = (rowData.chiffreAffaires / rowData.objectif) * 100; return ( = 100 ? 'text-green-500' : taux >= 80 ? 'text-orange-500' : 'text-red-500'}> {taux.toFixed(1)}% ); }} sortable /> ( = 0 ? 'text-green-500' : 'text-red-500'}> {rowData.croissance >= 0 ? '+' : ''}{rowData.croissance.toFixed(1)}% )} sortable /> ( )} />
); }; export default ChiffreAffairesPage;