Files
NextEdu/src/modules/dashboard/components/teacher-dashboard/teacher-dashboard-view.tsx
SpecialX 138b6f1b00 feat(dashboard,diagnostic,elective): add widgets, layout, parent dashboard, role-config, services, elective components
dashboard:

- Add comparison-badge, dashboard-notification-widget, dashboard-responsive-layout, dashboard-time-range-filter

- Add parent-dashboard components directory

- Add config, hooks, and services directories

diagnostic:

- Add role-config and services directory

elective:

- Add elective-course-detail, elective-stats-cards, parent-selection-view components

- Add data-access-settings and data-access-stats
2026-07-03 10:25:46 +08:00

113 lines
4.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { use } from "react"
import type { TeacherDashboardData } from "@/modules/dashboard/types"
import type { TeacherDashboardMetrics } from "@/modules/dashboard/lib/dashboard-utils"
import type { ActionState } from "@/shared/types/action-state"
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"
import { RecentSubmissions } from "./recent-submissions"
import { TeacherSchedule } from "./teacher-schedule"
import { TeacherStats } from "./teacher-stats"
import { TeacherGradeTrends } from "./teacher-grade-trends"
import { TeacherTodoCard } from "./teacher-todo-card"
type TeacherDashboardResult = ActionState<TeacherDashboardData & { metrics: TeacherDashboardMetrics }>
/**
* 教师仪表盘视图P2-1 流式架构)
*
* 接收未解析的 Promise用 React `use()` 消费。
* 页面外壳立即渲染,数据到达后在 DashboardSection 的 Suspense 边界内填充。
*/
export function TeacherDashboardView({ dataPromise }: { dataPromise: Promise<TeacherDashboardResult> }) {
const result = use(dataPromise)
if (!result.success || !result.data) {
throw new Error(result.message ?? "Failed to load teacher dashboard")
}
return <TeacherDashboardContent data={result.data} />
}
async function TeacherDashboardContent({ data }: { data: TeacherDashboardData & { metrics: TeacherDashboardMetrics } }) {
const t = await getTranslations("dashboard")
const { metrics } = data
// 待办聚合(使用预计算指标)
const todoItems: TeacherTodoItem[] = [
{
label: t("todo.toGrade"),
count: metrics.toGradeCount,
href: "/teacher/homework/submissions",
variant: metrics.toGradeCount > 0 ? "urgent" : "normal",
},
{
label: t("todo.todayAttendance"),
count: metrics.todayScheduleItems.length,
href: "/teacher/attendance/sheet",
variant: "info",
},
{
label: t("todo.activeAssignments"),
count: metrics.activeAssignmentsCount,
href: "/teacher/homework/assignments",
variant: "normal",
},
]
return (
<div className="flex h-full flex-col space-y-6 p-8">
<header>
<TeacherDashboardHeader teacherName={data.teacherName} />
</header>
<DashboardSection variant="stats" ariaLabel={t("sections.quickStats")}>
<TeacherStats
toGradeCount={metrics.toGradeCount}
activeAssignmentsCount={metrics.activeAssignmentsCount}
averageScore={metrics.averageScore}
submissionRate={metrics.submissionRate}
/>
</DashboardSection>
<div className="flex flex-col gap-6 lg:grid lg:grid-cols-12">
{/* 课表:移动端首位,桌面端右上 — 仅渲染一次P2-9 修复,原为双实例) */}
<div className="order-1 lg:col-start-9 lg:col-span-4 lg:row-start-1">
<DashboardSection variant="card" ariaLabel={t("sections.todaySchedule")}>
<TeacherSchedule items={metrics.todayScheduleItems} />
</DashboardSection>
</div>
<section aria-label={t("sections.pendingGrading")} className="flex flex-col gap-6 order-2 lg:col-start-1 lg:col-span-8 lg:row-start-1 lg:row-span-2">
<DashboardSection variant="card" ariaLabel={t("todo.title")}>
<TeacherTodoCard items={todoItems} />
</DashboardSection>
<DashboardSection variant="chart" ariaLabel={t("sections.gradeTrends")}>
<TeacherGradeTrends trends={data.gradeTrends} />
</DashboardSection>
<DashboardSection variant="list" ariaLabel={t("sections.recentSubmissions")}>
<RecentSubmissions
submissions={metrics.submissionsToGrade}
title={t("sections.pendingGrading")}
emptyTitle={t("empty.allGraded")}
emptyDescription={t("empty.allGradedDesc")}
/>
</DashboardSection>
</section>
<aside aria-label={t("sections.myClasses")} className="flex flex-col gap-6 order-3 lg:col-start-9 lg:col-span-4 lg:row-start-2">
<DashboardSection variant="list" ariaLabel={t("sections.homework")}>
<TeacherHomeworkCard assignments={data.assignments} />
</DashboardSection>
<DashboardSection variant="list" ariaLabel={t("sections.myClasses")}>
<TeacherClassesCard classes={data.classes} />
</DashboardSection>
</aside>
</div>
</div>
)
}