"use client"; /** * Your finished attempt: the score, then every question with your answer, * the correct one, its explanation, and how long you took. * * Correct options are filled solid so they read at a glance; a wrong pick is * outlined in red. "Your answer" marks whatever you chose either way. */ import Link from "next/link"; import { Check, Clock, X } from "lucide-react"; import { RichText } from "@/lib/rich-text"; import type { QuizResult } from "@/lib/quiz"; import { cn } from "@/lib/utils"; /** * "8.4s" / "1m 12s" / "3h 20m" / "2d 4h". Hours past 23h 59m roll up into * days. Mirrors fmtMs in the admin's quizzes/labels.ts. */ export function fmtMs(ms?: number | null): string { if (ms == null) return "—"; // Below 59.95s: one decimal place (anything higher would print "60.0s"). if (ms < 59_950) return `${(ms / 1000).toFixed(1)}s`; // Whole seconds from here, so rounding can't print "1m 60s". const total = Math.round(ms / 1000); const d = Math.floor(total / 86_400); const h = Math.floor((total % 86_400) / 3600); const m = Math.floor((total % 3600) / 60); const s = total % 60; // The two largest units: 2d 4h · 3h 20m · 12m 5s. if (d > 0) return `${d}d ${h}h`; if (h > 0) return `${h}h ${m}m`; return `${m}m ${s}s`; } export function QuizResultView({ result }: { result: QuizResult }) { const pct = result.total ? Math.round((result.score / result.total) * 100) : 0; return (

Your result

{result.score} /{result.total}

{pct}% correct · {fmtMs(result.total_ms)} in total

{result.quiz.target_path && ( Back to {result.quiz.target_title || "the reading"} )}
    {result.items.map((it, i) => (
  1. {i + 1}.

    {it.is_correct ? ( ) : ( )} {it.is_correct ? "Correct" : "Incorrect"}
    {it.multi && (

    Select all that apply

    )}
      {it.options.map((o) => { const chosen = it.chosen_option_ids.includes(o.id); return (
    • {o.is_correct ? ( ) : chosen ? ( ) : ( )} {chosen && ( Your answer )}
    • ); })}
    {it.explanation && (

    )}

    {fmtMs(it.response_ms)}

  2. ))}
); }