Compare commits
6 Commits
090cbc19a2
...
exos-rewor
| Author | SHA1 | Date | |
|---|---|---|---|
| f611bdcd7e | |||
| f7c846c85c | |||
| a74df51201 | |||
| 42650ca351 | |||
| 21a7359057 | |||
| 6bb7985512 |
@@ -406,6 +406,15 @@
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.identity-fields input:focus { outline: none; border-color: var(--amber); background: white; }
|
||||
.formateur-label { font-size: 11px; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; color: var(--text-muted); margin: 4px 0 2px; }
|
||||
.formateur-row { display: flex; gap: 8px; }
|
||||
.formateur-btn {
|
||||
flex: 1; font-family: 'DM Sans', sans-serif; font-size: 13.5px;
|
||||
padding: 9px 13px; border: 1px solid var(--border); border-radius: 10px;
|
||||
background: var(--bg); color: var(--text); cursor: pointer; transition: all 0.2s ease;
|
||||
}
|
||||
.formateur-btn:hover { border-color: var(--amber); }
|
||||
.formateur-btn.active { background: var(--amber); border-color: var(--amber); color: #fff; }
|
||||
|
||||
/* DECO */
|
||||
.deco-circle { position: fixed; border-radius: 50%; pointer-events: none; z-index: 0; }
|
||||
@@ -492,7 +501,12 @@
|
||||
<div class="identity-fields">
|
||||
<input type="text" id="idGroupe" placeholder="Nom du groupe / stagiaires (ex : Groupe 3 — Dupont / Martin)" autocomplete="off" oninput="clearGroupeWarning()">
|
||||
<input type="date" id="idDate">
|
||||
<input type="text" id="idFormateur" placeholder="Formateur (facultatif)" autocomplete="off">
|
||||
<input type="hidden" id="idFormateur">
|
||||
<div class="formateur-label">Formateur de la session</div>
|
||||
<div class="formateur-row">
|
||||
<button type="button" class="formateur-btn" data-formateur="Nathan" onclick="selectFormateur(this)">Nathan</button>
|
||||
<button type="button" class="formateur-btn" data-formateur="Gauthier" onclick="selectFormateur(this)">Gauthier</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="idWarning" style="display:none;margin-top:8px;font-size:12.5px;color:var(--coral);">⚠ Renseignez le nom du groupe pour continuer.</div>
|
||||
</div>
|
||||
@@ -689,6 +703,12 @@ function clearGroupeWarning() {
|
||||
if (w) w.style.display = 'none';
|
||||
}
|
||||
|
||||
function selectFormateur(btn) {
|
||||
document.querySelectorAll('.formateur-btn').forEach(b => b.classList.remove('active'));
|
||||
btn.classList.add('active');
|
||||
document.getElementById('idFormateur').value = btn.dataset.formateur;
|
||||
}
|
||||
|
||||
function goNext() {
|
||||
const cfg = slideConfig[currentSlide];
|
||||
if (!cfg || cfg.next == null) return;
|
||||
|
||||
@@ -647,21 +647,6 @@ span.hotspot { display: inline; padding: 0 2px; -webkit-box-decoration-break: cl
|
||||
.eti-pop .pop-reg { color: var(--text-muted); }
|
||||
.eti-pop .pop-close { position: absolute; top: 5px; right: 8px; cursor: pointer; color: var(--text-muted); font-size: 16px; line-height: 1; border: none; background: none; padding: 2px; }
|
||||
|
||||
/* Check-list de synthèse (modèle FHER p.15) */
|
||||
.checklist-table {
|
||||
width: 100%; max-width: 780px; border-collapse: collapse;
|
||||
background: #fff; border: 1px solid var(--border); border-radius: 12px; overflow: hidden;
|
||||
font-size: 12.5px; margin: 0.25rem 0 1rem;
|
||||
}
|
||||
.checklist-table th { background: var(--teal-light); color: var(--teal-dark); font-family: 'Syne', sans-serif; font-weight: 700; font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.03em; padding: 10px 12px; text-align: left; border-bottom: 2px solid var(--teal-mid); }
|
||||
.checklist-table td { padding: 9px 12px; border-bottom: 1px solid var(--border); color: var(--text-muted); vertical-align: top; }
|
||||
.checklist-table tr:last-child td { border-bottom: none; }
|
||||
.checklist-table td.cl-num { text-align: center; }
|
||||
.cl-badge { display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px; border-radius: 50%; background: #2e7d32; color: #fff; font-family: 'Syne', sans-serif; font-weight: 800; font-size: 10px; }
|
||||
.checklist-table strong { color: var(--text); }
|
||||
.cl-check { color: #2e7d32; font-weight: 700; }
|
||||
.cl-cross { color: #c62828; font-weight: 700; }
|
||||
|
||||
@media (max-width: 700px) {
|
||||
.lbl-cols { grid-template-columns: 1fr; }
|
||||
.lbl-title { font-size: 26px; }
|
||||
@@ -687,20 +672,6 @@ span.hotspot { display: inline; padding: 0 2px; -webkit-box-decoration-break: cl
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* ---- SLIDE 7 — récapitulatif : étiquette à gauche, tableau à droite ---- */
|
||||
.recap-layout { display: flex; gap: 20px; align-items: flex-start; justify-content: center; width: 100%; max-width: 1120px; }
|
||||
.recap-layout #recap-stage { flex: 1 1 0; min-width: 0; max-width: 560px; }
|
||||
.recap-layout .checklist-table { flex: 1 1 0; min-width: 0; margin: 0; }
|
||||
.recap-layout.animate #recap-stage { animation: recapSlideLeft 0.5s ease both; }
|
||||
.recap-layout.animate .checklist-table { animation: recapSlideRight 0.5s ease both; }
|
||||
@keyframes recapSlideLeft { from { opacity: 0; transform: translateX(-40px); } to { opacity: 1; transform: none; } }
|
||||
@keyframes recapSlideRight { from { opacity: 0; transform: translateX(40px); } to { opacity: 1; transform: none; } }
|
||||
|
||||
@media (max-width: 1000px) {
|
||||
.recap-layout { flex-direction: column; align-items: center; }
|
||||
.recap-layout #recap-stage { flex: none; max-width: 560px; width: 100%; }
|
||||
.recap-layout .checklist-table { flex: none; width: 100%; max-width: 640px; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -843,7 +814,7 @@ span.hotspot { display: inline; padding: 0 2px; -webkit-box-decoration-break: cl
|
||||
<p class="slide-intro">L'étiquette est l'interface directe entre le produit et l'utilisateur. Elle communique rapidement les <strong>dangers essentiels</strong>, les <strong>EPI</strong> et les <strong>consignes d'utilisation</strong>.</p>
|
||||
<div class="info-card" style="background:var(--teal-light);color:var(--teal-dark);">
|
||||
<span>🎯</span>
|
||||
<div>Sur une <strong>étiquette réelle</strong> (produit professionnel DÉS'INFECT), <strong>cliquez la zone</strong> qui répond à chaque question : nom commercial, pictogrammes, mention d'avertissement, phrases H, phrases P (EPI, premiers secours, élimination), contenu net et centre antipoison. Chaque clic ouvre la <strong>justification réglementaire</strong> de la zone.</div>
|
||||
<div>Sur une <strong>étiquette réelle</strong> (produit professionnel DÉS'INFECT), <strong>cliquez la zone</strong> qui répond à chaque question : nom commercial, pictogrammes, mention d'avertissement, phrases H, phrases P (EPI, premiers secours, élimination), contenu net et centre antipoison. La <strong>justification réglementaire</strong> de chaque zone est fournie sur la page de correction de l'exercice.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -926,18 +897,6 @@ span.hotspot { display: inline; padding: 0 2px; -webkit-box-decoration-break: cl
|
||||
<div id="etiquette-result" class="result-message"></div>
|
||||
</div>
|
||||
|
||||
<!-- ---- PANNEAU B : synthèse / check-list ---- -->
|
||||
<div id="eti-recap" hidden style="width:100%;display:flex;flex-direction:column;align-items:center;gap:0.75rem;">
|
||||
<div class="section-tag" style="background:var(--teal-light);color:var(--teal-dark);">Check-list d'étiquetage</div>
|
||||
<div class="slide-title">Récapitulatif — l'étiquette annotée</div>
|
||||
<p class="slide-intro">Les 9 zones réglementaires et leurs bonnes réponses, présentées comme la <strong>check-list officielle d'étiquetage des produits biocides</strong> (Guide FHER, p. 15) — un outil réutilisable en conditions réelles.</p>
|
||||
|
||||
<div class="recap-layout" id="recap-layout">
|
||||
<div class="eti-stage" id="recap-stage"></div>
|
||||
<table class="checklist-table" id="checklist-table"></table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -996,7 +955,7 @@ const fdsQuestions = [
|
||||
const ftQuestions = [
|
||||
{ id: 1, text: "Quel est le nom du produit et qui est le fabricant ?", answer: "Description" },
|
||||
{ id: 2, text: "Sur quelles matières ce produit est-il compatible ?", answer: "Absent" },
|
||||
{ id: 3, text: "Quel est (sont) le(s) type(s) de ce produit ?", answer: "Propriétés biocides" },
|
||||
{ id: 3, text: "Où se trouve la liste des TP (type de produit) ?", answer: "Propriétés biocides" },
|
||||
{ id: 4, text: "Quels sont les types de conditionnements disponibles ?", answer: "Conditionnements" },
|
||||
{ id: 5, text: "Où retrouver les différents modes d'utilisation de ce produit ?", answer: "Utilisations" }
|
||||
];
|
||||
@@ -1063,16 +1022,13 @@ let etiOrder = []; // ordre aléatoire des questions
|
||||
let etiPos = 0; // question courante
|
||||
let etiAnswers = {}; // { zoneCible : zoneCliquée en 1re tentative } — pour le PDF
|
||||
let etiFirstTry = {}; // { zoneCible : true si trouvée sans erreur }
|
||||
let etiCurError = false; // une erreur a-t-elle eu lieu sur la question courante
|
||||
let etiInit = false; // écouteur de clic posé une seule fois
|
||||
let etiRecapShown = false;// panneau récapitulatif affiché (slide 7)
|
||||
|
||||
function buildEtiquette() {
|
||||
etiOrder = shuffleArray(etiZones.map((_, i) => i));
|
||||
etiPos = 0;
|
||||
etiAnswers = {};
|
||||
etiFirstTry = {};
|
||||
etiCurError = false;
|
||||
|
||||
const card = document.getElementById('labelCard');
|
||||
card.querySelectorAll('.hotspot').forEach(h => h.classList.remove('hs-correct', 'hs-wrong'));
|
||||
@@ -1080,9 +1036,6 @@ function buildEtiquette() {
|
||||
closeEtiPop();
|
||||
const r = document.getElementById('etiquette-result');
|
||||
r.className = 'result-message'; r.innerHTML = '';
|
||||
etiRecapShown = false;
|
||||
document.getElementById('eti-exercise').style.display = 'flex';
|
||||
document.getElementById('eti-recap').style.display = 'none';
|
||||
if (currentSlide === 7) renderBarActions();
|
||||
|
||||
if (!etiInit) {
|
||||
@@ -1110,28 +1063,20 @@ function onEtiClick(e) {
|
||||
if (!hs) return;
|
||||
const clicked = hs.dataset.zone;
|
||||
|
||||
// Exercice terminé : plus d'interaction (les corrections sont dans la correction générale)
|
||||
// Exercice terminé : plus d'interaction (la correction est sur la page de correction).
|
||||
if (etiPos >= etiOrder.length) return;
|
||||
|
||||
marquerModifie();
|
||||
const target = etiZones[etiOrder[etiPos]];
|
||||
if (clicked === target.zone) {
|
||||
hs.classList.remove('hs-wrong');
|
||||
hs.classList.add('hs-correct');
|
||||
if (etiAnswers[target.zone] === undefined) etiAnswers[target.zone] = clicked;
|
||||
etiFirstTry[target.zone] = !etiCurError;
|
||||
|
||||
// La zone cliquée — bonne ou mauvaise — est enregistrée comme réponse, puis on passe
|
||||
// à la question suivante. Aucun retour de justesse n'est affiché ici : la correction
|
||||
// (étiquette annotée + check-list) est présentée sur la page de correction de l'exercice.
|
||||
etiAnswers[target.zone] = clicked;
|
||||
etiFirstTry[target.zone] = (clicked === target.zone);
|
||||
etiPos++;
|
||||
etiCurError = false;
|
||||
renderEtiQuestion();
|
||||
if (etiPos >= etiOrder.length) finishEtiquette();
|
||||
} else {
|
||||
if (etiAnswers[target.zone] === undefined) etiAnswers[target.zone] = clicked; // 1re réponse (erronée) mémorisée
|
||||
etiCurError = true;
|
||||
etiFirstTry[target.zone] = false;
|
||||
hs.classList.remove('hs-wrong'); void hs.offsetWidth; // relance l'animation
|
||||
hs.classList.add('hs-wrong');
|
||||
setTimeout(() => hs.classList.remove('hs-wrong'), 700);
|
||||
}
|
||||
}
|
||||
|
||||
function finishEtiquette() {
|
||||
@@ -1168,70 +1113,6 @@ function closeEtiPop() {
|
||||
|
||||
function resetEtiquette() { buildEtiquette(); }
|
||||
|
||||
/* ---- Synthèse : étiquette annotée + check-list ---- */
|
||||
function showEtiRecap() {
|
||||
closeEtiPop();
|
||||
const stage = document.getElementById('recap-stage');
|
||||
stage.innerHTML = '';
|
||||
|
||||
// Clone de l'étiquette, toutes zones en « correct » + numéros
|
||||
const clone = document.getElementById('labelCard').cloneNode(true);
|
||||
clone.removeAttribute('id');
|
||||
clone.classList.add('recap');
|
||||
clone.querySelectorAll('.hotspot').forEach(h => {
|
||||
h.classList.remove('hs-wrong');
|
||||
h.classList.add('hs-correct');
|
||||
const idx = etiZones.findIndex(z => z.zone === h.dataset.zone);
|
||||
if (idx >= 0) {
|
||||
const num = document.createElement('b');
|
||||
num.className = 'hs-num';
|
||||
num.textContent = idx + 1;
|
||||
h.insertBefore(num, h.firstChild);
|
||||
}
|
||||
});
|
||||
stage.appendChild(clone);
|
||||
|
||||
// Table check-list
|
||||
const tbl = document.getElementById('checklist-table');
|
||||
let rows = '<thead><tr><th style="width:34px;">N°</th><th>Élément d\'étiquetage</th><th>Base réglementaire</th><th style="width:70px;">Statut</th></tr></thead><tbody>';
|
||||
etiZones.forEach((z, i) => {
|
||||
const ok = etiFirstTry[z.zone];
|
||||
const answered = etiAnswers[z.zone] !== undefined;
|
||||
const statut = ok
|
||||
? '<span class="cl-check">✓ trouvé</span>'
|
||||
: (answered ? '<span class="cl-cross">✗ à revoir</span>' : '<span class="cl-cross">—</span>');
|
||||
rows += `<tr>
|
||||
<td class="cl-num"><span class="cl-badge">${i + 1}</span></td>
|
||||
<td><strong>${z.label}</strong></td>
|
||||
<td>${z.reg}</td>
|
||||
<td>${statut}</td>
|
||||
</tr>`;
|
||||
});
|
||||
rows += '</tbody>';
|
||||
tbl.innerHTML = rows;
|
||||
|
||||
document.getElementById('eti-exercise').style.display = 'none';
|
||||
document.getElementById('eti-recap').style.display = 'flex';
|
||||
etiRecapShown = true;
|
||||
renderBarActions();
|
||||
|
||||
// Animation : l'étiquette glisse à gauche, le tableau apparaît à sa droite
|
||||
const rl = document.getElementById('recap-layout');
|
||||
if (rl) { rl.classList.remove('animate'); void rl.offsetWidth; rl.classList.add('animate'); }
|
||||
|
||||
const sa = document.querySelector('#slide-7 .scroll-area');
|
||||
if (sa) sa.scrollTop = 0;
|
||||
}
|
||||
|
||||
function hideEtiRecap() {
|
||||
document.getElementById('eti-recap').style.display = 'none';
|
||||
document.getElementById('eti-exercise').style.display = 'flex';
|
||||
etiRecapShown = false;
|
||||
renderBarActions();
|
||||
const sa = document.querySelector('#slide-7 .scroll-area');
|
||||
if (sa) sa.scrollTop = 0;
|
||||
}
|
||||
|
||||
/* ---- FT : exercice « relier » ---- */
|
||||
const ftConnections = {}; // { qId: valeur de la section reliée }
|
||||
let ftDrag = null; // { qid } pendant un glissement
|
||||
@@ -1685,8 +1566,8 @@ function renderBarActions() {
|
||||
const bar = document.getElementById('barActions');
|
||||
if (!bar) return;
|
||||
bar.innerHTML = '';
|
||||
// Bouton retour intégré au groupe centré (sauf sur le récapitulatif, qui a son propre retour)
|
||||
if (history.length > 0 && !(currentSlide === 7 && etiRecapShown)) {
|
||||
// Bouton retour intégré au groupe centré
|
||||
if (history.length > 0) {
|
||||
barBtn(bar, '← Retour', 'is-secondary', goBack);
|
||||
}
|
||||
switch (currentSlide) {
|
||||
@@ -1713,14 +1594,9 @@ function renderBarActions() {
|
||||
barBtn(bar, 'Répondre aux questions →', 'is-primary', () => goTo(7));
|
||||
break;
|
||||
case 7:
|
||||
if (etiRecapShown) {
|
||||
barBtn(bar, '← Retour à l\'exercice', 'is-secondary', hideEtiRecap);
|
||||
barBtn(bar, 'Voir la synthèse générale →', 'is-primary', () => goTo(8));
|
||||
} else {
|
||||
barBtn(bar, '↺ Recommencer', 'is-secondary', resetEtiquette);
|
||||
barBtn(bar, 'Terminer l\'exercice →', 'is-primary', showEtiRecap,
|
||||
barBtn(bar, 'Terminer l\'exercice →', 'is-primary', () => goTo(8),
|
||||
{ id: 'eti-recap-btn', disabled: etiPos < etiOrder.length });
|
||||
}
|
||||
break;
|
||||
case 8:
|
||||
// Boutons placés sous le tableau comparatif, dans la slide (pas dans la bottom bar)
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
justify-content: center;
|
||||
padding: 2rem;
|
||||
padding-top: calc(70px + 2rem);
|
||||
padding-bottom: calc(52px + 2rem);
|
||||
padding-bottom: calc(84px + 2rem);
|
||||
opacity: 0;
|
||||
transition: opacity 0.4s ease;
|
||||
overflow-y: auto;
|
||||
@@ -38,73 +38,67 @@
|
||||
transition: width 0.4s ease;
|
||||
}
|
||||
|
||||
/* PHASE INDICATOR */
|
||||
.phase-badge {
|
||||
position: fixed;
|
||||
top: 80px; right: 16px;
|
||||
font-family: 'Syne', sans-serif;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
padding: 4px 12px;
|
||||
border-radius: 20px;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
/* BOTTOM BAR */
|
||||
.bottom-bar {
|
||||
position: fixed;
|
||||
bottom: 0; left: 0; right: 0;
|
||||
height: 52px;
|
||||
height: 84px;
|
||||
background: rgba(250, 238, 218, 0.98);
|
||||
border-top: 2px solid var(--amber);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
justify-content: center;
|
||||
padding: 0 20px;
|
||||
z-index: 100;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
/* SLIDE NAV */
|
||||
.bottom-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
.footer-btn {
|
||||
font-family: 'Syne', sans-serif;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
padding: 11px 24px;
|
||||
border-radius: 40px;
|
||||
border: 1.5px solid var(--amber);
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
line-height: 1;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.footer-btn:hover { transform: translateY(-1px); }
|
||||
.footer-btn:active { transform: scale(0.98); }
|
||||
.footer-btn.is-primary { background: var(--amber); color: #fff; }
|
||||
.footer-btn.is-primary:hover { background: var(--amber-dark); }
|
||||
.footer-btn.is-secondary { background: transparent; color: var(--amber-dark); }
|
||||
.footer-btn.is-secondary:hover { background: rgba(186,117,23,0.08); }
|
||||
.slide-counter {
|
||||
position: absolute;
|
||||
left: 20px;
|
||||
font-size: 12px;
|
||||
font-family: 'Syne', sans-serif;
|
||||
font-weight: 600;
|
||||
color: var(--amber-dark);
|
||||
}
|
||||
|
||||
/* BACK BUTTON */
|
||||
.back-btn {
|
||||
background: none;
|
||||
border: 0.5px solid var(--amber);
|
||||
border-radius: 20px;
|
||||
padding: 6px 16px;
|
||||
font-size: 12px;
|
||||
color: var(--amber-dark);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
font-family: 'DM Sans', sans-serif;
|
||||
@media (max-width: 560px) {
|
||||
.slide-counter { display: none; }
|
||||
}
|
||||
.back-btn:hover { background: rgba(186,117,23,0.08); }
|
||||
|
||||
/* FORMATEUR SELECTOR */
|
||||
.formateur-btn {
|
||||
font-family: 'Syne', sans-serif;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
padding: 10px 24px;
|
||||
border-radius: 30px;
|
||||
border: 1.5px solid var(--border);
|
||||
background: white;
|
||||
color: var(--text-muted);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
flex: 1;
|
||||
flex: 1; font-family: 'DM Sans', sans-serif; font-size: 13.5px;
|
||||
padding: 9px 13px; border: 1px solid var(--border); border-radius: 10px;
|
||||
background: var(--bg); color: var(--text); cursor: pointer; transition: all 0.2s ease;
|
||||
}
|
||||
.formateur-btn:hover { border-color: var(--amber); color: var(--amber); }
|
||||
.formateur-btn.selected { background: var(--amber-light); border-color: var(--amber); color: var(--amber-dark); font-weight: 700; }
|
||||
.formateur-btn:hover { border-color: var(--amber); }
|
||||
.formateur-btn.selected { background: var(--amber); border-color: var(--amber); color: #fff; }
|
||||
|
||||
/* CARDS & BUTTONS */
|
||||
.btn {
|
||||
@@ -246,52 +240,42 @@
|
||||
.q-item { flex-direction: column; align-items: stretch; }
|
||||
.q-item .q-head { display: flex; align-items: flex-start; gap: 12px; }
|
||||
|
||||
/* IDENTIFICATION SCREEN */
|
||||
.id-form {
|
||||
width: 100%;
|
||||
max-width: 420px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
margin: 1.25rem 0;
|
||||
}
|
||||
.id-field label {
|
||||
display: block;
|
||||
font-family: 'Syne', sans-serif;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.05em;
|
||||
text-transform: uppercase;
|
||||
color: var(--text-muted);
|
||||
margin-bottom: 6px;
|
||||
text-align: left;
|
||||
}
|
||||
.id-field input {
|
||||
width: 100%;
|
||||
font-family: 'DM Sans', sans-serif;
|
||||
font-size: 15px;
|
||||
padding: 12px 16px;
|
||||
border: 1.5px solid var(--border);
|
||||
border-radius: 10px;
|
||||
background: white;
|
||||
color: var(--text);
|
||||
}
|
||||
.id-field input:focus {
|
||||
outline: none;
|
||||
border-color: var(--amber);
|
||||
}
|
||||
/* Carte d'identité encadrée (modèle BioCID) */
|
||||
.id-card {
|
||||
/* IDENTIFICATION SCREEN — carte modèle BioCID / FDS */
|
||||
.identity-card {
|
||||
background: white;
|
||||
border: 0.5px solid var(--border);
|
||||
border-radius: 16px;
|
||||
padding: 20px 22px;
|
||||
border-radius: 14px;
|
||||
padding: 16px 18px;
|
||||
max-width: 460px;
|
||||
width: 100%;
|
||||
margin: 1.25rem auto;
|
||||
box-shadow: 0 8px 30px rgba(0,0,0,0.04);
|
||||
text-align: left;
|
||||
}
|
||||
.id-card .id-form { margin: 0; max-width: none; }
|
||||
.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; }
|
||||
.formateur-label { font-size: 11px; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; color: var(--text-muted); margin: 4px 0 2px; }
|
||||
.formateur-row { display: flex; gap: 8px; }
|
||||
|
||||
/* TABLE FORM CONTROLS */
|
||||
.risk-table input, .risk-table textarea {
|
||||
@@ -599,7 +583,7 @@
|
||||
gap: 12px;
|
||||
max-width: 640px;
|
||||
width: 100%;
|
||||
margin: 1.5rem 0;
|
||||
margin: 1.5rem auto;
|
||||
}
|
||||
.final-card {
|
||||
background: white;
|
||||
@@ -631,7 +615,7 @@
|
||||
/* SCROLLABLE AREA */
|
||||
.scroll-area {
|
||||
overflow-y: auto;
|
||||
max-height: calc(100vh - 232px);
|
||||
max-height: calc(100vh - 200px);
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -672,13 +656,14 @@
|
||||
<!-- Progress bar -->
|
||||
<div class="progress-bar"><div class="progress-fill" id="progressFill"></div></div>
|
||||
|
||||
<!-- Phase badge -->
|
||||
<div class="phase-badge" id="phaseBadge" style="display:none"></div>
|
||||
|
||||
<!-- Bottom bar -->
|
||||
<div class="bottom-bar" id="bottomBar" style="display:none;">
|
||||
<button class="back-btn" id="backBtn" onclick="goBack()" style="display:none;">← Retour</button>
|
||||
<div class="slide-counter" id="slideCounter"></div>
|
||||
<div class="bottom-actions">
|
||||
<button class="footer-btn is-secondary" id="backBtn" onclick="goBack()" style="display:none;">← Retour</button>
|
||||
<button class="footer-btn is-secondary" id="footerHintBtn" onclick="showHintCurrent()" style="display:none;">💡 Besoin d'aide ?</button>
|
||||
<button class="footer-btn is-primary" id="footerNextBtn" onclick="goNext()" style="display:none;">Étape suivante →</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal overlay -->
|
||||
@@ -691,52 +676,41 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ======================== SLIDE 0 — IDENTIFICATION ======================== -->
|
||||
<!-- ======================== SLIDE 0 — ACCUEIL ======================== -->
|
||||
<div class="slide active fade-in" id="slide-0">
|
||||
<div class="deco-circle" style="width:320px;height:320px;background:var(--amber-light);top:-80px;left:-80px;opacity:0.5;"></div>
|
||||
<div style="text-align:center; z-index:1; width:100%;">
|
||||
<div class="deco-circle" style="width:400px;height:400px;background:var(--amber-light);top:-100px;right:-80px;opacity:0.6;"></div>
|
||||
<div class="deco-circle" style="width:200px;height:200px;background:var(--amber-light);bottom:60px;left:-60px;opacity:0.5;"></div>
|
||||
<div style="text-align:center;">
|
||||
<div class="hero-label">Certibiocide — Travaux pratiques</div>
|
||||
<div class="hero-title">Étude de cas<br>Nuisibles</div>
|
||||
<p class="hero-sub">Votre mission, si vous l'acceptez : maîtriser l'identification, le traitement et la sécurité face aux nuisibles.</p>
|
||||
<button class="btn btn-amber" onclick="goTo(1)">Démarrer la mission →</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ======================== SLIDE 1 — CONTEXTE & IDENTIFICATION ======================== -->
|
||||
<div class="slide" id="slide-1">
|
||||
<div class="deco-circle" style="width:300px;height:300px;background:var(--amber-light);top:-60px;left:-60px;opacity:0.4;"></div>
|
||||
<div class="section-tag" style="background:var(--amber-light);color:var(--amber-dark);">Avant de commencer</div>
|
||||
<div class="slide-title">Identification du groupe</div>
|
||||
<p class="slide-intro" style="margin:0.5rem auto 0;max-width:420px;">Ces informations permettront au formateur de retrouver vos réponses.</p>
|
||||
<div class="id-card">
|
||||
<div class="id-form">
|
||||
<div class="id-field">
|
||||
<label for="inputNom">Nom du stagiaire ou du groupe</label>
|
||||
<input type="text" id="inputNom" placeholder="Ex : Groupe 3 - Dupont / Martin" autocomplete="off">
|
||||
<div class="slide-title">Contexte & accès</div>
|
||||
<p class="slide-intro">3 travaux pratiques vous attendent : protocole d'intervention, recherche produit sur BioCID, puis analyse de risques. Ces informations permettront au formateur de retrouver vos réponses.</p>
|
||||
<div class="info-card" style="max-width:620px;">
|
||||
<span>🎯</span>
|
||||
<div>Vous allez définir un <strong>protocole d'intervention</strong>, trouver votre <strong>produit sur BioCID</strong>, puis réaliser l'<strong>analyse de risques</strong>.</div>
|
||||
</div>
|
||||
<div class="id-field">
|
||||
<label for="inputDate">Date de la session</label>
|
||||
<div class="identity-card" style="max-width:620px;margin-top:0.5rem;">
|
||||
<div class="identity-title">👥 Identification du groupe</div>
|
||||
<div class="identity-fields">
|
||||
<input type="text" id="inputNom" placeholder="Nom du groupe / stagiaires (ex : Groupe 3 — Dupont / Martin)" autocomplete="off">
|
||||
<input type="date" id="inputDate">
|
||||
</div>
|
||||
<div class="id-field">
|
||||
<label>Formateur de la session</label>
|
||||
<div style="display:flex;gap:10px;margin-top:2px;">
|
||||
<div class="formateur-label">Formateur de la session</div>
|
||||
<div class="formateur-row">
|
||||
<button type="button" class="formateur-btn" id="btnNathan" onclick="selectFormateur('Nathan')">Nathan</button>
|
||||
<button type="button" class="formateur-btn" id="btnGauthier" onclick="selectFormateur('Gauthier')">Gauthier</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="idError" style="color:var(--coral);font-size:13px;margin-top:8px;display:none;">Merci de renseigner au moins le nom du groupe.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="idError" style="color:var(--coral);font-size:13px;margin-bottom:0.5rem;display:none;">Merci de renseigner au moins le nom du groupe.</div>
|
||||
<div style="display:flex;gap:12px;justify-content:center;flex-wrap:wrap;">
|
||||
<button class="btn btn-teal" onclick="confirmIdentite()">Continuer →</button>
|
||||
<a href="corriger-exercice.html?exo=tp" class="btn btn-outline btn-teal">Corriger</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ======================== SLIDE 1 — ACCUEIL ======================== -->
|
||||
<div class="slide" id="slide-1">
|
||||
<div class="deco-circle" style="width:400px;height:400px;background:var(--amber-light);top:-100px;right:-80px;opacity:0.6;"></div>
|
||||
<div class="deco-circle" style="width:200px;height:200px;background:var(--amber-light);bottom:60px;left:-60px;opacity:0.5;"></div>
|
||||
<div class="section-tag" style="background:var(--amber-light);color:var(--amber-dark);">Avant de commencer</div>
|
||||
<div class="slide-title">Certibiocide — Travaux pratiques</div>
|
||||
<p class="slide-intro">Votre mission, si vous l'acceptez : maîtriser l'identification, le traitement et la sécurité face aux nuisibles.</p>
|
||||
<div class="info-card">
|
||||
<span>🎯</span>
|
||||
<div>3 travaux pratiques vous attendent : définir un <strong>protocole d'intervention</strong>, trouver votre <strong>produit sur BioCID</strong>, puis réaliser l'<strong>analyse de risques</strong>.</div>
|
||||
</div>
|
||||
<button class="btn btn-teal" style="margin-top:1.5rem;" onclick="goTo(2)">Démarrer la mission →</button>
|
||||
</div>
|
||||
|
||||
<!-- ======================== SLIDE 2 — CHOIX DU CAS ======================== -->
|
||||
@@ -776,9 +750,6 @@
|
||||
<div class="case-sub">Camping</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="actions-row">
|
||||
<button class="btn btn-teal" id="btnConfirmCase" onclick="goTo(3)" style="display:none">Confirmer le choix →</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ======================== SLIDE 3 — CONSIGNES ======================== -->
|
||||
@@ -806,7 +777,6 @@
|
||||
<span>💡</span>
|
||||
<div>À tout moment, cliquez sur <strong>« Besoin d'aide ? »</strong> pour accéder à un indice. Le formateur corrigera à l'oral en fin de séance.</div>
|
||||
</div>
|
||||
<button class="btn btn-teal" onclick="goTo(4)">Commencer le TP 1 →</button>
|
||||
</div>
|
||||
|
||||
<!-- ======================== SLIDE 4 — TP1 INTRO ======================== -->
|
||||
@@ -824,7 +794,6 @@
|
||||
<div class="step-pill">2. Intervention</div>
|
||||
<div class="step-pill">3. Évaluation</div>
|
||||
</div>
|
||||
<button class="btn btn-amber" onclick="goTo(5)">Commencer →</button>
|
||||
</div>
|
||||
|
||||
<!-- ======================== SLIDE 5 — TP1 PREVENTION ======================== -->
|
||||
@@ -865,10 +834,6 @@
|
||||
<textarea class="answer-field" id="ans_prevention_4" placeholder="Votre réponse…" data-question="Éléments observés / preuve de présence du nuisible"></textarea>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="actions-row">
|
||||
<button class="hint-btn" onclick="showHint('prevention')">💡 Besoin d'aide ?</button>
|
||||
<button class="btn btn-amber" onclick="goTo(6)">Étape suivante →</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -903,10 +868,6 @@
|
||||
<textarea class="answer-field" id="ans_intervention_3" placeholder="Votre réponse…" data-question="Type de produit (TP) et mode de traitement"></textarea>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="actions-row">
|
||||
<button class="hint-btn" onclick="showHint('intervention')">💡 Besoin d'aide ?</button>
|
||||
<button class="btn btn-amber" onclick="goTo(7)">Étape suivante →</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -937,10 +898,6 @@
|
||||
<div style="background:var(--amber-light);border-radius:12px;padding:14px 18px;max-width:680px;width:100%;font-size:13px;color:var(--amber-dark);margin:0.75rem 0;">
|
||||
✅ <strong>TP 1 terminé !</strong> Préparez-vous à identifier le produit adapté à votre cas sur biocid-anses.fr.
|
||||
</div>
|
||||
<div class="actions-row">
|
||||
<button class="hint-btn" onclick="showHint('evaluation')">💡 Besoin d'aide ?</button>
|
||||
<button class="btn btn-coral" onclick="goTo(8)">Passer au TP 2 →</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -962,7 +919,6 @@
|
||||
</div>
|
||||
<a href="https://biocid-anses.fr" target="_blank" class="resource-link">Ouvrir le site ↗</a>
|
||||
</div>
|
||||
<button class="btn btn-coral" onclick="goTo(9)">Commencer →</button>
|
||||
</div>
|
||||
|
||||
<!-- ======================== SLIDE 9 — TP2 FDS & AMM ======================== -->
|
||||
@@ -1003,10 +959,6 @@
|
||||
<textarea class="answer-field" id="ans_fds_4" placeholder="Votre réponse…" data-question="Risques pour la santé"></textarea>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="actions-row">
|
||||
<button class="hint-btn" onclick="showHint('fds')">💡 Besoin d'aide ?</button>
|
||||
<button class="btn btn-coral" onclick="goTo(10)">Partie suivante →</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1051,10 +1003,6 @@
|
||||
<div style="background:var(--amber-light);border-radius:12px;padding:14px 18px;max-width:680px;width:100%;font-size:13px;color:var(--amber-dark);margin:0.75rem 0;">
|
||||
✅ <strong>TP 2 terminé !</strong> Passez maintenant à l'analyse de risques complète.
|
||||
</div>
|
||||
<div class="actions-row">
|
||||
<button class="hint-btn" onclick="showHint('epi')">💡 Besoin d'aide ?</button>
|
||||
<button class="btn btn-purple" onclick="goTo(11)">Passer au TP 3 →</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1071,9 +1019,6 @@
|
||||
<div class="pyr-level" style="width:320px;background:#D85A30;">Communication</div>
|
||||
<div class="pyr-level" style="width:360px;background:#BA7517;">EPI (dernier recours)</div>
|
||||
</div>
|
||||
<div class="actions-row" style="margin-top:1rem;">
|
||||
<button class="btn btn-purple" onclick="goTo(12)">Remplir le tableau →</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ======================== SLIDE 12 — TP3 TABLEAU ======================== -->
|
||||
@@ -1142,10 +1087,6 @@
|
||||
<div style="background:var(--amber-light);border-radius:12px;padding:14px 18px;max-width:780px;width:100%;font-size:13px;color:var(--amber-dark);margin:0.25rem 0;">
|
||||
✅ <strong>TP 3 terminé !</strong> Présentez vos réponses au formateur pour la correction.
|
||||
</div>
|
||||
<div class="actions-row">
|
||||
<button class="hint-btn" onclick="showHint('analyse')">💡 Aide générale</button>
|
||||
<button class="btn btn-blue" onclick="goTo(13)">Terminer la mission →</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1175,15 +1116,10 @@
|
||||
<div class="final-card-sub">Analyse de risques</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="submit-zone">
|
||||
<div style="display:flex;gap:10px;justify-content:center;flex-wrap:wrap;">
|
||||
<button class="btn btn-teal" onclick="exporterPDF()">⬇ Télécharger en PDF</button>
|
||||
<a href="corriger-exercice.html?exo=tp" class="btn btn-outline btn-teal">Corriger</a>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display:flex;gap:10px;justify-content:center;flex-wrap:wrap;margin-top:0.75rem;">
|
||||
<a href="https://biocid-anses.fr" target="_blank" class="btn btn-outline btn-teal" style="font-size:13px;padding:10px 20px;">biocid-anses.fr ↗</a>
|
||||
<button class="btn btn-teal" onclick="restartAll()">↺ Recommencer depuis le début</button>
|
||||
<div class="actions-row">
|
||||
<button class="btn btn-amber" onclick="exporterPDF()">⬇ Télécharger mes réponses (PDF)</button>
|
||||
<a href="corriger-exercice.html?exo=tp" class="btn btn-outline btn-teal">✔ Corriger un rendu</a>
|
||||
<button class="btn btn-outline btn-teal" onclick="restartAll()">↺ Recommencer</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1196,6 +1132,22 @@ const totalSlides = 13;
|
||||
const selectedCase = { key: '', icon: '', title: '', desc: '' };
|
||||
const stagiaire = { nom: '', date: '', formateur: '' };
|
||||
|
||||
// Configuration des boutons du footer par slide (modèle BioCID)
|
||||
const slideConfig = {
|
||||
1: { next: 2, nextLabel: 'Continuer →' },
|
||||
2: { next: 3, nextLabel: 'Confirmer le choix →' },
|
||||
3: { next: 4, nextLabel: 'Commencer le TP 1 →' },
|
||||
4: { next: 5, nextLabel: 'Commencer →' },
|
||||
5: { next: 6, nextLabel: 'Étape suivante →', hint: 'prevention' },
|
||||
6: { next: 7, nextLabel: 'Étape suivante →', hint: 'intervention' },
|
||||
7: { next: 8, nextLabel: 'Passer au TP 2 →', hint: 'evaluation' },
|
||||
8: { next: 9, nextLabel: 'Commencer →' },
|
||||
9: { next: 10, nextLabel: 'Partie suivante →', hint: 'fds' },
|
||||
10: { next: 11, nextLabel: 'Passer au TP 3 →', hint: 'epi' },
|
||||
11: { next: 12, nextLabel: 'Remplir le tableau →' },
|
||||
12: { next: 13, nextLabel: 'Terminer la mission →', hint: 'analyse' }
|
||||
};
|
||||
|
||||
const hints = {
|
||||
prevention: {
|
||||
icon: '💡',
|
||||
@@ -1282,11 +1234,28 @@ function goTo(n) {
|
||||
currentSlide = n;
|
||||
next.style.display = 'flex';
|
||||
setTimeout(() => { next.classList.add('active', 'fade-in'); }, 10);
|
||||
const sa = next.querySelector('.scroll-area');
|
||||
if (sa) sa.scrollTop = 0;
|
||||
if (n === 13) setTimeout(launchConfetti, 200);
|
||||
updateUI();
|
||||
updateCaseBanners();
|
||||
}
|
||||
|
||||
function goNext() {
|
||||
const cfg = slideConfig[currentSlide];
|
||||
if (!cfg || cfg.next == null) return;
|
||||
// Slide 1 : validation du nom du groupe avant de continuer
|
||||
if (currentSlide === 1) { confirmIdentite(); return; }
|
||||
// Slide 2 : un cas doit être sélectionné
|
||||
if (currentSlide === 2 && !selectedCase.key) return;
|
||||
goTo(cfg.next);
|
||||
}
|
||||
|
||||
function showHintCurrent() {
|
||||
const cfg = slideConfig[currentSlide];
|
||||
if (cfg && cfg.hint) showHint(cfg.hint);
|
||||
}
|
||||
|
||||
function goBack() {
|
||||
if (history.length === 0) return;
|
||||
const prev = history.pop();
|
||||
@@ -1311,26 +1280,19 @@ function updateUI() {
|
||||
if (bottomBar) bottomBar.style.display = currentSlide === 0 ? 'none' : 'flex';
|
||||
|
||||
const backBtn = document.getElementById('backBtn');
|
||||
backBtn.style.display = history.length > 0 ? 'block' : 'none';
|
||||
backBtn.style.display = history.length > 0 ? 'inline-flex' : 'none';
|
||||
|
||||
const badge = document.getElementById('phaseBadge');
|
||||
if (currentSlide === 0 || currentSlide === 1 || currentSlide === 2 || currentSlide === 3 || currentSlide === 13) {
|
||||
badge.style.display = 'none';
|
||||
} else if (currentSlide >= 4 && currentSlide <= 7) {
|
||||
badge.style.display = 'block';
|
||||
badge.style.background = 'var(--amber-light)';
|
||||
badge.style.color = 'var(--amber-dark)';
|
||||
badge.textContent = 'TP 1 — Protocole';
|
||||
} else if (currentSlide >= 8 && currentSlide <= 10) {
|
||||
badge.style.display = 'block';
|
||||
badge.style.background = 'var(--coral-light)';
|
||||
badge.style.color = 'var(--coral)';
|
||||
badge.textContent = 'TP 2 — Produit';
|
||||
} else if (currentSlide >= 11 && currentSlide <= 12) {
|
||||
badge.style.display = 'block';
|
||||
badge.style.background = 'var(--amber-light)';
|
||||
badge.style.color = 'var(--amber-dark)';
|
||||
badge.textContent = 'TP 3 — Risques';
|
||||
const cfg = slideConfig[currentSlide] || {};
|
||||
const hintBtn = document.getElementById('footerHintBtn');
|
||||
const nextBtn = document.getElementById('footerNextBtn');
|
||||
hintBtn.style.display = cfg.hint ? 'inline-flex' : 'none';
|
||||
if (cfg.next != null) {
|
||||
// Slide 2 : le bouton n'apparaît qu'une fois le cas choisi
|
||||
const gated = currentSlide === 2 && !selectedCase.key;
|
||||
nextBtn.style.display = gated ? 'none' : 'inline-flex';
|
||||
nextBtn.textContent = cfg.nextLabel;
|
||||
} else {
|
||||
nextBtn.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1342,7 +1304,7 @@ function selectCase(key, icon, title, desc) {
|
||||
selectedCase.desc = desc;
|
||||
document.querySelectorAll('.case-card').forEach(c => c.classList.remove('selected'));
|
||||
event.currentTarget.classList.add('selected');
|
||||
document.getElementById('btnConfirmCase').style.display = 'inline-flex';
|
||||
updateUI();
|
||||
}
|
||||
|
||||
function updateCaseBanners() {
|
||||
@@ -1412,7 +1374,7 @@ function confirmIdentite() {
|
||||
document.getElementById('idError').style.display = 'none';
|
||||
stagiaire.nom = nom;
|
||||
stagiaire.date = date || new Date().toISOString().slice(0, 10);
|
||||
goTo(1);
|
||||
goTo(2);
|
||||
}
|
||||
|
||||
/* ======================== TABLEAU TP3 — LIGNES DYNAMIQUES ======================== */
|
||||
|
||||
@@ -319,6 +319,57 @@ h1 {
|
||||
vertical-align: top;
|
||||
}
|
||||
.annex th { background: var(--amber-light); font-family: 'Syne', sans-serif; font-size: 12px; }
|
||||
|
||||
/* ======= Corrigé de référence — étiquette annotée (repris du TD FDS) ======= */
|
||||
.recap-intro { font-size: 13.5px; color: var(--text-muted); line-height: 1.6; margin: 0 0 16px; }
|
||||
.recap-layout { display: flex; gap: 20px; align-items: flex-start; flex-wrap: wrap; }
|
||||
.recap-stage { flex: 1 1 320px; min-width: 0; max-width: 560px; }
|
||||
.recap-layout .checklist-table { flex: 1 1 320px; min-width: 0; margin: 0; }
|
||||
|
||||
.label-card {
|
||||
position: relative;
|
||||
width: 100%; max-width: 640px;
|
||||
background: #fff; color: #1a1a18;
|
||||
border: 1px solid #d9d2c4; border-radius: 6px;
|
||||
padding: 16px 18px 0; overflow: hidden;
|
||||
font-family: 'DM Sans', sans-serif; font-size: 10.5px; line-height: 1.42;
|
||||
box-shadow: 0 8px 30px rgba(0,0,0,0.10);
|
||||
}
|
||||
.lbl-top { display: flex; align-items: center; gap: 12px; border-bottom: 2px solid #1a1a18; padding-bottom: 6px; margin-bottom: 10px; }
|
||||
.lbl-flag { font-size: 20px; }
|
||||
.lbl-title { font-family: 'Syne', sans-serif; font-weight: 800; font-size: clamp(24px, 5vw, 40px); letter-spacing: -0.01em; margin: 0; }
|
||||
.lbl-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
|
||||
.lbl-h { font-family: 'Syne', sans-serif; font-weight: 700; font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.02em; margin-bottom: 2px; }
|
||||
.lbl-block { margin-bottom: 9px; }
|
||||
.lbl-small { color: #555; font-size: 9.5px; line-height: 1.6; margin-top: 4px; }
|
||||
.lbl-bottom { display: grid; grid-template-columns: auto 1fr auto; align-items: end; gap: 14px; margin-top: 4px; padding: 10px 0; }
|
||||
.lbl-net { display: flex; flex-direction: column; gap: 2px; }
|
||||
.lbl-net span { font-size: 10px; }
|
||||
.lbl-net strong { font-family: 'Syne', sans-serif; font-size: 22px; line-height: 1; }
|
||||
.lbl-barcode { height: 42px; align-self: end; min-width: 90px; background: repeating-linear-gradient(90deg, #1a1a18 0 2px, #fff 2px 4px, #1a1a18 4px 5px, #fff 5px 9px); }
|
||||
.lbl-picto-wrap { display: flex; align-items: center; gap: 8px; }
|
||||
.pictos { display: flex; gap: 6px; align-items: center; }
|
||||
.ghs-img { width: 42px; height: 42px; object-fit: contain; display: block; }
|
||||
.lbl-danger { font-family: 'Syne', sans-serif; font-weight: 800; font-size: 15px; color: #1a1a18; letter-spacing: 0.02em; }
|
||||
.lbl-footer { margin: 6px -18px 0; padding: 8px 18px; background: #2b2b28; color: #e8e2d5; text-align: center; font-size: 9px; line-height: 1.5; }
|
||||
|
||||
.hotspot { position: relative; border-radius: 5px; }
|
||||
span.hotspot { display: inline; padding: 0 2px; -webkit-box-decoration-break: clone; box-decoration-break: clone; }
|
||||
.hotspot.hs-correct { background: rgba(46,125,50,0.14); outline: 1.5px solid #2e7d32; }
|
||||
.hs-num { display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 16px; border-radius: 50%; background: #2e7d32; color: #fff; font-family: 'Syne', sans-serif; font-weight: 800; font-size: 9px; margin-right: 3px; vertical-align: middle; }
|
||||
|
||||
.checklist-table { width: 100%; border-collapse: collapse; background: #fff; border: 1px solid var(--border); border-radius: 12px; overflow: hidden; font-size: 12.5px; }
|
||||
.checklist-table th { background: var(--teal-light); color: var(--teal); font-family: 'Syne', sans-serif; font-weight: 700; font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.03em; padding: 10px 12px; text-align: left; border-bottom: 2px solid var(--teal); }
|
||||
.checklist-table td { padding: 9px 12px; border-bottom: 1px solid var(--border); color: var(--text-muted); vertical-align: top; }
|
||||
.checklist-table tr:last-child td { border-bottom: none; }
|
||||
.checklist-table td.cl-num { text-align: center; }
|
||||
.cl-badge { display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px; border-radius: 50%; background: #2e7d32; color: #fff; font-family: 'Syne', sans-serif; font-weight: 800; font-size: 10px; }
|
||||
.checklist-table strong { color: var(--text); }
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.lbl-cols { grid-template-columns: 1fr; }
|
||||
.lbl-title { font-size: 26px; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -618,6 +669,109 @@ function fdsExpectedLabel(part, q) {
|
||||
return parts[0] === "absent" ? "Absent de la FDS" : "Rubrique " + parts[0];
|
||||
}
|
||||
|
||||
/* ---- Corrigé de référence : l'étiquette annotée + check-list (repris du TD) ---- */
|
||||
const ETI_ZONES = [
|
||||
{ zone: "z-nom", label: "Nom commercial",
|
||||
reg: "Obligation CLP : le nom commercial doit être identique à celui figurant sur la FDS (Guide FHER, Partie 3 — I.2)." },
|
||||
{ zone: "z-picto", label: "Pictogrammes de danger",
|
||||
reg: "CLP : pictogrammes aux dimensions requises, regroupés avec la mention d'avertissement et les phrases H/P, se détachant nettement du fond (Partie 3 — I.5)." },
|
||||
{ zone: "z-mention", label: "Mention d'avertissement",
|
||||
reg: "CLP : « Danger » ou « Attention », disposée dans l'encart d'étiquetage avec pictogrammes et phrases H/P (Partie 3 — I.5)." },
|
||||
{ zone: "z-h", label: "Mentions de danger (phrases H)",
|
||||
reg: "CLP : les phrases H décrivent la nature du danger ; regroupées dans l'encart d'étiquetage (Partie 3 — I.5)." },
|
||||
{ zone: "z-p-epi", label: "Conseils de prudence — EPI",
|
||||
reg: "CLP : conseils de prudence P (ici P280). Six conseils P au maximum, sauf si la nature et la gravité des dangers l'exigent (CLP Art. 28-3) — Partie 3, I.5." },
|
||||
{ zone: "z-p-secours", label: "Premiers secours (phrases P)",
|
||||
reg: "BPR : indications relatives aux premiers soins (Partie 3 — II.8), traduites par les conseils P 3xx (contact, ingestion, inhalation)." },
|
||||
{ zone: "z-p-elim", label: "Élimination du produit et de l'emballage",
|
||||
reg: "BPR : instructions pour l'élimination en toute sécurité du produit et de son emballage, le cas échéant avec interdiction de le réutiliser (Partie 3 — II.10). Ici P501." },
|
||||
{ zone: "z-net", label: "Contenu net",
|
||||
reg: "Quantité nette de produit contenue dans l'emballage — mention générale du pavé d'information produit." },
|
||||
{ zone: "z-antipoison", label: "Centre antipoison / n° d'urgence",
|
||||
reg: "Coordonnées d'urgence complétant les instructions de premiers soins (Partie 3 — II.8) ; les mélanges dangereux sont déclarés aux centres antipoison (annexe VIII du CLP)." },
|
||||
];
|
||||
|
||||
const ETI_LABEL_CARD = `
|
||||
<div class="label-card recap">
|
||||
<div class="lbl-top">
|
||||
<span class="lbl-flag">🇫🇷</span>
|
||||
<span class="hotspot" data-zone="z-nom"><span class="lbl-title">DÉS'INFECT</span></span>
|
||||
</div>
|
||||
<div class="lbl-cols">
|
||||
<div class="lbl-col">
|
||||
<div class="lbl-block"><div class="lbl-h">Propriétés :</div>Désinfecte et désodorise les sanitaires. Produit strictement destiné à un usage professionnel.</div>
|
||||
<div class="lbl-block"><div class="lbl-h">Mode d'emploi :</div>Concentré à diluer : 5 % pour une activité fongicide, 1 % pour une activité bactéricide. Pulvériser sur les parois, laisser agir 5 à 15 min, rincer à l'eau claire. Type de formulation : concentré soluble (SL). Utilisable en TP2.</div>
|
||||
<div class="lbl-small">UFI : 1234-1234-1234-1234<br>Date de péremption : MM/AAAA<br>N° LOT : 123456</div>
|
||||
</div>
|
||||
<div class="lbl-col">
|
||||
<div class="lbl-block"><div class="lbl-h">Substances actives :</div>Chlorure de didécyldiméthylammonium (N°CAS 7173-51-5) à 75 g/kg ; acide glycolique (CAS 79-14-1) à 35 g/kg.</div>
|
||||
<div class="lbl-block">
|
||||
<div class="lbl-h">Précautions d'emploi :</div>
|
||||
<span>Produit strictement professionnel (FR) — Lire le mode d'emploi ci-joint avant l'emploi. </span>
|
||||
<span class="hotspot" data-zone="z-antipoison">Formule déposée au centre anti-poison de Nancy : 03 83 22 50 50.</span>
|
||||
<span> </span>
|
||||
<span class="hotspot" data-zone="z-h">H302 Nocif en cas d'ingestion. H314 Provoque de graves brûlures de la peau et des lésions oculaires. H410 Très toxique pour les organismes aquatiques, entraîne des effets néfastes à long terme.</span>
|
||||
<span> </span>
|
||||
<span class="hotspot" data-zone="z-p-epi">P280 Porter des gants de protection, des vêtements de protection et un équipement de protection du visage.</span>
|
||||
<span> </span>
|
||||
<span class="hotspot" data-zone="z-p-secours">P301+P330+P331 EN CAS D'INGESTION : rincer la bouche, ne pas faire vomir. P305+P351+P338 EN CAS DE CONTACT AVEC LES YEUX : rincer avec précaution à l'eau pendant plusieurs minutes. P310 Appeler immédiatement un CENTRE ANTIPOISON ou un médecin.</span>
|
||||
<span> </span>
|
||||
<span class="hotspot" data-zone="z-p-elim">P501 Éliminer le contenu / le récipient dans un point de collecte des déchets spéciaux ou dangereux.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="lbl-bottom">
|
||||
<span class="hotspot" data-zone="z-net">
|
||||
<span class="lbl-net"><span>Volume net :</span><strong>5 L</strong></span>
|
||||
</span>
|
||||
<div class="lbl-barcode" aria-hidden="true"></div>
|
||||
<div class="lbl-picto-wrap">
|
||||
<span class="hotspot" data-zone="z-picto">
|
||||
<span class="pictos">
|
||||
<img class="ghs-img" src="../data/ghs09-environnement.jpg" alt="Pictogramme SGH09 — danger pour le milieu aquatique">
|
||||
<img class="ghs-img" src="../data/ghs05-corrosif.jpg" alt="Pictogramme SGH05 — corrosif">
|
||||
<img class="ghs-img" src="../data/ghs07-nocif.jpg" alt="Pictogramme SGH07 — nocif / irritant">
|
||||
</span>
|
||||
</span>
|
||||
<span class="hotspot" data-zone="z-mention"><span class="lbl-danger">DANGER</span></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="lbl-footer">FHER — 2, rue de Sèze, 75009 Paris — Tél. 01 23 45 67 89</div>
|
||||
</div>`;
|
||||
|
||||
// Construit le corrigé de référence : étiquette annotée (zones numérotées) + check-list réglementaire.
|
||||
function buildEtiquetteRecapHtml() {
|
||||
const wrap = document.createElement("div");
|
||||
wrap.innerHTML = ETI_LABEL_CARD;
|
||||
const card = wrap.querySelector(".label-card");
|
||||
card.querySelectorAll(".hotspot").forEach(h => {
|
||||
h.classList.add("hs-correct");
|
||||
const idx = ETI_ZONES.findIndex(z => z.zone === h.dataset.zone);
|
||||
if (idx >= 0) {
|
||||
const num = document.createElement("b");
|
||||
num.className = "hs-num";
|
||||
num.textContent = idx + 1;
|
||||
h.insertBefore(num, h.firstChild);
|
||||
}
|
||||
});
|
||||
|
||||
let rows = '<thead><tr><th style="width:34px;">N°</th><th>Élément d\'étiquetage</th><th>Base réglementaire</th></tr></thead><tbody>';
|
||||
ETI_ZONES.forEach((z, i) => {
|
||||
rows += `<tr>
|
||||
<td class="cl-num"><span class="cl-badge">${i + 1}</span></td>
|
||||
<td><strong>${escapeHtml(z.label)}</strong></td>
|
||||
<td>${escapeHtml(z.reg)}</td>
|
||||
</tr>`;
|
||||
});
|
||||
rows += "</tbody>";
|
||||
|
||||
return `<p class="recap-intro">Les 9 zones réglementaires et leurs bonnes réponses, présentées comme la <strong>check-list officielle d'étiquetage des produits biocides</strong> (Guide FHER, p. 15) — un outil réutilisable en conditions réelles.</p>
|
||||
<div class="recap-layout">
|
||||
<div class="recap-stage">${card.outerHTML}</div>
|
||||
<table class="checklist-table">${rows}</table>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
function parseFds(raw) {
|
||||
let clean = flatten(raw);
|
||||
clean = clean.replace(/FDS, FT (&|&) Étiquettes\s*[—–-]\s*Réponses\s*\([^)]*\)/gi, " ").replace(/\s+/g, " ").trim();
|
||||
@@ -644,7 +798,12 @@ function parseFds(raw) {
|
||||
}),
|
||||
}));
|
||||
|
||||
return { title: "FDS, FT & Étiquettes — Correction du rendu", meta, groups, scored: true };
|
||||
const annexes = [{
|
||||
title: "Récapitulatif — l'étiquette annotée (corrigé de référence)",
|
||||
html: buildEtiquetteRecapHtml(),
|
||||
}];
|
||||
|
||||
return { title: "FDS, FT & Étiquettes — Correction du rendu", meta, groups, scored: true, annexes };
|
||||
}
|
||||
|
||||
/* ======================= EXERCICE 3 — TP NUISIBLES ======================= */
|
||||
|
||||
Reference in New Issue
Block a user