refactor(modules): update existing module implementations across attendance, audit, auth, classes, course-plans, exams, files, homework, layout, proctoring, questions, scheduling, textbooks, users
- Update attendance components and data-access for record management - Update audit log views, filters, and data-access - Update auth login and register forms - Update classes actions, components, and data-access (admin, schedule, stats) - Update course-plans actions, form, list, progress, and schema - Update exams actions, AI pipeline, preview components, and hooks - Update files components (icon, list, preview, upload) and data-access - Update homework assignment form, review view, auto-save hook, and stats-service - Update layout sidebar, header, and navigation config - Update proctoring actions, anti-cheat monitor, and data-access - Update questions actions, components (dialog, actions, columns, filters), and data-access - Update scheduling actions, auto-scheduler, components, and schema - Update textbooks constants and text-selection hook - Update users class-registration, import-dialog, data-access, and user-service
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
"use server"
|
||||
|
||||
import { revalidatePath } from "next/cache"
|
||||
import { requirePermission, PermissionDeniedError } from "@/shared/lib/auth-guard"
|
||||
import { requirePermission } from "@/shared/lib/auth-guard"
|
||||
import { Permissions } from "@/shared/types/permissions"
|
||||
import type { ActionState } from "@/shared/types/action-state"
|
||||
import { handleActionError } from "@/shared/lib/action-utils"
|
||||
|
||||
import {
|
||||
CreateCoursePlanSchema,
|
||||
@@ -23,12 +24,6 @@ import {
|
||||
} from "./data-access"
|
||||
import type { CoursePlanWithItems, GetCoursePlansParams, CoursePlanListItem } from "./types"
|
||||
|
||||
const handleError = (e: unknown): ActionState<never> => {
|
||||
if (e instanceof PermissionDeniedError) return { success: false, message: e.message }
|
||||
if (e instanceof Error) return { success: false, message: e.message }
|
||||
return { success: false, message: "Unexpected error" }
|
||||
}
|
||||
|
||||
const revalidatePlanPaths = (id?: string) => {
|
||||
revalidatePath("/admin/course-plans")
|
||||
revalidatePath("/teacher/course-plans")
|
||||
@@ -72,7 +67,7 @@ export async function createCoursePlanAction(
|
||||
revalidatePlanPaths(id)
|
||||
return { success: true, message: "Course plan created", data: id }
|
||||
} catch (e) {
|
||||
return handleError(e)
|
||||
return handleActionError(e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,7 +110,7 @@ export async function updateCoursePlanAction(
|
||||
revalidatePlanPaths(id)
|
||||
return { success: true, message: "Course plan updated", data: id }
|
||||
} catch (e) {
|
||||
return handleError(e)
|
||||
return handleActionError(e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,7 +127,7 @@ export async function deleteCoursePlanAction(
|
||||
revalidatePlanPaths()
|
||||
return { success: true, message: "Course plan deleted" }
|
||||
} catch (e) {
|
||||
return handleError(e)
|
||||
return handleActionError(e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,7 +139,7 @@ export async function getCoursePlansAction(
|
||||
const data = await getCoursePlans(params)
|
||||
return { success: true, data }
|
||||
} catch (e) {
|
||||
return handleError(e)
|
||||
return handleActionError(e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,7 +152,7 @@ export async function getCoursePlanAction(
|
||||
if (!data) return { success: false, message: "Course plan not found" }
|
||||
return { success: true, data }
|
||||
} catch (e) {
|
||||
return handleError(e)
|
||||
return handleActionError(e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,7 +185,7 @@ export async function createCoursePlanItemAction(
|
||||
revalidatePlanPaths(parsed.data.planId)
|
||||
return { success: true, message: "Week plan added", data: itemId }
|
||||
} catch (e) {
|
||||
return handleError(e)
|
||||
return handleActionError(e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,7 +225,7 @@ export async function updateCoursePlanItemAction(
|
||||
revalidatePlanPaths()
|
||||
return { success: true, message: "Week plan updated", data: id }
|
||||
} catch (e) {
|
||||
return handleError(e)
|
||||
return handleActionError(e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,7 +238,7 @@ export async function deleteCoursePlanItemAction(
|
||||
revalidatePlanPaths()
|
||||
return { success: true, message: "Week plan deleted" }
|
||||
} catch (e) {
|
||||
return handleError(e)
|
||||
return handleActionError(e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,6 +258,6 @@ export async function toggleCoursePlanItemCompletedAction(
|
||||
message: completed ? "Marked as completed" : "Marked as incomplete",
|
||||
}
|
||||
} catch (e) {
|
||||
return handleError(e)
|
||||
return handleActionError(e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
subjects,
|
||||
users,
|
||||
} from "@/shared/db/schema"
|
||||
import { safeParseDate } from "@/shared/lib/action-utils"
|
||||
import type {
|
||||
CoursePlan,
|
||||
CoursePlanItem,
|
||||
@@ -203,8 +204,8 @@ export async function createCoursePlan(
|
||||
totalHours: data.totalHours,
|
||||
completedHours: 0,
|
||||
weeklyHours: data.weeklyHours,
|
||||
startDate: data.startDate ? new Date(data.startDate) : null,
|
||||
endDate: data.endDate ? new Date(data.endDate) : null,
|
||||
startDate: data.startDate ? safeParseDate(data.startDate, "开始日期") : null,
|
||||
endDate: data.endDate ? safeParseDate(data.endDate, "结束日期") : null,
|
||||
syllabus: data.syllabus,
|
||||
objectives: data.objectives,
|
||||
status: data.status,
|
||||
@@ -227,9 +228,9 @@ export async function updateCoursePlan(
|
||||
if (data.completedHours !== undefined) update.completedHours = data.completedHours
|
||||
if (data.weeklyHours !== undefined) update.weeklyHours = data.weeklyHours
|
||||
if (data.startDate !== undefined)
|
||||
update.startDate = data.startDate ? new Date(data.startDate) : null
|
||||
update.startDate = data.startDate ? safeParseDate(data.startDate, "开始日期") : null
|
||||
if (data.endDate !== undefined)
|
||||
update.endDate = data.endDate ? new Date(data.endDate) : null
|
||||
update.endDate = data.endDate ? safeParseDate(data.endDate, "结束日期") : null
|
||||
if (data.syllabus !== undefined) update.syllabus = data.syllabus
|
||||
if (data.objectives !== undefined) update.objectives = data.objectives
|
||||
if (data.status !== undefined) update.status = data.status
|
||||
@@ -273,7 +274,7 @@ export async function updateCoursePlanItem(
|
||||
if (data.notes !== undefined) update.notes = data.notes
|
||||
if (data.isCompleted !== undefined) update.isCompleted = data.isCompleted
|
||||
if (data.completedAt !== undefined)
|
||||
update.completedAt = data.completedAt ? new Date(data.completedAt) : null
|
||||
update.completedAt = data.completedAt ? safeParseDate(data.completedAt, "完成日期") : null
|
||||
|
||||
if (Object.keys(update).length === 0) return
|
||||
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import { z } from "zod"
|
||||
|
||||
const isValidDateString = (v: string | null | undefined): boolean => {
|
||||
if (v === null || v === undefined || v === "") return true
|
||||
const d = new Date(v)
|
||||
return !Number.isNaN(d.getTime())
|
||||
}
|
||||
|
||||
export const CreateCoursePlanSchema = z
|
||||
.object({
|
||||
classId: z.string().trim().min(1),
|
||||
@@ -9,8 +15,18 @@ export const CreateCoursePlanSchema = z
|
||||
semester: z.enum(["1", "2"]).optional(),
|
||||
totalHours: z.coerce.number().int().min(0).optional(),
|
||||
weeklyHours: z.coerce.number().int().min(0).optional(),
|
||||
startDate: z.string().trim().optional().nullable(),
|
||||
endDate: z.string().trim().optional().nullable(),
|
||||
startDate: z
|
||||
.string()
|
||||
.trim()
|
||||
.optional()
|
||||
.nullable()
|
||||
.refine(isValidDateString, "开始日期格式无效"),
|
||||
endDate: z
|
||||
.string()
|
||||
.trim()
|
||||
.optional()
|
||||
.nullable()
|
||||
.refine(isValidDateString, "结束日期格式无效"),
|
||||
syllabus: z.string().trim().optional().nullable(),
|
||||
objectives: z.string().trim().optional().nullable(),
|
||||
status: z.enum(["planning", "active", "completed", "paused"]).optional(),
|
||||
@@ -42,8 +58,18 @@ export const UpdateCoursePlanSchema = z
|
||||
totalHours: z.coerce.number().int().min(0).optional(),
|
||||
completedHours: z.coerce.number().int().min(0).optional(),
|
||||
weeklyHours: z.coerce.number().int().min(0).optional(),
|
||||
startDate: z.string().trim().optional().nullable(),
|
||||
endDate: z.string().trim().optional().nullable(),
|
||||
startDate: z
|
||||
.string()
|
||||
.trim()
|
||||
.optional()
|
||||
.nullable()
|
||||
.refine(isValidDateString, "开始日期格式无效"),
|
||||
endDate: z
|
||||
.string()
|
||||
.trim()
|
||||
.optional()
|
||||
.nullable()
|
||||
.refine(isValidDateString, "结束日期格式无效"),
|
||||
syllabus: z.string().trim().optional().nullable(),
|
||||
objectives: z.string().trim().optional().nullable(),
|
||||
status: z.enum(["planning", "active", "completed", "paused"]).optional(),
|
||||
@@ -116,7 +142,12 @@ export const UpdateCoursePlanItemSchema = z
|
||||
textbookChapter: z.string().trim().optional().nullable(),
|
||||
notes: z.string().trim().optional().nullable(),
|
||||
isCompleted: z.boolean().optional(),
|
||||
completedAt: z.string().trim().optional().nullable(),
|
||||
completedAt: z
|
||||
.string()
|
||||
.trim()
|
||||
.optional()
|
||||
.nullable()
|
||||
.refine(isValidDateString, "完成日期格式无效"),
|
||||
})
|
||||
.transform((v) => ({
|
||||
...v,
|
||||
|
||||
Reference in New Issue
Block a user