feat: Amélioration du dashboard

This commit is contained in:
N3WT DE COMPET
2025-05-29 10:36:38 +02:00
parent e30753f1d6
commit eb48523f7d
4 changed files with 210 additions and 51 deletions

View File

@ -0,0 +1,45 @@
import React from 'react';
export default function LineChart({ data }) {
if (!data || data.length === 0) {
return <div className="text-gray-400 text-center">Aucune donnée</div>;
}
// Hauteur max du graphique en pixels
const chartHeight = 120;
const maxValue = Math.max(...data.map((d) => d.value), 1);
// Trouver les indices des barres ayant la valeur max (pour gérer les égalités)
const maxIndices = data
.map((d, idx) => (d.value === maxValue ? idx : -1))
.filter((idx) => idx !== -1);
return (
<div
className="w-full flex items-end space-x-4"
style={{ height: chartHeight }}
>
{data.map((point, idx) => {
const barHeight = Math.max((point.value / maxValue) * chartHeight, 8); // min 8px
const isMax = maxIndices.includes(idx);
return (
<div key={idx} className="flex flex-col items-center flex-1">
{/* Valeur au-dessus de la barre */}
<span className="text-xs mb-1 text-gray-700 font-semibold">
{point.value}
</span>
<div
className={`${isMax ? 'bg-emerald-400' : 'bg-blue-400'} rounded-t w-4`}
style={{
height: `${barHeight}px`,
transition: 'height 0.3s',
}}
title={`${point.month}: ${point.value}`}
/>
<span className="text-xs mt-1 text-gray-600">{point.month}</span>
</div>
);
})}
</div>
);
}

View File

@ -0,0 +1,67 @@
import React from 'react';
const COLORS = [
'fill-blue-400 text-blue-400',
'fill-orange-400 text-orange-400',
'fill-purple-400 text-purple-400',
'fill-emerald-400 text-emerald-400',
];
export default function PieChart({ data }) {
if (!data || data.length === 0) {
return <div className="text-gray-400 text-center">Aucune donnée</div>;
}
const total = data.reduce((acc, d) => acc + d.value, 0);
if (total === 0) {
return <div className="text-gray-400 text-center">Aucune donnée</div>;
}
let cumulative = 0;
return (
<div className="flex items-center justify-center w-full">
<svg width={100} height={100} viewBox="0 0 32 32">
{data.map((slice, idx) => {
const value = (slice.value / total) * 100;
const startAngle = (cumulative / 100) * 360;
const endAngle = ((cumulative + value) / 100) * 360;
const largeArc = value > 50 ? 1 : 0;
const x1 = 16 + 16 * Math.cos((Math.PI * (startAngle - 90)) / 180);
const y1 = 16 + 16 * Math.sin((Math.PI * (startAngle - 90)) / 180);
const x2 = 16 + 16 * Math.cos((Math.PI * (endAngle - 90)) / 180);
const y2 = 16 + 16 * Math.sin((Math.PI * (endAngle - 90)) / 180);
const pathData = `
M16,16
L${x1},${y1}
A16,16 0 ${largeArc} 1 ${x2},${y2}
Z
`;
cumulative += value;
return (
<path
key={idx}
d={pathData}
className={COLORS[idx % COLORS.length].split(' ')[0]}
stroke="#fff"
strokeWidth="0.5"
/>
);
})}
</svg>
<div className="ml-4 flex flex-col space-y-1">
{data.map((slice, idx) => (
<div
key={idx}
className={`flex items-center text-xs font-semibold ${COLORS[idx % COLORS.length].split(' ')[1]}`}
>
<span
className={`inline-block w-3 h-3 mr-2 rounded ${COLORS[idx % COLORS.length].split(' ')[0]}`}
/>
{slice.label} : {slice.value}
</div>
))}
</div>
</div>
);
}