frontend/app/components/grades/grades-chart.tsx
Nathan 7947fca117
All checks were successful
Deploy to Netlify / Deploy to Netlify (push) Successful in 2m56s
Update app/components/grades/grades-chart.tsx
2025-09-30 18:03:02 +02:00

99 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 { Grade, User } from "~/lib/api";
import { useEffect, useState } from "react";
import { DateTime } from "luxon";
export const HARDCODED_HIDDEN_SUBJECTS = ["Colles préfet"]
export default function GradesChart({
grades,
subjects,
user,
}: {
grades: Grade[];
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 (grades.length > 0) {
const filteredGrades = grades
.filter(
(g) => !hiddenSubjects.includes(g.subject) && !isNaN(Number(g.grade))
)
.map((g) => Number(g.grade));
setMin(Math.max(0, Math.min(...filteredGrades) - 1));
setMax(Math.min(20, Math.max(...filteredGrades) + 1));
}
}, [grades, hiddenSubjects]);
return (
<ChartContainer config={{}} className="h-full w-full -ml-6 max-w-2xl">
<LineChart data={transformGrades(grades, subjects)}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="date" />
<YAxis domain={[min, max]} />
<ChartTooltip content={<ChartTooltipContent />} />
<Legend
onClick={(e) => {
if (e.dataKey) {
toggleSubjectVisibility(e.dataKey as string);
}
}}
/>
{subjects.filter((s) => !HARDCODED_HIDDEN_SUBJECTS.includes(s)).map((subject, index) => (
<Line
key={index}
type="linear"
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)`,
}}
connectNulls
/>
))}
</LineChart>
</ChartContainer>
);
}
function transformGrades(data: Grade[], subjects: string[]) {
const grouped: Record<string, any> = {};
data.forEach((g) => {
const d = DateTime.fromMillis(g.date).toISODate()!;
if (!grouped[d]) {
grouped[d] = { date: d };
// initialize all subjects with null
subjects.forEach((s) => (grouped[d][s] = null));
}
grouped[d][g.subject] = Number(g.grade); // or keep as string
});
return Object.values(grouped);
}