feat(dashboard): 新增分区 Error Boundary + Suspense 骨架屏(P2)

新增 components/dashboard-section.tsx,包含:

- DashboardSectionErrorBoundary:分区级 Error Boundary,单区块崩溃仅替换该区块不波及整页

- DashboardSectionSkeleton:5 种骨架变体(stats/card/chart/table/list),匹配不同数据区块布局

- DashboardSection:组合 Error Boundary + Suspense + 骨架屏的包装器

将 admin/teacher/student 三个仪表盘视图的每个独立数据区块用 DashboardSection 包裹,i18n 补充 sectionLoadFailed/sectionLoadFailedDesc 翻译键,同步更新架构图 004/005 文档
This commit is contained in:
SpecialX
2026-06-22 15:58:49 +08:00
parent 868ac5f9cf
commit 21c1e7a286
8 changed files with 454 additions and 167 deletions

View File

@@ -3,6 +3,7 @@ import type { TeacherDashboardMetrics } from "@/modules/dashboard/lib/dashboard-
import { getTranslations } from "next-intl/server"
import type { TeacherTodoItem } from "./teacher-todo-card"
import { DashboardSection } from "../dashboard-section"
import { TeacherClassesCard } from "./teacher-classes-card"
import { TeacherDashboardHeader } from "./teacher-dashboard-header"
import { TeacherHomeworkCard } from "./teacher-homework-card"
@@ -46,35 +47,51 @@ export async function TeacherDashboardView({ data }: TeacherDashboardViewProps)
<div className="flex h-full flex-col space-y-6 p-8">
<TeacherDashboardHeader teacherName={data.teacherName} />
<TeacherStats
toGradeCount={metrics.toGradeCount}
activeAssignmentsCount={metrics.activeAssignmentsCount}
averageScore={metrics.averageScore}
submissionRate={metrics.submissionRate}
/>
<DashboardSection variant="stats">
<TeacherStats
toGradeCount={metrics.toGradeCount}
activeAssignmentsCount={metrics.activeAssignmentsCount}
averageScore={metrics.averageScore}
submissionRate={metrics.submissionRate}
/>
</DashboardSection>
<div className="grid gap-6 lg:grid-cols-12">
{/* 移动端优先展示:今日课表 → 待办 → 待批改 */}
<div className="flex flex-col gap-6 lg:col-span-8">
<div className="lg:hidden">
<TeacherSchedule items={metrics.todayScheduleItems} />
<DashboardSection variant="card">
<TeacherSchedule items={metrics.todayScheduleItems} />
</DashboardSection>
</div>
<TeacherTodoCard items={todoItems} />
<TeacherGradeTrends trends={data.gradeTrends} />
<RecentSubmissions
submissions={metrics.submissionsToGrade}
title={t("sections.pendingGrading")}
emptyTitle={t("empty.allGraded")}
emptyDescription={t("empty.allGradedDesc")}
/>
<DashboardSection variant="card">
<TeacherTodoCard items={todoItems} />
</DashboardSection>
<DashboardSection variant="chart">
<TeacherGradeTrends trends={data.gradeTrends} />
</DashboardSection>
<DashboardSection variant="list">
<RecentSubmissions
submissions={metrics.submissionsToGrade}
title={t("sections.pendingGrading")}
emptyTitle={t("empty.allGraded")}
emptyDescription={t("empty.allGradedDesc")}
/>
</DashboardSection>
</div>
<div className="flex flex-col gap-6 lg:col-span-4">
<div className="hidden lg:block">
<TeacherSchedule items={metrics.todayScheduleItems} />
<DashboardSection variant="card">
<TeacherSchedule items={metrics.todayScheduleItems} />
</DashboardSection>
</div>
<TeacherHomeworkCard assignments={data.assignments} />
<TeacherClassesCard classes={data.classes} />
<DashboardSection variant="list">
<TeacherHomeworkCard assignments={data.assignments} />
</DashboardSection>
<DashboardSection variant="list">
<TeacherClassesCard classes={data.classes} />
</DashboardSection>
</div>
</div>
</div>