Files
Site_web/exercices/Exo_FDS.html
gauthier.chombart a418558393 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).
2026-07-15 23:07:45 +02:00

889 lines
34 KiB
HTML

<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>FDS, FT & Étiquettes — Gauthier Chombart & Nathan Chauwin</title>
<link href="https://fonts.googleapis.com/css2?family=Syne:wght@400;600;700;800&family=DM+Sans:ital,wght@0,300;0,400;0,500;1,300&display=swap" rel="stylesheet">
<link rel="stylesheet" href="../header-style.css"/>
<style>
/* SLIDES */
.slide {
position: absolute;
inset: 0;
display: none;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 2rem;
padding-top: calc(70px + 2rem);
padding-bottom: calc(52px + 2rem);
opacity: 0;
transition: opacity 0.4s ease;
overflow-y: auto;
min-height: calc(100vh - 70px);
}
.slide.active { display: flex; opacity: 1; }
.slide.fade-in { animation: fadeIn 0.5s ease forwards; }
@keyframes fadeIn {
from { opacity: 0; transform: translateY(16px); }
to { opacity: 1; transform: translateY(0); }
}
.progress-fill { background: var(--amber); 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;
background: rgba(250, 238, 218, 0.98);
border-top: 2px solid var(--amber);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 20px;
z-index: 100;
backdrop-filter: blur(10px);
}
.slide-counter { font-size: 12px; font-family: 'Syne', sans-serif; font-weight: 600; color: var(--amber-dark); }
.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;
}
.back-btn:hover { background: rgba(186,117,23,0.08); }
/* BUTTONS */
.btn {
font-family: 'Syne', sans-serif;
font-size: 14px;
font-weight: 600;
padding: 12px 28px;
border-radius: 40px;
border: none;
cursor: pointer;
transition: all 0.2s;
display: inline-flex;
align-items: center;
gap: 8px;
text-decoration: none;
}
.btn:hover { transform: translateY(-1px); }
.btn:active { transform: scale(0.98); }
.btn-amber { background: var(--amber); color: white; }
.btn-coral { background: var(--coral-mid); color: white; }
.btn-teal { background: var(--teal-mid); color: white; }
.btn-outline { background: transparent; border: 1.5px solid var(--amber); color: var(--amber-dark); }
/* HERO / TITLES */
.hero-label {
font-family: 'Syne', sans-serif;
font-size: 11px;
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--amber);
margin-bottom: 8px;
}
.hero-title {
font-family: 'Syne', sans-serif;
font-size: clamp(32px, 5.5vw, 58px);
font-weight: 800;
line-height: 1.02;
color: var(--text);
margin-bottom: 12px;
}
.hero-sub {
font-size: 16px;
color: var(--text-muted);
max-width: 480px;
text-align: center;
line-height: 1.6;
margin-bottom: 2rem;
}
.section-tag {
font-family: 'Syne', sans-serif;
font-size: 11px;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
padding: 4px 12px;
border-radius: 20px;
margin-bottom: 10px;
display: inline-block;
background: var(--amber-light);
color: var(--amber-dark);
}
.slide-title {
font-family: 'Syne', sans-serif;
font-size: clamp(22px, 3.5vw, 34px);
font-weight: 800;
line-height: 1.15;
margin-bottom: 0.5rem;
text-align: center;
}
.slide-intro {
font-size: 14px;
color: var(--text-muted);
text-align: center;
max-width: 580px;
line-height: 1.6;
margin-bottom: 1.5rem;
}
.actions-row {
display: flex;
gap: 12px;
align-items: center;
margin-top: 1.5rem;
flex-wrap: wrap;
justify-content: center;
}
/* OVERVIEW CARDS */
.overview-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 14px;
max-width: 680px;
width: 100%;
margin: 1.5rem 0;
}
.overview-card {
background: white;
border: 0.5px solid var(--border);
border-radius: 14px;
padding: 1.25rem;
text-align: center;
}
.overview-icon { font-size: 30px; margin-bottom: 8px; }
.overview-title { font-family: 'Syne', sans-serif; font-size: 13px; font-weight: 700; margin-bottom: 4px; }
.overview-sub { font-size: 12px; color: var(--text-muted); line-height: 1.4; }
.info-card {
border-radius: 12px;
padding: 16px 20px;
max-width: 620px;
width: 100%;
font-size: 14px;
line-height: 1.6;
display: flex;
gap: 10px;
align-items: flex-start;
background: var(--amber-light);
color: var(--amber-dark);
}
/* QUIZ ROWS */
.quiz-list { width: 100%; max-width: 720px; display: flex; flex-direction: column; gap: 12px; }
.answer-row {
background: white;
border: 1px solid var(--border);
border-radius: 12px;
padding: 14px 16px;
display: flex;
align-items: center;
gap: 16px;
transition: all 0.25s ease;
}
.answer-row:hover { border-color: var(--amber); box-shadow: 0 6px 16px rgba(186,117,23,0.08); }
.question-num {
font-family: 'Syne', sans-serif;
font-size: 12px;
font-weight: 800;
min-width: 30px;
height: 30px;
border-radius: 50%;
display: flex; align-items: center; justify-content: center;
background: var(--amber-light);
color: var(--amber-dark);
flex-shrink: 0;
}
.question-text { flex-grow: 1; font-size: 14px; color: var(--text); font-weight: 500; line-height: 1.4; }
.answer-label {
font-size: 11px;
font-weight: 700;
color: var(--amber-dark);
white-space: nowrap;
padding: 4px 10px;
background: var(--amber-light);
border: 1px solid var(--amber);
border-radius: 6px;
flex-shrink: 0;
}
.answer-icon { font-size: 1.2em; flex-shrink: 0; }
select {
font-family: 'DM Sans', sans-serif;
padding: 9px 12px;
border: 1px solid var(--border);
border-radius: 8px;
background: var(--bg);
color: var(--text);
font-size: 13px;
cursor: pointer;
transition: all 0.2s ease;
min-width: 175px;
flex-shrink: 0;
}
select:focus { outline: none; border-color: var(--amber); box-shadow: 0 0 0 3px rgba(186,117,23,0.1); }
.verify-btn {
font-family: 'Syne', sans-serif;
color: white;
border: none;
padding: 12px 30px;
border-radius: 40px;
cursor: pointer;
font-size: 14px;
font-weight: 600;
transition: all 0.2s ease;
}
.verify-btn:hover { transform: translateY(-1px); }
.result-message {
margin-top: 4px;
padding: 16px 20px;
border-radius: 10px;
font-weight: 600;
font-size: 14px;
display: none;
max-width: 720px;
width: 100%;
text-align: center;
}
.result-message.show { display: block; }
.result-message.success { background: var(--teal-light); border: 1.5px solid var(--teal-mid); color: var(--teal-dark); }
.result-message.error { background: var(--coral-light); border: 1.5px solid var(--coral-mid); color: var(--coral); }
/* COMPARISON TABLE */
.comparison-table {
width: 100%;
max-width: 760px;
border-collapse: collapse;
background: white;
border: 0.5px solid var(--border);
border-radius: 12px;
overflow: hidden;
font-size: 13px;
margin-bottom: 1.25rem;
}
.comparison-table th {
background: var(--amber-light);
color: var(--amber-dark);
padding: 12px 16px;
text-align: left;
font-family: 'Syne', sans-serif;
font-size: 11px;
font-weight: 700;
letter-spacing: 0.04em;
text-transform: uppercase;
border-bottom: 2px solid var(--amber);
}
.comparison-table td {
padding: 12px 16px;
border-bottom: 0.5px solid var(--border);
color: var(--text-muted);
}
.comparison-table tr:last-child td { border-bottom: none; }
.comparison-table strong { color: var(--text); }
/* TIPS LIST */
.tips-list { list-style: none; padding: 0; max-width: 680px; width: 100%; display: flex; flex-direction: column; gap: 8px; }
.tips-list li {
background: white;
border: 0.5px solid var(--border);
border-radius: 10px;
padding: 12px 16px 12px 40px;
position: relative;
font-size: 13.5px;
line-height: 1.5;
color: var(--text-muted);
}
.tips-list li::before { content: '💡'; position: absolute; left: 14px; }
.tips-list strong { color: var(--text); }
/* SCROLL AREA */
.scroll-area {
overflow-y: auto;
max-height: calc(100vh - 200px);
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
gap: 1rem;
padding: 0.5rem 0 4rem;
}
.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; }
@media (max-width: 700px) {
.overview-grid { grid-template-columns: 1fr; }
.answer-row { flex-wrap: wrap; }
select { width: 100%; min-width: 0; }
}
</style>
</head>
<body>
<!-- HEADER -->
<header>
<div class="logo">Gauthier Chombart & Nathan Chauwin</div>
<nav>
<a href="../index.html">Accueil</a>
<a href="../index.html#formations">Exercices</a>
<a href="../index.html#documentation">Documentation</a>
<a href="../index.html#rapports">Rapports</a>
<a href="../index.html#about">À propos</a>
<a href="../index.html#contact">Contact</a>
</nav>
</header>
<!-- 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>
<!-- ============ SLIDE 0 — ACCUEIL ============ -->
<div class="slide active fade-in" id="slide-0">
<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(--coral-light);bottom:60px;left:-60px;opacity:0.5;"></div>
<div style="text-align:center;">
<div class="hero-label">Certibiocide — Travaux dirigés</div>
<div class="hero-title">FDS, FT<br>& Étiquettes</div>
<p class="hero-sub">Maîtrisez les trois documents essentiels des produits biocides : fiche de sécurité, fiche technique et étiquetage réglementaire.</p>
<button class="btn btn-amber" onclick="goTo(1)">Démarrer le TD →</button>
</div>
</div>
<!-- ============ SLIDE 1 — DÉROULÉ ============ -->
<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">Déroulé du TD</div>
<div class="slide-title">Trois documents, trois usages</div>
<p class="slide-intro">Vous allez apprendre à localiser une information dans chacun des documents de référence d'un produit biocide.</p>
<div class="overview-grid">
<div class="overview-card">
<div class="overview-icon">📄</div>
<div class="overview-title">Partie 1 — FDS</div>
<div class="overview-sub">Fiche de Données de Sécurité — 16 rubriques</div>
</div>
<div class="overview-card">
<div class="overview-icon">📋</div>
<div class="overview-title">Partie 2 — FT</div>
<div class="overview-sub">Fiche Technique — mode d'emploi produit</div>
</div>
<div class="overview-card">
<div class="overview-icon">🏷️</div>
<div class="overview-title">Partie 3 — Étiquette</div>
<div class="overview-sub">Interface directe avec l'utilisateur</div>
</div>
</div>
<div class="info-card">
<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>
<!-- ============ SLIDE 2 — FDS INTRO ============ -->
<div class="slide" id="slide-2">
<div class="deco-circle" style="width:280px;height:280px;background:var(--amber-light);top:-40px;right:-40px;opacity:0.5;"></div>
<div class="section-tag">Partie 1 / 3 — 10 min</div>
<div class="slide-title">📄 Fiche de Données de Sécurité</div>
<p class="slide-intro">La FDS est le document de référence pour connaître les <strong>dangers</strong>, les <strong>EPI</strong>, les <strong>premiers secours</strong> et l'<strong>élimination</strong> d'un produit biocide. Elle contient 16 rubriques standardisées (règlement REACH).</p>
<div class="info-card">
<span>🎯</span>
<div>Pour chaque question, indiquez dans quelle <strong>rubrique (1 à 16)</strong> trouver la réponse — ou « Absent » si l'information n'y figure pas. Les questions sont mélangées à chaque chargement.</div>
</div>
<button class="btn btn-amber" onclick="goTo(3)" style="margin-top:1.5rem;">Répondre aux questions →</button>
</div>
<!-- ============ SLIDE 3 — FDS QUIZ ============ -->
<div class="slide" id="slide-3">
<div class="scroll-area">
<div class="section-tag">Partie 1 / 3 — FDS</div>
<div class="slide-title">Où trouver l'information ?</div>
<div class="quiz-list" id="fds-questions"></div>
<button class="verify-btn btn-amber" onclick="verifyFDS()">✓ Vérifier mes réponses</button>
<div id="fds-result" class="result-message"></div>
<div class="actions-row">
<button class="btn btn-coral" onclick="goTo(4)">Passer à la partie 2 →</button>
</div>
</div>
</div>
<!-- ============ SLIDE 4 — FT INTRO ============ -->
<div class="slide" id="slide-4">
<div class="deco-circle" style="width:280px;height:280px;background:var(--coral-light);top:-40px;left:-40px;opacity:0.5;"></div>
<div class="section-tag" style="background:var(--coral-light);color:var(--coral);">Partie 2 / 3 — 10 min</div>
<div class="slide-title">📋 Fiche Technique</div>
<p class="slide-intro">La FT complète la FDS : elle fournit le <strong>mode d'emploi</strong>, les <strong>utilisations</strong>, les <strong>conditionnements</strong> et les <strong>compatibilités matières</strong>. C'est le document pratique de l'utilisateur.</p>
<div class="info-card" style="background:var(--coral-light);color:var(--coral);">
<span>🎯</span>
<div>Indiquez la <strong>section</strong> de la FT où trouver chaque information : Description, Propriétés biocides, Conditionnements, Utilisations — ou « Absent ».</div>
</div>
<button class="btn btn-coral" onclick="goTo(5)" style="margin-top:1.5rem;">Répondre aux questions →</button>
</div>
<!-- ============ SLIDE 5 — FT QUIZ ============ -->
<div class="slide" id="slide-5">
<div class="scroll-area">
<div class="section-tag" style="background:var(--coral-light);color:var(--coral);">Partie 2 / 3 — FT</div>
<div class="slide-title">Sections de la fiche technique</div>
<div class="quiz-list" id="ft-questions"></div>
<button class="verify-btn btn-coral" onclick="verifyFT()">✓ Vérifier mes réponses</button>
<div id="ft-result" class="result-message"></div>
<div class="actions-row">
<button class="btn btn-teal" onclick="goTo(6)">Passer à la partie 3 →</button>
</div>
</div>
</div>
<!-- ============ SLIDE 6 — ÉTIQUETTE INTRO ============ -->
<div class="slide" id="slide-6">
<div class="deco-circle" style="width:280px;height:280px;background:var(--teal-light);bottom:-40px;right:-40px;opacity:0.6;"></div>
<div class="section-tag" style="background:var(--teal-light);color:var(--teal-dark);">Partie 3 / 3 — 10 min</div>
<div class="slide-title">🏷️ Étiquetage</div>
<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>Indiquez la <strong>zone de l'étiquette</strong> où trouver l'information : Identité, Pictogrammes, Mots d'avertissement, Mentions de danger, Conseils de prudence, Contenu net, Numéro d'urgence — ou « Absent ».</div>
</div>
<button class="btn btn-teal" onclick="goTo(7)" style="margin-top:1.5rem;">Répondre aux questions →</button>
</div>
<!-- ============ SLIDE 7 — ÉTIQUETTE QUIZ ============ -->
<div class="slide" id="slide-7">
<div class="scroll-area">
<div class="section-tag" style="background:var(--teal-light);color:var(--teal-dark);">Partie 3 / 3 — Étiquette</div>
<div class="slide-title">Zones de l'étiquette</div>
<div class="quiz-list" id="etiquette-questions"></div>
<button class="verify-btn btn-teal" onclick="verifyEtiquette()">✓ Vérifier mes réponses</button>
<div id="etiquette-result" class="result-message"></div>
<div class="actions-row">
<button class="btn btn-amber" onclick="goTo(8)">Voir la synthèse →</button>
</div>
</div>
</div>
<!-- ============ SLIDE 8 — SYNTHÈSE / FIN ============ -->
<div class="slide" id="slide-8">
<div class="scroll-area">
<div class="deco-circle" style="width:320px;height:320px;background:var(--amber-light);bottom:-80px;right:-60px;opacity:0.5;"></div>
<div style="text-align:center;">
<div style="font-size:44px;margin-bottom:0.5rem;">🎉</div>
<div class="section-tag">TD terminé</div>
<div class="slide-title" style="margin-top:0.4rem;">Synthèse comparative</div>
</div>
<table class="comparison-table">
<thead>
<tr><th>Document</th><th>Utilité principale</th><th>Consultation</th><th>Public</th></tr>
</thead>
<tbody>
<tr><td><strong>FDS</strong></td><td>Sécurité, dangers, EPI, premiers secours</td><td>AVANT manipulation</td><td>Professionnels</td></tr>
<tr><td><strong>FT</strong></td><td>Mode d'emploi, applications, conditionnements</td><td>Pour l'utilisation</td><td>Utilisateurs finaux</td></tr>
<tr><td><strong>Étiquette</strong></td><td>Dangers essentiels, interface rapide</td><td>À chaque prise du produit</td><td>Tous les utilisateurs</td></tr>
</tbody>
</table>
<ul class="tips-list">
<li><strong>FDS = Sécurité et dangers</strong> → à consulter AVANT manipulation.</li>
<li><strong>FT = Mode d'emploi et applications</strong> → à consulter pour l'utilisation.</li>
<li><strong>Étiquette = Interface rapide</strong> → lecture obligatoire avant ouverture du produit.</li>
<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="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>
</div>
<script>
let currentSlide = 0;
let history = [];
const totalSlides = 8;
/* ======================== QUESTIONS ======================== */
const fdsQuestions = [
{ id: 1, text: "Quel est le nom du produit et qui est le fabricant ?", answer: "1" },
{ id: 2, text: "Quels sont les dangers du produit ?", answer: "2" },
{ id: 3, text: "Quelles sont les molécules actives et leurs concentrations ?", answer: "3" },
{ id: 4, text: "Quels gestes adopter en premiers secours ?", answer: "4" },
{ id: 5, text: "En cas d'incendie, quels sont les moyens d'extinction conseillés ?", answer: "5" },
{ id: 6, text: "Que faire en cas de dispersion accidentelle ?", answer: "6" },
{ id: 7, text: "Dans quelles conditions stocker mon produit ?", answer: "7" },
{ id: 8, text: "Quels équipements de protection individuelle (EPI) dois-je porter ?", answer: "8" },
{ id: 9, text: "Quel est le pH de ce produit ?", answer: "9" },
{ id: 10, text: "Le produit est-il stable dans des conditions normales ?", answer: "10" },
{ id: 11, text: "Quels sont les effets potentiels sur la santé ?", answer: "11" },
{ id: 12, text: "Quel est le degré de toxicité pour les poissons ? Potentiel de bioaccumulation ?", answer: "12" },
{ id: 13, text: "Comment éliminer le produit ?", answer: "13" },
{ id: 14, text: "Est-ce que mon produit est classé dangereux pour le transport ?", answer: "14" },
{ id: 15, text: "En cas d'ingestion du produit, que faire ?", answer: "4,11" },
{ id: 16, text: "Où trouver le mode d'emploi du produit ?", answer: "absent" }
];
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: "Description" },
{ id: 3, text: "Quel est (sont) le(s) type(s) de ce 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" }
];
const ftOptions = ["Description", "Propriétés biocides", "Conditionnements", "Utilisations"];
const etiquetteQuestions = [
{ id: 1, text: "Quel est le nom du produit et le nom de l'entreprise ?", answer: "Identité" },
{ id: 2, text: "Quels pictogrammes de danger sont affichés ?", answer: "Pictogrammes" },
{ id: 3, text: "Quels sont les mots d'avertissement (« Attention » ou « Danger ») ?", answer: "Mots d'avertissement" },
{ id: 4, text: "Quelles sont les mentions de danger (H-statements) identifiées ?", answer: "Mentions de danger" },
{ id: 5, text: "Quels équipements de protection individuelle (EPI) sont recommandés ?", answer: "Conseils de prudence" },
{ id: 6, text: "Quels premiers secours sont indiqués ?", answer: "Conseils de prudence" },
{ id: 7, text: "Comment éliminer l'emballage après utilisation ?", answer: "Conseils de prudence" },
{ id: 8, text: "Quel est le contenu net du produit ?", answer: "Contenu net" },
{ id: 9, text: "Y a-t-il un numéro d'appel d'urgence mentionné ?", answer: "Numéro d'urgence" },
{ id: 10, text: "Quelles consignes de stockage sont affichées ?", answer: "Conseils de prudence" }
];
const etiquetteOptions = ["Identité", "Pictogrammes", "Mots d'avertissement", "Mentions de danger", "Conseils de prudence", "Contenu net", "Numéro d'urgence"];
function shuffleArray(array) {
const a = [...array];
for (let i = a.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[a[i], a[j]] = [a[j], a[i]];
}
return a;
}
function buildRow(qNum, selectId, answerAttr, text, options, badgeStyle) {
let opts = '<option value="">-- Sélectionner --</option>';
options.forEach(o => { opts += `<option value="${o.value}">${o.label}</option>`; });
return `
<div class="answer-row">
<div class="question-num" style="${badgeStyle}">Q${qNum}</div>
<div class="question-text">${text}</div>
<select id="${selectId}" data-answer="${answerAttr}">${opts}</select>
</div>`;
}
function generateQuizzes() {
// FDS — shuffled, 16 rubriques
const rubriqueOpts = Array.from({ length: 16 }, (_, i) => ({ value: String(i + 1), label: 'Rubrique ' + (i + 1) }));
rubriqueOpts.push({ value: 'absent', label: 'Absent' });
const fdsC = document.getElementById('fds-questions');
fdsC.innerHTML = '';
shuffleArray(fdsQuestions).forEach((q, i) => {
fdsC.innerHTML += buildRow(i + 1, 'fds' + q.id, q.answer, q.text, rubriqueOpts, 'background:var(--amber-light);color:var(--amber-dark);');
});
// FT
const ftOpts = ftOptions.map(o => ({ value: o, label: o }));
ftOpts.push({ value: 'absent', label: 'Absent' });
const ftC = document.getElementById('ft-questions');
ftC.innerHTML = '';
ftQuestions.forEach((q, i) => {
ftC.innerHTML += buildRow(i + 1, 'ft' + q.id, q.answer, q.text, ftOpts, 'background:var(--coral-light);color:var(--coral);');
});
// Étiquette
const etiOpts = etiquetteOptions.map(o => ({ value: o, label: o }));
etiOpts.push({ value: 'absent', label: 'Absent' });
const etiC = document.getElementById('etiquette-questions');
etiC.innerHTML = '';
etiquetteQuestions.forEach((q, i) => {
etiC.innerHTML += buildRow(i + 1, 'eti' + q.id, q.answer, q.text, etiOpts, 'background:var(--teal-light);color:var(--teal-dark);');
});
}
/* ======================== VÉRIFICATION ======================== */
function showInlineResult(selectEl, isCorrect, isUnanswered, correctAnswerText) {
const row = selectEl.closest('.answer-row');
row.querySelector('.answer-label')?.remove();
row.querySelector('.answer-icon')?.remove();
const label = document.createElement('div');
label.className = 'answer-label';
label.textContent = correctAnswerText;
selectEl.before(label);
const icon = document.createElement('div');
icon.className = 'answer-icon';
icon.textContent = isUnanswered ? '❓' : (isCorrect ? '✅' : '❌');
selectEl.after(icon);
}
function showScore(resultDiv, correct, total) {
const p = Math.round((correct / total) * 100);
if (p === 100) {
resultDiv.className = 'result-message success show';
resultDiv.innerHTML = `<strong>🎉 Excellent !</strong> ${correct}/${total} bonnes réponses (${p}%)`;
} else if (p >= 80) {
resultDiv.className = 'result-message success show';
resultDiv.innerHTML = `<strong>✓ Très bien !</strong> ${correct}/${total} bonnes réponses (${p}%)`;
} else if (p >= 60) {
resultDiv.className = 'result-message error show';
resultDiv.innerHTML = `<strong>⚠️ À revoir</strong> — ${correct}/${total} bonnes réponses (${p}%)`;
} else {
resultDiv.className = 'result-message error show';
resultDiv.innerHTML = `<strong>❌ Insuffisant</strong> — ${correct}/${total} bonnes réponses (${p}%)`;
}
}
function verifyFDS() {
let correct = 0;
fdsQuestions.forEach(q => {
const select = document.getElementById('fds' + q.id);
const userAnswer = select.value;
const expected = q.answer.split(',');
const isCorrect = expected.includes(userAnswer);
const labelText = expected.length > 1
? 'Rubriques ' + expected.join(' et ')
: (expected[0] === 'absent' ? 'Absent' : 'Rubrique ' + expected[0]);
if (isCorrect) correct++;
showInlineResult(select, isCorrect, userAnswer === '', labelText);
});
showScore(document.getElementById('fds-result'), correct, fdsQuestions.length);
}
function verifyFT() {
let correct = 0;
ftQuestions.forEach(q => {
const select = document.getElementById('ft' + q.id);
const userAnswer = select.value;
const isCorrect = userAnswer === q.answer;
if (isCorrect) correct++;
showInlineResult(select, isCorrect, userAnswer === '', q.answer);
});
showScore(document.getElementById('ft-result'), correct, ftQuestions.length);
}
function verifyEtiquette() {
let correct = 0;
etiquetteQuestions.forEach(q => {
const select = document.getElementById('eti' + q.id);
const userAnswer = select.value;
const isCorrect = userAnswer === q.answer;
if (isCorrect) correct++;
showInlineResult(select, isCorrect, userAnswer === '', q.answer);
});
showScore(document.getElementById('etiquette-result'), correct, etiquetteQuestions.length);
}
/* ======================== EXPORT PDF ======================== */
function escapeHtml(str) {
if (!str) return '';
return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
}
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 &amp; É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);
const next = document.getElementById('slide-' + n);
if (!next) return;
history.push(currentSlide);
cur.classList.remove('active', 'fade-in');
cur.style.display = 'none';
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;
updateUI();
}
function goBack() {
if (history.length === 0) return;
const prev = history.pop();
const cur = document.getElementById('slide-' + currentSlide);
const target = document.getElementById('slide-' + prev);
cur.classList.remove('active', 'fade-in');
cur.style.display = 'none';
currentSlide = prev;
target.style.display = 'flex';
setTimeout(() => target.classList.add('active', 'fade-in'), 10);
updateUI();
}
function updateUI() {
document.getElementById('progressFill').style.width =
currentSlide === 0 ? '0%' : ((currentSlide / totalSlides) * 100) + '%';
document.getElementById('slideCounter').textContent =
currentSlide === 0 ? '' : currentSlide + ' / ' + totalSlides;
document.getElementById('bottomBar').style.display = currentSlide === 0 ? 'none' : 'flex';
document.getElementById('backBtn').style.display = history.length > 0 ? 'block' : 'none';
const badge = document.getElementById('phaseBadge');
const set = (bg, col, txt) => { badge.style.display = 'block'; badge.style.background = bg; badge.style.color = col; badge.textContent = txt; };
if (currentSlide === 2 || currentSlide === 3) set('var(--amber-light)', 'var(--amber-dark)', 'Partie 1 — FDS');
else if (currentSlide === 4 || currentSlide === 5) set('var(--coral-light)', 'var(--coral)', 'Partie 2 — FT');
else if (currentSlide === 6 || currentSlide === 7) set('var(--teal-light)', 'var(--teal-dark)', 'Partie 3 — Étiquette');
else badge.style.display = 'none';
}
function restartAll() {
history = [];
document.querySelectorAll('.slide').forEach(s => { s.classList.remove('active', 'fade-in'); s.style.display = 'none'; });
document.querySelectorAll('.result-message').forEach(r => { r.className = 'result-message'; });
generateQuizzes();
currentSlide = 0;
const first = document.getElementById('slide-0');
first.style.display = 'flex';
setTimeout(() => first.classList.add('active', 'fade-in'), 10);
updateUI();
}
document.addEventListener('DOMContentLoaded', () => {
generateQuizzes();
updateUI();
});
</script>
</body>
</html>