Initial commit
This commit is contained in:
340
app/(main)/employes/stats/page.tsx
Normal file
340
app/(main)/employes/stats/page.tsx
Normal file
@@ -0,0 +1,340 @@
|
||||
'use client';
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Card } from 'primereact/card';
|
||||
import { Chart } from 'primereact/chart';
|
||||
import { Button } from 'primereact/button';
|
||||
import { Toolbar } from 'primereact/toolbar';
|
||||
import { DataTable } from 'primereact/datatable';
|
||||
import { Column } from 'primereact/column';
|
||||
import { Tag } from 'primereact/tag';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { apiClient } from '../../../../services/api-client';
|
||||
|
||||
interface StatistiquesEmployes {
|
||||
totalEmployes: number;
|
||||
employesActifs: number;
|
||||
employesInactifs: number;
|
||||
employesEnConge: number;
|
||||
employesEnFormation: number;
|
||||
repartitionParMetier: { [key: string]: number };
|
||||
repartitionParExperience: { [key: string]: number };
|
||||
repartitionParEquipe: { [key: string]: number };
|
||||
employesSansCertification: number;
|
||||
employesAvecCertifications: number;
|
||||
moyenneAgeEmployes: number;
|
||||
ancienneteMoyenne: number;
|
||||
tauxDisponibilite: number;
|
||||
topCompetences: Array<{ competence: string; nombre: number }>;
|
||||
topCertifications: Array<{ certification: string; nombre: number }>;
|
||||
}
|
||||
|
||||
const StatistiquesEmployesPage = () => {
|
||||
const [stats, setStats] = useState<StatistiquesEmployes | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [chartOptions] = useState({
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'bottom'
|
||||
}
|
||||
}
|
||||
});
|
||||
const router = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
loadStatistiques();
|
||||
}, []);
|
||||
|
||||
const loadStatistiques = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
console.log('🔄 Chargement des statistiques employés...');
|
||||
const response = await apiClient.get('/api/employes/statistiques');
|
||||
console.log('✅ Statistiques employés chargées:', response.data);
|
||||
setStats(response.data);
|
||||
} catch (error) {
|
||||
console.error('❌ Erreur lors du chargement des statistiques:', error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const getStatutChartData = () => {
|
||||
if (!stats) return {};
|
||||
|
||||
return {
|
||||
labels: ['Actifs', 'Inactifs', 'En congé', 'En formation'],
|
||||
datasets: [{
|
||||
data: [
|
||||
stats.employesActifs,
|
||||
stats.employesInactifs,
|
||||
stats.employesEnConge,
|
||||
stats.employesEnFormation
|
||||
],
|
||||
backgroundColor: [
|
||||
'#4CAF50',
|
||||
'#F44336',
|
||||
'#FF9800',
|
||||
'#2196F3'
|
||||
],
|
||||
borderWidth: 2
|
||||
}]
|
||||
};
|
||||
};
|
||||
|
||||
const getMetierChartData = () => {
|
||||
if (!stats) return {};
|
||||
|
||||
return {
|
||||
labels: Object.keys(stats.repartitionParMetier),
|
||||
datasets: [{
|
||||
data: Object.values(stats.repartitionParMetier),
|
||||
backgroundColor: [
|
||||
'#FF6384',
|
||||
'#36A2EB',
|
||||
'#FFCE56',
|
||||
'#4BC0C0',
|
||||
'#9966FF',
|
||||
'#FF9F40',
|
||||
'#FF6384',
|
||||
'#C9CBCF'
|
||||
],
|
||||
borderWidth: 2
|
||||
}]
|
||||
};
|
||||
};
|
||||
|
||||
const getExperienceChartData = () => {
|
||||
if (!stats) return {};
|
||||
|
||||
return {
|
||||
labels: Object.keys(stats.repartitionParExperience),
|
||||
datasets: [{
|
||||
label: 'Nombre d\'employés',
|
||||
data: Object.values(stats.repartitionParExperience),
|
||||
backgroundColor: '#36A2EB',
|
||||
borderColor: '#36A2EB',
|
||||
borderWidth: 1
|
||||
}]
|
||||
};
|
||||
};
|
||||
|
||||
const leftToolbarTemplate = () => {
|
||||
return (
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Button
|
||||
label="Retour aux employés"
|
||||
icon="pi pi-arrow-left"
|
||||
className="p-button-outlined"
|
||||
onClick={() => router.push('/employes')}
|
||||
/>
|
||||
<Button
|
||||
label="Export PDF"
|
||||
icon="pi pi-file-pdf"
|
||||
className="p-button-danger"
|
||||
onClick={() => console.log('Export PDF')}
|
||||
/>
|
||||
<Button
|
||||
label="Export Excel"
|
||||
icon="pi pi-file-excel"
|
||||
className="p-button-success"
|
||||
onClick={() => console.log('Export Excel')}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const rightToolbarTemplate = () => {
|
||||
return (
|
||||
<Button
|
||||
icon="pi pi-refresh"
|
||||
className="p-button-outlined"
|
||||
onClick={loadStatistiques}
|
||||
tooltip="Actualiser"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="grid">
|
||||
<div className="col-12">
|
||||
<Card>
|
||||
<div className="flex justify-content-center">
|
||||
<i className="pi pi-spin pi-spinner" style={{ fontSize: '2rem' }} />
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!stats) {
|
||||
return (
|
||||
<div className="grid">
|
||||
<div className="col-12">
|
||||
<Card>
|
||||
<div className="text-center">
|
||||
<p>Aucune donnée disponible</p>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="grid">
|
||||
<div className="col-12">
|
||||
<Toolbar
|
||||
className="mb-4"
|
||||
left={leftToolbarTemplate}
|
||||
right={rightToolbarTemplate}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Métriques principales */}
|
||||
<div className="col-12 lg:col-3 md:col-6">
|
||||
<Card className="bg-blue-100">
|
||||
<div className="flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<span className="block text-500 font-medium mb-3">Total Employés</span>
|
||||
<div className="text-900 font-medium text-xl">{stats.totalEmployes}</div>
|
||||
</div>
|
||||
<div className="flex align-items-center justify-content-center bg-blue-500 border-round" style={{ width: '2.5rem', height: '2.5rem' }}>
|
||||
<i className="pi pi-users text-white text-xl" />
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div className="col-12 lg:col-3 md:col-6">
|
||||
<Card className="bg-green-100">
|
||||
<div className="flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<span className="block text-500 font-medium mb-3">Employés Actifs</span>
|
||||
<div className="text-900 font-medium text-xl">{stats.employesActifs}</div>
|
||||
</div>
|
||||
<div className="flex align-items-center justify-content-center bg-green-500 border-round" style={{ width: '2.5rem', height: '2.5rem' }}>
|
||||
<i className="pi pi-check-circle text-white text-xl" />
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div className="col-12 lg:col-3 md:col-6">
|
||||
<Card className="bg-orange-100">
|
||||
<div className="flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<span className="block text-500 font-medium mb-3">Taux Disponibilité</span>
|
||||
<div className="text-900 font-medium text-xl">{stats.tauxDisponibilite}%</div>
|
||||
</div>
|
||||
<div className="flex align-items-center justify-content-center bg-orange-500 border-round" style={{ width: '2.5rem', height: '2.5rem' }}>
|
||||
<i className="pi pi-calendar-check text-white text-xl" />
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div className="col-12 lg:col-3 md:col-6">
|
||||
<Card className="bg-purple-100">
|
||||
<div className="flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<span className="block text-500 font-medium mb-3">Ancienneté Moyenne</span>
|
||||
<div className="text-900 font-medium text-xl">{stats.ancienneteMoyenne} ans</div>
|
||||
</div>
|
||||
<div className="flex align-items-center justify-content-center bg-purple-500 border-round" style={{ width: '2.5rem', height: '2.5rem' }}>
|
||||
<i className="pi pi-clock text-white text-xl" />
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Graphiques */}
|
||||
<div className="col-12 lg:col-6">
|
||||
<Card title="Répartition par Statut">
|
||||
<Chart type="doughnut" data={getStatutChartData()} options={chartOptions} style={{ height: '300px' }} />
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div className="col-12 lg:col-6">
|
||||
<Card title="Répartition par Métier">
|
||||
<Chart type="pie" data={getMetierChartData()} options={chartOptions} style={{ height: '300px' }} />
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div className="col-12">
|
||||
<Card title="Répartition par Niveau d'Expérience">
|
||||
<Chart type="bar" data={getExperienceChartData()} options={chartOptions} style={{ height: '300px' }} />
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Tableaux de données */}
|
||||
<div className="col-12 lg:col-6">
|
||||
<Card title="Top Compétences">
|
||||
<DataTable value={stats.topCompetences} responsiveLayout="scroll">
|
||||
<Column field="competence" header="Compétence" />
|
||||
<Column
|
||||
field="nombre"
|
||||
header="Nombre d'employés"
|
||||
body={(rowData) => <Tag value={rowData.nombre} severity="info" />}
|
||||
/>
|
||||
</DataTable>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div className="col-12 lg:col-6">
|
||||
<Card title="Top Certifications">
|
||||
<DataTable value={stats.topCertifications} responsiveLayout="scroll">
|
||||
<Column field="certification" header="Certification" />
|
||||
<Column
|
||||
field="nombre"
|
||||
header="Nombre d'employés"
|
||||
body={(rowData) => <Tag value={rowData.nombre} severity="success" />}
|
||||
/>
|
||||
</DataTable>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Informations supplémentaires */}
|
||||
<div className="col-12">
|
||||
<Card title="Informations Complémentaires">
|
||||
<div className="grid">
|
||||
<div className="col-12 md:col-4">
|
||||
<div className="text-center">
|
||||
<i className="pi pi-graduation-cap text-4xl text-blue-500 mb-3" />
|
||||
<h4>Certifications</h4>
|
||||
<p className="text-600">
|
||||
{stats.employesAvecCertifications} employés avec certifications<br />
|
||||
{stats.employesSansCertification} sans certification
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12 md:col-4">
|
||||
<div className="text-center">
|
||||
<i className="pi pi-calendar text-4xl text-green-500 mb-3" />
|
||||
<h4>Âge Moyen</h4>
|
||||
<p className="text-600">
|
||||
{stats.moyenneAgeEmployes} ans
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12 md:col-4">
|
||||
<div className="text-center">
|
||||
<i className="pi pi-users text-4xl text-orange-500 mb-3" />
|
||||
<h4>Équipes</h4>
|
||||
<p className="text-600">
|
||||
{Object.keys(stats.repartitionParEquipe).length} équipes actives
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default StatistiquesEmployesPage;
|
||||
Reference in New Issue
Block a user