feat: improved validation error rendering of booking form

This commit is contained in:
Björn Fromme
2025-05-21 15:20:09 +02:00
parent 0ffd770f9b
commit 2e602b85b2
4 changed files with 31 additions and 22 deletions
+2 -2
View File
@@ -8,7 +8,7 @@ label.required:after {
} }
.form-field--has-error { .form-field--has-error {
@apply border-red-500; @apply border-red-700;
} }
.form-checkbox, .form-checkbox,
@@ -18,7 +18,7 @@ label.required:after {
.form-checkbox--has-error, .form-checkbox--has-error,
.form-radio--has-error { .form-radio--has-error {
@apply border-red-500; @apply border-red-700;
} }
select[readonly] { select[readonly] {
+9 -11
View File
@@ -19,19 +19,17 @@
{% endif %} {% endif %}
</div> </div>
<div class="ml-3"> <div class="ml-3">
{% if messages | length == 1 %} {% if title is defined %}
<p class="{{ html_classes('text-sm font-medium', { 'text-red-800': level == 'error', 'text-green-800': level == 'success', 'text-blue-800': level == 'info' }) }}"> <div class="{{ html_classes('text-sm', { 'text-red-700': level == 'error', 'text-green-800': level == 'success', 'text-blue-800': level == 'info', 'mb-2': messages is defined }) }}">
{{ messages[0] }} {{ title }}
</p> </div>
{% else %} {% endif %}
<h3 class="{{ html_classes('text-sm font-medium', { 'text-red-800': level == 'error', 'text-green-800': level == 'success', 'text-blue-800': level == 'info' }) }}"> {% if messages is defined %}
{{ title is defined ? title : 'Es gab mehrere Probleme' }} <div class="{{ html_classes('text-sm', { 'text-red-700': level == 'error', 'text-green-800': level == 'success', 'text-blue-800': level == 'info' }) }}">
</h3> <ul role="list" class="list-disc space-y-1 pl-4">
<div class="{{ html_classes('mt-2 text-sm', { 'text-red-700': level == 'error', 'text-green-700': level == 'success', 'text-blue-700': level == 'info' }) }}">
<ul role="list" class="list-disc space-y-1 pl-5">
{% for message in messages %} {% for message in messages %}
<li> <li>
{{ message }} {{ message|raw }}
</li> </li>
{% endfor %} {% endfor %}
</ul> </ul>
+17 -6
View File
@@ -4,8 +4,19 @@
{% include '_partials/_flashes.html.twig' %} {% include '_partials/_flashes.html.twig' %}
{{ form_start(form) }} {{ form_start(form) }}
{% if not form.vars.valid %} {% if not form.vars.valid %}
{% set messages = form.vars.errors|map(error => error.message) %} {% include '_partials/_alert.html.twig' with { 'level': 'error', 'title': 'Die Buchung konnte nicht aktualisiert werden' } %}
{% include '_partials/_alert.html.twig' with { 'level': 'error', 'title': 'Die Buchung konnte nicht aktualisiert werden', 'messages': messages } %} {% for child in form.participants.children %}
{% if not child.vars.valid %}
{% set participant = child.vars.data %}
{% set messages = [] %}
{% for field in child.children %}
{% for error in field.vars.errors %}
{% set messages = messages|merge([field.vars.label ~ ': ' ~ error.message]) %}
{% endfor %}
{% endfor %}
{% include '_partials/_alert.html.twig' with { 'level': 'error', 'title': 'Teilnehmer:in ' ~ participant.index ~ ':' ~ participant.firstName ~ participant.lastName, 'messages': messages } %}
{% endif %}
{% endfor %}
{% endif %} {% endif %}
<div class="flex flex-col space-y-8 pb-8" {{ stimulus_controller('booking', { 'availabilities': availabilities.items }) }} {{ stimulus_controller('iframe', { 'offsetTop': 100 }) }}> <div class="flex flex-col space-y-8 pb-8" {{ stimulus_controller('booking', { 'availabilities': availabilities.items }) }} {{ stimulus_controller('iframe', { 'offsetTop': 100 }) }}>
<div class="p-8 border border-gray-300 rounded-md" <div class="p-8 border border-gray-300 rounded-md"
@@ -103,16 +114,16 @@
</div> </div>
</div> </div>
{% for child in form.participants %} {% for child in form.participants %}
<div> {% set participant = child.vars.data %}
{% set participant = child.vars.data %} <div id="participant-{{ participant.index }}">
{% if participant.status == 'S' %} {% if participant.status == 'S' %}
{% do child.setRendered %} {% do child.setRendered %}
{% endif %} {% endif %}
<div class="{{ html_classes('p-8 border rounded-md mb-4', { 'border-gray-300': child.vars.valid, 'border-red-500': not child.vars.valid }) }}" {{ stimulus_controller('toggle', {'open': child.vars.valid == false}, { 'closed': 'hidden' }) }}> <div class="{{ html_classes('p-8 border rounded-md mb-4', { 'border-gray-300': child.vars.valid, 'border-red-700': not child.vars.valid }) }}" {{ stimulus_controller('toggle', {'open': child.vars.valid == false}, { 'closed': 'hidden' }) }}>
<div role="button" class="flex items-center justify-between" {{ stimulus_action('toggle', 'toggle', 'click') }}> <div role="button" class="flex items-center justify-between" {{ stimulus_action('toggle', 'toggle', 'click') }}>
<div class="flex items-center space-x-4"> <div class="flex items-center space-x-4">
<span class="text-2xl leading-none font-semibold">Teilnehmer:in {{ participant.index }}: {{ participant.firstName }} {{ participant.lastName }}</span> <span class="text-2xl leading-none font-semibold">Teilnehmer:in {{ participant.index }}: {{ participant.firstName }} {{ participant.lastName }}</span>
{% if participant.status == 'S' %} <span class="inline-block px-2 py-1 text-xs bg-red-500 text-white">storniert</span>{% endif %} {% if participant.status == 'S' %} <span class="inline-block px-2 py-1 text-xs bg-red-700 text-white">storniert</span>{% endif %}
</div> </div>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6" {{ stimulus_target('toggle', 'icon') }}> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6" {{ stimulus_target('toggle', 'icon') }}>
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" /> <path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
+3 -3
View File
@@ -17,7 +17,7 @@
{%- block form_label -%} {%- block form_label -%}
{% set class = 'font-semibold' %} {% set class = 'font-semibold' %}
{% if errors|length %} {% if errors|length %}
{% set class = class ~ ' text-red-500' %} {% set class = class ~ ' text-red-700' %}
{% endif %} {% endif %}
{% set label_attr = label_attr|merge({'class': (label_attr.class|default('') ~ class)|trim}) %} {% set label_attr = label_attr|merge({'class': (label_attr.class|default('') ~ class)|trim}) %}
{{ parent() }} {{ parent() }}
@@ -27,7 +27,7 @@
{%- if errors|length > 0 -%} {%- if errors|length > 0 -%}
<ul class="pb-2"> <ul class="pb-2">
{%- for error in errors -%} {%- for error in errors -%}
<li class="text-red-500">{{ error.message }}</li> <li class="text-red-700">{{ error.message }}</li>
{%- endfor -%} {%- endfor -%}
</ul> </ul>
{%- endif -%} {%- endif -%}
@@ -99,7 +99,7 @@
<div class="flex h-6 items-center"> <div class="flex h-6 items-center">
{{ form_widget(form) }} {{ form_widget(form) }}
</div> </div>
<div class="{{ html_classes('ml-1 leading-6', { 'text-red-500': errors|length }) }}"> <div class="{{ html_classes('ml-1 leading-6', { 'text-red-700': errors|length }) }}">
<label for="{{ form.vars.id }}" class="{{ html_classes({ 'pointer-events-none': form.vars.attr.readonly is defined }) }}"> <label for="{{ form.vars.id }}" class="{{ html_classes({ 'pointer-events-none': form.vars.attr.readonly is defined }) }}">
{{ form.vars.label | raw }} {{ form.vars.label | raw }}
</label> </label>