"use client"; import Link from "next/link"; import { motion } from "framer-motion"; import { useI18n } from "@/lib/i18n"; import { useCountUp } from "@/lib/motion"; import type { Activity, Deck } from "@/lib/schemas"; import { downloadDeck } from "@/lib/storage"; import { useIsPhone } from "@/lib/use-phone"; import { cn } from "@/lib/utils"; import { BoltIcon, DownloadIcon, FlameIcon, HomeIcon, RefreshIcon, SnowflakeIcon, SparklesIcon } from "@/components/icons"; import { Button, Chip } from "@/components/ui"; import { AskButton } from "@/components/chat/AskButton"; import type { AttemptResult } from "./StudyFeed"; type Translator = (key: string, vars?: Record) => string; export function answerFor(activity: Activity, tr: Translator): string { switch (activity.type) { case "flashcard": return activity.back; case "multiple_choice": return activity.options[activity.correctIndex]; case "true_false": return activity.answer ? tr("results.true") : tr("results.false"); case "fill_blank": return activity.answer; case "matching": return activity.pairs.map((pair) => `${pair.term} → ${pair.definition}`).join("; "); case "ordering": return activity.items.map((item, index) => `${index + 1}. ${item}`).join(" "); case "short_answer": return activity.referenceAnswer; } } export function promptFor(activity: Activity): string { switch (activity.type) { case "flashcard": return activity.front; case "multiple_choice": return activity.question; case "true_false": return activity.statement; case "fill_blank": return activity.sentence.replace(/_{2,}/, "____"); case "matching": return activity.instruction; case "ordering": return activity.instruction; case "short_answer": return activity.question; } } export function ResultsScreen({ deck, xp, bestStreak, total, correct, missed, results, retryMode, onRetryMissed, onRestart, onRegenerate, regenerating, regenerateError, }: { deck: Deck; xp: number; bestStreak: number; total: number; correct: number; missed: Activity[]; results: Record; retryMode: boolean; onRetryMissed: () => void; onRestart: () => void; onRegenerate: () => void; regenerating: boolean; regenerateError: string | null; }) { const { t } = useI18n(); const isPhone = useIsPhone(); const percent = total > 0 ? Math.round((correct / total) * 100) : 0; /* Phones: the score and XP count up; desktop jumps straight in. */ const percentShown = useCountUp(percent, !isPhone); const xpShown = useCountUp(xp, !isPhone); const perfect = missed.length === 0 && total > 0; const tone = percent >= 80 ? "emerald" : percent >= 50 ? "frost" : "amber"; return (

{perfect ? t("results.perfect") : percent >= 60 ? t("results.nice") : t("results.start")}

{perfect ? t("results.acedAll", { n: total, title: deck.title }) : t("results.gotOf", { correct, total, retry: retryMode ? t("results.retrySuffix") : "", })}

{t("results.xpEarned", { n: xpShown })} {t("results.bestStreak", { n: bestStreak })} {t("results.difficulty", { level: deck.difficulty === "easy" ? t("upload.easy") : deck.difficulty === "hard" ? t("upload.hard") : t("upload.medium"), })} {deck.bestScore !== undefined && !retryMode ? ( {t("results.best", { n: deck.bestScore })} ) : null}
{missed.length > 0 ? (

{t("results.missed", { n: missed.length })}

    {missed.map((activity) => (
  • {promptFor(activity)}

    {results[activity.id]?.userAnswer ? (

    {t("results.yourAnswer", { answer: results[activity.id]?.userAnswer ?? "" })}

    ) : null}

    {t("results.answer")} {answerFor(activity, t)}

  • ))}
) : null}
{missed.length > 0 ? ( ) : ( )} {missed.length > 0 ? ( ) : null} {regenerateError ? (

{regenerateError}

) : null} `${promptFor(activity)} — ${t("results.answer")} ${answerFor(activity, t)}`), ].join("\n"), }} />
{t("common.library")}
); } function ScoreRing({ percent, display, tone }: { percent: number; display: number; tone: "emerald" | "frost" | "amber" }) { const { t } = useI18n(); const radius = 52; const circumference = 2 * Math.PI * radius; const offset = circumference * (1 - Math.min(100, Math.max(0, percent)) / 100); const colors: Record = { emerald: "stroke-emerald-500", frost: "stroke-accent-500", amber: "stroke-amber-500", }; return (
{display}% {t("results.score")}
); }