diff options
Diffstat (limited to 'static/quiz-engine.js')
| -rw-r--r-- | static/quiz-engine.js | 170 |
1 files changed, 170 insertions, 0 deletions
diff --git a/static/quiz-engine.js b/static/quiz-engine.js new file mode 100644 index 0000000..03e50a5 --- /dev/null +++ b/static/quiz-engine.js @@ -0,0 +1,170 @@ +/** + * Quiz Engine - Renderiza questões na página + */ + +function _renderQuestion(q) { + if (!q) return; + const body = document.getElementById('quiz-body'); + if (!body) return; + + const counter = document.getElementById('q-counter'); + if (counter) { + counter.textContent = `${_qState.qIndex + 1} de ${_qState.questions.length}`; + } + + const prog = document.getElementById('prog'); + if (prog) { + const pct = ((_qState.qIndex + 1) / _qState.questions.length) * 100; + prog.style.width = pct + '%'; + } + + if (q.type === 'mc') { + body.innerHTML = `<div class="mc-area fade"> + <p class="q-text">${md(q.text)}</p> + <div class="options"> + ${q.options + .map( + (o, i) => `<button class="opt" data-opt="${esc(o)}" data-answer="${esc(q.answer)}" onclick="selectOpt(this)" aria-label="Opção ${LETRAS[i]}: ${o}"> + <span class="opt-letter">${LETRAS[i]}</span> + <span>${md(o)}</span> + </button>` + ) + .join('')} + </div> + <div class="feedback-line" id="feedback" aria-live="polite" aria-atomic="true"><span class="feedback-dot"></span><span id="feedback-msg"></span></div> + ${q.comment ? `<div class="comment-block" id="comment"><div class="comment-label">comentário</div><div>${md(q.comment)}</div></div>` : ''} + </div>`; + } else { + // Modo Flashcard para questões abertas + const isRevealed = _qState.flipped || false; + body.innerHTML = `<div class="flashcard-area fade"> + <p class="q-text">${md(q.text)}</p> + <div class="flashcard-simple" id="flashcard"> + ${!isRevealed ? `<button class="btn btn-reveal" onclick="flipFlashcard()">Revelar Resposta</button>` : `<div class="ref-label">resposta de referência</div><div class="ref-content">${md(q.reference)}</div>`} + </div> + <div class="feedback-line" id="feedback" aria-live="polite" aria-atomic="true"><span class="feedback-dot"></span><span id="feedback-msg"></span></div> + ${q.comment ? `<div class="comment-block" id="comment"><div class="comment-label">comentário</div><div>${md(q.comment)}</div></div>` : ''} + <div class="flashcard-btns" id="flashcard-btns"> + <button class="btn btn-ok" onclick="markFlashcardOk()">✓ Acertei</button> + <button class="btn btn-bad" onclick="markFlashcardBad()">✗ Errei</button> + </div> + </div>`; + } +} + +function selectOpt(btn) { + if (_qState.answered) return; + _qState.answered = true; + const chosen = btn.dataset.opt; + const correct = btn.dataset.answer; + btn.closest('.options').querySelectorAll('.opt').forEach((b) => { + b.disabled = true; + if (b.dataset.opt === correct) b.classList.add('correct'); + else if (b === btn) b.classList.add('wrong'); + else b.classList.add('revealed'); + }); + if (chosen === correct) { + _qState.acertos++; + setFeedback(true, 'Correto'); + } else { + _qState.erros++; + setFeedback(false, 'Incorreto — a correta está destacada'); + } + document.getElementById('comment')?.classList.add('show'); + document.getElementById('btn-next').style.display = ''; +} + +function flipFlashcard() { + const card = document.getElementById('flashcard'); + if (card) { + _qState.flipped = true; + _renderQuestion(_qState.questions[_qState.qIndex]); + } +} + +function markFlashcardOk() { + _qState.answered = true; + _qState.acertos++; + setFeedback(true, 'Marcado como acertado'); + document.getElementById('comment')?.classList.add('show'); + document.getElementById('flashcard-btns').innerHTML = ''; + document.getElementById('btn-next').style.display = ''; +} + +function markFlashcardBad() { + _qState.answered = true; + _qState.erros++; + setFeedback(false, 'Marcado como errado'); + document.getElementById('comment')?.classList.add('show'); + document.getElementById('flashcard-btns').innerHTML = ''; + document.getElementById('btn-next').style.display = ''; +} + +function setFeedback(ok, msg) { + const fb = document.getElementById('feedback'); + fb.className = 'feedback-line show ' + (ok ? 'ok' : 'bad'); + document.getElementById('feedback-msg').textContent = msg; +} + +function _buildResult() { + const { acertos, erros, questions } = _qState; + const total = questions.length; + const pct = total ? Math.round((acertos / total) * 100) : 0; + let nivel, detalhe; + if (pct >= 90) { + nivel = 'Excelente'; + detalhe = 'Domínio consistente do conteúdo. Mantenha revisão periódica.'; + } else if (pct >= 70) { + nivel = 'Bom'; + detalhe = 'Base sólida com pontos a refinar. Revise os erros e releia os comentários.'; + } else if (pct >= 50) { + nivel = 'Regular'; + detalhe = 'Compreensão parcial, com lacunas importantes. Retome o material teórico.'; + } else { + nivel = 'Insuficiente'; + detalhe = 'Os conceitos centrais ainda não estão consolidados. Estude o tema e refaça.'; + } + document.getElementById('res-pct').textContent = pct + '%'; + document.getElementById('res-a').textContent = acertos; + document.getElementById('res-a2').textContent = acertos; + document.getElementById('res-e').textContent = erros; + document.getElementById('res-t').textContent = total; + document.getElementById('res-msg-body').innerHTML = `<strong>${nivel}</strong> — ${acertos} de ${total} (${pct}%). ${detalhe}`; +} + +function _saveProgress(key) { + try { + localStorage.setItem(PROGRESS_PREFIX + key, JSON.stringify({ + questions: _qState.questions, + qIndex: _qState.qIndex, + acertos: _qState.acertos, + erros: _qState.erros, + flipped: _qState.flipped, + })); + } catch (e) {} +} + +function _loadProgress(key) { + try { + const s = localStorage.getItem(PROGRESS_PREFIX + key); + if (!s) return false; + const d = JSON.parse(s); + if (!d.questions || !d.questions.length) return false; + Object.assign(_qState, { + questions: d.questions, + qIndex: d.qIndex || 0, + acertos: d.acertos || 0, + erros: d.erros || 0, + flipped: d.flipped || false, + }); + return true; + } catch (e) { + return false; + } +} + +function _clearProgress(key) { + try { + localStorage.removeItem(PROGRESS_PREFIX + key); + } catch (e) {} +} |
