"use client"; import { useMemo, useState } from "react"; import type { Activity } from "@/lib/schemas"; import { cn, shuffle } from "@/lib/utils"; import { ActivityHeader, FeedbackPanel, Hint, PromptCard, type ActivityComponentProps, } from "./shared"; type MatchingActivity = Extract; export function Matching({ activity, onAnswered, onNext, isLast }: ActivityComponentProps) { const terms = useMemo( () => shuffle(activity.pairs.map((pair, index) => ({ index, text: pair.term }))), [activity], ); const definitions = useMemo( () => shuffle(activity.pairs.map((pair, index) => ({ index, text: pair.definition }))), [activity], ); const [selectedTerm, setSelectedTerm] = useState(null); const [matched, setMatched] = useState([]); const [misses, setMisses] = useState(0); const [flash, setFlash] = useState<{ term: number; definition: number } | null>(null); const [done, setDone] = useState(false); const total = activity.pairs.length; const complete = matched.length === total; const onDefinitionTap = (definitionIndex: number) => { if (done || selectedTerm === null || matched.includes(definitionIndex)) return; if (selectedTerm === definitionIndex) { const next = [...matched, definitionIndex]; setMatched(next); setSelectedTerm(null); if (next.length === total) { const correct = misses === 0; setDone(true); onAnswered(correct); } } else { setMisses((value) => value + 1); setFlash({ term: selectedTerm, definition: definitionIndex }); window.setTimeout(() => setFlash(null), 500); setSelectedTerm(null); } }; return ( <> {matched.length}/{total} matched } />

{activity.instruction}

{done ? "All matched!" : selectedTerm === null ? "Tap a term on the left, then its match on the right." : "Now tap the matching definition →"}
{terms.map((term) => { const isMatched = matched.includes(term.index); const isSelected = selectedTerm === term.index; const isFlashing = flash?.term === term.index; return ( ); })}
{definitions.map((definition) => { const isMatched = matched.includes(definition.index); const isFlashing = flash?.definition === definition.index; return ( ); })}
{complete ? ( {misses === 0 ? "You matched everything first try." : `You made ${misses} wrong ${misses === 1 ? "attempt" : "attempts"}.`} } /> ) : null} ); }