import { useMemo, useState } from "react"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { CheckCircle2, XCircle, RotateCcw, Loader2 } from "lucide-react"; import { supabase } from "@/integrations/supabase/client"; import { useAuth } from "@/hooks/useAuth"; import { useToast } from "@/hooks/use-toast"; import { cn } from "@/lib/utils"; import type { QuizRow } from "@/store/workspace"; type Answer = string; function normalize(s: string) { return s.toLowerCase().replace(/[^\p{L}\p{N}]+/gu, " ").trim(); } function isCorrect(userAns: string, correct: string, type: string): boolean { if (!userAns) return false; if (type === "short_answer") { const u = normalize(userAns); const c = normalize(correct); if (!u || !c) return false; return u === c || u.includes(c) || c.includes(u); } return userAns === correct; } export default function QuizPlayer({ quiz }: { quiz: QuizRow }) { const { user } = useAuth(); const { toast } = useToast(); const [answers, setAnswers] = useState>({}); const [submitted, setSubmitted] = useState(false); const [saving, setSaving] = useState(false); const total = quiz.questions.length; const score = useMemo(() => { return quiz.questions.reduce((acc, q) => acc + (isCorrect(answers[q.id] ?? "", q.correct, q.type) ? 1 : 0), 0); }, [answers, quiz.questions]); const allAnswered = quiz.questions.every((q) => (answers[q.id] ?? "").trim().length > 0); const submit = async () => { setSubmitted(true); if (!user) return; setSaving(true); try { const payload = quiz.questions.map((q) => ({ question_id: q.id, answer: answers[q.id] ?? "", correct: isCorrect(answers[q.id] ?? "", q.correct, q.type), })); const { error } = await supabase.from("quiz_attempts").insert({ user_id: user.id, quiz_id: quiz.id, answers: payload, score, total, }); if (error) throw error; toast({ title: "Attempt saved", description: `Scored ${score} / ${total}` }); } catch (e: any) { toast({ title: "Could not save attempt", description: e.message, variant: "destructive" }); } finally { setSaving(false); } }; const reset = () => { setAnswers({}); setSubmitted(false); }; return (
{submitted && (
Your score
{score} / {total}
)} {quiz.questions.map((q, i) => { const userAns = answers[q.id] ?? ""; const correct = isCorrect(userAns, q.correct, q.type); return (
{i + 1}
{q.question}
{q.type === "mcq" ? "Multiple choice" : q.type === "true_false" ? "True / False" : "Short answer"}
{submitted && (correct ? ( ) : ( ))}
{q.type === "mcq" && q.choices && (
{q.choices.map((c, j) => { const selected = userAns === c; const isAnswer = c === q.correct; return ( ); })}
)} {q.type === "true_false" && (
{["True", "False"].map((c) => { const selected = userAns === c; const isAnswer = c === q.correct; return ( ); })}
)} {q.type === "short_answer" && (
setAnswers((a) => ({ ...a, [q.id]: e.target.value }))} disabled={submitted} placeholder="Type your answer…" />
)} {submitted && (
{!correct && (
Correct answer: {q.correct}
)} {q.explanation && (
{q.explanation}
)}
)}
); })} {!submitted && (
{!allAnswered && (

Answer all {total} questions to submit.

)}
)}
); }