diff --git a/document_engine/rendering/render_document_element.py b/document_engine/rendering/render_document_element.py index 1904c53b..4b87eb44 100644 --- a/document_engine/rendering/render_document_element.py +++ b/document_engine/rendering/render_document_element.py @@ -467,10 +467,17 @@ def _render_scenario_player(config: dict[str, Any]) -> str: 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 en situation séquentielles, pas des éléments à faire correspondre ou - retrouver — un mélange n'aurait ici aucun sens pédagogique. 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.""" + retrouver — un mélange n'aurait ici aucun sens pédagogique. Un seul + bloc de texte (.docScenarioSituation) sert successivement à afficher + la situation PUIS, une fois un choix fait, la conséquence à sa place + (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) return ( f'
' @@ -479,7 +486,6 @@ def _render_scenario_player(config: dict[str, Any]) -> str: f'
' f'
' f'
' - f'
' f'
' f'' f"
" diff --git a/document_engine/rendering/rendering.md b/document_engine/rendering/rendering.md index 94bdb477..4e635d8d 100644 --- a/document_engine/rendering/rendering.md +++ b/document_engine/rendering/rendering.md @@ -102,14 +102,19 @@ regroupement à chaque appel. scénarios) — sanitizée (`sanitize_scenario_config`) avant lecture. Si au moins un scénario existe, s'y ajoute (fonction privée `_render_scenario_player`) la mise en situation RÉELLE et interactive : - l'apprenant lit la situation, choisit une option, découvre la - conséquence de SON choix et si c'était le bon, puis passe au scénario - suivant. Les scénarios gardent l'ORDRE 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 + l'apprenant lit la situation, choisit une option, puis la conséquence + de SON choix REMPLACE l'affichage de la situation (les boutons de + choix disparaissent avec elle) avant de passer au scénario suivant — + volontairement PAS le comportement du Quiz, où la question resterait + 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"). Les scénarios gardent l'ORDRE + 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 — 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 diff --git a/static/document/js/document-editor.js b/static/document/js/document-editor.js index ff6fda59..6b8d87b7 100644 --- a/static/document/js/document-editor.js +++ b/static/document/js/document-editor.js @@ -1163,14 +1163,15 @@ function forgeDocRenderScenarioPlayerScenario(playerEl, state) { const item = state.scenarios[state.current]; const situationEl = playerEl.querySelector('.docScenarioSituation'); const choicesEl = playerEl.querySelector('.docScenarioChoices'); - const consequenceEl = playerEl.querySelector('.docScenarioConsequence'); const progEl = playerEl.querySelector('.docScenarioProg'); 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; choicesEl.innerHTML = ''; - consequenceEl.className = 'docScenarioConsequence docQuizFeedback'; - consequenceEl.textContent = ''; nextBtn.classList.remove('is-visible'); playerEl.querySelector('.docMinigameRestartBar').classList.remove('is-visible'); item.choices.forEach((choice, idx) => { @@ -1180,30 +1181,31 @@ function forgeDocRenderScenarioPlayerScenario(playerEl, state) { btn.textContent = choice.text; btn.addEventListener('click', (e) => { e.stopPropagation(); - forgeDocScenarioPlayerAnswer(playerEl, state, idx, btn); + forgeDocScenarioPlayerAnswer(playerEl, state, idx); }); choicesEl.appendChild(btn); }); 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; state.answered = true; const item = state.scenarios[state.current]; - const buttons = playerEl.querySelectorAll('.docScenarioChoice'); - buttons.forEach((b) => { b.disabled = true; }); - const consequenceEl = playerEl.querySelector('.docScenarioConsequence'); + const situationEl = playerEl.querySelector('.docScenarioSituation'); + const choicesEl = playerEl.querySelector('.docScenarioChoices'); const isLast = state.current === state.scenarios.length - 1; - if (idx === item.correct_index) { - btn.classList.add('is-correct'); - consequenceEl.classList.add('is-visible', 'is-ok'); - } else { - btn.classList.add('is-wrong'); - buttons[item.correct_index].classList.add('is-correct'); - consequenceEl.classList.add('is-visible', 'is-ko'); - } - consequenceEl.textContent = item.choices[idx].consequence || 'Aucune conséquence renseignée pour ce choix.'; + const isCorrect = idx === item.correct_index; + const consequenceText = item.choices[idx].consequence || 'Aucune conséquence renseignée pour ce choix.'; + + // Ce n'est pas un Quiz : la conséquence REMPLACE la situation (le récit + // avance), et les boutons de choix disparaissent avec elle — jamais un + // encart de feedback séparé pendant que la situation resterait affichée + // (retour utilisateur du 20/09/2026). + situationEl.className = `docScenarioSituation docQuizFeedback is-visible ${isCorrect ? 'is-ok' : 'is-ko'}`; + situationEl.textContent = consequenceText; + choicesEl.innerHTML = ''; + if (isLast) { playerEl.querySelector('.docMinigameRestartBar').classList.add('is-visible'); } else {