feat: info text and correct field type for service group 'VEG'

addresses #869bvch71
This commit is contained in:
Björn Fromme
2026-03-16 12:02:59 +01:00
parent 1e653e213a
commit aa3b23ecb8
5 changed files with 66 additions and 29 deletions
+1
View File
@@ -53,6 +53,7 @@ APP_TRAVEL_INFO_BASE_URL="https://www.ep-reisen.de/reiseinformationen/"
APP_TERMS_AND_CONDITIONS_URL="https://www.ep-reisen.de/skireisen/infos/allgemeine-infos/agb/" APP_TERMS_AND_CONDITIONS_URL="https://www.ep-reisen.de/skireisen/infos/allgemeine-infos/agb/"
APP_CMS_API_BASE_URL="https://www.ep-reisen.de/" APP_CMS_API_BASE_URL="https://www.ep-reisen.de/"
APP_WEBSITE_BASE_URL="https://www.ep-reisen.de/" APP_WEBSITE_BASE_URL="https://www.ep-reisen.de/"
APP_CUSTOMER_SERVICE_EMAIL="[email protected]"
# Travel Data Service Configuration # Travel Data Service Configuration
APP_TRAVEL_PREFER_REMOTE=false APP_TRAVEL_PREFER_REMOTE=false
+1
View File
@@ -4,6 +4,7 @@ twig:
globals: globals:
htmx_change_trigger: 'change delay:100ms' htmx_change_trigger: 'change delay:100ms'
website_base_url: '%env(APP_WEBSITE_BASE_URL)%' website_base_url: '%env(APP_WEBSITE_BASE_URL)%'
customer_service_email: '%env(APP_CUSTOMER_SERVICE_EMAIL)%'
when@test: when@test:
twig: twig:
@@ -258,7 +258,7 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
]; ];
}; };
// Veg (vegetarian/vegan) field provider - provides dietary preference options as radio buttons (mutually exclusive) // Veg (vegetarian/vegan) field provider - provides dietary preference options as dropdown
// Returns empty array when no dietary options available // Returns empty array when no dietary options available
$this->fieldOptionProviders['veg'] = function (BookingDto $bookingDto, int $participantIndex, array $options = []): array { $this->fieldOptionProviders['veg'] = function (BookingDto $bookingDto, int $participantIndex, array $options = []): array {
$choices = $this->filterServicesByAgeConstraints( $choices = $this->filterServicesByAgeConstraints(
@@ -274,11 +274,21 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
return [ return [
'label' => 'Verpflegungswunsch', 'label' => 'Verpflegungswunsch',
'multiple' => false, 'multiple' => false,
'expanded' => true, 'expanded' => false,
'required' => false, 'required' => false,
'placeholder' => 'Verpflegungswunsch...',
'choices' => $choices, 'choices' => $choices,
'choice_value' => 'id', 'choice_value' => 'id',
'choice_label' => fn (?Service $service) => $service?->label, 'choice_label' => function (?Service $service): ?string {
if (null === $service) {
return null;
}
$priceLabel = (null === $service->price || 0.0 === $service->price)
? 'inkl.'
: number_format($service->price, 2, ',', '.') . ' €';
return $service->label . ' (' . $priceLabel . ')';
},
'choice_attr' => function (?Service $service) use ($bookingDto, $participantIndex) { 'choice_attr' => function (?Service $service) use ($bookingDto, $participantIndex) {
if (null === $service) { if (null === $service) {
return []; return [];
+2 -4
View File
@@ -39,7 +39,7 @@
{%- block choice_widget_expanded -%} {%- block choice_widget_expanded -%}
<table class="w-full table-fixed border-collapse"> <table class="w-full table-fixed border-collapse">
{%- for child in form %} {%- for child in form %}
{% set choiceData = form.vars.choices[loop.index0].data %} {% set choiceData = form.vars.choices[loop.index0] is defined ? form.vars.choices[loop.index0].data : null %}
{% set description = child.vars.attr['data-description']|default(null) %} {% set description = child.vars.attr['data-description']|default(null) %}
{% set isReadonly = child.vars.attr.readonly is defined %} {% set isReadonly = child.vars.attr.readonly is defined %}
{%- set child_attr = {} -%} {%- set child_attr = {} -%}
@@ -65,14 +65,12 @@
</div> </div>
{%- endif -%} {%- endif -%}
</td> </td>
{% if choiceData %}
<td class="border border-primary-bg p-2 align-top w-24 text-right whitespace-nowrap"> <td class="border border-primary-bg p-2 align-top w-24 text-right whitespace-nowrap">
{% if choiceData.price is defined %} {% if choiceData and choiceData.price is defined %}
{% set isPkw = choiceData.subType is defined and choiceData.subType in ['PKW', 'CAR'] %} {% set isPkw = choiceData.subType is defined and choiceData.subType in ['PKW', 'CAR'] %}
{{ choiceData.price | format_service_price(not isPkw) }} {{ choiceData.price | format_service_price(not isPkw) }}
{% endif %} {% endif %}
</td> </td>
{% endif %}
<td class="border border-primary-bg p-2 align-top w-12 text-center"> <td class="border border-primary-bg p-2 align-top w-12 text-center">
{% set childTooltip = child.vars.attr['data-tooltip']|default(null) %} {% set childTooltip = child.vars.attr['data-tooltip']|default(null) %}
{% if isReadonly or childTooltip is not null %} {% if isReadonly or childTooltip is not null %}
+31 -4
View File
@@ -328,23 +328,50 @@
} }
}) }} }) }}
{{ macros.service_field(form, 'board', 'Verpflegung', { {# Board and VEG fields combined in one fieldset #}
{% if form.board is defined or form.veg is defined %}
<div class="pb-4">
<fieldset class="border border-primary-bg">
<legend class="w-full bg-primary-bg p-2 font-semibold uppercase">Verpflegung</legend>
{% if form.board is defined %}
{{ form_widget(form.board, {
'attr': { 'attr': {
'hx-trigger': htmx_change_trigger, 'hx-trigger': htmx_change_trigger,
'hx-post': path(refreshRouteName, refreshRouteParams|default({index: participantIndex})), 'hx-post': path(refreshRouteName, refreshRouteParams|default({index: participantIndex})),
'hx-target': '#main-content', 'hx-swap': 'innerHTML' 'hx-target': '#main-content', 'hx-swap': 'innerHTML'
} }
}) }} }) }}
{% endif %}
{# VEG field - only render when defined (hide completely when no VEG services available) #}
{% if form.veg is defined %} {% if form.veg is defined %}
{{ macros.service_field(form, 'veg', 'Verpflegungswunsch', { <div class="p-2 border-t border-primary-bg">
{{ form_widget(form.veg, {
'attr': { 'attr': {
'hx-trigger': htmx_change_trigger, 'hx-trigger': htmx_change_trigger,
'hx-post': path(refreshRouteName, refreshRouteParams|default({index: participantIndex})), 'hx-post': path(refreshRouteName, refreshRouteParams|default({index: participantIndex})),
'hx-target': '#main-content', 'hx-swap': 'innerHTML' 'hx-target': '#main-content', 'hx-swap': 'innerHTML'
} }
}) }} }) }}
{{ form_errors(form.veg) }}
</div>
{% endif %}
</fieldset>
{% if form.board is defined %}
{{ form_errors(form.board) }}
{% endif %}
<div class="text-sm mt-2 px-2 flex items-start space-x-1">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5 flex-shrink-0">
<path stroke-linecap="round" stroke-linejoin="round" d="M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z" />
</svg>
<span>Bitte teile uns eventuelle Unverträglichkeiten an <a href="mailto:{{ customer_service_email }}" class="text-primary-light hover:underline">{{ customer_service_email }}</a> (inkl. der Buchungsnummer) mit.</span>
</div>
</div>
{% else %}
<div class="pb-4">
<fieldset class="border border-primary-bg">
<legend class="w-full bg-primary-bg p-2 font-semibold uppercase">Verpflegung</legend>
<div class="p-2 text-sm text-gray-500">Nicht wählbar</div>
</fieldset>
</div>
{% endif %} {% endif %}
{% set htmxAttr = { {% set htmxAttr = {