Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | 2x 12x 12x 12x 3x 12x 12x 12x 12x 12x 10x 6x | import { useMemo, useState } from "react";
import { alpha } from "@mui/material/styles";
import { Box, Card, Chip, Skeleton, ToggleButton, ToggleButtonGroup, Typography } from "@mui/material";
import BarChartIcon from "@mui/icons-material/BarChart";
import StackedColumnChart from "@/utils/components/charts/StackedColumnChart";
const METRICS = {
grade: {
label: "Grade",
subtitle: "Average grade per enrolled class",
seriesName: "Average Grade",
},
submissions: {
label: "Submissions",
subtitle: "Number of submissions per enrolled class",
seriesName: "Submissions",
},
};
export default function GradesChart({ chartCategories, gradeData, submissionsData, loading, classCount }) {
// Real class count: the categories may include a folded "Other (n)" bucket.
const shownClassCount = classCount || chartCategories.length;
const [metric, setMetric] = useState("grade");
const handleMetricChange = (_event, next) => {
if (next) setMetric(next);
};
const chartData = useMemo(() => {
const data = metric === "submissions" ? submissionsData : gradeData;
return [{ name: METRICS[metric].seriesName, data: data ?? [] }];
}, [metric, gradeData, submissionsData]);
return (
<Card
elevation={0}
sx={{ border: (t) => `1px solid ${t.palette.divider}`, borderRadius: 3, overflow: "hidden" }}
>
<Box
sx={{
px: 3,
pt: 2.5,
pb: 1,
display: "flex",
alignItems: "flex-start",
justifyContent: "space-between",
gap: 2,
flexWrap: "wrap",
}}
>
<Box>
<Typography variant="subtitle1" fontWeight={700} color="text.primary">
Grade Overview
</Typography>
<Typography variant="caption" color="text.secondary">
{METRICS[metric].subtitle}
</Typography>
</Box>
<Box sx={{ display: "flex", alignItems: "center", gap: 1.5, flexShrink: 0 }}>
<ToggleButtonGroup
id="student-dashboard-grades-chart-metric-toggle"
value={metric}
exclusive
size="small"
onChange={handleMetricChange}
>
<ToggleButton id="student-dashboard-grades-chart-metric-grade" value="grade">
{METRICS.grade.label}
</ToggleButton>
<ToggleButton id="student-dashboard-grades-chart-metric-submissions" value="submissions">
{METRICS.submissions.label}
</ToggleButton>
</ToggleButtonGroup>
{!loading && chartCategories.length > 0 && (
<Chip
label={`${shownClassCount} class${shownClassCount !== 1 ? "es" : ""}`}
size="small"
sx={{
fontWeight: 700,
bgcolor: (t) => alpha(t.palette.info.main, 0.1),
color: "info.main",
}}
/>
)}
</Box>
</Box>
<Box sx={{ px: 2, pb: 2 }}>
{loading ? (
<Box
sx={{
display: "flex",
alignItems: "flex-end",
gap: 1.5,
height: 300,
px: 2,
pb: 2,
pt: 2,
}}
>
{[55, 80, 40, 90, 65, 75].map((h, i) => (
<Skeleton key={i} variant="rounded" sx={{ flex: 1, height: `${h}%`, borderRadius: 1 }} />
))}
</Box>
) : chartCategories.length === 0 ? (
<Box
sx={{
height: 300,
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
gap: 1,
}}
>
<BarChartIcon sx={{ fontSize: 52, color: "text.disabled" }} />
<Typography variant="body2" color="text.secondary">
No grade data available yet
</Typography>
</Box>
) : (
<StackedColumnChart title="" data={chartData} categories={chartCategories} loading={false} />
)}
</Box>
</Card>
);
}
|