Ajoute la correction sur fichier aux exercices BioCID et FDS
Reprend le fonctionnement du TP Nuisibles (« Corriger » à partir du PDF généré avec les réponses du groupe) pour les deux exercices. - BioCID et FDS : carte « Identification du groupe » (groupe, date, formateur) sur l'écran d'intro, reprise dans l'en-tête du PDF exporté. - FDS : ajout d'un export PDF (inexistant) récapitulant réponse du groupe et réponse attendue, partie par partie, avec le score global. - Bouton « Corriger un rendu » sur l'écran final des deux exercices. - Nouvelle page correction-biocid.html : lecture du PDF, réponses regroupées par exercice avec badge renseigné / sans réponse. - Nouvelle page correction-fds.html : notation automatique du QCM (anneau de réussite, score par partie, ✓/✗/? par question).
This commit is contained in:
@@ -336,6 +336,40 @@ select:focus { outline: none; border-color: var(--amber); box-shadow: 0 0 0 3px
|
||||
.scroll-area::-webkit-scrollbar { width: 4px; }
|
||||
.scroll-area::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
|
||||
|
||||
/* IDENTITY CARD */
|
||||
.identity-card {
|
||||
background: white;
|
||||
border: 0.5px solid var(--border);
|
||||
border-radius: 14px;
|
||||
padding: 16px 18px;
|
||||
max-width: 560px;
|
||||
width: 100%;
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
.identity-card .identity-title {
|
||||
font-family: 'Syne', sans-serif;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.05em;
|
||||
text-transform: uppercase;
|
||||
color: var(--amber-dark);
|
||||
margin-bottom: 10px;
|
||||
display: flex; align-items: center; gap: 6px;
|
||||
}
|
||||
.identity-fields { display: flex; flex-direction: column; gap: 8px; }
|
||||
.identity-fields input {
|
||||
font-family: 'DM Sans', sans-serif;
|
||||
font-size: 13.5px;
|
||||
color: var(--text);
|
||||
background: var(--bg);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 10px;
|
||||
padding: 9px 13px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.identity-fields input:focus { outline: none; border-color: var(--amber); background: white; }
|
||||
|
||||
/* DECO */
|
||||
.deco-circle { position: fixed; border-radius: 50%; pointer-events: none; z-index: 0; }
|
||||
.slide > * { position: relative; z-index: 1; }
|
||||
@@ -413,6 +447,14 @@ select:focus { outline: none; border-color: var(--amber); box-shadow: 0 0 0 3px
|
||||
<span>⏱️</span>
|
||||
<div>Durée ≈ 30 min. Pour chaque question, sélectionnez la <strong>zone du document</strong> où trouver l'information, puis vérifiez votre score.</div>
|
||||
</div>
|
||||
<div class="identity-card">
|
||||
<div class="identity-title">👥 Identification du groupe</div>
|
||||
<div class="identity-fields">
|
||||
<input type="text" id="idGroupe" placeholder="Nom du groupe / stagiaires (ex : Groupe 3 — Dupont / Martin)" autocomplete="off">
|
||||
<input type="date" id="idDate">
|
||||
<input type="text" id="idFormateur" placeholder="Formateur (facultatif)" autocomplete="off">
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn btn-amber" onclick="goTo(2)" style="margin-top:1.5rem;">Commencer la partie 1 →</button>
|
||||
</div>
|
||||
|
||||
@@ -523,7 +565,11 @@ select:focus { outline: none; border-color: var(--amber); box-shadow: 0 0 0 3px
|
||||
<li>Les trois documents sont <strong>complémentaires</strong>, jamais redondants — chacun cible un besoin et un public différents.</li>
|
||||
</ul>
|
||||
<div class="actions-row">
|
||||
<button class="btn btn-amber" onclick="restartAll()">↺ Recommencer</button>
|
||||
<button class="btn btn-amber" onclick="exporterPDF()">⬇ Télécharger mes réponses (PDF)</button>
|
||||
<a href="correction-fds.html" class="btn btn-outline">✔ Corriger un rendu</a>
|
||||
</div>
|
||||
<div class="actions-row" style="margin-top:0.5rem;">
|
||||
<button class="btn btn-outline" onclick="restartAll()">↺ Recommencer</button>
|
||||
<a href="../index.html#formations" class="btn btn-outline">Retour aux exercices</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -700,6 +746,82 @@ function verifyEtiquette() {
|
||||
showScore(document.getElementById('etiquette-result'), correct, etiquetteQuestions.length);
|
||||
}
|
||||
|
||||
/* ======================== EXPORT PDF ======================== */
|
||||
function escapeHtml(str) {
|
||||
if (!str) return '';
|
||||
return String(str).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||
}
|
||||
function idVal(id) {
|
||||
const el = document.getElementById(id);
|
||||
return el ? el.value.trim() : '';
|
||||
}
|
||||
|
||||
// Libellé lisible d'une réponse FDS (numéro de rubrique) et de l'attendu
|
||||
function fdsAnswerLabel(v) {
|
||||
if (!v) return '(non répondu)';
|
||||
if (v === 'absent') return 'Absent';
|
||||
return 'Rubrique ' + v;
|
||||
}
|
||||
function fdsExpectedLabel(answer) {
|
||||
const parts = answer.split(',');
|
||||
if (parts.length > 1) return 'Rubrique ' + parts.join(' ou ');
|
||||
return parts[0] === 'absent' ? 'Absent' : 'Rubrique ' + parts[0];
|
||||
}
|
||||
function textAnswerLabel(v) {
|
||||
if (!v) return '(non répondu)';
|
||||
return v === 'absent' ? 'Absent' : v;
|
||||
}
|
||||
|
||||
function isFdsCorrect(q, v) { return v !== '' && q.answer.split(',').includes(v); }
|
||||
function isTextCorrect(q, v) { return v !== '' && v === q.answer; }
|
||||
|
||||
function exporterPDF() {
|
||||
const groupe = idVal('idGroupe');
|
||||
const date = idVal('idDate') || new Date().toISOString().slice(0, 10);
|
||||
const formateur = idVal('idFormateur');
|
||||
|
||||
let scoreTotal = 0, nbTotal = 0;
|
||||
|
||||
function partHtml(prefix, title, questions, selPrefix, answerLabelFn, expectedLabelFn, correctFn) {
|
||||
const rows = questions.map((q, i) => {
|
||||
const sel = document.getElementById(selPrefix + q.id);
|
||||
const v = sel ? sel.value : '';
|
||||
const ok = correctFn(q, v);
|
||||
nbTotal++; if (ok) scoreTotal++;
|
||||
const mark = v === '' ? '❓' : (ok ? '✓' : '✗');
|
||||
return `<div style="margin-bottom:10px;">
|
||||
<div><strong>[${prefix}-Q${i + 1}]</strong> ${escapeHtml(q.text)}</div>
|
||||
<div style="margin-top:2px;">Réponse du groupe : <strong>${escapeHtml(answerLabelFn(v))}</strong> — Attendu : ${escapeHtml(expectedLabelFn(q.answer))} — ${mark}</div>
|
||||
</div>`;
|
||||
}).join('');
|
||||
return `<h2>${title}</h2>${rows}`;
|
||||
}
|
||||
|
||||
const p1 = partHtml('P1', 'Partie 1 — FDS (Fiche de Données de Sécurité)', fdsQuestions, 'fds', fdsAnswerLabel, fdsExpectedLabel, isFdsCorrect);
|
||||
const p2 = partHtml('P2', 'Partie 2 — FT (Fiche Technique)', ftQuestions, 'ft', textAnswerLabel, a => a, isTextCorrect);
|
||||
const p3 = partHtml('P3', 'Partie 3 — Étiquette', etiquetteQuestions, 'eti', textAnswerLabel, a => a, isTextCorrect);
|
||||
|
||||
const w = window.open('', '_blank');
|
||||
w.document.write(`
|
||||
<html><head><meta charset="utf-8"><title>FDS, FT & Étiquettes — Réponses (${escapeHtml(groupe) || 'sans nom'})</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; padding: 30px; color: #1a1a18; }
|
||||
h1 { font-size: 20px; border-bottom: 2px solid #BA7517; padding-bottom: 8px; }
|
||||
h2 { font-size: 15px; margin-top: 26px; color: #633806; }
|
||||
.meta { font-size: 13px; color: #555; margin: 2px 0; }
|
||||
</style></head>
|
||||
<body>
|
||||
<h1>TD — FDS, FT & Étiquettes</h1>
|
||||
<p class="meta"><strong>Groupe / Stagiaire :</strong> ${escapeHtml(groupe) || '—'}</p>
|
||||
<p class="meta"><strong>Date de session :</strong> ${escapeHtml(date)}</p>
|
||||
<p class="meta"><strong>Formateur :</strong> ${escapeHtml(formateur) || '—'}</p>
|
||||
<p class="meta"><strong>Score global :</strong> ${scoreTotal} / ${nbTotal}</p>
|
||||
${p1}${p2}${p3}
|
||||
</body></html>`);
|
||||
w.document.close();
|
||||
setTimeout(() => w.print(), 300);
|
||||
}
|
||||
|
||||
/* ======================== NAVIGATION ======================== */
|
||||
function goTo(n) {
|
||||
const cur = document.getElementById('slide-' + currentSlide);
|
||||
|
||||
Reference in New Issue
Block a user