Files
NextEdu/src/app/(dashboard)/parent/attendance/page.tsx
SpecialX 21142f9b99 feat(app): add error/loading boundaries across all dashboard routes and new routes
- Add error.tsx and loading.tsx boundaries for admin, parent, student, teacher routes

- Add admin announcements edit, audit-logs overview, curriculum-map, invitation-codes, permissions, questions, roles routes

- Add admin elective detail and components, files, course-plans, users, scheduling boundaries

- Add messages group-compose route

- Add parent course-plans, elective, grades report-card, practice routes

- Add student course-plans, elective detail, error-book dialogs, grades report-card, learning study-path, leave, schedule boundaries

- Add teacher attendance report, classes boundaries, course-plans boundaries, elective, exams analytics/edit-rich/all/create/new, grades report-card, homework boundaries, leave, lesson-plans calendar

- Add auth loading, onboarding loading, api cron
2026-07-03 10:26:25 +08:00

102 lines
3.7 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 { getTranslations } from "next-intl/server"
import { getAuthContext } from "@/shared/lib/auth-guard"
import { CalendarCheck } from "lucide-react"
import { createAttendanceReadService } from "@/modules/attendance/services/attendance-data-service"
import type { AttendanceReadService } from "@/modules/attendance/services/types"
import {
ParentChildrenDataPage,
ParentNoChildrenPage,
} from "@/modules/parent/components/parent-children-data-page"
import { ParentAttendanceWarning } from "@/modules/parent/components/parent-attendance-warning"
import { ParentAttendanceRateCard } from "@/modules/parent/components/parent-attendance-rate-card"
import { ParentAttendanceCalendar } from "@/modules/parent/components/parent-attendance-calendar"
import { ParentStudentAttendanceDetail } from "@/modules/parent/components/parent-student-attendance-detail"
import type { ParentStudentAttendanceSummary } from "@/modules/parent/types"
export const dynamic = "force-dynamic"
/**
* 将 attendance 模块的 StudentAttendanceSummary 映射为 parent 模块的视图模型。
* parent 模块仅消费自身类型,不依赖 attendance 内部类型变更P1-2 解耦)。
*/
function toParentSummary(
summary: Awaited<ReturnType<AttendanceReadService["getStudentSummary"]>>,
): ParentStudentAttendanceSummary | null {
if (!summary) return null
return {
studentId: summary.studentId,
studentName: summary.studentName,
stats: {
total: summary.stats.total,
present: summary.stats.present,
absent: summary.stats.absent,
late: summary.stats.late,
presentRate: summary.stats.presentRate,
},
recentRecords: summary.recentRecords.map((r) => ({
id: r.id,
date: r.date,
status: r.status,
reason: r.reason,
remark: r.remark,
})),
}
}
export default async function ParentAttendancePage() {
const t = await getTranslations("attendance")
const ctx = await getAuthContext()
if (ctx.dataScope.type !== "children" || ctx.dataScope.childrenIds.length === 0) {
return (
<ParentNoChildrenPage
title={t("title.parent")}
description={t("description.parent")}
icon={CalendarCheck}
emptyTitle={t("parent.noChildrenTitle")}
emptyDescription={t("parent.noChildrenDescription")}
/>
)
}
// 通过接口抽象消费 attendance 数据P1-2 修复:不再直接 import data-access
const attendanceService = createAttendanceReadService(ctx.dataScope)
// 使用 allSettled 容错:单个子女查询失败不影响其他子女展示
const results = await Promise.allSettled(
ctx.dataScope.childrenIds.map((id) => attendanceService.getStudentSummary(id)),
)
const validSummaries = results
.map((r) =>
r.status === "fulfilled" ? toParentSummary(r.value) : null,
)
.filter((s): s is ParentStudentAttendanceSummary => s !== null)
return (
<ParentChildrenDataPage
title={t("title.parent")}
description={t("parent.compareDescription")}
icon={CalendarCheck}
noRecordsTitle={t("list.empty")}
noRecordsDescription={t("parent.noRecordsDescription")}
items={validSummaries}
renderItem={(summary) => (
<div className="space-y-6">
<h3 className="text-lg font-semibold border-b pb-2">{summary.studentName}</h3>
<ParentAttendanceCalendar summary={summary} />
<ParentStudentAttendanceDetail summary={summary} />
</div>
)}
headerExtra={
validSummaries.length > 0 ? (
<div className="space-y-4">
<ParentAttendanceRateCard summaries={validSummaries} />
<ParentAttendanceWarning summaries={validSummaries} />
</div>
) : null
}
/>
)
}