"use server"; import { revalidatePath } from "next/cache" import { and, eq, sql } from "drizzle-orm" import { auth } from "@/auth" import { db } from "@/shared/db" import { grades } from "@/shared/db/schema" import type { ActionState } from "@/shared/types/action-state" import { createAdminClass, createClassScheduleItem, createTeacherClass, deleteAdminClass, deleteClassScheduleItem, deleteTeacherClass, enrollStudentByEmail, enrollStudentByInvitationCode, ensureClassInvitationCode, regenerateClassInvitationCode, setClassSubjectTeachers, setStudentEnrollmentStatus, updateAdminClass, updateClassScheduleItem, updateTeacherClass, } from "./data-access" import { DEFAULT_CLASS_SUBJECTS, type ClassSubject } from "./types" const isClassSubject = (v: string): v is ClassSubject => DEFAULT_CLASS_SUBJECTS.includes(v as ClassSubject) export async function createTeacherClassAction( prevState: ActionState | null, formData: FormData ): Promise> { const schoolName = formData.get("schoolName") const schoolId = formData.get("schoolId") const name = formData.get("name") const grade = formData.get("grade") const gradeId = formData.get("gradeId") const homeroom = formData.get("homeroom") const room = formData.get("room") if (typeof name !== "string" || name.trim().length === 0) { return { success: false, message: "Class name is required" } } if (typeof grade !== "string" || grade.trim().length === 0) { return { success: false, message: "Grade is required" } } const session = await auth() if (!session?.user) return { success: false, message: "Unauthorized" } const role = String(session.user.role ?? "") if (role !== "admin") { const userId = String(session.user.id ?? "").trim() if (!userId) return { success: false, message: "Unauthorized" } const normalizedGradeId = typeof gradeId === "string" ? gradeId.trim() : "" const normalizedGradeName = grade.trim().toLowerCase() const where = normalizedGradeId ? and(eq(grades.id, normalizedGradeId), eq(grades.gradeHeadId, userId)) : and(eq(grades.gradeHeadId, userId), sql`LOWER(${grades.name}) = ${normalizedGradeName}`) const [ownedGrade] = await db.select({ id: grades.id }).from(grades).where(where).limit(1) if (!ownedGrade) { return { success: false, message: "Only admins and grade heads can create classes" } } } try { const id = await createTeacherClass({ schoolName: typeof schoolName === "string" ? schoolName : null, schoolId: typeof schoolId === "string" ? schoolId : null, name, grade, gradeId: typeof gradeId === "string" ? gradeId : null, homeroom: typeof homeroom === "string" ? homeroom : null, room: typeof room === "string" ? room : null, }) 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" } } } export async function updateTeacherClassAction( classId: string, prevState: ActionState | null, formData: FormData ): Promise { const schoolName = formData.get("schoolName") const schoolId = formData.get("schoolId") const name = formData.get("name") const grade = formData.get("grade") const gradeId = formData.get("gradeId") const homeroom = formData.get("homeroom") const room = formData.get("room") if (typeof classId !== "string" || classId.trim().length === 0) { return { success: false, message: "Missing class id" } } try { await updateTeacherClass(classId, { schoolName: typeof schoolName === "string" ? schoolName : undefined, schoolId: typeof schoolId === "string" ? schoolId : undefined, name: typeof name === "string" ? name : undefined, grade: typeof grade === "string" ? grade : undefined, gradeId: typeof gradeId === "string" ? gradeId : undefined, homeroom: typeof homeroom === "string" ? homeroom : undefined, room: typeof room === "string" ? room : undefined, }) 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" } } } export async function deleteTeacherClassAction(classId: string): Promise { if (typeof classId !== "string" || classId.trim().length === 0) { return { success: false, message: "Missing class id" } } try { await deleteTeacherClass(classId) 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" } } } export async function enrollStudentByEmailAction( classId: string, prevState: ActionState | null, formData: FormData ): Promise { const email = formData.get("email") if (typeof classId !== "string" || classId.trim().length === 0) { return { success: false, message: "Please select a class" } } if (typeof email !== "string" || email.trim().length === 0) { return { success: false, message: "Student email is required" } } try { await enrollStudentByEmail(classId, email) revalidatePath("/teacher/classes/students") revalidatePath("/teacher/classes/my") return { success: true, message: "Student added successfully" } } catch (error) { return { success: false, message: error instanceof Error ? error.message : "Failed to add student" } } } export async function joinClassByInvitationCodeAction( prevState: ActionState<{ classId: string }> | null, formData: FormData ): Promise> { const code = formData.get("code") if (typeof code !== "string" || code.trim().length === 0) { return { success: false, message: "Invitation code is required" } } const session = await auth() if (!session?.user?.id || String(session.user.role ?? "") !== "student") { return { success: false, message: "Unauthorized" } } try { const classId = await enrollStudentByInvitationCode(session.user.id, code) revalidatePath("/student/learning/courses") revalidatePath("/student/schedule") revalidatePath("/profile") return { success: true, message: "Joined class successfully", data: { classId } } } catch (error) { return { success: false, message: error instanceof Error ? error.message : "Failed to join class" } } } export async function ensureClassInvitationCodeAction(classId: string): Promise> { if (typeof classId !== "string" || classId.trim().length === 0) { return { success: false, message: "Missing class id" } } try { const code = await ensureClassInvitationCode(classId) revalidatePath("/teacher/classes/my") revalidatePath(`/teacher/classes/my/${encodeURIComponent(classId)}`) return { success: true, message: "Invitation code ready", data: { code } } } catch (error) { return { success: false, message: error instanceof Error ? error.message : "Failed to generate code" } } } export async function regenerateClassInvitationCodeAction(classId: string): Promise> { if (typeof classId !== "string" || classId.trim().length === 0) { return { success: false, message: "Missing class id" } } try { const code = await regenerateClassInvitationCode(classId) revalidatePath("/teacher/classes/my") revalidatePath(`/teacher/classes/my/${encodeURIComponent(classId)}`) return { success: true, message: "Invitation code updated", data: { code } } } catch (error) { return { success: false, message: error instanceof Error ? error.message : "Failed to regenerate code" } } } export async function setStudentEnrollmentStatusAction( classId: string, studentId: string, status: "active" | "inactive" ): Promise { if (!classId?.trim() || !studentId?.trim()) { return { success: false, message: "Missing enrollment info" } } try { await setStudentEnrollmentStatus(classId, studentId, status) revalidatePath("/teacher/classes/students") revalidatePath("/teacher/classes/my") return { success: true, message: "Student updated successfully" } } catch (error) { return { success: false, message: error instanceof Error ? error.message : "Failed to update student" } } } export async function createClassScheduleItemAction( prevState: ActionState | null, formData: FormData ): Promise> { const classId = formData.get("classId") const weekday = formData.get("weekday") const startTime = formData.get("startTime") const endTime = formData.get("endTime") const course = formData.get("course") const location = formData.get("location") if (typeof classId !== "string" || classId.trim().length === 0) { return { success: false, message: "Please select a class" } } if (typeof weekday !== "string" || weekday.trim().length === 0) { return { success: false, message: "Weekday is required" } } const weekdayNum = Number(weekday) if (!Number.isInteger(weekdayNum) || weekdayNum < 1 || weekdayNum > 7) { return { success: false, message: "Invalid weekday" } } if (typeof course !== "string" || course.trim().length === 0) { return { success: false, message: "Course is required" } } if (typeof startTime !== "string" || typeof endTime !== "string") { return { success: false, message: "Time is required" } } try { const id = await createClassScheduleItem({ classId, weekday: weekdayNum as 1 | 2 | 3 | 4 | 5 | 6 | 7, startTime, endTime, course, location: typeof location === "string" ? location : null, }) revalidatePath("/teacher/classes/schedule") return { success: true, message: "Schedule item created successfully", data: id } } catch (error) { return { success: false, message: error instanceof Error ? error.message : "Failed to create schedule item" } } } export async function updateClassScheduleItemAction( scheduleId: string, prevState: ActionState | null, formData: FormData ): Promise { const classId = formData.get("classId") const weekday = formData.get("weekday") const startTime = formData.get("startTime") const endTime = formData.get("endTime") const course = formData.get("course") const location = formData.get("location") if (typeof scheduleId !== "string" || scheduleId.trim().length === 0) { return { success: false, message: "Missing schedule id" } } const weekdayNum = typeof weekday === "string" && weekday.trim().length > 0 ? Number(weekday) : undefined if (weekdayNum !== undefined && (!Number.isInteger(weekdayNum) || weekdayNum < 1 || weekdayNum > 7)) { return { success: false, message: "Invalid weekday" } } try { await updateClassScheduleItem(scheduleId, { classId: typeof classId === "string" ? classId : undefined, weekday: weekdayNum as 1 | 2 | 3 | 4 | 5 | 6 | 7 | undefined, startTime: typeof startTime === "string" ? startTime : undefined, endTime: typeof endTime === "string" ? endTime : undefined, course: typeof course === "string" ? course : undefined, location: typeof location === "string" ? location : undefined, }) revalidatePath("/teacher/classes/schedule") return { success: true, message: "Schedule item updated successfully" } } catch (error) { return { success: false, message: error instanceof Error ? error.message : "Failed to update schedule item" } } } export async function deleteClassScheduleItemAction(scheduleId: string): Promise { if (typeof scheduleId !== "string" || scheduleId.trim().length === 0) { return { success: false, message: "Missing schedule id" } } try { await deleteClassScheduleItem(scheduleId) revalidatePath("/teacher/classes/schedule") return { success: true, message: "Schedule item deleted successfully" } } catch (error) { return { success: false, message: error instanceof Error ? error.message : "Failed to delete schedule item" } } } export async function createAdminClassAction( prevState: ActionState | undefined, formData: FormData ): Promise> { const session = await auth() if (!session?.user?.id || String(session.user.role ?? "") !== "admin") { return { success: false, message: "Unauthorized" } } const schoolName = formData.get("schoolName") const schoolId = formData.get("schoolId") const name = formData.get("name") const grade = formData.get("grade") const gradeId = formData.get("gradeId") const teacherId = formData.get("teacherId") const homeroom = formData.get("homeroom") const room = formData.get("room") if (typeof name !== "string" || name.trim().length === 0) { return { success: false, message: "Class name is required" } } if (typeof grade !== "string" || grade.trim().length === 0) { return { success: false, message: "Grade is required" } } if (typeof teacherId !== "string" || teacherId.trim().length === 0) { return { success: false, message: "Teacher is required" } } try { const id = await createAdminClass({ schoolName: typeof schoolName === "string" ? schoolName : null, schoolId: typeof schoolId === "string" ? schoolId : null, name, grade, gradeId: typeof gradeId === "string" ? gradeId : null, teacherId, homeroom: typeof homeroom === "string" ? homeroom : null, room: typeof room === "string" ? 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" } } } export async function updateAdminClassAction( classId: string, prevState: ActionState | undefined, formData: FormData ): Promise { const schoolName = formData.get("schoolName") const schoolId = formData.get("schoolId") const name = formData.get("name") const grade = formData.get("grade") const gradeId = formData.get("gradeId") const teacherId = formData.get("teacherId") const homeroom = formData.get("homeroom") const room = formData.get("room") const subjectTeachers = formData.get("subjectTeachers") if (typeof classId !== "string" || classId.trim().length === 0) { return { success: false, message: "Missing class id" } } try { await updateAdminClass(classId, { schoolName: typeof schoolName === "string" ? schoolName : undefined, schoolId: typeof schoolId === "string" ? schoolId : undefined, name: typeof name === "string" ? name : undefined, grade: typeof grade === "string" ? grade : undefined, gradeId: typeof gradeId === "string" ? gradeId : undefined, teacherId: typeof teacherId === "string" ? teacherId : undefined, homeroom: typeof homeroom === "string" ? homeroom : undefined, room: typeof room === "string" ? room : undefined, }) if (typeof subjectTeachers === "string" && subjectTeachers.trim().length > 0) { const parsed = JSON.parse(subjectTeachers) as unknown if (!Array.isArray(parsed)) throw new Error("Invalid subject teachers") await setClassSubjectTeachers({ classId, assignments: parsed.flatMap((item) => { if (!item || typeof item !== "object") return [] const subject = (item as { subject?: unknown }).subject const teacherId = (item as { teacherId?: unknown }).teacherId if (typeof subject !== "string" || !isClassSubject(subject)) return [] if (teacherId === null || typeof teacherId === "undefined") { return [{ subject, teacherId: null }] } if (typeof teacherId !== "string") return [] const trimmed = teacherId.trim() return [{ subject, teacherId: trimmed.length > 0 ? trimmed : null }] }), }) } 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" } } } export async function deleteAdminClassAction(classId: string): Promise { if (typeof classId !== "string" || classId.trim().length === 0) { return { success: false, message: "Missing class id" } } try { await deleteAdminClass(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" } } }