All checks were successful
Deploy to Netlify / Deploy to Netlify (push) Successful in 1m51s
97 lines
2.9 KiB
TypeScript
97 lines
2.9 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 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.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);
|
|
}
|