feat(app): add lesson-plans, practice, and grade dashboard routes

- Add admin/lesson-plans, parent/lesson-plans, student/lesson-plans routes

- Add student/practice and teacher/practice routes for adaptive practice

- Add management/grade/dashboard and management/grade/practice routes

- Add teacher/lesson-plans error and loading boundaries

- Update existing admin, parent, student, teacher pages with new features

- Update globals.css and proxy middleware
This commit is contained in:
SpecialX
2026-06-24 12:03:47 +08:00
parent 8c2fe14c20
commit 37d2688a28
84 changed files with 2665 additions and 661 deletions

View File

@@ -1,5 +1,6 @@
import Link from "next/link"
import { BookOpen, PenTool, Library, ArrowRight } from "lucide-react"
import { BookOpen, PenTool, Library, ArrowRight, UserX } from "lucide-react"
import { getTranslations } from "next-intl/server"
import { getStudentClasses } from "@/modules/classes/data-access"
import { getStudentHomeworkAssignments } from "@/modules/homework/data-access"
@@ -7,16 +8,16 @@ import { getCurrentStudentUser } from "@/modules/users/data-access"
import { getTextbooks } from "@/modules/textbooks/data-access"
import { Card, CardContent, CardHeader, CardTitle } from "@/shared/components/ui/card"
import { EmptyState } from "@/shared/components/ui/empty-state"
import { UserX } from "lucide-react"
export const dynamic = "force-dynamic"
export default async function StudentLearningPage() {
const t = await getTranslations("student")
const student = await getCurrentStudentUser()
if (!student) {
return (
<div className="space-y-8">
<EmptyState title="No user found" description="Create a student user to see learning." icon={UserX} />
<EmptyState title={t("learning.noUser")} description={t("learning.noUserDesc")} icon={UserX} />
</div>
)
}
@@ -40,33 +41,33 @@ export default async function StudentLearningPage() {
const cards = [
{
title: "Courses",
description: "Your enrolled classes.",
title: t("learning.courses"),
description: t("learning.coursesDesc"),
icon: BookOpen,
href: "/student/learning/courses",
stat: `${classes.length} enrolled`,
stat: t("learning.enrolled", { count: classes.length }),
},
{
title: "Assignments",
description: "Homework and practice.",
title: t("learning.assignments"),
description: t("learning.assignmentsDesc"),
icon: PenTool,
href: "/student/learning/assignments",
stat: `${pendingCount} pending${dueSoonCount > 0 ? ` · ${dueSoonCount} due soon` : ""}`,
stat: t("learning.pending", { count: pendingCount }) + (dueSoonCount > 0 ? t("learning.dueSoon", { count: dueSoonCount }) : ""),
},
{
title: "Textbooks",
description: "Browse course materials.",
title: t("learning.textbooks"),
description: t("learning.textbooksDesc"),
icon: Library,
href: "/student/learning/textbooks",
stat: `${textbooks.length} available`,
stat: t("learning.available", { count: textbooks.length }),
},
]
return (
<div className="space-y-8">
<div>
<h2 className="text-2xl font-bold tracking-tight">My Learning</h2>
<p className="text-muted-foreground">Your learning hub: courses, assignments, and textbooks.</p>
<h2 className="text-2xl font-bold tracking-tight">{t("learning.title")}</h2>
<p className="text-muted-foreground">{t("learning.description")}</p>
</div>
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">