From fcbffead8a53c11b9ae66309d40b19cd96f21933 Mon Sep 17 00:00:00 2001 From: gosangam Date: Thu, 15 Oct 2026 20:59:13 +0530 Subject: [PATCH] quiz reset --- app/(dashboard)/quizzes/[id]/results.tsx | 43 ++++++++++++++++++++++++ lib/api.ts | 3 ++ 2 files changed, 46 insertions(+) diff --git a/app/(dashboard)/quizzes/[id]/results.tsx b/app/(dashboard)/quizzes/[id]/results.tsx index 969b08a..ab7165d 100644 --- a/app/(dashboard)/quizzes/[id]/results.tsx +++ b/app/(dashboard)/quizzes/[id]/results.tsx @@ -6,6 +6,9 @@ * Response times are measured by the BFF — from the moment a question was * served to the moment its answer arrived — so they can't be massaged by the * browser. They include any time the person spent away from the screen. + * + * Staff with quizzes:reset can delete one person's attempt, which puts the + * quiz back to pending for them so they can take it again. */ import { useEffect, useState } from "react"; @@ -18,6 +21,9 @@ import { type QuizResult, } from "@/lib/api"; import { Avatar } from "@/components/ui/avatar"; +import { Button } from "@/components/ui/button"; +import { useConfirm } from "@/components/confirm"; +import { Can, useStaff } from "@/components/staff-context"; import { Modal } from "@/components/ui/modal"; import { Table, THead, TH, TR, TD } from "@/components/ui/table"; import { RichText } from "@/lib/rich-text"; @@ -31,6 +37,30 @@ export function Participants({ quizId }: { quizId: string }) { const [rows, setRows] = useState(null); const [open, setOpen] = useState(null); const [detail, setDetail] = useState(null); + const [resetting, setResetting] = useState(null); + const confirm = useConfirm(); + const { can } = useStaff(); + + const reset = async (p: QuizParticipant) => { + const ok = await confirm({ + title: `Reset ${p.name}'s attempt?`, + message: + "Their answers and score are deleted and the quiz becomes pending for them, so they can take it again. This can't be undone.", + confirmLabel: "Reset attempt", + destructive: true, + }); + if (!ok) return; + setResetting(p.user_id); + try { + await quizzes.resetParticipant(quizId, p.user_id); + setRows((rs) => rs?.filter((r) => r.user_id !== p.user_id) ?? rs); + toast.success(`${p.name}'s attempt was reset`); + } catch (err) { + toast.error(errMsg(err, "Could not reset the attempt")); + } finally { + setResetting(null); + } + }; useEffect(() => { quizzes @@ -70,6 +100,7 @@ export function Participants({ quizId }: { quizId: string }) { Progress Time Started + {can("quizzes:reset") && Actions} @@ -118,6 +149,18 @@ export function Participants({ quizId }: { quizId: string }) { {fmtDate(p.started_at)} + + + + + ))} diff --git a/lib/api.ts b/lib/api.ts index ea6b649..47d0c32 100644 --- a/lib/api.ts +++ b/lib/api.ts @@ -744,4 +744,7 @@ export const quizzes = { call(`/admin/quizzes/${id}/participants/${userId}`).then((r) => r.data), responses: (id: string) => call(`/admin/quizzes/${id}/responses`).then((r) => r.data), + /** Deletes one person's attempt; the quiz is pending for them again. */ + resetParticipant: (id: string, userId: string) => + call(`/admin/quizzes/${id}/participants/${userId}`, { method: "DELETE" }), };