Ajoute une page unique de correction des exercices
- Nouvelle page corriger-exercice.html : sélection de l'exercice (BioCID, FDS, TP Nuisibles) + zone de dépôt PDF - Le PDF déposé est transmis à la page de correction dédiée qui se charge automatiquement (?auto=1) - Carte 'Corriger un exercice' ajoutée à la section Exercices de l'index - Slide 6 BioCID : suppression du bouton biocid-anses.fr, alignement des boutons Télécharger / Corriger / Recommencer - Le bouton 'Corriger un rendu' redirige vers la nouvelle page
This commit is contained in:
@@ -603,10 +603,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
<div class="actions-row">
|
<div class="actions-row">
|
||||||
<button class="btn btn-amber" onclick="exporterPDF()">⬇ Télécharger mes réponses (PDF)</button>
|
<button class="btn btn-amber" onclick="exporterPDF()">⬇ Télécharger mes réponses (PDF)</button>
|
||||||
<a href="correction-biocid.html" class="btn btn-outline">✔ Corriger un rendu</a>
|
<a href="corriger-exercice.html?exo=biocid" class="btn btn-outline">✔ Corriger un rendu</a>
|
||||||
</div>
|
|
||||||
<div class="actions-row" style="margin-top:0.5rem;">
|
|
||||||
<a href="https://biocid-anses.fr/biocid" target="_blank" class="btn btn-outline">biocid-anses.fr ↗</a>
|
|
||||||
<button class="btn btn-outline" onclick="restartAll()">↺ Recommencer</button>
|
<button class="btn btn-outline" onclick="restartAll()">↺ Recommencer</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -502,6 +502,24 @@ dropzone.addEventListener("drop", e => {
|
|||||||
if (file) handleFile(file);
|
if (file) handleFile(file);
|
||||||
});
|
});
|
||||||
document.getElementById("filePicker").addEventListener("change", e => handleFile(e.target.files?.[0]));
|
document.getElementById("filePicker").addEventListener("change", e => handleFile(e.target.files?.[0]));
|
||||||
|
|
||||||
|
// Chargement automatique depuis la page « Corriger un exercice » (?auto=1)
|
||||||
|
(function autoLoadFromHub() {
|
||||||
|
const params = new URLSearchParams(location.search);
|
||||||
|
if (params.get("auto") !== "1") return;
|
||||||
|
const dataUrl = localStorage.getItem("correctionFile");
|
||||||
|
const name = localStorage.getItem("correctionFileName") || "rendu.pdf";
|
||||||
|
localStorage.removeItem("correctionFile");
|
||||||
|
localStorage.removeItem("correctionFileName");
|
||||||
|
localStorage.removeItem("correctionExo");
|
||||||
|
if (!dataUrl) return;
|
||||||
|
try {
|
||||||
|
const bin = atob(dataUrl.split(",").pop());
|
||||||
|
const bytes = new Uint8Array(bin.length);
|
||||||
|
for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);
|
||||||
|
handleFile(new File([bytes], name, { type: "application/pdf" }));
|
||||||
|
} catch (e) { console.error(e); }
|
||||||
|
})();
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -599,6 +599,24 @@ dropzone.addEventListener("drop", e => {
|
|||||||
if (file) handleFile(file);
|
if (file) handleFile(file);
|
||||||
});
|
});
|
||||||
document.getElementById("filePicker").addEventListener("change", e => handleFile(e.target.files?.[0]));
|
document.getElementById("filePicker").addEventListener("change", e => handleFile(e.target.files?.[0]));
|
||||||
|
|
||||||
|
// Chargement automatique depuis la page « Corriger un exercice » (?auto=1)
|
||||||
|
(function autoLoadFromHub() {
|
||||||
|
const params = new URLSearchParams(location.search);
|
||||||
|
if (params.get("auto") !== "1") return;
|
||||||
|
const dataUrl = localStorage.getItem("correctionFile");
|
||||||
|
const name = localStorage.getItem("correctionFileName") || "rendu.pdf";
|
||||||
|
localStorage.removeItem("correctionFile");
|
||||||
|
localStorage.removeItem("correctionFileName");
|
||||||
|
localStorage.removeItem("correctionExo");
|
||||||
|
if (!dataUrl) return;
|
||||||
|
try {
|
||||||
|
const bin = atob(dataUrl.split(",").pop());
|
||||||
|
const bytes = new Uint8Array(bin.length);
|
||||||
|
for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);
|
||||||
|
handleFile(new File([bytes], name, { type: "application/pdf" }));
|
||||||
|
} catch (e) { console.error(e); }
|
||||||
|
})();
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -645,6 +645,24 @@ dropzone.addEventListener("drop", e => {
|
|||||||
if (file) handleFile(file);
|
if (file) handleFile(file);
|
||||||
});
|
});
|
||||||
document.getElementById("filePicker").addEventListener("change", e => handleFile(e.target.files?.[0]));
|
document.getElementById("filePicker").addEventListener("change", e => handleFile(e.target.files?.[0]));
|
||||||
|
|
||||||
|
// Chargement automatique depuis la page « Corriger un exercice » (?auto=1)
|
||||||
|
(function autoLoadFromHub() {
|
||||||
|
const params = new URLSearchParams(location.search);
|
||||||
|
if (params.get("auto") !== "1") return;
|
||||||
|
const dataUrl = localStorage.getItem("correctionFile");
|
||||||
|
const name = localStorage.getItem("correctionFileName") || "rendu.pdf";
|
||||||
|
localStorage.removeItem("correctionFile");
|
||||||
|
localStorage.removeItem("correctionFileName");
|
||||||
|
localStorage.removeItem("correctionExo");
|
||||||
|
if (!dataUrl) return;
|
||||||
|
try {
|
||||||
|
const bin = atob(dataUrl.split(",").pop());
|
||||||
|
const bytes = new Uint8Array(bin.length);
|
||||||
|
for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);
|
||||||
|
handleFile(new File([bytes], name, { type: "application/pdf" }));
|
||||||
|
} catch (e) { console.error(e); }
|
||||||
|
})();
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
317
exercices/corriger-exercice.html
Normal file
317
exercices/corriger-exercice.html
Normal file
@@ -0,0 +1,317 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="fr">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Corriger un exercice — Certibiocide</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Syne:wght@600;700;800&family=DM+Sans:ital,wght@0,400;0,500;1,400&display=swap" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="../header-style.css"/>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
padding: calc(36px + 70px) 20px 80px;
|
||||||
|
}
|
||||||
|
.wrap { max-width: 820px; margin: 0 auto; }
|
||||||
|
.eyebrow {
|
||||||
|
font-family: 'Syne', sans-serif;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--amber);
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
font-family: 'Syne', sans-serif;
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: -0.5px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
.lead { color: var(--text-muted); font-size: 14.5px; line-height: 1.6; margin-bottom: 28px; max-width: 620px; }
|
||||||
|
|
||||||
|
/* STEP LABEL */
|
||||||
|
.step-label {
|
||||||
|
font-family: 'Syne', sans-serif;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--text);
|
||||||
|
margin: 0 0 14px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
.step-num {
|
||||||
|
width: 24px; height: 24px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: var(--amber);
|
||||||
|
color: #fff;
|
||||||
|
font-size: 12px;
|
||||||
|
display: flex; align-items: center; justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* EXERCISE SELECTOR */
|
||||||
|
.exo-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 36px;
|
||||||
|
}
|
||||||
|
.exo-option { position: relative; }
|
||||||
|
.exo-option input {
|
||||||
|
position: absolute;
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.exo-option label {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 14px;
|
||||||
|
background: #fff;
|
||||||
|
border: 1.5px solid var(--border);
|
||||||
|
border-radius: 14px;
|
||||||
|
padding: 16px 18px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: border-color 0.15s, box-shadow 0.15s, background 0.15s;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.exo-option label:hover { border-color: var(--amber); }
|
||||||
|
.exo-option input:checked + label {
|
||||||
|
border-color: var(--amber);
|
||||||
|
background: var(--amber-light);
|
||||||
|
box-shadow: 0 4px 16px rgba(186,117,23,0.12);
|
||||||
|
}
|
||||||
|
.exo-option input:focus-visible + label { outline: 2px solid var(--amber); outline-offset: 2px; }
|
||||||
|
.exo-icon {
|
||||||
|
width: 44px; height: 44px;
|
||||||
|
border-radius: 12px;
|
||||||
|
background: var(--amber-light);
|
||||||
|
display: flex; align-items: center; justify-content: center;
|
||||||
|
font-size: 22px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.exo-option input:checked + label .exo-icon { background: #fff; }
|
||||||
|
.exo-text { flex: 1; min-width: 0; }
|
||||||
|
.exo-name { display: block; font-family: 'Syne', sans-serif; font-weight: 700; font-size: 14.5px; }
|
||||||
|
.exo-sub { display: block; font-size: 12.5px; color: var(--text-muted); margin-top: 2px; }
|
||||||
|
.exo-check {
|
||||||
|
width: 22px; height: 22px;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 1.5px solid var(--border);
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex; align-items: center; justify-content: center;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 12px;
|
||||||
|
transition: background 0.15s, border-color 0.15s;
|
||||||
|
}
|
||||||
|
.exo-option input:checked + label .exo-check {
|
||||||
|
background: var(--amber);
|
||||||
|
border-color: var(--amber);
|
||||||
|
}
|
||||||
|
.exo-option input:checked + label .exo-check::after { content: '✓'; }
|
||||||
|
|
||||||
|
/* DROPZONE */
|
||||||
|
#dropzone {
|
||||||
|
background: #fff;
|
||||||
|
border: 2px dashed rgba(186,117,23,0.35);
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 56px 24px;
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: border-color 0.2s, background 0.2s, opacity 0.2s;
|
||||||
|
}
|
||||||
|
#dropzone.drag { border-color: var(--amber); background: var(--amber-light); }
|
||||||
|
#dropzone.disabled { opacity: 0.5; cursor: not-allowed; pointer-events: none; }
|
||||||
|
#dropzone .icon { font-size: 40px; margin-bottom: 12px; }
|
||||||
|
#dropzone p.title { font-family: 'Syne', sans-serif; font-weight: 700; font-size: 16px; margin-bottom: 6px; }
|
||||||
|
#dropzone p.sub { color: var(--text-muted); font-size: 13.5px; margin-bottom: 20px; }
|
||||||
|
#filePicker { display: none; }
|
||||||
|
.btn-pick {
|
||||||
|
font-family: 'Syne', sans-serif;
|
||||||
|
display: inline-block;
|
||||||
|
background: var(--amber);
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 13px;
|
||||||
|
padding: 10px 26px;
|
||||||
|
border-radius: 30px;
|
||||||
|
cursor: pointer;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
#status { margin-top: 16px; font-size: 13px; min-height: 18px; }
|
||||||
|
#status.hint { color: var(--text-muted); }
|
||||||
|
#status.loading { color: var(--amber); }
|
||||||
|
#status.error { color: var(--coral); }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<div class="wrap">
|
||||||
|
<div class="eyebrow">Certibiocide — Correction des rendus</div>
|
||||||
|
<h1>Corriger un exercice</h1>
|
||||||
|
<p class="lead">Sélectionnez l'exercice concerné, puis déposez le fichier PDF exporté par le groupe. La lecture du rendu s'ouvre automatiquement.</p>
|
||||||
|
|
||||||
|
<p class="step-label"><span class="step-num">1</span> Choisissez l'exercice à corriger</p>
|
||||||
|
<div class="exo-grid" id="exoGrid">
|
||||||
|
<div class="exo-option">
|
||||||
|
<input type="radio" name="exo" id="exo-biocid" value="biocid">
|
||||||
|
<label for="exo-biocid">
|
||||||
|
<span class="exo-icon">🧪</span>
|
||||||
|
<span class="exo-text">
|
||||||
|
<span class="exo-name">Plateforme BioCID</span>
|
||||||
|
<span class="exo-sub">Recherche de produits biocides</span>
|
||||||
|
</span>
|
||||||
|
<span class="exo-check"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="exo-option">
|
||||||
|
<input type="radio" name="exo" id="exo-fds" value="fds">
|
||||||
|
<label for="exo-fds">
|
||||||
|
<span class="exo-icon">📋</span>
|
||||||
|
<span class="exo-text">
|
||||||
|
<span class="exo-name">FDS, FT & Étiquettes</span>
|
||||||
|
<span class="exo-sub">Lecture documentaire</span>
|
||||||
|
</span>
|
||||||
|
<span class="exo-check"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="exo-option">
|
||||||
|
<input type="radio" name="exo" id="exo-tp" value="tp">
|
||||||
|
<label for="exo-tp">
|
||||||
|
<span class="exo-icon">📚</span>
|
||||||
|
<span class="exo-text">
|
||||||
|
<span class="exo-name">TP Nuisibles final</span>
|
||||||
|
<span class="exo-sub">Étude de cas de synthèse</span>
|
||||||
|
</span>
|
||||||
|
<span class="exo-check"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="step-label"><span class="step-num">2</span> Déposez le rendu PDF</p>
|
||||||
|
<div id="dropzone" class="disabled">
|
||||||
|
<div class="icon">📄</div>
|
||||||
|
<p class="title">Déposez le PDF du groupe ici</p>
|
||||||
|
<p class="sub">ou cliquez pour sélectionner le fichier exporté depuis l'exercice</p>
|
||||||
|
<label class="btn-pick">Choisir un fichier
|
||||||
|
<input type="file" id="filePicker" accept="application/pdf">
|
||||||
|
</label>
|
||||||
|
<div id="status" class="hint">Sélectionnez d'abord un exercice ci-dessus.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<p>© 2026 Gauthier Chombart & Nathan Chauwin<br>Tous droits réservés — Formations Certibiocide & Techniques — Formateurs indépendants</p>
|
||||||
|
<p style="margin-top: 10px; opacity: 0.6;">Tous droits réservés — Gauthier Chombart & Nathan Chauwin — Formateurs indépendants</p>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Chaque exercice pointe vers sa page de correction dédiée (parseur spécifique).
|
||||||
|
const CORRECTORS = {
|
||||||
|
biocid: "correction-biocid.html",
|
||||||
|
fds: "correction-fds.html",
|
||||||
|
tp: "correction-tp-nuisibles.html",
|
||||||
|
};
|
||||||
|
|
||||||
|
const dropzone = document.getElementById("dropzone");
|
||||||
|
const status = document.getElementById("status");
|
||||||
|
const filePicker = document.getElementById("filePicker");
|
||||||
|
|
||||||
|
function selectedExo() {
|
||||||
|
const el = document.querySelector('input[name="exo"]:checked');
|
||||||
|
return el ? el.value : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function refreshDropzone() {
|
||||||
|
const exo = selectedExo();
|
||||||
|
if (exo) {
|
||||||
|
dropzone.classList.remove("disabled");
|
||||||
|
status.className = "hint";
|
||||||
|
status.textContent = "Prêt à recevoir le PDF.";
|
||||||
|
} else {
|
||||||
|
dropzone.classList.add("disabled");
|
||||||
|
status.className = "hint";
|
||||||
|
status.textContent = "Sélectionnez d'abord un exercice ci-dessus.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Préselection via ?exo=biocid|fds|tp
|
||||||
|
(function preselect() {
|
||||||
|
const params = new URLSearchParams(location.search);
|
||||||
|
const exo = params.get("exo");
|
||||||
|
if (exo && CORRECTORS[exo]) {
|
||||||
|
const input = document.getElementById("exo-" + exo);
|
||||||
|
if (input) input.checked = true;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
document.querySelectorAll('input[name="exo"]').forEach(r =>
|
||||||
|
r.addEventListener("change", refreshDropzone)
|
||||||
|
);
|
||||||
|
refreshDropzone();
|
||||||
|
|
||||||
|
function handleFile(file) {
|
||||||
|
const exo = selectedExo();
|
||||||
|
if (!exo) {
|
||||||
|
status.className = "error";
|
||||||
|
status.textContent = "Choisissez un exercice avant de déposer un fichier.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!file) return;
|
||||||
|
if (file.type !== "application/pdf" && !/\.pdf$/i.test(file.name)) {
|
||||||
|
status.className = "error";
|
||||||
|
status.textContent = "Le fichier doit être un PDF.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
status.className = "loading";
|
||||||
|
status.textContent = "Ouverture du correcteur…";
|
||||||
|
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = () => {
|
||||||
|
try {
|
||||||
|
// On transmet le PDF à la page de correction dédiée via localStorage
|
||||||
|
localStorage.setItem("correctionFile", reader.result);
|
||||||
|
localStorage.setItem("correctionFileName", file.name);
|
||||||
|
localStorage.setItem("correctionExo", exo);
|
||||||
|
window.location.href = CORRECTORS[exo] + "?auto=1";
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
status.className = "error";
|
||||||
|
status.textContent = "Fichier trop volumineux pour être transmis. Ouvrez la page de correction dédiée.";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
reader.onerror = () => {
|
||||||
|
status.className = "error";
|
||||||
|
status.textContent = "Impossible de lire le fichier.";
|
||||||
|
};
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
dropzone.addEventListener("dragover", e => {
|
||||||
|
if (dropzone.classList.contains("disabled")) return;
|
||||||
|
e.preventDefault();
|
||||||
|
dropzone.classList.add("drag");
|
||||||
|
});
|
||||||
|
dropzone.addEventListener("dragleave", () => dropzone.classList.remove("drag"));
|
||||||
|
dropzone.addEventListener("drop", e => {
|
||||||
|
if (dropzone.classList.contains("disabled")) return;
|
||||||
|
e.preventDefault();
|
||||||
|
dropzone.classList.remove("drag");
|
||||||
|
handleFile(e.dataTransfer.files && e.dataTransfer.files[0]);
|
||||||
|
});
|
||||||
|
filePicker.addEventListener("change", e => handleFile(e.target.files && e.target.files[0]));
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
13
index.html
13
index.html
@@ -137,6 +137,19 @@
|
|||||||
<a href="exercices/memory-pictogrammes/memory.html" class="formation-link">Jouer</a>
|
<a href="exercices/memory-pictogrammes/memory.html" class="formation-link">Jouer</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Correction : page unique pour tous les exercices -->
|
||||||
|
<div class="formation-card">
|
||||||
|
<div class="formation-header">
|
||||||
|
<div class="formation-icon">✔️</div>
|
||||||
|
<h3 class="formation-title">Corriger un exercice</h3>
|
||||||
|
<p class="formation-subtitle">Correction des rendus</p>
|
||||||
|
</div>
|
||||||
|
<div class="formation-body">
|
||||||
|
<p class="formation-description">Déposez le PDF exporté par un groupe et obtenez la lecture de son rendu. Page unique de correction pour l'ensemble des exercices.</p>
|
||||||
|
<a href="exercices/corriger-exercice.html" class="formation-link">Corriger</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user