"use client"; import { useState, useEffect, useCallback } from "react"; import { useParams, useRouter } from "next/navigation"; import { QuizViewer } from "@/components/quizzes/QuizViewer"; import { AttemptHistory } from "@/components/quizzes/AttemptHistory"; import { ShareMenu } from "@/components/ui/ShareMenu"; interface Option { id: string; text: string; isCorrect: boolean; } interface Question { id: string; type: string; prompt: string; rationale: string; category: string; options: Option[]; } interface QuizAttempt { id: string; score: number; maxScore: number; answersJson: string; reviewSnapshotJson?: string | null; isPartialRetake: boolean; completedAt: string; } interface QuizData { id: string; name: string; description: string | null; questions: Question[]; class: { slug: string; name: string }; progress: Array<{ mode: string; currentIndex: number; orderJson: string; answersJson: string | null; sessionId: string; revision: number; }>; } export default function QuizStudyPage() { const params = useParams(); const router = useRouter(); const classSlug = params.classSlug as string; const quizId = params.quizId as string; const [quiz, setQuiz] = useState(null); const [attempts, setAttempts] = useState([]); const [loading, setLoading] = useState(true); const [view, setView] = useState<"history" | "take">("take"); const [retakeIds, setRetakeIds] = useState(null); const [restartKey, setRestartKey] = useState(0); async function handleRestart() { if (!quiz) return; const progress = quiz.progress.find((item) => item.mode === "SEQUENTIAL"); if (progress) { const response = await fetch("/api/progress", { method: "DELETE", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ contentType: "QUIZ", contentId: quiz.id, mode: "SEQUENTIAL", sessionId: progress.sessionId, }), }).catch(() => null); if (!response?.ok) { window.alert("The quiz could not be restarted. Your saved progress was kept."); return; } } // Clear locally and force remount setQuiz({ ...quiz, progress: [] }); setRetakeIds(null); setRestartKey(prev => prev + 1); setView("take"); } const fetchQuizAndAttempts = useCallback(async () => { try { const [quizRes, attemptsRes] = await Promise.all([ fetch(`/api/quizzes/${quizId}`), fetch(`/api/quizzes/${quizId}/attempt`) ]); if (!quizRes.ok) throw new Error("Not found"); const [quizData, attemptsData] = await Promise.all([ quizRes.json(), attemptsRes.ok ? attemptsRes.json() : [] ]); setQuiz(quizData); setAttempts(attemptsData); // Optional: keep track of attempt count, but default view is already "take" if (attemptsData.length > 0) { // We can do something here if we wanted to auto-switch to history, but user wants take } } catch { router.push(`/${classSlug}/quizzes`); } finally { setLoading(false); } }, [quizId, classSlug, router]); useEffect(() => { const timer = window.setTimeout(() => void fetchQuizAndAttempts(), 0); return () => window.clearTimeout(timer); }, [fetchQuizAndAttempts]); const [showTopics, setShowTopics] = useState(false); const categories = quiz ? Array.from(new Set(quiz.questions.map(q => q.category.toUpperCase()))).join(" ยท ") : ""; if (loading || !quiz) { return (
); } return (
{/* Header */}
{showTopics && (
{categories}
)} {quiz.description && (

{quiz.description}

)}
{/* View toggle & Share */}
{view === "take" && ( )} {attempts.length > 0 && (
)}
{/* Content */} {view === "history" && attempts.length > 0 ? ( { setRetakeIds(missedIds); setView("take"); }} /> ) : ( { router.push(`/${classSlug}/quizzes`); }} /> )}
); }