Files
btpxpress-frontend/app/(main)/notifications/statistiques/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

293 lines
11 KiB
TypeScript

'use client';
export const dynamic = 'force-dynamic';
import React, { useState, useEffect } from 'react';
import { Card } from 'primereact/card';
import { Chart } from 'primereact/chart';
import { ProgressBar } from 'primereact/progressbar';
import notificationService, { NotificationStats } from '../../../../services/notificationService';
const NotificationsStatistiquesPage = () => {
const [stats, setStats] = useState<NotificationStats | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
loadStats();
}, []);
const loadStats = async () => {
try {
setLoading(true);
const data = await notificationService.getNotificationStats();
setStats(data);
} catch (error) {
console.error('Erreur lors du chargement des statistiques:', error);
} finally {
setLoading(false);
}
};
const chartData = {
labels: ['Info', 'Avertissement', 'Succès', 'Erreur'],
datasets: [
{
data: stats ? [
stats.parType.info || 0,
stats.parType.warning || 0,
stats.parType.success || 0,
stats.parType.error || 0
] : [0, 0, 0, 0],
backgroundColor: [
'#3B82F6',
'#F59E0B',
'#10B981',
'#EF4444'
],
hoverBackgroundColor: [
'#2563EB',
'#D97706',
'#059669',
'#DC2626'
]
}
]
};
const chartOptions = {
plugins: {
legend: {
labels: {
usePointStyle: true
}
}
}
};
const tendanceChartData = {
labels: stats?.tendance.map(t => t.periode) || [],
datasets: [
{
label: 'Notifications',
data: stats?.tendance.map(t => t.nombre) || [],
fill: false,
borderColor: '#3B82F6',
tension: 0.4
}
]
};
const tendanceChartOptions = {
maintainAspectRatio: false,
aspectRatio: 0.6,
plugins: {
legend: {
labels: {
color: '#495057'
}
}
},
scales: {
x: {
ticks: {
color: '#495057'
},
grid: {
color: '#ebedef'
}
},
y: {
ticks: {
color: '#495057'
},
grid: {
color: '#ebedef'
}
}
}
};
if (loading) {
return (
<div className="grid">
<div className="col-12">
<Card>
<div className="text-center p-5">
<i className="pi pi-spin pi-spinner text-4xl"></i>
<p>Chargement des statistiques...</p>
</div>
</Card>
</div>
</div>
);
}
if (!stats) {
return (
<div className="grid">
<div className="col-12">
<Card>
<div className="text-center p-5">
<i className="pi pi-exclamation-triangle text-4xl text-orange-500"></i>
<p>Impossible de charger les statistiques</p>
</div>
</Card>
</div>
</div>
);
}
const pourcentageLues = stats.total > 0
? Math.round(((stats.total - stats.nonLues) / stats.total) * 100)
: 0;
return (
<div className="grid">
<div className="col-12">
<h2>Statistiques des Notifications</h2>
</div>
{/* Cartes de statistiques */}
<div className="col-12 md:col-6 lg:col-3">
<Card>
<div className="flex justify-content-between align-items-center">
<div>
<span className="block text-500 font-medium mb-3">Total</span>
<div className="text-900 font-medium text-xl">{stats.total}</div>
</div>
<div className="flex align-items-center justify-content-center bg-blue-100 border-round" style={{ width: '2.5rem', height: '2.5rem' }}>
<i className="pi pi-bell text-blue-500 text-xl"></i>
</div>
</div>
</Card>
</div>
<div className="col-12 md:col-6 lg:col-3">
<Card>
<div className="flex justify-content-between align-items-center">
<div>
<span className="block text-500 font-medium mb-3">Non lues</span>
<div className="text-900 font-medium text-xl">{stats.nonLues}</div>
</div>
<div className="flex align-items-center justify-content-center bg-orange-100 border-round" style={{ width: '2.5rem', height: '2.5rem' }}>
<i className="pi pi-exclamation-circle text-orange-500 text-xl"></i>
</div>
</div>
</Card>
</div>
<div className="col-12 md:col-6 lg:col-3">
<Card>
<div className="flex justify-content-between align-items-center">
<div>
<span className="block text-500 font-medium mb-3">Lues</span>
<div className="text-900 font-medium text-xl">{stats.total - stats.nonLues}</div>
</div>
<div className="flex align-items-center justify-content-center bg-green-100 border-round" style={{ width: '2.5rem', height: '2.5rem' }}>
<i className="pi pi-check-circle text-green-500 text-xl"></i>
</div>
</div>
</Card>
</div>
<div className="col-12 md:col-6 lg:col-3">
<Card>
<div className="flex justify-content-between align-items-center">
<div>
<span className="block text-500 font-medium mb-3">Taux de lecture</span>
<div className="text-900 font-medium text-xl">{pourcentageLues}%</div>
</div>
<div className="flex align-items-center justify-content-center bg-cyan-100 border-round" style={{ width: '2.5rem', height: '2.5rem' }}>
<i className="pi pi-chart-line text-cyan-500 text-xl"></i>
</div>
</div>
</Card>
</div>
{/* Graphique par type */}
<div className="col-12 md:col-6">
<Card title="Répartition par type">
<Chart type="pie" data={chartData} options={chartOptions} />
</Card>
</div>
{/* Graphique de tendance */}
<div className="col-12 md:col-6">
<Card title="Tendance hebdomadaire">
<Chart type="line" data={tendanceChartData} options={tendanceChartOptions} style={{ height: '300px' }} />
</Card>
</div>
{/* Détails par type */}
<div className="col-12">
<Card title="Détails par type">
<div className="grid">
<div className="col-12 md:col-6 lg:col-3">
<div className="mb-3">
<span className="text-500 font-medium">Info</span>
<div className="flex align-items-center mt-2">
<ProgressBar
value={stats.total > 0 ? (stats.parType.info / stats.total) * 100 : 0}
showValue={false}
className="flex-1 mr-2"
color="#3B82F6"
/>
<span className="font-bold">{stats.parType.info}</span>
</div>
</div>
</div>
<div className="col-12 md:col-6 lg:col-3">
<div className="mb-3">
<span className="text-500 font-medium">Avertissement</span>
<div className="flex align-items-center mt-2">
<ProgressBar
value={stats.total > 0 ? (stats.parType.warning / stats.total) * 100 : 0}
showValue={false}
className="flex-1 mr-2"
color="#F59E0B"
/>
<span className="font-bold">{stats.parType.warning}</span>
</div>
</div>
</div>
<div className="col-12 md:col-6 lg:col-3">
<div className="mb-3">
<span className="text-500 font-medium">Succès</span>
<div className="flex align-items-center mt-2">
<ProgressBar
value={stats.total > 0 ? (stats.parType.success / stats.total) * 100 : 0}
showValue={false}
className="flex-1 mr-2"
color="#10B981"
/>
<span className="font-bold">{stats.parType.success}</span>
</div>
</div>
</div>
<div className="col-12 md:col-6 lg:col-3">
<div className="mb-3">
<span className="text-500 font-medium">Erreur</span>
<div className="flex align-items-center mt-2">
<ProgressBar
value={stats.total > 0 ? (stats.parType.error / stats.total) * 100 : 0}
showValue={false}
className="flex-1 mr-2"
color="#EF4444"
/>
<span className="font-bold">{stats.parType.error}</span>
</div>
</div>
</div>
</div>
</Card>
</div>
</div>
);
};
export default NotificationsStatistiquesPage;