From a75bc14638297252336bead5c55b00fe7a105726 Mon Sep 17 00:00:00 2001 From: "coderabbitai[bot]" <136622811+coderabbitai[bot]@users.noreply.github.com> Date: Tue, 7 Jul 2026 06:46:09 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=9D=20CodeRabbit=20Chat:=20Add=20retry?= =?UTF-8?q?=20and=20unsaved=20notice=20for=20question=20list=20ordering?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bo/question_list_controller.js | 46 ++++++++++++++++--- .../backoffice/quiz/tab_overview.html.twig | 19 ++++++++ translations/messages+intl-icu.nl.xliff | 8 ++++ 3 files changed, 66 insertions(+), 7 deletions(-) diff --git a/assets/controllers/bo/question_list_controller.js b/assets/controllers/bo/question_list_controller.js index 3121894..b611253 100644 --- a/assets/controllers/bo/question_list_controller.js +++ b/assets/controllers/bo/question_list_controller.js @@ -1,7 +1,10 @@ import {Controller} from '@hotwired/stimulus'; +import {Modal} from 'bootstrap'; + +const RETRY_DELAY_MS = 1000; export default class extends Controller { - static targets = ['list', 'item', 'status']; + static targets = ['list', 'item', 'status', 'noticeModal']; static values = { reorderUrl: String, csrf: String, @@ -24,6 +27,10 @@ export default class extends Controller { handle.setAttribute('draggable', 'true'); handle.addEventListener('dragstart', (e) => { + if (this._locked) { + e.preventDefault(); + return; + } this._dragging = el; e.dataTransfer.effectAllowed = 'move'; setTimeout(() => el.classList.add('opacity-50'), 0); @@ -107,15 +114,40 @@ export default class extends Controller { if (numberEl) numberEl.textContent = String(i + 1); }); - try { + const attempt = async () => { const res = await fetch(this.reorderUrlValue, {method: 'POST', body: params}); - if (res.ok) { - this._setStatus('saved'); - } else { - this._setStatus('error'); + if (!res.ok) { + throw new Error(`Unexpected response status: ${res.status}`); } + }; + + try { + await attempt(); } catch { - this._setStatus('error'); + await new Promise(resolve => setTimeout(resolve, RETRY_DELAY_MS)); + try { + await attempt(); + } catch { + this._setStatus('error'); + this._lockReordering(); + return; + } + } + + this._setStatus('saved'); + } + + _lockReordering() { + this._locked = true; + this.itemTargets.forEach(el => { + const handle = el.querySelector('[data-drag-handle]'); + if (handle) { + handle.removeAttribute('draggable'); + handle.classList.add('opacity-25', 'pe-none'); + } + }); + if (this.hasNoticeModalTarget) { + Modal.getOrCreateInstance(this.noticeModalTarget).show(); } } } diff --git a/templates/backoffice/quiz/tab_overview.html.twig b/templates/backoffice/quiz/tab_overview.html.twig index 885809f..3676252 100644 --- a/templates/backoffice/quiz/tab_overview.html.twig +++ b/templates/backoffice/quiz/tab_overview.html.twig @@ -152,6 +152,25 @@ + + {{ _self.confirm_modal( diff --git a/translations/messages+intl-icu.nl.xliff b/translations/messages+intl-icu.nl.xliff index 85dc0f7..be486ef 100644 --- a/translations/messages+intl-icu.nl.xliff +++ b/translations/messages+intl-icu.nl.xliff @@ -181,6 +181,10 @@ Could not find candidate with name {name} in elimination. Kon geen kandidaat vinden met de naam {name} in de eliminatie + + Could not save order + Kon volgorde niet opslaan + Create a season Maak een seizoen aan @@ -649,6 +653,10 @@ Sync latest changes to this quiz Laatste wijzigingen synchroniseren naar deze quiz + + The new question order could not be saved. Reordering has been disabled until you refresh the page. + De nieuwe volgorde van de vragen kon niet worden opgeslagen. Herordenen is uitgeschakeld totdat u de pagina vernieuwt. + The password fields must match. De wachtwoorden moeten overeen komen.