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
This commit is contained in:
SpecialX
2026-07-03 10:25:46 +08:00
parent dfffb61e94
commit 138b6f1b00
58 changed files with 3313 additions and 695 deletions

View File

@@ -12,6 +12,7 @@ import { getParentIdsByStudentIds } from "@/modules/parent/data-access"
import {
generateDiagnosticReport,
generateClassDiagnosticReport,
generateGradeDiagnosticReport,
publishDiagnosticReport,
deleteDiagnosticReport,
getDiagnosticReportById,
@@ -24,6 +25,7 @@ import {
import {
GenerateStudentReportSchema,
GenerateClassReportSchema,
GenerateGradeReportSchema,
PublishReportSchema,
DeleteReportSchema,
} from "./schema"
@@ -80,6 +82,32 @@ export async function generateClassReportAction(
}
}
/** v4-P2-3: 生成年级诊断报告 */
export async function generateGradeReportAction(
prevState: ActionState<string> | null,
formData: FormData
): Promise<ActionState<string>> {
try {
const ctx = await requirePermission(Permissions.DIAGNOSTIC_MANAGE)
const parsed = GenerateGradeReportSchema.safeParse({
gradeId: formData.get("gradeId"),
period: formData.get("period"),
})
if (!parsed.success) {
return { success: false, message: "Missing gradeId or period" }
}
const { gradeId, period } = parsed.data
const id = await generateGradeDiagnosticReport(gradeId, period, ctx.userId)
revalidatePath("/teacher/diagnostic")
revalidatePath("/admin/diagnostic")
return { success: true, message: "Grade diagnostic report generated", data: id }
} catch (e) {
return handleActionError(e)
}
}
/** 发布诊断报告 */
export async function publishReportAction(
prevState: ActionState<string> | null,
@@ -126,7 +154,7 @@ export async function publishReportAction(
try {
await createNotification({
userId: studentId,
type: "grade",
type: "diagnostic",
title,
content,
link,
@@ -144,7 +172,7 @@ export async function publishReportAction(
try {
await createNotification({
userId: parentId,
type: "grade",
type: "diagnostic",
title,
content: report.summary ?? "您的孩子有一份新的学情诊断报告,请查看详情。",
link: "/parent/diagnostic",
@@ -208,7 +236,7 @@ export async function exportDiagnosticReportAction(
}
const buffer = await exportDiagnosticReportToExcel({ reportId })
const filename = buildDiagnosticReportFilename(report.period)
const filename = await buildDiagnosticReportFilename(report.period)
return {
success: true,