frontend/app/components/grades/index.tsx
Nathan Lamy b42236e730
All checks were successful
Deploy to Netlify / Deploy to Netlify (push) Successful in 1m51s
feat: add grades chart
2025-08-22 11:58:20 +02:00

172 lines
5.5 KiB
TypeScript

import {
PolarAngleAxis,
PolarGrid,
PolarRadiusAxis,
Radar,
RadarChart,
} from "recharts";
import { useAverages, useGrades, type User } from "~/lib/api";
import { ChartContainer, ChartTooltip, ChartTooltipContent } from "../ui/chart";
import { Award } from "lucide-react";
import { useState } from "react";
import { Tabs, TabsList, tabsStyle, TabsTrigger } from "../ui/tabs";
import AverageChart from "./average-chart";
import GradesChart from "./grades-chart";
const periods = [
{
id: "YEAR",
label: "Cette année",
},
{
id: "TRIMESTER",
label: "Ce trimestre",
},
{
id: "MONTH",
label: "Ce mois",
},
];
export default function GradesPage({ user }: { user: User }) {
const [period, setPeriod] = useState(periods[0].id);
const { grades, averages, subjects, isLoading, isError } = useGrades(period);
const {
subjectAverages,
globalAverage,
isLoading: loading,
isError: error,
} = useAverages(period);
return (
<div className="space-y-6 pb-20 md:pb-0">
{/* Tabs */}
<Tabs
defaultValue={periods[0].id}
value={period}
onValueChange={setPeriod}
className="max-w-md w-full"
>
<TabsList className="w-full p-0 bg-background justify-start border-b rounded-none">
{periods.map((period) => (
<TabsTrigger
key={period.id}
value={period.id}
className={tabsStyle}
>
{period.label}
</TabsTrigger>
))}
</TabsList>
</Tabs>
{isNaN(globalAverage) ? (
<div className="text-center text-muted-foreground">
Aucune note disponible pour cette période.
</div>
) : isLoading || loading ? (
<div className="flex items-center justify-center h-64">
<span className="text-muted-foreground">Chargement...</span>
</div>
) : isError || error ? (
<div className="text-center text-red-500">
Une erreur est survenue lors de la récupération des données.
</div>
) : (
<>
{/* Global average */}
<div className="bg-muted/50 rounded-lg p-4 space-y-2">
<div className="flex items-center justify-between">
<span className="text-xs md:text-sm font-medium text-muted-foreground">
Moyenne générale
</span>
<Award className="h-4 w-4 text-muted-foreground" />
</div>
<div className="text-xl md:text-2xl font-bold">
{globalAverage} / 20
</div>
</div>
{/* Charts - Mobile First Layout */}
<div className="space-y-8">
{/* Grade Trends */}
<div className="space-y-4">
<div className="flex items-center justify-between">
<div>
<h2 className="text-lg md:text-xl font-semibold">
Votre progression
</h2>
<p className="text-sm text-muted-foreground">
Suivez vos notes au fil du temps
</p>
</div>
</div>
<div className="bg-muted/50 rounded-lg py-4">
<GradesChart grades={grades} subjects={subjects} user={user} />
</div>
</div>
{/* Grade Trends */}
<div className="space-y-4">
<div className="flex items-center justify-between">
<div>
<h2 className="text-lg md:text-xl font-semibold">
Vos moyennes par matière
</h2>
<p className="text-sm text-muted-foreground">
Comparez vos moyennes par matière
</p>
</div>
</div>
<div className="bg-muted/50 rounded-lg py-4">
<AverageChart
averages={averages}
subjects={subjects}
user={user}
/>
</div>
</div>
{/* Radar chart */}
<div className="space-y-4">
<div className="flex items-center justify-between">
<div>
<h2 className="text-lg md:text-xl font-semibold">
Vos points forts
</h2>
<p className="text-sm text-muted-foreground">
Visualisez vos performances par matière
</p>
</div>
</div>
<div className="bg-muted/50 rounded-lg py-4">
<ChartContainer
config={{}}
className="h-full w-full -ml-6 max-w-2xl"
>
<RadarChart
data={subjectAverages}
margin={{ top: 10, right: 10, bottom: 10, left: 10 }}
>
<PolarGrid />
<PolarAngleAxis dataKey="subject" />
<PolarRadiusAxis angle={90} domain={[0, 20]} />
<Radar
name="Moyenne"
dataKey="average"
stroke="var(--chart-2)"
fill="var(--chart-2)"
fillOpacity={0.3}
/>
<ChartTooltip content={<ChartTooltipContent />} />
</RadarChart>
</ChartContainer>
</div>
</div>
</div>
</>
)}
</div>
);
}