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_CMS_API_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
APP_TRAVEL_PREFER_REMOTE=false
+1
View File
@@ -4,6 +4,7 @@ twig:
globals:
htmx_change_trigger: 'change delay:100ms'
website_base_url: '%env(APP_WEBSITE_BASE_URL)%'
customer_service_email: '%env(APP_CUSTOMER_SERVICE_EMAIL)%'
when@test:
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
$this->fieldOptionProviders['veg'] = function (BookingDto $bookingDto, int $participantIndex, array $options = []): array {
$choices = $this->filterServicesByAgeConstraints(
@@ -274,11 +274,21 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider
return [
'label' => 'Verpflegungswunsch',
'multiple' => false,
'expanded' => true,
'expanded' => false,
'required' => false,
'placeholder' => 'Verpflegungswunsch...',
'choices' => $choices,
'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) {
if (null === $service) {
return [];
+7 -9
View File
@@ -39,7 +39,7 @@
{%- block choice_widget_expanded -%}
<table class="w-full table-fixed border-collapse">
{%- 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 isReadonly = child.vars.attr.readonly is defined %}
{%- set child_attr = {} -%}
@@ -65,14 +65,12 @@
</div>
{%- endif -%}
</td>
{% if choiceData %}
<td class="border border-primary-bg p-2 align-top w-24 text-right whitespace-nowrap">
{% if choiceData.price is defined %}
{% set isPkw = choiceData.subType is defined and choiceData.subType in ['PKW', 'CAR'] %}
{{ choiceData.price | format_service_price(not isPkw) }}
{% endif %}
</td>
{% endif %}
<td class="border border-primary-bg p-2 align-top w-24 text-right whitespace-nowrap">
{% if choiceData and choiceData.price is defined %}
{% set isPkw = choiceData.subType is defined and choiceData.subType in ['PKW', 'CAR'] %}
{{ choiceData.price | format_service_price(not isPkw) }}
{% endif %}
</td>
<td class="border border-primary-bg p-2 align-top w-12 text-center">
{% set childTooltip = child.vars.attr['data-tooltip']|default(null) %}
{% if isReadonly or childTooltip is not null %}
+44 -17
View File
@@ -328,23 +328,50 @@
}
}) }}
{{ macros.service_field(form, 'board', 'Verpflegung', {
'attr': {
'hx-trigger': htmx_change_trigger,
'hx-post': path(refreshRouteName, refreshRouteParams|default({index: participantIndex})),
'hx-target': '#main-content', 'hx-swap': 'innerHTML'
}
}) }}
{# VEG field - only render when defined (hide completely when no VEG services available) #}
{% if form.veg is defined %}
{{ macros.service_field(form, 'veg', 'Verpflegungswunsch', {
'attr': {
'hx-trigger': htmx_change_trigger,
'hx-post': path(refreshRouteName, refreshRouteParams|default({index: participantIndex})),
'hx-target': '#main-content', 'hx-swap': 'innerHTML'
}
}) }}
{# 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': {
'hx-trigger': htmx_change_trigger,
'hx-post': path(refreshRouteName, refreshRouteParams|default({index: participantIndex})),
'hx-target': '#main-content', 'hx-swap': 'innerHTML'
}
}) }}
{% endif %}
{% if form.veg is defined %}
<div class="p-2 border-t border-primary-bg">
{{ form_widget(form.veg, {
'attr': {
'hx-trigger': htmx_change_trigger,
'hx-post': path(refreshRouteName, refreshRouteParams|default({index: participantIndex})),
'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 %}
{% set htmxAttr = {