summaryrefslogtreecommitdiff
path: root/static/quiz-engine.js
diff options
context:
space:
mode:
authorSivaldo <sivaldodavi@disroot.org>2026-06-13 19:20:23 -0300
committerSivaldo <sivaldodavi@disroot.org>2026-06-13 19:20:23 -0300
commit7316238f16655bd127b04476bbf7864b27f940f9 (patch)
tree61f25290b3e5fd14894e52754cfe6dd4e85845ef /static/quiz-engine.js
Commit inicialHEADmain
Diffstat (limited to 'static/quiz-engine.js')
-rw-r--r--static/quiz-engine.js170
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) {}
+}