Files
btpxpress-frontend/app/(main)/rapports/clients/page.tsx
dahoud a8825a058b Fix: Corriger toutes les erreurs de build du frontend
- Correction des erreurs TypeScript dans userService.ts et workflowTester.ts
- Ajout des propriétés manquantes aux objets User mockés
- Conversion des dates de string vers objets Date
- Correction des appels asynchrones et des types incompatibles
- Ajout de dynamic rendering pour résoudre les erreurs useSearchParams
- Enveloppement de useSearchParams dans Suspense boundary
- Configuration de force-dynamic au niveau du layout principal

Build réussi: 126 pages générées avec succès

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-18 13:23:08 +00:00

890 lines
41 KiB
TypeScript

'use client';
export const dynamic = 'force-dynamic';
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<ClientAnalyse[]>([]);
const [segments, setSegments] = useState<SegmentAnalyse[]>([]);
const [tendances, setTendances] = useState<TendanceClient[]>([]);
const [selectedClients, setSelectedClients] = useState<ClientAnalyse[]>([]);
const [dateDebut, setDateDebut] = useState<Date>(new Date(new Date().getFullYear(), 0, 1));
const [dateFin, setDateFin] = useState<Date>(new Date());
const [selectedPeriod, setSelectedPeriod] = useState('annee');
const [selectedSegment, setSelectedSegment] = useState('tous');
const [globalFilter, setGlobalFilter] = useState('');
const [activeIndex, setActiveIndex] = useState(0);
const toast = useRef<Toast>(null);
const dt = useRef<DataTable<ClientAnalyse[]>>(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 (
<div className="flex flex-wrap gap-2 align-items-center">
<Dropdown
value={selectedPeriod}
options={periodOptions}
onChange={onPeriodChange}
placeholder="Période"
/>
<Dropdown
value={selectedSegment}
options={segmentOptions}
onChange={(e) => setSelectedSegment(e.value)}
placeholder="Segment"
/>
{selectedPeriod === 'custom' && (
<>
<Calendar
value={dateDebut}
onChange={(e) => setDateDebut(e.value || new Date())}
dateFormat="dd/mm/yy"
placeholder="Date début"
/>
<Calendar
value={dateFin}
onChange={(e) => setDateFin(e.value || new Date())}
dateFormat="dd/mm/yy"
placeholder="Date fin"
/>
</>
)}
</div>
);
};
const rightToolbarTemplate = () => {
return (
<div className="flex flex-wrap gap-2">
<Button
label="PDF"
icon="pi pi-file-pdf"
severity="danger"
onClick={exportPDF}
/>
<Button
label="Excel"
icon="pi pi-file-excel"
severity="success"
onClick={exportExcel}
/>
<Button
label="Actualiser"
icon="pi pi-refresh"
onClick={loadClientData}
/>
</div>
);
};
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: string = 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 <Tag value={label} severity={severity} />;
};
const segmentBodyTemplate = (rowData: ClientAnalyse) => {
let severity: "success" | "warning" | "danger" = 'success';
let label: string = 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 <Tag value={label} severity={severity} />;
};
const risqueBodyTemplate = (rowData: ClientAnalyse) => {
let severity: "success" | "warning" | "danger" = 'success';
let label: string = 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 <Tag value={label} severity={severity} />;
};
const satisfactionBodyTemplate = (rowData: ClientAnalyse) => {
return <Rating value={rowData.satisfactionMoyenne} readOnly cancel={false} />;
};
const fideliteBodyTemplate = (rowData: ClientAnalyse) => {
return (
<div>
<ProgressBar value={rowData.fidelite} showValue={false} />
<small>{rowData.fidelite}%</small>
</div>
);
};
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 (
<div>
<div>{rowData.dernierContact.toLocaleDateString('fr-FR')}</div>
<small className={color}>Il y a {daysDiff} jours</small>
</div>
);
};
const header = (
<div className="flex flex-column md:flex-row md:justify-content-between md:align-items-center">
<h5 className="m-0">Analyse Clients</h5>
<span className="block mt-2 md:mt-0 p-input-icon-left">
<i className="pi pi-search" />
<InputText
type="search"
placeholder="Rechercher..."
onInput={(e) => setGlobalFilter(e.currentTarget.value)}
/>
</span>
</div>
);
// 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 (
<div className="grid">
<div className="col-12">
<Card>
<Toast ref={toast} />
<Toolbar className="mb-4" left={leftToolbarTemplate} right={rightToolbarTemplate} />
<TabView activeIndex={activeIndex} onTabChange={(e) => setActiveIndex(e.index)}>
<TabPanel header="Vue d'Ensemble" leftIcon="pi pi-chart-line mr-2">
<div className="grid">
{/* Indicateurs principaux */}
<div className="col-12 md:col-3">
<Card className="text-center">
<div className="text-6xl text-primary mb-2">
<i className="pi pi-users"></i>
</div>
<div className="text-3xl font-bold text-primary mb-1">
{totalClients}
</div>
<div className="text-lg text-color-secondary">
Total Clients
</div>
</Card>
</div>
<div className="col-12 md:col-3">
<Card className="text-center">
<div className="text-6xl text-green-500 mb-2">
<i className="pi pi-check-circle"></i>
</div>
<div className="text-3xl font-bold text-green-500 mb-1">
{clientsActifs}
</div>
<div className="text-lg text-color-secondary">
Clients Actifs
</div>
</Card>
</div>
<div className="col-12 md:col-3">
<Card className="text-center">
<div className="text-6xl text-orange-500 mb-2">
<i className="pi pi-money-bill"></i>
</div>
<div className="text-3xl font-bold text-orange-500 mb-1">
{formatCurrency(panierMoyenGlobal)}
</div>
<div className="text-lg text-color-secondary">
Panier Moyen
</div>
</Card>
</div>
<div className="col-12 md:col-3">
<Card className="text-center">
<div className="text-6xl text-purple-500 mb-2">
<i className="pi pi-star"></i>
</div>
<div className="text-3xl font-bold text-purple-500 mb-1">
{satisfactionMoyenne.toFixed(1)}/5
</div>
<div className="text-lg text-color-secondary">
Satisfaction Moyenne
</div>
</Card>
</div>
{/* Analyse par segment */}
<div className="col-12">
<Card title="Analyse par Segment">
<DataTable
value={segments}
loading={loading}
emptyMessage="Aucune donnée de segment"
>
<Column field="segment" header="Segment" />
<Column field="nbClients" header="Nb Clients" />
<Column
field="chiffreAffaires"
header="Chiffre d'Affaires"
body={(rowData) => formatCurrency(rowData.chiffreAffaires)}
/>
<Column
field="pourcentageCA"
header="% CA Total"
body={(rowData) => `${rowData.pourcentageCA.toFixed(1)}%`}
/>
<Column
field="panierMoyen"
header="Panier Moyen"
body={(rowData) => formatCurrency(rowData.panierMoyen)}
/>
<Column
field="satisfaction"
header="Satisfaction"
body={(rowData) => (
<Rating value={rowData.satisfaction} readOnly cancel={false} />
)}
/>
<Column
field="fidelite"
header="Fidélité"
body={(rowData) => (
<div>
<ProgressBar value={rowData.fidelite} showValue={false} />
<small>{rowData.fidelite.toFixed(1)}%</small>
</div>
)}
/>
</DataTable>
</Card>
</div>
{/* Graphiques */}
<div className="col-12 md:col-4">
<Card title="Répartition CA par Segment">
<Chart
type="doughnut"
data={segmentChartData}
options={chartOptions}
style={{ height: '300px' }}
/>
</Card>
</div>
<div className="col-12 md:col-8">
<Card title="Évolution Clients">
<Chart
type="line"
data={tendanceChartData}
options={chartOptions}
style={{ height: '300px' }}
/>
</Card>
</div>
<div className="col-12">
<Card title="Chiffre d'Affaires par Client">
<Chart
type="bar"
data={caClientChartData}
options={{
...chartOptions,
scales: {
y: {
beginAtZero: true,
ticks: {
callback: function(value: any) {
return new Intl.NumberFormat('fr-FR', {
style: 'currency',
currency: 'EUR',
minimumFractionDigits: 0
}).format(value);
}
}
}
}
}}
style={{ height: '300px' }}
/>
</Card>
</div>
</div>
</TabPanel>
<TabPanel header="Clients Détaillés" leftIcon="pi pi-list mr-2">
<div className="grid">
<div className="col-12">
<Card>
<DataTable
ref={dt}
value={clients}
selection={selectedClients}
onSelectionChange={(e) => setSelectedClients(e.value)}
selectionMode="checkbox"
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}
>
<Column selectionMode="multiple" headerStyle={{ width: '4rem' }} />
<Column field="prenom" header="Prénom" sortable />
<Column field="nom" header="Nom" sortable />
<Column field="entreprise" header="Entreprise" sortable />
<Column field="email" header="Email" sortable />
<Column field="telephone" header="Téléphone" />
<Column field="dateCreation" header="Création" body={dateBodyTemplate} sortable />
<Column field="dernierContact" header="Dernier Contact" body={dernierContactBodyTemplate} sortable />
<Column field="statut" header="Statut" body={statutBodyTemplate} sortable />
<Column field="segment" header="Segment" body={segmentBodyTemplate} sortable />
<Column field="nbChantiers" header="Chantiers" sortable />
<Column
field="chiffreAffairesTotal"
header="CA Total"
body={(rowData) => formatCurrency(rowData.chiffreAffairesTotal)}
sortable
/>
<Column
field="chiffreAffairesAnnee"
header="CA Année"
body={(rowData) => formatCurrency(rowData.chiffreAffairesAnnee)}
sortable
/>
<Column
field="moyennePanier"
header="Panier Moyen"
body={(rowData) => formatCurrency(rowData.moyennePanier)}
sortable
/>
<Column field="satisfactionMoyenne" header="Satisfaction" body={satisfactionBodyTemplate} sortable />
<Column field="delaiPaiementMoyen" header="Délai Paiement" body={(rowData) => `${rowData.delaiPaiementMoyen}j`} sortable />
<Column field="fidelite" header="Fidélité" body={fideliteBodyTemplate} sortable />
<Column field="risque" header="Risque" body={risqueBodyTemplate} sortable />
<Column field="recommandations" header="Recommandations" sortable />
</DataTable>
</Card>
</div>
</div>
</TabPanel>
<TabPanel header="Analyse Comportementale" leftIcon="pi pi-chart-bar mr-2">
<div className="grid">
<div className="col-12 md:col-6">
<Card title="Clients par Niveau de Risque">
<div className="grid">
<div className="col-4 text-center">
<div className="text-3xl text-green-500 font-bold">
{clients.filter(c => c.risque === 'FAIBLE').length}
</div>
<div className="text-sm">Risque Faible</div>
</div>
<div className="col-4 text-center">
<div className="text-3xl text-orange-500 font-bold">
{clients.filter(c => c.risque === 'MOYEN').length}
</div>
<div className="text-sm">Risque Moyen</div>
</div>
<div className="col-4 text-center">
<div className="text-3xl text-red-500 font-bold">
{clients.filter(c => c.risque === 'ELEVE').length}
</div>
<div className="text-sm">Risque Élevé</div>
</div>
</div>
</Card>
</div>
<div className="col-12 md:col-6">
<Card title="Distribution Fidélité">
<Chart
type="bar"
data={{
labels: ['0-25%', '26-50%', '51-75%', '76-100%'],
datasets: [
{
label: 'Nombre de clients',
data: [
clients.filter(c => 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' }}
/>
</Card>
</div>
<div className="col-12">
<Card title="Matrice Valeur-Fidélité">
<div className="text-center p-4">
<p className="text-color-secondary mb-4">
Analyse de la relation entre la valeur client (CA) et leur niveau de fidélité
</p>
<div className="grid">
{clients.map((client, index) => (
<div key={index} className="col-12 md:col-4 mb-3">
<Card className="text-center">
<div className="font-bold text-lg">{client.prenom} {client.nom}</div>
<div className="text-sm text-color-secondary mb-2">{client.entreprise}</div>
<div className="grid">
<div className="col-6">
<div className="text-primary font-bold">CA Total</div>
<div className="text-sm">{formatCurrency(client.chiffreAffairesTotal)}</div>
</div>
<div className="col-6">
<div className="text-orange-500 font-bold">Fidélité</div>
<div className="text-sm">{client.fidelite}%</div>
</div>
</div>
<div className="mt-2">
<ProgressBar value={client.fidelite} showValue={false} />
</div>
<div className="mt-2">
{segmentBodyTemplate(client)}
{' '}
{risqueBodyTemplate(client)}
</div>
</Card>
</div>
))}
</div>
</div>
</Card>
</div>
</div>
</TabPanel>
</TabView>
</Card>
</div>
</div>
);
};
export default SuiviClientsPage;