- 新增 SettingsService 接口 + Context 注入,组件层不再直接 import users/messaging actions - 新增 resolveRoleSettingsConfig 配置驱动角色路由,删除 parent/student/teacher-settings-view 冗余文件 - 新增 SettingsSectionErrorBoundary,每个 TabsContent + profile 角色概览区块均包裹 - 新增 ProfileStudentOverview/ProfileTeacherOverview 异步 Server Component + 骨架屏,支持流式渲染 - 抽取 buildStudentOverviewData 等纯函数到 lib/student-overview-data.ts,便于单元测试 - 新增 settings.json 翻译文件(zh-CN + en),所有组件改用 useTranslations/getTranslations - 重构 profile/page.tsx:i18n 适配 + Suspense 分区加载 + 业务逻辑抽离 - 同步更新架构图 004/005
94 lines
3.3 KiB
TypeScript
94 lines
3.3 KiB
TypeScript
import type { ReactElement } from "react"
|
||
import { getTranslations } from "next-intl/server"
|
||
|
||
import { StudentGradesCard } from "@/modules/dashboard/components/student-dashboard/student-grades-card"
|
||
import { StudentStatsGrid } from "@/modules/dashboard/components/student-dashboard/student-stats-grid"
|
||
import { StudentTodayScheduleCard } from "@/modules/dashboard/components/student-dashboard/student-today-schedule-card"
|
||
import { StudentUpcomingAssignmentsCard } from "@/modules/dashboard/components/student-dashboard/student-upcoming-assignments-card"
|
||
import { getStudentClasses, getStudentSchedule } from "@/modules/classes/data-access"
|
||
import { getStudentDashboardGrades, getStudentHomeworkAssignments } from "@/modules/homework/data-access"
|
||
import { buildStudentOverviewData } from "@/modules/settings/lib/student-overview-data"
|
||
import { Separator } from "@/shared/components/ui/separator"
|
||
|
||
interface ProfileStudentOverviewProps {
|
||
userId: string
|
||
}
|
||
|
||
/**
|
||
* 学生概览区块(Server Component)
|
||
*
|
||
* 独立获取学生数据并渲染,可被 Suspense + ErrorBoundary 包裹实现流式渲染与局部容错。
|
||
*/
|
||
export async function ProfileStudentOverview({
|
||
userId,
|
||
}: ProfileStudentOverviewProps): Promise<ReactElement> {
|
||
const t = await getTranslations("settings.profilePage.studentOverview")
|
||
|
||
const [classes, schedule, assignmentsAll, grades] = await Promise.all([
|
||
getStudentClasses(userId),
|
||
getStudentSchedule(userId),
|
||
getStudentHomeworkAssignments(userId),
|
||
getStudentDashboardGrades(userId),
|
||
])
|
||
|
||
const data = buildStudentOverviewData({
|
||
classes,
|
||
schedule,
|
||
assignments: assignmentsAll,
|
||
grades,
|
||
})
|
||
|
||
return (
|
||
<div className="space-y-6">
|
||
<Separator />
|
||
<div className="space-y-1">
|
||
<h2 className="text-xl font-semibold tracking-tight">{t("title")}</h2>
|
||
<div className="text-sm text-muted-foreground">{t("description")}</div>
|
||
</div>
|
||
|
||
<StudentStatsGrid
|
||
enrolledClassCount={data.enrolledClassCount}
|
||
dueSoonCount={data.dueSoonCount}
|
||
overdueCount={data.overdueCount}
|
||
gradedCount={data.gradedCount}
|
||
ranking={data.grades.ranking}
|
||
/>
|
||
|
||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||
<div className="lg:col-span-2 space-y-6">
|
||
<StudentUpcomingAssignmentsCard upcomingAssignments={data.upcomingAssignments} />
|
||
<StudentGradesCard grades={data.grades} />
|
||
</div>
|
||
<div className="space-y-6">
|
||
<StudentTodayScheduleCard items={data.todayScheduleItems} />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
/**
|
||
* 学生概览骨架屏
|
||
*/
|
||
export function ProfileStudentOverviewSkeleton(): ReactElement {
|
||
return (
|
||
<div className="space-y-6">
|
||
<Separator />
|
||
<div className="space-y-2">
|
||
<div className="h-6 w-40 animate-pulse rounded bg-muted" />
|
||
<div className="h-4 w-64 animate-pulse rounded bg-muted" />
|
||
</div>
|
||
<div className="grid grid-cols-2 gap-4 lg:grid-cols-4">
|
||
{Array.from({ length: 4 }).map((_, i) => (
|
||
<div key={i} className="h-24 animate-pulse rounded-lg bg-muted" />
|
||
))}
|
||
</div>
|
||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||
<div className="lg:col-span-2 h-64 animate-pulse rounded-lg bg-muted" />
|
||
<div className="h-64 animate-pulse rounded-lg bg-muted" />
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|