frontend/app/components/grades/average-chart.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

110 lines
3 KiB
TypeScript

import { CartesianGrid, Legend, Line, LineChart, XAxis, YAxis } from "recharts";
import { ChartContainer, ChartTooltip, ChartTooltipContent } from "../ui/chart";
import { getSubjectEmoji, getSubjectColor } from "~/lib/utils";
import type { PeriodResult, User } from "~/lib/api";
import { useEffect, useState } from "react";
export default function AverageChart({
averages,
subjects,
user,
}: {
averages: PeriodResult[];
subjects: string[];
user: User;
}) {
const [hiddenSubjects, setHiddenSubjects] = useState<string[]>([]);
const toggleSubjectVisibility = (subject: string) => {
setHiddenSubjects((prev) =>
prev.includes(subject)
? prev.filter((s) => s !== subject)
: [...prev, subject]
);
};
const [min, setMin] = useState(0);
const [max, setMax] = useState(20);
useEffect(() => {
if (averages.length > 0) {
const filteredGrades = averages.map((o) =>
Object.entries(o)
.filter(([key, value]) => {
return !hiddenSubjects.includes(key) && !isNaN(parseFloat(value));
})
.map(([_, v]) => parseFloat(v))
.filter(Boolean)
);
setMin(getMin(filteredGrades));
setMax(getMax(filteredGrades));
}
}, [averages, hiddenSubjects]);
return (
<ChartContainer config={{}} className="h-full w-full -ml-6 max-w-2xl">
<LineChart data={averages}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="period" />
<YAxis domain={[min, max]} />
<ChartTooltip content={<ChartTooltipContent />} />
<Legend
onClick={(e) => {
if (e.dataKey) {
toggleSubjectVisibility(e.dataKey as string);
}
}}
/>
{subjects.map((subject, index) => (
<Line
key={index}
type="monotone"
dataKey={subject}
name={subject + " " + getSubjectEmoji(subject, user.preferences)}
hide={hiddenSubjects.includes(subject)}
stroke={`var(--color-${getSubjectColor(
subject,
user.preferences
)}-800`}
dot={{
fill: `var(--color-${getSubjectColor(
subject,
user.preferences
)}-800`,
stroke: `var(--color-${getSubjectColor(
subject,
user.preferences
)}-800)`,
}}
/>
))}
<Line
type="monotone"
dataKey="average"
name="Moyenne"
stroke="var(--primary)"
strokeWidth={2}
dot={false}
strokeDasharray="5 5"
hide={hiddenSubjects.includes("average")}
/>
</LineChart>
</ChartContainer>
);
}
function getMin(grades: number[][]) {
return Math.max(
Math.round(
Math.min(...grades.map((g) => Math.min(...g)).filter(Boolean)) - 1
),
0
);
}
function getMax(grades: number[][]) {
return Math.min(
Math.round(
Math.max(...grades.map((g) => Math.max(...g)).filter(Boolean)) + 1
),
20
);
}