"use client"; import { useMemo, useState } from "react"; import { useI18n } from "@/lib/i18n"; 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, nextLabel }: ActivityComponentProps) { const { t, plural } = useI18n(); 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 ( <> {t("matching.matched", { n: matched.length, total })} } />

{activity.instruction}

{done ? t("matching.allMatched") : selectedTerm === null ? t("matching.tapTerm") : t("matching.tapDef")}
{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 ? t("matching.firstTry") : plural(misses, "matching.wrongAttempts")} } /> ) : null} ); }