feat(app): add error/loading boundaries and update dashboard routes

- Add error.tsx and loading.tsx boundaries for admin, parent, student, teacher routes

- Add dashboard-error-fallback and dashboard-loading-skeleton components

- Add student/learning page, parent/leave routes, teacher textbook components

- Update existing app routes across auth, dashboard, and API endpoints

- Update proxy middleware and next-auth type declarations
This commit is contained in:
SpecialX
2026-06-23 17:38:28 +08:00
parent c4d3433cc9
commit 1a9377222c
90 changed files with 1690 additions and 741 deletions

View File

@@ -1,4 +1,4 @@
import Link from "next/link"
import Link from "next/link"
import type { Metadata } from "next"
import type { JSX } from "react"
import { BarChart3 } from "lucide-react"
@@ -7,13 +7,16 @@ import { requirePermission } from "@/shared/lib/auth-guard"
import { Permissions } from "@/shared/types/permissions"
import { getGrades } from "@/modules/school/data-access"
import { getGradeHomeworkInsights } from "@/modules/classes/data-access"
import { getSchoolWideGradeSummary } from "@/modules/grades/data-access-analytics"
import { SchoolWideSummaryCard } from "@/modules/grades/components/school-wide-summary-card"
import { EmptyState } from "@/shared/components/ui/empty-state"
import { StatCard } from "@/shared/components/ui/stat-card"
import { Card, CardContent, CardHeader, CardTitle } from "@/shared/components/ui/card"
import { Badge } from "@/shared/components/ui/badge"
import { Button } from "@/shared/components/ui/button"
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/shared/components/ui/table"
import { formatDate, formatNumber, getSearchParam, type SearchParams } from "@/shared/lib/utils"
import { formatDate, formatNumber } from "@/shared/lib/utils"
import { getParam, type SearchParams } from "@/shared/lib/search-params"
export const metadata: Metadata = {
title: "年级作业洞察 - Next_Edu",
@@ -27,15 +30,17 @@ export default async function AdminGradeInsightsPage({
}: {
searchParams: Promise<SearchParams>
}): Promise<JSX.Element> {
await requirePermission(Permissions.SCHOOL_MANAGE)
const ctx = await requirePermission(Permissions.SCHOOL_MANAGE)
const params = await searchParams
const gradeId = getSearchParam(params, "gradeId")
const gradeId = getParam(params, "gradeId")
const selected = gradeId && gradeId !== "all" ? gradeId : ""
// gradesinsights 无数据依赖,并行查询
const [grades, insights] = await Promise.all([
// gradesinsights、全校汇总无数据依赖,并行查询
const [grades, insights, schoolWideSummary] = await Promise.all([
getGrades(),
selected ? getGradeHomeworkInsights({ gradeId: selected, limit: 50 }) : Promise.resolve(null),
// v3-P2-9管理员全校成绩汇总视图
getSchoolWideGradeSummary(ctx.dataScope),
])
return (
@@ -50,6 +55,11 @@ export default async function AdminGradeInsightsPage({
</Button>
</div>
{/* v3-P2-9: 全校成绩汇总视图 */}
{schoolWideSummary.grades.length > 0 && (
<SchoolWideSummaryCard summary={schoolWideSummary} />
)}
<Card className="shadow-none">
<CardHeader className="flex flex-row items-center justify-between space-y-0">
<CardTitle className="text-base"></CardTitle>