P0-1/P0-2: 删除 grade-management 死模块,年级 CRUD 统一由 school 模块负责 P0-3: classes/actions.ts 从 974 行拆分为 6 个职责文件 + barrel re-export P0-5: 13 个页面 i18n 全量接入(grades/departments/academic-year/classes/insights) P1-1: 角色硬编码改为 hasAdminScope/hasTeacherScope/hasStudentScope 基于 dataScope.type P1-3: 新增 SchoolErrorBoundary + SchoolListSkeleton/SchoolCardSkeleton,4 个页面包裹 Error Boundary P1-4: classes/types.ts 跨领域类型添加归属决策注释 P1-5: schools-view.tsx 拆分为组合模式(SchoolFormDialog + SchoolDeleteDialog + SchoolListToolbar) P1-6: 新增 getSchoolsForUser/getGradesForUser 权限感知查询函数 P2-1: 抽取 useSchoolData hook,对话框状态管理与 UI 分离 同步更新架构图文档 004/005
152 lines
4.9 KiB
TypeScript
152 lines
4.9 KiB
TypeScript
"use server"
|
|
|
|
import { revalidatePath } from "next/cache"
|
|
import { requirePermission, PermissionDeniedError } from "@/shared/lib/auth-guard"
|
|
import { Permissions } from "@/shared/types/permissions"
|
|
|
|
import type { ActionState } from "@/shared/types/action-state"
|
|
import {
|
|
createAdminClass,
|
|
deleteAdminClass,
|
|
setClassSubjectTeachers,
|
|
updateAdminClass,
|
|
} from "./data-access"
|
|
import {
|
|
CreateAdminClassSchema,
|
|
UpdateAdminClassSchema,
|
|
DeleteAdminClassSchema,
|
|
} from "./schema"
|
|
import { parseSubjectTeachers } from "./actions-shared"
|
|
|
|
export async function createAdminClassAction(
|
|
prevState: ActionState<string> | undefined,
|
|
formData: FormData
|
|
): Promise<ActionState<string>> {
|
|
try {
|
|
await requirePermission(Permissions.CLASS_CREATE)
|
|
|
|
const parsed = CreateAdminClassSchema.safeParse({
|
|
name: formData.get("name"),
|
|
grade: formData.get("grade"),
|
|
teacherId: formData.get("teacherId"),
|
|
schoolName: formData.get("schoolName"),
|
|
schoolId: formData.get("schoolId"),
|
|
gradeId: formData.get("gradeId"),
|
|
homeroom: formData.get("homeroom"),
|
|
room: formData.get("room"),
|
|
})
|
|
if (!parsed.success) {
|
|
return { success: false, message: "Class name, grade and teacher are required" }
|
|
}
|
|
|
|
const { name, grade, teacherId, schoolName, schoolId, gradeId, homeroom, room } = parsed.data
|
|
|
|
try {
|
|
const id = await createAdminClass({
|
|
schoolName: schoolName ?? null,
|
|
schoolId: schoolId ?? null,
|
|
name,
|
|
grade,
|
|
gradeId: gradeId ?? null,
|
|
teacherId,
|
|
homeroom: homeroom ?? null,
|
|
room: room ?? null,
|
|
})
|
|
revalidatePath("/admin/school/classes")
|
|
revalidatePath("/teacher/classes/my")
|
|
revalidatePath("/teacher/classes/students")
|
|
revalidatePath("/teacher/classes/schedule")
|
|
return { success: true, message: "Class created successfully", data: id }
|
|
} catch (error) {
|
|
return { success: false, message: error instanceof Error ? error.message : "Failed to create class" }
|
|
}
|
|
} catch (e) {
|
|
if (e instanceof PermissionDeniedError) return { success: false, message: e.message }
|
|
throw e
|
|
}
|
|
}
|
|
|
|
export async function updateAdminClassAction(
|
|
classId: string,
|
|
prevState: ActionState | undefined,
|
|
formData: FormData
|
|
): Promise<ActionState> {
|
|
try {
|
|
await requirePermission(Permissions.CLASS_UPDATE)
|
|
|
|
const parsed = UpdateAdminClassSchema.safeParse({
|
|
classId,
|
|
schoolName: formData.get("schoolName"),
|
|
schoolId: formData.get("schoolId"),
|
|
name: formData.get("name"),
|
|
grade: formData.get("grade"),
|
|
gradeId: formData.get("gradeId"),
|
|
teacherId: formData.get("teacherId"),
|
|
homeroom: formData.get("homeroom"),
|
|
room: formData.get("room"),
|
|
})
|
|
if (!parsed.success) {
|
|
return { success: false, message: "Missing class id" }
|
|
}
|
|
|
|
const { classId: validatedClassId, schoolName, schoolId, name, grade, gradeId, teacherId, homeroom, room } = parsed.data
|
|
const subjectTeachers = parseSubjectTeachers(formData.get("subjectTeachers") as string | null)
|
|
|
|
try {
|
|
await updateAdminClass(validatedClassId, {
|
|
schoolName: schoolName ?? undefined,
|
|
schoolId: schoolId ?? undefined,
|
|
name: name ?? undefined,
|
|
grade: grade ?? undefined,
|
|
gradeId: gradeId ?? undefined,
|
|
teacherId: teacherId ?? undefined,
|
|
homeroom: homeroom ?? undefined,
|
|
room: room ?? undefined,
|
|
})
|
|
|
|
if (subjectTeachers) {
|
|
await setClassSubjectTeachers({
|
|
classId: validatedClassId,
|
|
assignments: subjectTeachers,
|
|
})
|
|
}
|
|
|
|
revalidatePath("/admin/school/classes")
|
|
revalidatePath("/teacher/classes/my")
|
|
revalidatePath("/teacher/classes/students")
|
|
revalidatePath("/teacher/classes/schedule")
|
|
return { success: true, message: "Class updated successfully" }
|
|
} catch (error) {
|
|
return { success: false, message: error instanceof Error ? error.message : "Failed to update class" }
|
|
}
|
|
} catch (e) {
|
|
if (e instanceof PermissionDeniedError) return { success: false, message: e.message }
|
|
throw e
|
|
}
|
|
}
|
|
|
|
export async function deleteAdminClassAction(classId: string): Promise<ActionState> {
|
|
try {
|
|
await requirePermission(Permissions.CLASS_DELETE)
|
|
|
|
const parsed = DeleteAdminClassSchema.safeParse({ classId })
|
|
if (!parsed.success) {
|
|
return { success: false, message: "Missing class id" }
|
|
}
|
|
|
|
try {
|
|
await deleteAdminClass(parsed.data.classId)
|
|
revalidatePath("/admin/school/classes")
|
|
revalidatePath("/teacher/classes/my")
|
|
revalidatePath("/teacher/classes/students")
|
|
revalidatePath("/teacher/classes/schedule")
|
|
return { success: true, message: "Class deleted successfully" }
|
|
} catch (error) {
|
|
return { success: false, message: error instanceof Error ? error.message : "Failed to delete class" }
|
|
}
|
|
} catch (e) {
|
|
if (e instanceof PermissionDeniedError) return { success: false, message: e.message }
|
|
throw e
|
|
}
|
|
}
|