'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 { Rating } from 'primereact/rating'; import { Tag } from 'primereact/tag'; import { InputText } from 'primereact/inputtext'; import { ProgressBar } from 'primereact/progressbar'; interface ClientAnalyse { id: string; nom: string; prenom: string; entreprise: string; email: string; telephone: string; dateCreation: Date; dernierContact: Date; nbChantiers: number; chantiersActifs: number; chantiersTermines: number; chiffreAffairesTotal: number; chiffreAffairesAnnee: number; moyennePanier: number; satisfactionMoyenne: number; delaiPaiementMoyen: number; statut: 'ACTIF' | 'INACTIF' | 'PROSPECT' | 'VIP'; segment: 'PREMIUM' | 'STANDARD' | 'ECONOMIQUE'; risque: 'FAIBLE' | 'MOYEN' | 'ELEVE'; fidelite: number; recommandations: number; } interface SegmentAnalyse { segment: string; nbClients: number; chiffreAffaires: number; pourcentageCA: number; panierMoyen: number; satisfaction: number; fidelite: number; } interface TendanceClient { mois: string; nouveauxClients: number; clientsActifs: number; clientsPerdus: number; chiffreAffaires: number; satisfaction: number; } const SuiviClientsPage = () => { const [loading, setLoading] = useState(true); const [clients, setClients] = useState([]); const [segments, setSegments] = useState([]); const [tendances, setTendances] = useState([]); const [selectedClients, setSelectedClients] = useState([]); const [dateDebut, setDateDebut] = useState(new Date(new Date().getFullYear(), 0, 1)); const [dateFin, setDateFin] = useState(new Date()); const [selectedPeriod, setSelectedPeriod] = useState('annee'); const [selectedSegment, setSelectedSegment] = useState('tous'); const [globalFilter, setGlobalFilter] = useState(''); const [activeIndex, setActiveIndex] = useState(0); const toast = useRef(null); const dt = useRef>(null); const periodOptions = [ { label: 'Ce mois', value: 'mois' }, { label: 'Ce trimestre', value: 'trimestre' }, { label: 'Cette année', value: 'annee' }, { label: 'Personnalisé', value: 'custom' } ]; const segmentOptions = [ { label: 'Tous les segments', value: 'tous' }, { label: 'Premium', value: 'PREMIUM' }, { label: 'Standard', value: 'STANDARD' }, { label: 'Économique', value: 'ECONOMIQUE' } ]; useEffect(() => { loadClientData(); }, [dateDebut, dateFin, selectedSegment]); const loadClientData = async () => { try { setLoading(true); // Données mockées const mockClients: ClientAnalyse[] = [ { id: '1', nom: 'Kouassi', prenom: 'Jean', entreprise: 'Entreprise Kouassi', email: 'jean.kouassi@email.com', telephone: '07 12 34 56 78', dateCreation: new Date('2023-01-15'), dernierContact: new Date('2024-06-10'), nbChantiers: 8, chantiersActifs: 2, chantiersTermines: 6, chiffreAffairesTotal: 2500000, chiffreAffairesAnnee: 950000, moyennePanier: 312500, satisfactionMoyenne: 4.5, delaiPaiementMoyen: 28, statut: 'VIP', segment: 'PREMIUM', risque: 'FAIBLE', fidelite: 92, recommandations: 3 }, { id: '2', nom: 'Traoré', prenom: 'Fatou', entreprise: 'Traoré SARL', email: 'fatou.traore@email.com', telephone: '07 98 76 54 32', dateCreation: new Date('2023-06-20'), dernierContact: new Date('2024-05-25'), nbChantiers: 3, chantiersActifs: 1, chantiersTermines: 2, chiffreAffairesTotal: 850000, chiffreAffairesAnnee: 420000, moyennePanier: 283333, satisfactionMoyenne: 4.2, delaiPaiementMoyen: 32, statut: 'ACTIF', segment: 'STANDARD', risque: 'FAIBLE', fidelite: 78, recommandations: 1 }, { id: '3', nom: 'Diabaté', prenom: 'Mamadou', entreprise: 'Diabaté & Fils', email: 'mamadou.diabate@email.com', telephone: '07 55 44 33 22', dateCreation: new Date('2024-02-10'), dernierContact: new Date('2024-06-05'), nbChantiers: 2, chantiersActifs: 1, chantiersTermines: 1, chiffreAffairesTotal: 320000, chiffreAffairesAnnee: 320000, moyennePanier: 160000, satisfactionMoyenne: 3.8, delaiPaiementMoyen: 45, statut: 'ACTIF', segment: 'ECONOMIQUE', risque: 'MOYEN', fidelite: 65, recommandations: 0 }, { id: '4', nom: 'Koné', prenom: 'Mariame', entreprise: 'Bureau Koné', email: 'mariame.kone@email.com', telephone: '07 11 22 33 44', dateCreation: new Date('2023-09-05'), dernierContact: new Date('2024-01-20'), nbChantiers: 1, chantiersActifs: 0, chantiersTermines: 1, chiffreAffairesTotal: 220000, chiffreAffairesAnnee: 0, moyennePanier: 220000, satisfactionMoyenne: 4.8, delaiPaiementMoyen: 15, statut: 'INACTIF', segment: 'STANDARD', risque: 'ELEVE', fidelite: 45, recommandations: 1 }, { id: '5', nom: 'Ouattara', prenom: 'Ibrahim', entreprise: 'Garage Ouattara', email: 'ibrahim.ouattara@email.com', telephone: '07 66 77 88 99', dateCreation: new Date('2024-04-15'), dernierContact: new Date('2024-06-15'), nbChantiers: 1, chantiersActifs: 1, chantiersTermines: 0, chiffreAffairesTotal: 180000, chiffreAffairesAnnee: 180000, moyennePanier: 180000, satisfactionMoyenne: 4.0, delaiPaiementMoyen: 30, statut: 'PROSPECT', segment: 'ECONOMIQUE', risque: 'FAIBLE', fidelite: 0, recommandations: 0 } ]; const mockSegments: SegmentAnalyse[] = [ { segment: 'PREMIUM', nbClients: 1, chiffreAffaires: 2500000, pourcentageCA: 61.0, panierMoyen: 312500, satisfaction: 4.5, fidelite: 92 }, { segment: 'STANDARD', nbClients: 2, chiffreAffaires: 1070000, pourcentageCA: 26.1, panierMoyen: 251667, satisfaction: 4.5, fidelite: 61.5 }, { segment: 'ECONOMIQUE', nbClients: 2, chiffreAffaires: 500000, pourcentageCA: 12.2, panierMoyen: 170000, satisfaction: 3.9, fidelite: 32.5 } ]; const mockTendances: TendanceClient[] = [ { mois: 'Jan 2024', nouveauxClients: 2, clientsActifs: 3, clientsPerdus: 0, chiffreAffaires: 350000, satisfaction: 4.2 }, { mois: 'Fév 2024', nouveauxClients: 1, clientsActifs: 4, clientsPerdus: 0, chiffreAffaires: 420000, satisfaction: 4.3 }, { mois: 'Mar 2024', nouveauxClients: 0, clientsActifs: 4, clientsPerdus: 1, chiffreAffaires: 380000, satisfaction: 4.1 }, { mois: 'Avr 2024', nouveauxClients: 1, clientsActifs: 4, clientsPerdus: 0, chiffreAffaires: 480000, satisfaction: 4.4 }, { mois: 'Mai 2024', nouveauxClients: 1, clientsActifs: 5, clientsPerdus: 0, chiffreAffaires: 520000, satisfaction: 4.3 }, { mois: 'Jun 2024', nouveauxClients: 0, clientsActifs: 4, clientsPerdus: 1, chiffreAffaires: 450000, satisfaction: 4.2 } ]; setClients(selectedSegment === 'tous' ? mockClients : mockClients.filter(c => c.segment === selectedSegment)); setSegments(mockSegments); setTendances(mockTendances); } catch (error) { console.error('Erreur lors du chargement:', error); toast.current?.show({ severity: 'error', summary: 'Erreur', detail: 'Impossible de charger les données clients', life: 3000 }); } finally { setLoading(false); } }; const onPeriodChange = (e: any) => { setSelectedPeriod(e.value); const now = new Date(); let debut = new Date(); switch (e.value) { 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 = () => { dt.current?.exportCSV(); }; const leftToolbarTemplate = () => { return (
setSelectedSegment(e.value)} placeholder="Segment" /> {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 (
); }; const formatCurrency = (amount: number) => { return new Intl.NumberFormat('fr-FR', { style: 'currency', currency: 'EUR' }).format(amount); }; const statutBodyTemplate = (rowData: ClientAnalyse) => { let severity: "success" | "warning" | "danger" | "info" = 'info'; let label = rowData.statut; switch (rowData.statut) { case 'VIP': severity = 'success'; label = 'VIP'; break; case 'ACTIF': severity = 'info'; label = 'Actif'; break; case 'INACTIF': severity = 'warning'; label = 'Inactif'; break; case 'PROSPECT': severity = 'danger'; label = 'Prospect'; break; } return ; }; const segmentBodyTemplate = (rowData: ClientAnalyse) => { let severity: "success" | "warning" | "danger" = 'success'; let label = rowData.segment; switch (rowData.segment) { case 'PREMIUM': severity = 'success'; label = 'Premium'; break; case 'STANDARD': severity = 'warning'; label = 'Standard'; break; case 'ECONOMIQUE': severity = 'danger'; label = 'Économique'; break; } return ; }; const risqueBodyTemplate = (rowData: ClientAnalyse) => { let severity: "success" | "warning" | "danger" = 'success'; let label = rowData.risque; switch (rowData.risque) { case 'FAIBLE': severity = 'success'; label = 'Faible'; break; case 'MOYEN': severity = 'warning'; label = 'Moyen'; break; case 'ELEVE': severity = 'danger'; label = 'Élevé'; break; } return ; }; const satisfactionBodyTemplate = (rowData: ClientAnalyse) => { return ; }; const fideliteBodyTemplate = (rowData: ClientAnalyse) => { return (
{rowData.fidelite}%
); }; const dateBodyTemplate = (rowData: ClientAnalyse) => { return rowData.dateCreation.toLocaleDateString('fr-FR'); }; const dernierContactBodyTemplate = (rowData: ClientAnalyse) => { const daysDiff = Math.floor((new Date().getTime() - rowData.dernierContact.getTime()) / (1000 * 3600 * 24)); const color = daysDiff > 90 ? 'text-red-500' : daysDiff > 30 ? 'text-orange-500' : 'text-green-500'; return (
{rowData.dernierContact.toLocaleDateString('fr-FR')}
Il y a {daysDiff} jours
); }; const header = (
Analyse Clients
setGlobalFilter(e.currentTarget.value)} />
); // Calculs pour les indicateurs globaux const totalClients = clients.length; const clientsActifs = clients.filter(c => c.statut === 'ACTIF' || c.statut === 'VIP').length; const totalCA = clients.reduce((sum, c) => sum + c.chiffreAffairesAnnee, 0); const panierMoyenGlobal = totalClients > 0 ? totalCA / totalClients : 0; const satisfactionMoyenne = totalClients > 0 ? clients.reduce((sum, c) => sum + c.satisfactionMoyenne, 0) / totalClients : 0; const fideliteMoyenne = totalClients > 0 ? clients.reduce((sum, c) => sum + c.fidelite, 0) / totalClients : 0; // Données pour les graphiques const segmentChartData = { labels: segments.map(s => s.segment), datasets: [ { data: segments.map(s => s.pourcentageCA), backgroundColor: ['#10B981', '#F59E0B', '#EF4444'], hoverBackgroundColor: ['#059669', '#D97706', '#DC2626'] } ] }; const tendanceChartData = { labels: tendances.map(t => t.mois), datasets: [ { label: 'Nouveaux Clients', data: tendances.map(t => t.nouveauxClients), borderColor: '#10B981', backgroundColor: 'rgba(16, 185, 129, 0.1)', tension: 0.4, fill: true }, { label: 'Clients Actifs', data: tendances.map(t => t.clientsActifs), borderColor: '#3B82F6', backgroundColor: 'rgba(59, 130, 246, 0.1)', tension: 0.4, fill: true }, { label: 'Clients Perdus', data: tendances.map(t => t.clientsPerdus), borderColor: '#EF4444', backgroundColor: 'rgba(239, 68, 68, 0.1)', tension: 0.4, fill: true } ] }; const caClientChartData = { labels: tendances.map(t => t.mois), datasets: [ { label: 'CA par Client', data: tendances.map(t => t.chiffreAffaires / Math.max(t.clientsActifs, 1)), backgroundColor: '#8B5CF6', borderColor: '#7C3AED', borderWidth: 1 } ] }; const chartOptions = { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'bottom' as const } } }; return (
setActiveIndex(e.index)}>
{/* Indicateurs principaux */}
{totalClients}
Total Clients
{clientsActifs}
Clients Actifs
{formatCurrency(panierMoyenGlobal)}
Panier Moyen
{satisfactionMoyenne.toFixed(1)}/5
Satisfaction Moyenne
{/* Analyse par segment */}
formatCurrency(rowData.chiffreAffaires)} /> `${rowData.pourcentageCA.toFixed(1)}%`} /> formatCurrency(rowData.panierMoyen)} /> ( )} /> (
{rowData.fidelite.toFixed(1)}%
)} />
{/* Graphiques */}
setSelectedClients(e.value)} dataKey="id" paginator rows={10} rowsPerPageOptions={[5, 10, 25]} className="datatable-responsive" paginatorTemplate="FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink CurrentPageReport RowsPerPageDropdown" currentPageReportTemplate="Affichage de {first} à {last} sur {totalRecords} clients" globalFilter={globalFilter} emptyMessage="Aucun client trouvé." header={header} loading={loading} > formatCurrency(rowData.chiffreAffairesTotal)} sortable /> formatCurrency(rowData.chiffreAffairesAnnee)} sortable /> formatCurrency(rowData.moyennePanier)} sortable /> `${rowData.delaiPaiementMoyen}j`} sortable />
{clients.filter(c => c.risque === 'FAIBLE').length}
Risque Faible
{clients.filter(c => c.risque === 'MOYEN').length}
Risque Moyen
{clients.filter(c => c.risque === 'ELEVE').length}
Risque Élevé
c.fidelite <= 25).length, clients.filter(c => c.fidelite > 25 && c.fidelite <= 50).length, clients.filter(c => c.fidelite > 50 && c.fidelite <= 75).length, clients.filter(c => c.fidelite > 75).length ], backgroundColor: ['#EF4444', '#F59E0B', '#3B82F6', '#10B981'], borderColor: ['#DC2626', '#D97706', '#2563EB', '#059669'], borderWidth: 1 } ] }} options={chartOptions} style={{ height: '300px' }} />

Analyse de la relation entre la valeur client (CA) et leur niveau de fidélité

{clients.map((client, index) => (
{client.prenom} {client.nom}
{client.entreprise}
CA Total
{formatCurrency(client.chiffreAffairesTotal)}
Fidélité
{client.fidelite}%
{segmentBodyTemplate(client)} {' '} {risqueBodyTemplate(client)}
))}
); }; export default SuiviClientsPage;