// Quiz.jsx — motor de cuestionarios del curso. window.Quiz const { useState: useStateQ, useMemo: useMemoQ } = React; function shuffle(arr) { const a = arr.slice(); for (let i = a.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [a[i], a[j]] = [a[j], a[i]]; } return a; } function sample(bank, n) { return shuffle(bank).slice(0, Math.min(n, bank.length)); } function gradeOne(p, ans) { if (p.type === 'vf') return ans === p.correcta; if (p.type === 'abierta_num') { if (ans === '' || ans === null || ans === undefined) return false; const v = parseFloat(String(ans).replace(',', '.')); if (isNaN(v)) return false; const tol = p.tolerancia || 0; return Math.abs(v - p.correcta) <= tol + 1e-9; } return ans === p.correcta; // multiple → índice } function Quiz({ title, subtitle, bank, n, min, onResult, onClose }) { const [round, setRound] = useStateQ(0); const qs = useMemoQ(() => sample(bank, n), [round, bank, n]); const [answers, setAnswers] = useStateQ({}); const [graded, setGraded] = useStateQ(false); const [result, setResult] = useStateQ(null); const setAns = (i, v) => { if (graded) return; setAnswers(a => ({ ...a, [i]: v })); }; const submit = () => { let score = 0; qs.forEach((p, i) => { if (gradeOne(p, answers[i])) score++; }); const passed = score * 2 >= qs.length; // 50% de aciertos setGraded(true); setResult({ score, total: qs.length, passed }); if (onResult) onResult({ score, total: qs.length, passed }); document.querySelector('#quiz-top')?.scrollIntoView({ block: 'start' }); }; const retry = () => { setAnswers({}); setGraded(false); setResult(null); setRound(r => r + 1); window.scrollTo(0, 0); }; const answeredCount = Object.keys(answers).filter(k => answers[k] !== '' && answers[k] !== undefined && answers[k] !== null).length; const notaTxt = result ? (Math.round((result.score / result.total) * 100) / 10).toFixed(1).replace('.', ',') : ''; return (