feat: initialisation complète du projet CESIZen

- Backend Laravel 11 (API REST, JWT auth, PostgreSQL)
- Frontend Next.js 14 (React, TypeScript, Tailwind CSS)
- Docker Compose (PostgreSQL, backend, frontend)
- Modules : auth, profil, tracker émotions, informations, admin
This commit is contained in:
Azmog
2026-03-11 16:27:01 +01:00
commit e3da625927
158 changed files with 25139 additions and 0 deletions
@@ -0,0 +1,160 @@
'use client';
import React from 'react';
import {
BarChart,
Bar,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
ResponsiveContainer,
PieChart,
Pie,
Cell,
LineChart,
Line,
Legend,
} from 'recharts';
import type { RapportData } from '@/types';
interface EmotionChartProps {
data: RapportData;
type?: 'repartition' | 'evolution' | 'intensite';
}
export default function EmotionChart({ data, type = 'repartition' }: EmotionChartProps) {
if (type === 'repartition') {
const pieData = data.repartition.map((item) => ({
name: item.nom,
value: item.count,
color: item.couleur,
pourcentage: item.pourcentage,
}));
if (pieData.length === 0) {
return <EmptyChart message="Aucune donnée pour cette période" />;
}
return (
<div className="w-full h-80">
<ResponsiveContainer width="100%" height={320}>
<PieChart>
<Pie
data={pieData}
cx="50%"
cy="50%"
innerRadius={60}
outerRadius={120}
paddingAngle={3}
dataKey="value"
label={({ name, pourcentage }: any) => `${name} (${pourcentage}%)`}
>
{pieData.map((entry, index) => (
<Cell key={`cell-${index}`} fill={entry.color} />
))}
</Pie>
<Tooltip
formatter={(value: any, name: any) => [`${value} saisies`, name]}
contentStyle={{
backgroundColor: 'rgba(255,255,255,0.95)',
border: 'none',
borderRadius: '12px',
boxShadow: '0 4px 12px rgba(0,0,0,0.1)',
}}
/>
</PieChart>
</ResponsiveContainer>
</div>
);
}
if (type === 'evolution') {
const lineData = data.evolution.labels.map((label, index) => {
const point: Record<string, string | number> = { date: label };
data.evolution.emotions.forEach((emotion) => {
point[emotion.nom] = emotion.data[index] || 0;
});
return point;
});
if (lineData.length === 0) {
return <EmptyChart message="Aucune donnée d'évolution" />;
}
return (
<div className="w-full h-80">
<ResponsiveContainer width="100%" height={320}>
<LineChart data={lineData}>
<CartesianGrid strokeDasharray="3 3" className="opacity-30" />
<XAxis dataKey="date" tick={{ fontSize: 12 }} />
<YAxis tick={{ fontSize: 12 }} />
<Tooltip
contentStyle={{
backgroundColor: 'rgba(255,255,255,0.95)',
border: 'none',
borderRadius: '12px',
boxShadow: '0 4px 12px rgba(0,0,0,0.1)',
}}
/>
<Legend />
{data.evolution.emotions.map((emotion) => (
<Line
key={emotion.nom}
type="monotone"
dataKey={emotion.nom}
stroke={emotion.couleur}
strokeWidth={2}
dot={{ fill: emotion.couleur, r: 4 }}
activeDot={{ r: 6 }}
/>
))}
</LineChart>
</ResponsiveContainer>
</div>
);
}
if (type === 'intensite') {
const barData = data.evolution.labels.map((label, index) => ({
date: label,
intensite: data.evolution.intensite_moyenne[index] || 0,
}));
if (barData.length === 0) {
return <EmptyChart message="Aucune donnée d'intensité" />;
}
return (
<div className="w-full h-80">
<ResponsiveContainer width="100%" height={320}>
<BarChart data={barData}>
<CartesianGrid strokeDasharray="3 3" className="opacity-30" />
<XAxis dataKey="date" tick={{ fontSize: 12 }} />
<YAxis domain={[0, 10]} tick={{ fontSize: 12 }} />
<Tooltip
contentStyle={{
backgroundColor: 'rgba(255,255,255,0.95)',
border: 'none',
borderRadius: '12px',
boxShadow: '0 4px 12px rgba(0,0,0,0.1)',
}}
formatter={(value: any) => [`${Number(value).toFixed(1)}`, 'Intensité moyenne']}
/>
<Bar dataKey="intensite" fill="#fce117" radius={[6, 6, 0, 0]} />
</BarChart>
</ResponsiveContainer>
</div>
);
}
return null;
}
function EmptyChart({ message }: { message: string }) {
return (
<div className="w-full h-80 flex items-center justify-center text-gray-400 dark:text-gray-600">
<p className="text-lg">{message}</p>
</div>
);
}