feat(classes): optimize teacher dashboard ui and implement grade management
This commit is contained in:
@@ -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" }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user