diff --git a/exercices/Exo_FDS.html b/exercices/Exo_FDS.html index dad8fe6..db12305 100644 --- a/exercices/Exo_FDS.html +++ b/exercices/Exo_FDS.html @@ -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; } -} @@ -843,7 +814,7 @@ span.hotspot { display: inline; padding: 0 2px; -webkit-box-decoration-break: cl

L'étiquette est l'interface directe entre le produit et l'utilisateur. Elle communique rapidement les dangers essentiels, les EPI et les consignes d'utilisation.

🎯 -
Sur une étiquette réelle (produit professionnel DÉS'INFECT), cliquez la zone 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 justification réglementaire de la zone.
+
Sur une étiquette réelle (produit professionnel DÉS'INFECT), cliquez la zone 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 justification réglementaire de chaque zone est fournie sur la page de correction de l'exercice.
@@ -926,18 +897,6 @@ span.hotspot { display: inline; padding: 0 2px; -webkit-box-decoration-break: cl
- - - @@ -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; - 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); - } + + // 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++; + renderEtiQuestion(); + if (etiPos >= etiOrder.length) finishEtiquette(); } 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 = 'N°Élément d\'étiquetageBase réglementaireStatut'; - etiZones.forEach((z, i) => { - const ok = etiFirstTry[z.zone]; - const answered = etiAnswers[z.zone] !== undefined; - const statut = ok - ? '✓ trouvé' - : (answered ? '✗ à revoir' : ''); - rows += ` - ${i + 1} - ${z.label} - ${z.reg} - ${statut} - `; - }); - rows += ''; - 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, - { id: 'eti-recap-btn', disabled: etiPos < etiOrder.length }); - } + barBtn(bar, '↺ Recommencer', 'is-secondary', resetEtiquette); + 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) diff --git a/exercices/corriger-exercice.html b/exercices/corriger-exercice.html index e919a2a..8877969 100644 --- a/exercices/corriger-exercice.html +++ b/exercices/corriger-exercice.html @@ -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; } +} @@ -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 = ` +
+
+ 🇫🇷 + DÉS'INFECT +
+
+
+
Propriétés :
Désinfecte et désodorise les sanitaires. Produit strictement destiné à un usage professionnel.
+
Mode d'emploi :
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.
+
UFI : 1234-1234-1234-1234
Date de péremption : MM/AAAA
N° LOT : 123456
+
+
+
Substances actives :
Chlorure de didécyldiméthylammonium (N°CAS 7173-51-5) à 75 g/kg ; acide glycolique (CAS 79-14-1) à 35 g/kg.
+
+
Précautions d'emploi :
+ Produit strictement professionnel (FR) — Lire le mode d'emploi ci-joint avant l'emploi. + Formule déposée au centre anti-poison de Nancy : 03 83 22 50 50. + + 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. + + P280 Porter des gants de protection, des vêtements de protection et un équipement de protection du visage. + + 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. + + P501 Éliminer le contenu / le récipient dans un point de collecte des déchets spéciaux ou dangereux. +
+
+
+
+ + Volume net :5 L + + +
+ + + Pictogramme SGH09 — danger pour le milieu aquatique + Pictogramme SGH05 — corrosif + Pictogramme SGH07 — nocif / irritant + + + DANGER +
+
+ +
`; + +// 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 = 'N°Élément d\'étiquetageBase réglementaire'; + ETI_ZONES.forEach((z, i) => { + rows += ` + ${i + 1} + ${escapeHtml(z.label)} + ${escapeHtml(z.reg)} + `; + }); + rows += ""; + + return `

Les 9 zones réglementaires et leurs bonnes réponses, présentées comme la check-list officielle d'étiquetage des produits biocides (Guide FHER, p. 15) — un outil réutilisable en conditions réelles.

+
+
${card.outerHTML}
+ ${rows}
+
`; +} + 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 ======================= */