feat(classes): optimize teacher dashboard ui and implement grade management

This commit is contained in:
SpecialX
2026-01-14 13:59:11 +08:00
parent ade8d4346c
commit 9bfc621d3f
104 changed files with 12793 additions and 2309 deletions

View File

@@ -5,8 +5,9 @@ import { ActionState } from "@/shared/types/action-state"
import { z } from "zod"
import { createId } from "@paralleldrive/cuid2"
import { db } from "@/shared/db"
import { exams, examQuestions } from "@/shared/db/schema"
import { exams, examQuestions, subjects, grades } from "@/shared/db/schema"
import { eq } from "drizzle-orm"
import { omitScheduledAtFromDescription } from "./data-access"
const ExamCreateSchema = z.object({
title: z.string().min(1),
@@ -56,9 +57,17 @@ export async function createExamAction(
const examId = createId()
const scheduled = input.scheduledAt || undefined
// Retrieve names for JSON description (to maintain compatibility)
const subjectRecord = await db.query.subjects.findFirst({
where: eq(subjects.id, input.subject),
})
const gradeRecord = await db.query.grades.findFirst({
where: eq(grades.id, input.grade),
})
const meta = {
subject: input.subject,
grade: input.grade,
subject: subjectRecord?.name ?? input.subject,
grade: gradeRecord?.name ?? input.grade,
difficulty: input.difficulty,
totalScore: input.totalScore,
durationMin: input.durationMin,
@@ -71,11 +80,14 @@ export async function createExamAction(
id: examId,
title: input.title,
description: JSON.stringify(meta),
creatorId: user?.id ?? "user_teacher_123",
creatorId: user?.id ?? "user_teacher_math",
subjectId: input.subject,
gradeId: input.grade,
startTime: scheduled ? new Date(scheduled) : null,
status: "draft",
})
} catch {
} catch (error) {
console.error("Failed to create exam:", error)
return {
success: false,
message: "Database error: Failed to create exam",
@@ -215,19 +227,6 @@ const ExamDuplicateSchema = z.object({
examId: z.string().min(1),
})
const omitScheduledAtFromDescription = (description: string | null) => {
if (!description) return null
try {
const parsed: unknown = JSON.parse(description)
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return description
const meta = parsed as Record<string, unknown>
if ("scheduledAt" in meta) delete meta.scheduledAt
return JSON.stringify(meta)
} catch {
return description
}
}
export async function duplicateExamAction(
prevState: ActionState<string> | null,
formData: FormData
@@ -271,7 +270,7 @@ export async function duplicateExamAction(
id: newExamId,
title: `${source.title} (Copy)`,
description: omitScheduledAtFromDescription(source.description),
creatorId: user?.id ?? "user_teacher_123",
creatorId: user?.id ?? "user_teacher_math",
startTime: null,
endTime: null,
status: "draft",
@@ -305,6 +304,78 @@ export async function duplicateExamAction(
}
}
async function getCurrentUser() {
return { id: "user_teacher_123", role: "teacher" }
export async function getExamPreviewAction(examId: string) {
try {
const exam = await db.query.exams.findFirst({
where: eq(exams.id, examId),
with: {
questions: {
orderBy: (examQuestions, { asc }) => [asc(examQuestions.order)],
with: {
question: true
}
}
}
})
if (!exam) {
return { success: false, message: "Exam not found" }
}
// Extract questions from the relation
const questions = exam.questions.map(eq => eq.question)
return {
success: true,
data: {
structure: exam.structure,
questions: questions
}
}
} catch (error) {
console.error(error)
return { success: false, message: "Failed to load exam preview" }
}
}
export async function getSubjectsAction(): Promise<ActionState<{ id: string; name: string }[]>> {
try {
const allSubjects = await db.query.subjects.findMany({
orderBy: (subjects, { asc }) => [asc(subjects.order), asc(subjects.name)],
})
return {
success: true,
data: allSubjects.map((s) => ({ id: s.id, name: s.name })),
}
} catch (error) {
console.error("Failed to fetch subjects:", error)
return {
success: false,
message: "Failed to load subjects",
}
}
}
export async function getGradesAction(): Promise<ActionState<{ id: string; name: string }[]>> {
try {
const allGrades = await db.query.grades.findMany({
orderBy: (grades, { asc }) => [asc(grades.order), asc(grades.name)],
})
return {
success: true,
data: allGrades.map((g) => ({ id: g.id, name: g.name })),
}
} catch (error) {
console.error("Failed to fetch grades:", error)
return {
success: false,
message: "Failed to load grades",
}
}
}
async function getCurrentUser() {
return { id: "user_teacher_math", role: "teacher" }
}