Files
NextEdu/src/modules/classes/components/grade-classes-view.tsx
SpecialX dfffb61e94 feat(homework,classes,course-plans): add scans, student data, take confirm, error boundaries, dialogs, hooks, calendar
homework:

- Add data-access-scans, data-access-student, data-access-utils, data-access-exam-cross

- Add excellent-submissions, homework-take-confirm-dialog, homework-take-sidebar components

classes:

- Add class-delete-dialog, class-error-boundary, class-form-dialog, class-form-utils

- Add class-list-table, class-list-toolbar, class-skeleton

- Add schedule-create-dialog, schedule-delete-dialog, schedule-edit-dialog, schedule-utils

- Add data-access-teacher and hooks directory

course-plans:

- Add course-plan-calendar, sortable-week-row, template-picker-dialog components

- Add lib directory
2026-07-03 10:25:35 +08:00

153 lines
4.5 KiB
TypeScript

"use client"
import { useMemo } from "react"
import { toast } from "sonner"
import { useRouter } from "next/navigation"
import { useTranslations } from "next-intl"
import type { AdminClassListItem, TeacherOption } from "../types"
import { createGradeClassAction, deleteGradeClassAction, updateGradeClassAction } from "../actions"
import { useClassData } from "../hooks/use-class-data"
import { ClassDeleteDialog } from "./class-delete-dialog"
import { ClassFormDialog } from "./class-form-dialog"
import { ClassListTable } from "./class-list-table"
import { ClassListToolbar } from "./class-list-toolbar"
export function GradeClassesClient({
classes,
teachers,
managedGrades,
}: {
classes: AdminClassListItem[]
teachers: TeacherOption[]
managedGrades: { id: string; name: string; schoolId: string; schoolName: string | null }[]
}) {
const t = useTranslations("classes")
const router = useRouter()
const defaultTeacherId = useMemo(() => teachers[0]?.id ?? "", [teachers])
const defaultGradeId = useMemo(() => managedGrades[0]?.id ?? "", [managedGrades])
const data = useClassData({ defaultTeacherId, defaultGradeId })
const handleCreate = async (formData: FormData): Promise<void> => {
data.setIsWorking(true)
try {
const res = await createGradeClassAction(undefined, formData)
if (res.success) {
toast.success(res.message)
data.setCreateOpen(false)
router.refresh()
} else {
toast.error(res.message || t("list.failedCreate"))
}
} catch {
toast.error(t("list.failedCreate"))
} finally {
data.setIsWorking(false)
}
}
const handleUpdate = async (formData: FormData): Promise<void> => {
if (!data.editItem) return
data.setIsWorking(true)
try {
const res = await updateGradeClassAction(data.editItem.id, undefined, formData)
if (res.success) {
toast.success(res.message)
data.setEditItem(null)
router.refresh()
} else {
toast.error(res.message || t("list.failedUpdate"))
}
} catch {
toast.error(t("list.failedUpdate"))
} finally {
data.setIsWorking(false)
}
}
const handleDelete = async (): Promise<void> => {
if (!data.deleteItem) return
data.setIsWorking(true)
try {
const res = await deleteGradeClassAction(data.deleteItem.id)
if (res.success) {
toast.success(res.message)
data.setDeleteItem(null)
router.refresh()
} else {
toast.error(res.message || t("list.failedDelete"))
}
} catch {
toast.error(t("list.failedDelete"))
} finally {
data.setIsWorking(false)
}
}
return (
<>
<ClassListToolbar
count={classes.length}
onNew={() => data.setCreateOpen(true)}
isWorking={data.isWorking}
disabled={managedGrades.length === 0}
/>
<ClassListTable
classes={classes}
onEdit={data.setEditItem}
onDelete={data.setDeleteItem}
isWorking={data.isWorking}
emptyDescription={managedGrades.length === 0 ? t("grade.noManagedGradesDescription") : undefined}
/>
<ClassFormDialog
open={data.createOpen}
onOpenChange={data.setCreateOpen}
mode="create"
teachers={teachers}
grades={managedGrades}
onSubmit={handleCreate}
isWorking={data.isWorking}
teacherId={data.createTeacherId}
schoolId={data.createSchoolId}
gradeId={data.createGradeId}
onTeacherIdChange={data.setCreateTeacherId}
onSchoolIdChange={data.setCreateSchoolId}
onGradeIdChange={data.setCreateGradeId}
/>
<ClassFormDialog
open={Boolean(data.editItem)}
onOpenChange={(o) => {
if (!o) data.setEditItem(null)
}}
mode="edit"
editItem={data.editItem}
teachers={teachers}
grades={managedGrades}
onSubmit={handleUpdate}
isWorking={data.isWorking}
teacherId={data.editTeacherId}
schoolId={data.editSchoolId}
gradeId={data.editGradeId}
subjectTeachers={data.editSubjectTeachers}
onTeacherIdChange={data.setEditTeacherId}
onSchoolIdChange={data.setEditSchoolId}
onGradeIdChange={data.setEditGradeId}
onSubjectTeacherChange={data.setSubjectTeacher}
/>
<ClassDeleteDialog
open={Boolean(data.deleteItem)}
onOpenChange={(o) => {
if (!o) data.setDeleteItem(null)
}}
item={data.deleteItem}
onConfirm={handleDelete}
isWorking={data.isWorking}
/>
</>
)
}