feat(dashboard): optimize teacher dashboard ui and layout

- Refactor layout: move Needs Grading to main column, Homework to sidebar
- Enhance TeacherStats: replace static counts with actionable metrics (Needs Grading, Active Assignments, Avg Score, Submission Rate)
- Update RecentSubmissions: table view with quick grade actions and late status
- Update TeacherSchedule: vertical timeline view with scroll hints
- Update TeacherHomeworkCard: compact list view
- Integrate Recharts: add TeacherGradeTrends chart and shared chart component
- Update documentation
This commit is contained in:
SpecialX
2026-01-12 11:38:27 +08:00
parent 8577280ab2
commit ade8d4346c
17 changed files with 1383 additions and 234 deletions

View File

@@ -1,20 +1,22 @@
import Link from "next/link";
import { Card, CardContent, CardHeader, CardTitle } from "@/shared/components/ui/card";
import { Users, BookOpen, FileCheck, Calendar } from "lucide-react";
import { FileCheck, PenTool, TrendingUp, BarChart } from "lucide-react";
import { Skeleton } from "@/shared/components/ui/skeleton";
import { cn } from "@/shared/lib/utils";
interface TeacherStatsProps {
totalStudents: number;
classCount: number;
toGradeCount: number;
todayScheduleCount: number;
activeAssignmentsCount: number;
averageScore: number;
submissionRate: number;
isLoading?: boolean;
}
export function TeacherStats({
totalStudents,
classCount,
toGradeCount,
todayScheduleCount,
activeAssignmentsCount,
averageScore,
submissionRate,
isLoading = false,
}: TeacherStatsProps) {
if (isLoading) {
@@ -38,48 +40,59 @@ export function TeacherStats({
const stats = [
{
title: "Total Students",
value: String(totalStudents),
description: "Across all your classes",
icon: Users,
},
{
title: "My Classes",
value: String(classCount),
description: "Active classes you manage",
icon: BookOpen,
},
{
title: "To Grade",
title: "Needs Grading",
value: String(toGradeCount),
description: "Submitted homework waiting for grading",
description: "Submissions pending review",
icon: FileCheck,
href: "/teacher/homework/submissions?status=submitted",
highlight: toGradeCount > 0,
color: "text-amber-500",
},
{
title: "Today",
value: String(todayScheduleCount),
description: "Scheduled items today",
icon: Calendar,
title: "Active Assignments",
value: String(activeAssignmentsCount),
description: "Published and ongoing",
icon: PenTool,
href: "/teacher/homework/assignments?status=published",
color: "text-blue-500",
},
{
title: "Average Score",
value: `${Math.round(averageScore)}%`,
description: "Across recent assignments",
icon: TrendingUp,
href: "#grade-trends",
color: "text-emerald-500",
},
{
title: "Submission Rate",
value: `${Math.round(submissionRate)}%`,
description: "Overall completion rate",
icon: BarChart,
href: "#grade-trends",
color: "text-purple-500",
},
] as const;
return (
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
{stats.map((stat, i) => (
<Card key={i}>
<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">{stat.value}</div>
<p className="text-xs text-muted-foreground">
{stat.description}
</p>
</CardContent>
</Card>
<Link key={i} href={stat.href} className="block transition-transform hover:-translate-y-1">
<Card className={cn(stat.highlight && "border-amber-200 bg-amber-50/50 dark:border-amber-900 dark:bg-amber-950/20")}>
<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", stat.color)} />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">{stat.value}</div>
<p className="text-xs text-muted-foreground">
{stat.description}
</p>
</CardContent>
</Card>
</Link>
))}
</div>
);