direito

Repositório direito
git clone git://git.sivaldodavi.com/direito.git
Log | Files | Refs

quiz-engine.js (6112B)


      1 /**
      2  * Quiz Engine - Renderiza questões na página
      3  */
      4 
      5 function _renderQuestion(q) {
      6   if (!q) return;
      7   const body = document.getElementById('quiz-body');
      8   if (!body) return;
      9 
     10   const counter = document.getElementById('q-counter');
     11   if (counter) {
     12     counter.textContent = `${_qState.qIndex + 1} de ${_qState.questions.length}`;
     13   }
     14 
     15   const prog = document.getElementById('prog');
     16   if (prog) {
     17     const pct = ((_qState.qIndex + 1) / _qState.questions.length) * 100;
     18     prog.style.width = pct + '%';
     19   }
     20 
     21   if (q.type === 'mc') {
     22     body.innerHTML = `<div class="mc-area fade">
     23       <p class="q-text">${md(q.text)}</p>
     24       <div class="options">
     25         ${q.options
     26           .map(
     27             (o, i) => `<button class="opt" data-opt="${esc(o)}" data-answer="${esc(q.answer)}" onclick="selectOpt(this)" aria-label="Opção ${LETRAS[i]}: ${o}">
     28             <span class="opt-letter">${LETRAS[i]}</span>
     29             <span>${md(o)}</span>
     30           </button>`
     31           )
     32           .join('')}
     33       </div>
     34       <div class="feedback-line" id="feedback" aria-live="polite" aria-atomic="true"><span class="feedback-dot"></span><span id="feedback-msg"></span></div>
     35       ${q.comment ? `<div class="comment-block" id="comment"><div class="comment-label">comentário</div><div>${md(q.comment)}</div></div>` : ''}
     36     </div>`;
     37   } else {
     38     // Modo Flashcard para questões abertas
     39     const isRevealed = _qState.flipped || false;
     40     body.innerHTML = `<div class="flashcard-area fade">
     41       <p class="q-text">${md(q.text)}</p>
     42       <div class="flashcard-simple" id="flashcard">
     43         ${!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>`}
     44       </div>
     45       <div class="feedback-line" id="feedback" aria-live="polite" aria-atomic="true"><span class="feedback-dot"></span><span id="feedback-msg"></span></div>
     46       ${q.comment ? `<div class="comment-block" id="comment"><div class="comment-label">comentário</div><div>${md(q.comment)}</div></div>` : ''}
     47       <div class="flashcard-btns" id="flashcard-btns">
     48         <button class="btn btn-ok" onclick="markFlashcardOk()">✓ Acertei</button>
     49         <button class="btn btn-bad" onclick="markFlashcardBad()">✗ Errei</button>
     50       </div>
     51     </div>`;
     52   }
     53 }
     54 
     55 function selectOpt(btn) {
     56   if (_qState.answered) return;
     57   _qState.answered = true;
     58   const chosen = btn.dataset.opt;
     59   const correct = btn.dataset.answer;
     60   btn.closest('.options').querySelectorAll('.opt').forEach((b) => {
     61     b.disabled = true;
     62     if (b.dataset.opt === correct) b.classList.add('correct');
     63     else if (b === btn) b.classList.add('wrong');
     64     else b.classList.add('revealed');
     65   });
     66   if (chosen === correct) {
     67     _qState.acertos++;
     68     setFeedback(true, 'Correto');
     69   } else {
     70     _qState.erros++;
     71     setFeedback(false, 'Incorreto — a correta está destacada');
     72   }
     73   document.getElementById('comment')?.classList.add('show');
     74   document.getElementById('btn-next').style.display = '';
     75 }
     76 
     77 function flipFlashcard() {
     78   const card = document.getElementById('flashcard');
     79   if (card) {
     80     _qState.flipped = true;
     81     _renderQuestion(_qState.questions[_qState.qIndex]);
     82   }
     83 }
     84 
     85 function markFlashcardOk() {
     86   _qState.answered = true;
     87   _qState.acertos++;
     88   setFeedback(true, 'Marcado como acertado');
     89   document.getElementById('comment')?.classList.add('show');
     90   document.getElementById('flashcard-btns').innerHTML = '';
     91   document.getElementById('btn-next').style.display = '';
     92 }
     93 
     94 function markFlashcardBad() {
     95   _qState.answered = true;
     96   _qState.erros++;
     97   setFeedback(false, 'Marcado como errado');
     98   document.getElementById('comment')?.classList.add('show');
     99   document.getElementById('flashcard-btns').innerHTML = '';
    100   document.getElementById('btn-next').style.display = '';
    101 }
    102 
    103 function setFeedback(ok, msg) {
    104   const fb = document.getElementById('feedback');
    105   fb.className = 'feedback-line show ' + (ok ? 'ok' : 'bad');
    106   document.getElementById('feedback-msg').textContent = msg;
    107 }
    108 
    109 function _buildResult() {
    110   const { acertos, erros, questions } = _qState;
    111   const total = questions.length;
    112   const pct = total ? Math.round((acertos / total) * 100) : 0;
    113   let nivel, detalhe;
    114   if (pct >= 90) {
    115     nivel = 'Excelente';
    116     detalhe = 'Domínio consistente do conteúdo. Mantenha revisão periódica.';
    117   } else if (pct >= 70) {
    118     nivel = 'Bom';
    119     detalhe = 'Base sólida com pontos a refinar. Revise os erros e releia os comentários.';
    120   } else if (pct >= 50) {
    121     nivel = 'Regular';
    122     detalhe = 'Compreensão parcial, com lacunas importantes. Retome o material teórico.';
    123   } else {
    124     nivel = 'Insuficiente';
    125     detalhe = 'Os conceitos centrais ainda não estão consolidados. Estude o tema e refaça.';
    126   }
    127   document.getElementById('res-pct').textContent = pct + '%';
    128   document.getElementById('res-a').textContent = acertos;
    129   document.getElementById('res-a2').textContent = acertos;
    130   document.getElementById('res-e').textContent = erros;
    131   document.getElementById('res-t').textContent = total;
    132   document.getElementById('res-msg-body').innerHTML = `<strong>${nivel}</strong> — ${acertos} de ${total} (${pct}%). ${detalhe}`;
    133 }
    134 
    135 function _saveProgress(key) {
    136   try {
    137     localStorage.setItem(PROGRESS_PREFIX + key, JSON.stringify({
    138       questions: _qState.questions,
    139       qIndex: _qState.qIndex,
    140       acertos: _qState.acertos,
    141       erros: _qState.erros,
    142       flipped: _qState.flipped,
    143     }));
    144   } catch (e) {}
    145 }
    146 
    147 function _loadProgress(key) {
    148   try {
    149     const s = localStorage.getItem(PROGRESS_PREFIX + key);
    150     if (!s) return false;
    151     const d = JSON.parse(s);
    152     if (!d.questions || !d.questions.length) return false;
    153     Object.assign(_qState, {
    154       questions: d.questions,
    155       qIndex: d.qIndex || 0,
    156       acertos: d.acertos || 0,
    157       erros: d.erros || 0,
    158       flipped: d.flipped || false,
    159     });
    160     return true;
    161   } catch (e) {
    162     return false;
    163   }
    164 }
    165 
    166 function _clearProgress(key) {
    167   try {
    168     localStorage.removeItem(PROGRESS_PREFIX + key);
    169   } catch (e) {}
    170 }