"use client"; import { useEffect, useState } from "react"; import Link from "next/link"; import { useParams } from "next/navigation"; import ProgressBar from "@/components/ProgressBar"; import { getPracticeBySlug, mockPracticeModules } from "@/lib/data/mockPractice"; const attemptsKey = (slug: string) => `acve.practice-attempts.${slug}`; type AttemptRecord = { completedAt: string; score: number; total: number; }; export default function PracticeExercisePage() { const params = useParams<{ slug: string }>(); const practiceModule = getPracticeBySlug(params.slug); const [started, setStarted] = useState(false); const [index, setIndex] = useState(0); const [score, setScore] = useState(0); const [finished, setFinished] = useState(false); const [selected, setSelected] = useState(null); const [attempts, setAttempts] = useState([]); const loadAttempts = () => { if (!practiceModule) return; if (typeof window === "undefined") return; const raw = window.localStorage.getItem(attemptsKey(practiceModule.slug)); if (!raw) { setAttempts([]); return; } try { setAttempts(JSON.parse(raw) as AttemptRecord[]); } catch { setAttempts([]); } }; useEffect(() => { loadAttempts(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [practiceModule?.slug]); if (!practiceModule) { return (

Practice module not found

The requested practice slug does not exist in mock data.

Back to practice
); } if (!practiceModule.isInteractive || !practiceModule.questions?.length) { return (

{practiceModule.title}

This practice module is scaffolded and will be enabled in a later iteration.

); } const total = practiceModule.questions.length; const current = practiceModule.questions[index]; const progress = Math.round((index / total) * 100); const pick = (choiceIndex: number) => { if (selected !== null) return; setSelected(choiceIndex); if (choiceIndex === current.answerIndex) { setScore((value) => value + 1); } }; const next = () => { if (index + 1 >= total) { if (typeof window !== "undefined") { const raw = window.localStorage.getItem(attemptsKey(practiceModule.slug)); const parsed = raw ? ((JSON.parse(raw) as AttemptRecord[]) ?? []) : []; const nextAttempts = [ { completedAt: new Date().toISOString(), score, total, }, ...parsed, ].slice(0, 5); window.localStorage.setItem(attemptsKey(practiceModule.slug), JSON.stringify(nextAttempts)); setAttempts(nextAttempts); } setFinished(true); return; } setIndex((value) => value + 1); setSelected(null); }; const restart = () => { setStarted(true); setIndex(0); setScore(0); setSelected(null); setFinished(false); }; const start = () => { setStarted(true); setFinished(false); setIndex(0); setScore(0); setSelected(null); }; if (finished) { return (

Practice Results

Exercise complete

Final score: {score}/{total}

Back to modules

Attempt History (Mock)

{attempts.length === 0 ? (

No attempts recorded yet.

) : (
    {attempts.map((attempt) => (
  • Score {attempt.score}/{attempt.total} on {new Date(attempt.completedAt).toLocaleString()}
  • ))}
)}
); } if (!started) { return (

Practice Session

{practiceModule.title}

{practiceModule.description}

Questions

{total}

Estimated time

{Math.max(3, total * 2)} min

Difficulty

Intermediate

Back to modules

Attempt History (Mock)

{attempts.length === 0 ? (

No attempts recorded yet for this module.

) : (
    {attempts.map((attempt) => (
  • Score {attempt.score}/{attempt.total} on {new Date(attempt.completedAt).toLocaleString()}
  • ))}
)}
); } return (

{practiceModule.title} | Question {index + 1} / {total}

Score: {score}/{total}

{mockPracticeModules.map((module, moduleIndex) => { const isActive = module.slug === practiceModule.slug; return (
{moduleIndex === 0 ? "A/" : moduleIndex === 1 ? "[]" : "O"}

{module.title}

{module.description}

); })}

Attempt History (Mock)

{attempts.length === 0 ? (

No attempts recorded yet.

) : (
    {attempts.map((attempt) => (
  • Score {attempt.score}/{attempt.total} on {new Date(attempt.completedAt).toLocaleString()}
  • ))}
)}

Prompt

{current.prompt.replace("Spanish term: ", "")}

{current.choices.map((choice, choiceIndex) => { const isPicked = selected === choiceIndex; const isCorrect = choiceIndex === current.answerIndex; const stateClass = selected === null ? "border-slate-300 hover:bg-slate-50" : isCorrect ? "border-[#2f9d73] bg-[#edf9f4]" : isPicked ? "border-[#bf3c5f] bg-[#fff1f4]" : "border-slate-300"; return ( ); })}
); }