La consequence du Scenario remplace la situation, pas un feedback Quiz
Build and deploy / test-python (push) Failing after 1m2s
Build and deploy / test-js (push) Successful in 51s
Build and deploy / lint-python (push) Failing after 58s
Build and deploy / lint-js (push) Failing after 51s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Build and deploy / sonarqube (push) Failing after 1m1s

Retour utilisateur : "ce n'est pas un quiz, la consequence s'affiche a
la place de la situation precedente". L'ancien design affichait la
situation ET les choix en permanence avec un encart de feedback separe
en dessous (calque sur le Quiz). Desormais un seul bloc de texte
(.docScenarioSituation) sert successivement a la situation PUIS, une
fois un choix fait, a la consequence a sa place ; les boutons de choix
disparaissent avec elle. Supprime l'element .docScenarioConsequence
devenu inutile.

Verifie via simulation DOM reelle (jsdom) : la consequence remplace bien
le texte de la situation (jamais affichee a cote), les choix
disparaissent, le retour a une situation neutre au scenario suivant/au
redemarrage fonctionne.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
william
2026-09-20 17:10:56 +02:00
co-authored by Claude Sonnet 5
parent b2292de122
commit c04a81cfec
3 changed files with 43 additions and 30 deletions
@@ -467,10 +467,17 @@ def _render_scenario_player(config: dict[str, Any]) -> str:
scénarios gardent l'ORDRE d'écriture du créateur (contrairement à scénarios gardent l'ORDRE d'écriture du créateur (contrairement à
l'Association/Memory/Mots mêlés, jamais mélangés) : ce sont des mises l'Association/Memory/Mots mêlés, jamais mélangés) : ce sont des mises
en situation séquentielles, pas des éléments à faire correspondre ou en situation séquentielles, pas des éléments à faire correspondre ou
retrouver — un mélange n'aurait ici aucun sens pédagogique. Réutilise retrouver — un mélange n'aurait ici aucun sens pédagogique. Un seul
les classes visuelles du Quiz (.docQuizOptions/.docQuizFeedback/ bloc de texte (.docScenarioSituation) sert successivement à afficher
.docQuizNextBar/.docQuizQuestionText) plutôt que de dupliquer ces la situation PUIS, une fois un choix fait, la conséquence à sa place
règles, même esprit que .docAssocCard/.docMinigameRestartBar.""" (les boutons de choix disparaissent aussi) — volontairement PAS le
comportement du Quiz, où la question reste affichée à côté d'un
encart de feedback séparé (retour utilisateur du 20/09/2026 : "ce
n'est pas un quiz, la conséquence s'affiche à la place de la
situation précédente"). Réutilise les classes visuelles du Quiz
(.docQuizOptions/.docQuizFeedback/.docQuizNextBar/
.docQuizQuestionText) plutôt que de dupliquer ces règles, même esprit
que .docAssocCard/.docMinigameRestartBar."""
config_json = html_lib.escape(json.dumps({"scenarios": config["scenarios"]}), quote=True) config_json = html_lib.escape(json.dumps({"scenarios": config["scenarios"]}), quote=True)
return ( return (
f'<div class="docScenarioPlayer" data-scenario-config="{config_json}">' f'<div class="docScenarioPlayer" data-scenario-config="{config_json}">'
@@ -479,7 +486,6 @@ def _render_scenario_player(config: dict[str, Any]) -> str:
f'<div class="docAssocMeta"><span class="docScenarioProg"></span></div>' f'<div class="docAssocMeta"><span class="docScenarioProg"></span></div>'
f'<div class="docScenarioSituation docQuizQuestionText"></div>' f'<div class="docScenarioSituation docQuizQuestionText"></div>'
f'<div class="docScenarioChoices docQuizOptions"></div>' f'<div class="docScenarioChoices docQuizOptions"></div>'
f'<div class="docScenarioConsequence docQuizFeedback"></div>'
f'<div class="docQuizNextBar">' f'<div class="docQuizNextBar">'
f'<button type="button" class="docQuizNextBtn docScenarioNextBtn">Scénario suivant</button>' f'<button type="button" class="docQuizNextBtn docScenarioNextBtn">Scénario suivant</button>'
f"</div>" f"</div>"
+13 -8
View File
@@ -102,14 +102,19 @@ regroupement à chaque appel.
scénarios) — sanitizée (`sanitize_scenario_config`) avant lecture. Si scénarios) — sanitizée (`sanitize_scenario_config`) avant lecture. Si
au moins un scénario existe, s'y ajoute (fonction privée au moins un scénario existe, s'y ajoute (fonction privée
`_render_scenario_player`) la mise en situation RÉELLE et interactive : `_render_scenario_player`) la mise en situation RÉELLE et interactive :
l'apprenant lit la situation, choisit une option, découvre la l'apprenant lit la situation, choisit une option, puis la conséquence
conséquence de SON choix et si c'était le bon, puis passe au scénario de SON choix REMPLACE l'affichage de la situation (les boutons de
suivant. Les scénarios gardent l'ORDRE d'écriture du créateur (jamais choix disparaissent avec elle) avant de passer au scénario suivant —
mélangés, contrairement à Association/Memory/Mots mêlés — ce sont des volontairement PAS le comportement du Quiz, où la question resterait
mises en situation séquentielles, pas des éléments à faire affichée à côté d'un encart de feedback séparé (retour utilisateur du
correspondre/retrouver). Réutilise les classes visuelles du Quiz 20/09/2026 : "ce n'est pas un quiz, la conséquence s'affiche à la
(`.docQuizOptions`/`.docQuizFeedback`/`.docQuizNextBar`/ place de la situation précédente"). Les scénarios gardent l'ORDRE
`.docQuizQuestionText`) plutôt que de dupliquer ces règles. Embarqué en d'écriture du créateur (jamais mélangés, contrairement à
Association/Memory/Mots mêlés — ce sont des mises en situation
séquentielles, pas des éléments à faire correspondre/retrouver).
Réutilise les classes visuelles du Quiz (`.docQuizOptions`/
`.docQuizFeedback`/`.docQuizNextBar`/`.docQuizQuestionText`) plutôt que
de dupliquer ces règles. Embarqué en
JSON dans un attribut `data-scenario-config`, échappé pour l'HTML — JSON dans un attribut `data-scenario-config`, échappé pour l'HTML —
même principe que les autres mini-jeux, aucun aller-retour serveur même principe que les autres mini-jeux, aucun aller-retour serveur
pendant qu'on joue. Comme l'Association/Memory/Mots mêlés (mais pendant qu'on joue. Comme l'Association/Memory/Mots mêlés (mais
+19 -17
View File
@@ -1163,14 +1163,15 @@ function forgeDocRenderScenarioPlayerScenario(playerEl, state) {
const item = state.scenarios[state.current]; const item = state.scenarios[state.current];
const situationEl = playerEl.querySelector('.docScenarioSituation'); const situationEl = playerEl.querySelector('.docScenarioSituation');
const choicesEl = playerEl.querySelector('.docScenarioChoices'); const choicesEl = playerEl.querySelector('.docScenarioChoices');
const consequenceEl = playerEl.querySelector('.docScenarioConsequence');
const progEl = playerEl.querySelector('.docScenarioProg'); const progEl = playerEl.querySelector('.docScenarioProg');
const nextBtn = playerEl.querySelector('.docScenarioNextBtn'); const nextBtn = playerEl.querySelector('.docScenarioNextBtn');
// Repart d'un bloc "situation" neutre (jamais un résidu des classes
// is-visible/is-ok/is-ko posées par une réponse précédente, voir
// forgeDocScenarioPlayerAnswer ci-dessous).
situationEl.className = 'docScenarioSituation docQuizQuestionText';
situationEl.textContent = item.situation; situationEl.textContent = item.situation;
choicesEl.innerHTML = ''; choicesEl.innerHTML = '';
consequenceEl.className = 'docScenarioConsequence docQuizFeedback';
consequenceEl.textContent = '';
nextBtn.classList.remove('is-visible'); nextBtn.classList.remove('is-visible');
playerEl.querySelector('.docMinigameRestartBar').classList.remove('is-visible'); playerEl.querySelector('.docMinigameRestartBar').classList.remove('is-visible');
item.choices.forEach((choice, idx) => { item.choices.forEach((choice, idx) => {
@@ -1180,30 +1181,31 @@ function forgeDocRenderScenarioPlayerScenario(playerEl, state) {
btn.textContent = choice.text; btn.textContent = choice.text;
btn.addEventListener('click', (e) => { btn.addEventListener('click', (e) => {
e.stopPropagation(); e.stopPropagation();
forgeDocScenarioPlayerAnswer(playerEl, state, idx, btn); forgeDocScenarioPlayerAnswer(playerEl, state, idx);
}); });
choicesEl.appendChild(btn); choicesEl.appendChild(btn);
}); });
progEl.textContent = `Scénario ${state.current + 1} sur ${state.scenarios.length}`; progEl.textContent = `Scénario ${state.current + 1} sur ${state.scenarios.length}`;
} }
function forgeDocScenarioPlayerAnswer(playerEl, state, idx, btn) { function forgeDocScenarioPlayerAnswer(playerEl, state, idx) {
if (state.answered) return; if (state.answered) return;
state.answered = true; state.answered = true;
const item = state.scenarios[state.current]; const item = state.scenarios[state.current];
const buttons = playerEl.querySelectorAll('.docScenarioChoice'); const situationEl = playerEl.querySelector('.docScenarioSituation');
buttons.forEach((b) => { b.disabled = true; }); const choicesEl = playerEl.querySelector('.docScenarioChoices');
const consequenceEl = playerEl.querySelector('.docScenarioConsequence');
const isLast = state.current === state.scenarios.length - 1; const isLast = state.current === state.scenarios.length - 1;
if (idx === item.correct_index) { const isCorrect = idx === item.correct_index;
btn.classList.add('is-correct'); const consequenceText = item.choices[idx].consequence || 'Aucune conséquence renseignée pour ce choix.';
consequenceEl.classList.add('is-visible', 'is-ok');
} else { // Ce n'est pas un Quiz : la conséquence REMPLACE la situation (le récit
btn.classList.add('is-wrong'); // avance), et les boutons de choix disparaissent avec elle — jamais un
buttons[item.correct_index].classList.add('is-correct'); // encart de feedback séparé pendant que la situation resterait affichée
consequenceEl.classList.add('is-visible', 'is-ko'); // (retour utilisateur du 20/09/2026).
} situationEl.className = `docScenarioSituation docQuizFeedback is-visible ${isCorrect ? 'is-ok' : 'is-ko'}`;
consequenceEl.textContent = item.choices[idx].consequence || 'Aucune conséquence renseignée pour ce choix.'; situationEl.textContent = consequenceText;
choicesEl.innerHTML = '';
if (isLast) { if (isLast) {
playerEl.querySelector('.docMinigameRestartBar').classList.add('is-visible'); playerEl.querySelector('.docMinigameRestartBar').classList.add('is-visible');
} else { } else {