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 {
@apply border-red-500;
@apply border-red-700;
}
.form-checkbox,
@@ -18,7 +18,7 @@ label.required:after {
.form-checkbox--has-error,
.form-radio--has-error {
@apply border-red-500;
@apply border-red-700;
}
select[readonly] {
+9 -11
View File
@@ -19,19 +19,17 @@
{% endif %}
</div>
<div class="ml-3">
{% if messages | length == 1 %}
<p class="{{ html_classes('text-sm font-medium', { 'text-red-800': level == 'error', 'text-green-800': level == 'success', 'text-blue-800': level == 'info' }) }}">
{{ messages[0] }}
</p>
{% else %}
<h3 class="{{ html_classes('text-sm font-medium', { 'text-red-800': level == 'error', 'text-green-800': level == 'success', 'text-blue-800': level == 'info' }) }}">
{{ title is defined ? title : 'Es gab mehrere Probleme' }}
</h3>
<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">
{% if title is defined %}
<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 }) }}">
{{ title }}
</div>
{% endif %}
{% if messages is defined %}
<div class="{{ html_classes('text-sm', { 'text-red-700': level == 'error', 'text-green-800': level == 'success', 'text-blue-800': level == 'info' }) }}">
<ul role="list" class="list-disc space-y-1 pl-4">
{% for message in messages %}
<li>
{{ message }}
{{ message|raw }}
</li>
{% endfor %}
</ul>
+17 -6
View File
@@ -4,8 +4,19 @@
{% include '_partials/_flashes.html.twig' %}
{{ form_start(form) }}
{% 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', 'messages': messages } %}
{% include '_partials/_alert.html.twig' with { 'level': 'error', 'title': 'Die Buchung konnte nicht aktualisiert werden' } %}
{% 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 %}
<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"
@@ -103,16 +114,16 @@
</div>
</div>
{% 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' %}
{% do child.setRendered %}
{% 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 class="flex items-center space-x-4">
<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>
<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" />
+3 -3
View File
@@ -17,7 +17,7 @@
{%- block form_label -%}
{% set class = 'font-semibold' %}
{% if errors|length %}
{% set class = class ~ ' text-red-500' %}
{% set class = class ~ ' text-red-700' %}
{% endif %}
{% set label_attr = label_attr|merge({'class': (label_attr.class|default('') ~ class)|trim}) %}
{{ parent() }}
@@ -27,7 +27,7 @@
{%- if errors|length > 0 -%}
<ul class="pb-2">
{%- for error in errors -%}
<li class="text-red-500">{{ error.message }}</li>
<li class="text-red-700">{{ error.message }}</li>
{%- endfor -%}
</ul>
{%- endif -%}
@@ -99,7 +99,7 @@
<div class="flex h-6 items-center">
{{ form_widget(form) }}
</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 }) }}">
{{ form.vars.label | raw }}
</label>