feat(classes): optimize teacher dashboard ui and implement grade management

This commit is contained in:
SpecialX
2026-01-14 13:59:11 +08:00
parent ade8d4346c
commit 9bfc621d3f
104 changed files with 12793 additions and 2309 deletions

View File

@@ -1,9 +1,13 @@
"use client"
import Link from "next/link"
import { BarChart3 } from "lucide-react"
import { CartesianGrid, Line, LineChart, XAxis, YAxis } from "recharts"
import { Card, CardContent, CardHeader, CardTitle } from "@/shared/components/ui/card"
import { EmptyState } from "@/shared/components/ui/empty-state"
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/shared/components/ui/table"
import { ChartContainer, ChartTooltip, ChartTooltipContent } from "@/shared/components/ui/chart"
import { formatDate } from "@/shared/lib/utils"
import type { StudentDashboardGradeProps } from "@/modules/homework/types"
@@ -11,6 +15,24 @@ export function StudentGradesCard({ grades }: { grades: StudentDashboardGradePro
const hasGradeTrend = grades.trend.length > 0
const hasRecentGrades = grades.recent.length > 0
const chartData = grades.trend.map((item) => ({
title: item.assignmentTitle,
score: Math.round(item.percentage),
fullTitle: item.assignmentTitle,
submittedAt: formatDate(item.submittedAt),
rawScore: item.score,
maxScore: item.maxScore,
}))
const chartConfig = {
score: {
label: "Score (%)",
color: "hsl(var(--primary))",
},
}
const latestGrade = grades.trend[grades.trend.length - 1]
return (
<Card>
<CardHeader>
@@ -30,37 +52,79 @@ export function StudentGradesCard({ grades }: { grades: StudentDashboardGradePro
) : (
<div className="space-y-4">
<div className="rounded-md border bg-card p-4">
<svg viewBox="0 0 100 40" className="h-24 w-full">
<polyline
fill="none"
stroke="currentColor"
strokeWidth="2"
points={grades.trend
.map((p, i) => {
const t = grades.trend.length > 1 ? i / (grades.trend.length - 1) : 0
const x = t * 100
const v = Number.isFinite(p.percentage) ? Math.max(0, Math.min(100, p.percentage)) : 0
const y = 40 - (v / 100) * 40
return `${x},${y}`
})
.join(" ")}
className="text-primary"
/>
</svg>
<div className="mt-3 flex items-center justify-between text-sm text-muted-foreground">
<div>
Latest:{" "}
<span className="font-medium text-foreground tabular-nums">
{Math.round(grades.trend[grades.trend.length - 1]?.percentage ?? 0)}%
</span>
<ChartContainer config={chartConfig} className="h-[200px] w-full">
<LineChart
data={chartData}
margin={{
left: 12,
right: 12,
top: 12,
bottom: 12,
}}
>
<CartesianGrid vertical={false} strokeDasharray="4 4" strokeOpacity={0.4} />
<XAxis
dataKey="title"
tickLine={false}
axisLine={false}
tickMargin={8}
tickFormatter={(value) => value.slice(0, 10) + (value.length > 10 ? "..." : "")}
/>
<YAxis
domain={[0, 100]}
tickLine={false}
axisLine={false}
tickFormatter={(value) => `${value}%`}
width={30}
/>
<ChartTooltip
cursor={{
stroke: "hsl(var(--muted-foreground))",
strokeWidth: 1,
strokeDasharray: "4 4",
}}
content={
<ChartTooltipContent
indicator="line"
labelKey="fullTitle"
className="w-[200px]"
/>
}
/>
<Line
dataKey="score"
type="monotone"
stroke="var(--color-score)"
strokeWidth={2}
dot={{
fill: "var(--color-score)",
r: 4,
strokeWidth: 2,
}}
activeDot={{
r: 6,
strokeWidth: 0,
}}
/>
</LineChart>
</ChartContainer>
{latestGrade && (
<div className="mt-3 flex items-center justify-between text-sm text-muted-foreground">
<div>
Latest:{" "}
<span className="font-medium text-foreground tabular-nums">
{Math.round(latestGrade.percentage)}%
</span>
</div>
<div>
Points:{" "}
<span className="font-medium text-foreground tabular-nums">
{latestGrade.score}/{latestGrade.maxScore}
</span>
</div>
</div>
<div>
Points:{" "}
<span className="font-medium text-foreground tabular-nums">
{grades.trend[grades.trend.length - 1]?.score ?? 0}/{grades.trend[grades.trend.length - 1]?.maxScore ?? 0}
</span>
</div>
</div>
)}
</div>
{!hasRecentGrades ? null : (