feat: replace fetch-based modal forms with Turbo Frames (#181)

Enable @hotwired/turbo with Drive explicitly disabled, then migrate the
bo--modal-form Stimulus controller (custom fetch + X-Modal-Request pattern)
to a thin bo--modal controller that lets Turbo handle HTTP and DOM swap.
Adds frame templates for quiz questions and bank questions; controllers now
detect Turbo-Frame header instead of X-Modal-Request.
This commit is contained in:
2026-07-06 23:13:55 +02:00
parent 3b6ff680e5
commit cb577d60d3
12 changed files with 175 additions and 120 deletions
@@ -1,9 +1,10 @@
<div class="row">
<div class="col-md-8 col-12" data-controller="bo--modal-form">
<div class="col-md-8 col-12" data-controller="bo--modal"
data-action="turbo:frame-load->bo--modal#frameLoad turbo:submit-end->bo--modal#frameSubmitEnd">
<div class="mb-3">
<button class="btn btn-sm btn-outline-primary"
data-action="click->bo--modal-form#open"
data-url="{{ path('tvdt_backoffice_question_bank_new', {seasonCode: season.seasonCode}) }}"
data-action="click->bo--modal#open"
data-src="{{ path('tvdt_backoffice_question_bank_new', {seasonCode: season.seasonCode}) }}"
data-modal-title="{{ 'Add question'|trans }}">
{{ 'Add question'|trans }}
</button>
@@ -126,8 +127,8 @@
{% endif %}
<div class="btn-group btn-group-sm" role="group">
<button type="button" class="btn btn-outline-secondary"
data-action="click->bo--modal-form#open"
data-url="{{ path('tvdt_backoffice_question_bank_edit', {seasonCode: season.seasonCode, bankQuestion: bankQuestion.id}) }}"
data-action="click->bo--modal#open"
data-src="{{ path('tvdt_backoffice_question_bank_edit', {seasonCode: season.seasonCode, bankQuestion: bankQuestion.id}) }}"
title="{{ 'Edit'|trans }}"><i class="bi bi-pencil"></i></button>
<button type="button" class="btn btn-outline-danger" data-bs-toggle="modal"
data-bs-target="#deleteBankQuestion-{{ bankQuestion.id }}"
@@ -170,7 +171,7 @@
</tbody>
</table>
<div class="modal fade" tabindex="-1"
data-bo--modal-form-target="modal"
data-bo--modal-target="modal"
aria-labelledby="bankQuestionEditModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
@@ -178,8 +179,7 @@
<h5 class="modal-title" id="bankQuestionEditModalLabel">{{ 'Edit question'|trans }}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body" data-bo--modal-form-target="modalBody"></div>
<div class="modal-footer" data-bo--modal-form-target="modalFooter"></div>
<turbo-frame id="bank-question-modal-frame" data-bo--modal-target="frame"></turbo-frame>
</div>
</div>
</div>
@@ -2,9 +2,9 @@
<div class="col-md-6 col-12">
<div class="d-flex align-items-center gap-2 mb-3">
<a class="btn btn-sm btn-outline-primary"
href="{{ path('tvdt_backoffice_quiz_add', {seasonCode: season.seasonCode}) }}">{{ 'Import Quiz from Excel'|trans }}</a>
<a class="btn btn-sm btn-outline-secondary"
href="{{ path('tvdt_backoffice_quiz_add_blank', {seasonCode: season.seasonCode}) }}">{{ 'Add Empty Quiz'|trans }}</a>
<a class="btn btn-sm btn-outline-secondary"
href="{{ path('tvdt_backoffice_quiz_add', {seasonCode: season.seasonCode}) }}">{{ 'Import Quiz from Excel'|trans }}</a>
</div>
<div class="list-group mb-3">
{% for quiz in season.quizzes %}