feat: exam actions and data safety fixes
This commit is contained in:
@@ -32,6 +32,7 @@ import {
|
||||
DialogTitle,
|
||||
} from "@/shared/components/ui/dialog"
|
||||
|
||||
import { deleteExamAction, duplicateExamAction, updateExamAction } from "../actions"
|
||||
import { Exam } from "../types"
|
||||
|
||||
interface ExamActionsProps {
|
||||
@@ -42,31 +43,70 @@ export function ExamActions({ exam }: ExamActionsProps) {
|
||||
const router = useRouter()
|
||||
const [showViewDialog, setShowViewDialog] = useState(false)
|
||||
const [showDeleteDialog, setShowDeleteDialog] = useState(false)
|
||||
const [isWorking, setIsWorking] = useState(false)
|
||||
|
||||
const copyId = () => {
|
||||
navigator.clipboard.writeText(exam.id)
|
||||
toast.success("Exam ID copied to clipboard")
|
||||
}
|
||||
|
||||
const publishExam = async () => {
|
||||
toast.success("Exam published")
|
||||
const setStatus = async (status: Exam["status"]) => {
|
||||
setIsWorking(true)
|
||||
try {
|
||||
const formData = new FormData()
|
||||
formData.set("examId", exam.id)
|
||||
formData.set("status", status)
|
||||
const result = await updateExamAction(null, formData)
|
||||
if (result.success) {
|
||||
toast.success(status === "published" ? "Exam published" : status === "archived" ? "Exam archived" : "Exam moved to draft")
|
||||
router.refresh()
|
||||
} else {
|
||||
toast.error(result.message || "Failed to update exam")
|
||||
}
|
||||
} catch {
|
||||
toast.error("Failed to update exam")
|
||||
} finally {
|
||||
setIsWorking(false)
|
||||
}
|
||||
}
|
||||
|
||||
const unpublishExam = async () => {
|
||||
toast.success("Exam moved to draft")
|
||||
}
|
||||
|
||||
const archiveExam = async () => {
|
||||
toast.success("Exam archived")
|
||||
const duplicateExam = async () => {
|
||||
setIsWorking(true)
|
||||
try {
|
||||
const formData = new FormData()
|
||||
formData.set("examId", exam.id)
|
||||
const result = await duplicateExamAction(null, formData)
|
||||
if (result.success && result.data) {
|
||||
toast.success("Exam duplicated")
|
||||
router.push(`/teacher/exams/${result.data}/build`)
|
||||
router.refresh()
|
||||
} else {
|
||||
toast.error(result.message || "Failed to duplicate exam")
|
||||
}
|
||||
} catch {
|
||||
toast.error("Failed to duplicate exam")
|
||||
} finally {
|
||||
setIsWorking(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleDelete = async () => {
|
||||
setIsWorking(true)
|
||||
try {
|
||||
await new Promise((r) => setTimeout(r, 800))
|
||||
toast.success("Exam deleted successfully")
|
||||
setShowDeleteDialog(false)
|
||||
} catch (e) {
|
||||
const formData = new FormData()
|
||||
formData.set("examId", exam.id)
|
||||
const result = await deleteExamAction(null, formData)
|
||||
if (result.success) {
|
||||
toast.success("Exam deleted successfully")
|
||||
setShowDeleteDialog(false)
|
||||
router.refresh()
|
||||
} else {
|
||||
toast.error(result.message || "Failed to delete exam")
|
||||
}
|
||||
} catch {
|
||||
toast.error("Failed to delete exam")
|
||||
} finally {
|
||||
setIsWorking(false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,25 +128,39 @@ export function ExamActions({ exam }: ExamActionsProps) {
|
||||
<DropdownMenuItem onClick={() => setShowViewDialog(true)}>
|
||||
<Eye className="mr-2 h-4 w-4" /> View
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => router.push(`/teacher/exams/${exam.id}/build`)}>
|
||||
<Pencil className="mr-2 h-4 w-4" /> Edit
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => router.push(`/teacher/exams/${exam.id}/build`)}>
|
||||
<MoreHorizontal className="mr-2 h-4 w-4" /> Build
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem onClick={publishExam}>
|
||||
<DropdownMenuItem onClick={duplicateExam} disabled={isWorking}>
|
||||
<Copy className="mr-2 h-4 w-4" /> Duplicate
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
onClick={() => setStatus("published")}
|
||||
disabled={isWorking || exam.status === "published"}
|
||||
>
|
||||
<UploadCloud className="mr-2 h-4 w-4" /> Publish
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={unpublishExam}>
|
||||
<DropdownMenuItem
|
||||
onClick={() => setStatus("draft")}
|
||||
disabled={isWorking || exam.status === "draft"}
|
||||
>
|
||||
<Undo2 className="mr-2 h-4 w-4" /> Move to Draft
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={archiveExam}>
|
||||
<DropdownMenuItem
|
||||
onClick={() => setStatus("archived")}
|
||||
disabled={isWorking || exam.status === "archived"}
|
||||
>
|
||||
<Archive className="mr-2 h-4 w-4" /> Archive
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
className="text-destructive focus:text-destructive"
|
||||
onClick={() => setShowDeleteDialog(true)}
|
||||
disabled={isWorking}
|
||||
>
|
||||
<Trash className="mr-2 h-4 w-4" /> Delete
|
||||
</DropdownMenuItem>
|
||||
@@ -159,6 +213,7 @@ export function ExamActions({ exam }: ExamActionsProps) {
|
||||
e.preventDefault()
|
||||
handleDelete()
|
||||
}}
|
||||
disabled={isWorking}
|
||||
>
|
||||
Delete
|
||||
</AlertDialogAction>
|
||||
|
||||
Reference in New Issue
Block a user