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,12 +1,17 @@
import { BookOpen, CheckCircle2, PenTool, TriangleAlert } from "lucide-react"
import Link from "next/link"
import { BookOpen, PenTool, TriangleAlert, Trophy, TrendingUp } from "lucide-react"
import { Card, CardContent, CardHeader, CardTitle } from "@/shared/components/ui/card"
import { cn } from "@/shared/lib/utils"
import type { StudentRanking } from "@/modules/homework/types"
type Stat = {
title: string
value: string
description: string
icon: typeof BookOpen
href: string
color?: string
}
export function StudentStatsGrid({
@@ -14,52 +19,64 @@ export function StudentStatsGrid({
dueSoonCount,
overdueCount,
gradedCount,
ranking,
}: {
enrolledClassCount: number
dueSoonCount: number
overdueCount: number
gradedCount: number
ranking: StudentRanking | null
}) {
const stats: readonly Stat[] = [
const stats: Stat[] = [
{
title: "My Classes",
value: String(enrolledClassCount),
description: "Enrolled classes",
icon: BookOpen,
title: "Average Score",
value: ranking ? `${Math.round(ranking.percentage)}%` : "-",
description: ranking ? "Overall performance" : "No grades yet",
icon: TrendingUp,
href: "/student/learning/assignments",
color: "text-blue-500",
},
{
title: "Class Rank",
value: ranking ? `${ranking.rank}/${ranking.classSize}` : "-",
description: ranking ? "Current position" : "No ranking yet",
icon: Trophy,
href: "/student/learning/assignments",
color: "text-purple-500",
},
{
title: "Due Soon",
value: String(dueSoonCount),
description: "Next 7 days",
icon: PenTool,
href: "/student/learning/assignments",
color: dueSoonCount > 0 ? "text-orange-500" : undefined,
},
{
title: "Overdue",
value: String(overdueCount),
description: "Needs attention",
icon: TriangleAlert,
},
{
title: "Graded",
value: String(gradedCount),
description: "With score",
icon: CheckCircle2,
href: "/student/learning/assignments",
color: overdueCount > 0 ? "text-red-500" : undefined,
},
]
return (
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
{stats.map((stat) => (
<Card key={stat.title}>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">{stat.title}</CardTitle>
<stat.icon className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold tabular-nums">{stat.value}</div>
<div className="text-xs text-muted-foreground">{stat.description}</div>
</CardContent>
</Card>
<Link key={stat.title} href={stat.href}>
<Card className="hover:bg-muted/50 transition-colors cursor-pointer h-full">
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">{stat.title}</CardTitle>
<stat.icon className={cn("h-4 w-4 text-muted-foreground", stat.color)} />
</CardHeader>
<CardContent>
<div className={cn("text-2xl font-bold tabular-nums", stat.color)}>{stat.value}</div>
<div className="text-xs text-muted-foreground">{stat.description}</div>
</CardContent>
</Card>
</Link>
))}
</div>
)