549 lines
30 KiB
Twig
549 lines
30 KiB
Twig
{# Macro to render a form field or static value based on field state #}
|
||
{% macro field_or_static(form, fieldName, label, staticValue, bookingDto, participantIndex, options = {}) %}
|
||
{% if form[fieldName] is defined %}
|
||
{# Field is in form structure - render normally as form input #}
|
||
{{ form_row(form[fieldName], options) }}
|
||
{% elseif is_static_text(fieldName, bookingDto, participantIndex) %}
|
||
{# Field excluded from form but should render as static text #}
|
||
<div class="mb-1">
|
||
<label class="font-semibold block">{{ label }}</label>
|
||
<div class="text-sm text-gray-600">{{ staticValue ?: '-' }}</div>
|
||
</div>
|
||
{% endif %}
|
||
{# If neither condition is true, don't render anything (truly hidden) #}
|
||
{% endmacro %}
|
||
|
||
{# Macro to render a field or placeholder with consistent fieldset structure #}
|
||
{% macro service_field(form, fieldName, label, options = {}, undefinedLabel = 'Nicht wählbar') %}
|
||
{% if form[fieldName] is defined %}
|
||
{{ form_row(form[fieldName], options) }}
|
||
{% else %}
|
||
<fieldset class="mb-1">
|
||
<legend class="font-semibold mb-1">{{ label }}</legend>
|
||
<div class="text-sm text-gray-500">{{ undefinedLabel }}</div>
|
||
</fieldset>
|
||
{% endif %}
|
||
{% endmacro %}
|
||
|
||
{# Macro to render a service choice table row with optional price and tooltip #}
|
||
{% macro service_choice_row(child, price, tooltipText, tooltipLabel, widgetAttr = {}) %}
|
||
{% set isReadonly = child.vars.attr.readonly is defined %}
|
||
<tr>
|
||
<td class="border border-primary-bg p-2 align-top">
|
||
<div>{{ child.vars.label }}</div>
|
||
{%- if tooltipText is not null -%}
|
||
<div {{ stimulus_controller('tooltip') }}>
|
||
<button type="button" data-tooltip-target="trigger" class="text-sm text-primary-light">
|
||
{{ tooltipLabel|default('Details') }}
|
||
</button>
|
||
<template data-tooltip-target="template">
|
||
<div class="text-sm">{{ tooltipText|raw }}</div>
|
||
</template>
|
||
</div>
|
||
{%- endif -%}
|
||
</td>
|
||
<td class="border border-primary-bg p-2 align-top w-24 text-right whitespace-nowrap">
|
||
{% if price %}
|
||
{{ price | format_currency('EUR') }}
|
||
{% 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) %}
|
||
<div class="{{ html_classes({ 'cursor-not-allowed': isReadonly }) }}"{% if childTooltip is not null %} {{ stimulus_controller('tooltip', { 'content': childTooltip }) }}{% endif %}>
|
||
<div class="{{ isReadonly ? 'pointer-events-none' : '' }}">
|
||
{{ form_widget(child, { 'attr': widgetAttr }) }}
|
||
</div>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
{% endmacro %}
|
||
|
||
{# Macro to render a checkbox table row (for trailing checkboxes like rental insurance, parking, bulk insurance) #}
|
||
{% macro checkbox_row(checkboxField, label, price, tooltipText, tooltipLabel, widgetAttr = {}) %}
|
||
{% set isReadonly = checkboxField.vars.attr.readonly is defined %}
|
||
<tr>
|
||
<td class="border border-primary-bg p-2 align-top">
|
||
<div>{{ label }}</div>
|
||
{%- if tooltipText is not null -%}
|
||
<div {{ stimulus_controller('tooltip') }}>
|
||
<button type="button" data-tooltip-target="trigger" class="text-sm text-primary-light">
|
||
{{ tooltipLabel|default('Info') }}
|
||
</button>
|
||
<template data-tooltip-target="template">
|
||
<div class="text-sm">{{ tooltipText|raw }}</div>
|
||
</template>
|
||
</div>
|
||
{%- endif -%}
|
||
</td>
|
||
<td class="border border-primary-bg p-2 align-top w-24 text-right whitespace-nowrap">
|
||
{% if price %}
|
||
{{ price | format_currency('EUR') }}
|
||
{% endif %}
|
||
</td>
|
||
<td class="border border-primary-bg p-2 align-top w-12 text-center">
|
||
{% set fieldTooltip = checkboxField.vars.attr['data-tooltip']|default(null) %}
|
||
<div class="{{ html_classes({ 'cursor-not-allowed': isReadonly }) }}"{% if fieldTooltip is not null %} {{ stimulus_controller('tooltip', { 'content': fieldTooltip }) }}{% endif %}>
|
||
<div class="{{ isReadonly ? 'pointer-events-none' : '' }}">
|
||
{{ form_widget(checkboxField, { 'attr': widgetAttr }) }}
|
||
</div>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
{% endmacro %}
|
||
|
||
|
||
{# Macro to render insurance choice table row with product info links #}
|
||
{% macro insurance_choice_row(child, price, urlsProductInfo, widgetAttr = {}) %}
|
||
{% set isReadonly = child.vars.attr.readonly is defined %}
|
||
<tr>
|
||
<td class="border border-primary-bg p-2 align-top">
|
||
<div>{{ child.vars.label }}</div>
|
||
{%- if urlsProductInfo is not empty -%}
|
||
<div {{ stimulus_controller('tooltip') }}>
|
||
<button type="button" data-tooltip-target="trigger" class="text-sm text-primary-light">
|
||
Details
|
||
</button>
|
||
<template data-tooltip-target="template">
|
||
<div class="text-sm">
|
||
{%- for url in urlsProductInfo -%}
|
||
<a href="{{ url }}" target="_blank" rel="noopener noreferrer" class="block">
|
||
Produktinformation {{ urlsProductInfo|length > 1 ? loop.index : '' }}
|
||
</a>
|
||
{%- endfor -%}
|
||
</div>
|
||
</template>
|
||
</div>
|
||
{%- endif -%}
|
||
</td>
|
||
<td class="border border-primary-bg p-2 align-top w-24 text-right whitespace-nowrap">
|
||
{% if price %}
|
||
{{ price | format_currency('EUR') }}
|
||
{% 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) %}
|
||
<div class="{{ html_classes({ 'cursor-not-allowed': isReadonly }) }}"{% if childTooltip is not null %} {{ stimulus_controller('tooltip', { 'content': childTooltip }) }}{% endif %}>
|
||
<div class="{{ isReadonly ? 'pointer-events-none' : '' }}">
|
||
{{ form_widget(child, { 'attr': widgetAttr }) }}
|
||
</div>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
{% endmacro %}
|
||
|
||
{# Standalone participant form view (replaces main content area) #}
|
||
{% block participant_form %}
|
||
{% form_theme form 'booking/_form_theme.html.twig' %}
|
||
{% import _self as macros %}
|
||
|
||
<div class="flex items-center gap-4 pb-4 mb-4 border-b border-primary-bg">
|
||
<div class="w-12 h-12 inline-flex flex-shrink-0 items-center justify-center rounded-full bg-primary-dark text-white text-2xl font-bold">
|
||
{{ participantIndex + 1 }}
|
||
</div>
|
||
<div class="text-2xl">
|
||
{{ participantIndex == 0 ? 'Anmelder:in' : 'Teilnehmer:in ' ~ (participantIndex + 1) }}
|
||
</div>
|
||
</div>
|
||
|
||
<div id="participant-form" class="space-y-4" hx-disinherit="hx-boost">
|
||
|
||
{% from '_partials/_validation_errors.html.twig' import validation_alert %}
|
||
{{ validation_alert(form) }}
|
||
|
||
{# Eligibility checks #}
|
||
{% set participantData = form.vars.data.participant %}
|
||
{% set hasDateOfBirth = participantData and participantData.dateOfBirth %}
|
||
{% set isEligible = hasDateOfBirth and is_participant_eligible(bookingDto, participantIndex) %}
|
||
|
||
{% if not hasDateOfBirth %}
|
||
{% include '_partials/_alert.html.twig' with {
|
||
level: 'info',
|
||
messages: ['Leistungen sind erst nach Angabe des Geburtsdatums buchbar']
|
||
} %}
|
||
{% elseif not isEligible %}
|
||
{% include '_partials/_alert.html.twig' with {
|
||
level: 'error',
|
||
messages: ['Buchung wegen des Alters von Teilnehmer:in ' ~ (participantIndex + 1) ~ ' nicht möglich']
|
||
} %}
|
||
{% endif %}
|
||
|
||
{# Personal data section #}
|
||
<div class="grid lg:grid-cols-2 lg:gap-x-8">
|
||
{{ macros.field_or_static(form, 'firstName', 'Vorname', form.vars.data.participant.firstName, bookingDto, participantIndex) }}
|
||
{{ macros.field_or_static(form, 'lastName', 'Nachname', form.vars.data.participant.lastName, bookingDto, participantIndex) }}
|
||
{{ macros.field_or_static(
|
||
form,
|
||
'dateOfBirth',
|
||
'Geburtsdatum',
|
||
form.vars.data.participant.dateOfBirth ? form.vars.data.participant.dateOfBirth|date('d.m.Y') : null,
|
||
bookingDto,
|
||
participantIndex,
|
||
{
|
||
'attr': {
|
||
'hx-trigger': 'change',
|
||
'hx-post': path(refreshRouteName, refreshRouteParams|default({index: participantIndex})),
|
||
'hx-target': '#main-content',
|
||
'hx-swap': 'innerHTML'
|
||
}
|
||
}
|
||
) }}
|
||
|
||
{% set genderLabel = form.vars.data.participant.gender == 'M' ? 'männlich' : (form.vars.data.participant.gender == 'W' ? 'weiblich' : (form.vars.data.participant.gender == 'D' ? 'divers' : null)) %}
|
||
{{ macros.field_or_static(form, 'gender', 'Geschlecht', genderLabel, bookingDto, participantIndex) }}
|
||
|
||
{{ macros.field_or_static(form, 'nationality', 'Nationalität', form.vars.data.participant.nationality|map_nationality, bookingDto, participantIndex) }}
|
||
|
||
{# Contact information #}
|
||
{{ macros.field_or_static(form, 'email', 'E-Mail', form.vars.data.participant.email, bookingDto, participantIndex) }}
|
||
{{ macros.field_or_static(form, 'mobile', 'Telefon (mobil)', form.vars.data.participant.mobile, bookingDto, participantIndex) }}
|
||
</div>
|
||
|
||
{# Address #}
|
||
{% if form.address is defined %}
|
||
<div class="grid lg:grid-cols-2 lg:gap-x-8">
|
||
{# Address field is in form - render as editable form fields #}
|
||
{{ form_row(form.address.street) }}
|
||
{{ form_row(form.address.postCode) }}
|
||
{{ form_row(form.address.city) }}
|
||
{{ form_row(form.address.country) }}
|
||
</div>
|
||
{% elseif is_static_text('address', bookingDto, participantIndex) %}
|
||
{# Address excluded from form but should render as static text #}
|
||
{# For applicant (index 0), use booking.applicant.address which has full data from BPN #}
|
||
{# For other participants, use participants[index].address (may only have country in BPN response) #}
|
||
{% set addressSource = (0 == participantIndex and bookingDto.booking) ? bookingDto.booking.applicant : bookingDto.participants[participantIndex] %}
|
||
<div>
|
||
<label class="font-semibold mb-1 block">Adresse</label>
|
||
{% if addressSource.address %}
|
||
<div class="text-sm text-gray-600">
|
||
{% if addressSource.address.street %}{{ addressSource.address.street }}<br>{% endif %}
|
||
{% if addressSource.address.postCode or addressSource.address.city %}
|
||
{{ addressSource.address.postCode }} {{ addressSource.address.city }}<br>
|
||
{% endif %}
|
||
{% if addressSource.address.country %}{{ addressSource.address.country|map_country }}{% endif %}
|
||
</div>
|
||
{% else %}
|
||
<div class="text-sm text-gray-600">-</div>
|
||
{% endif %}
|
||
</div>
|
||
{% endif %}
|
||
|
||
{# Room assignment - hidden until date of birth is provided, or shown as static text in edit mode #}
|
||
{% if form.assignedRoomId is defined or is_static_text('assignedRoomId', bookingDto, participantIndex) %}
|
||
{% set assignedRoom = bookingDto.travel.getRoomById(form.vars.data.participant.assignedRoomId) %}
|
||
{% set roomLabel = assignedRoom ? assignedRoom.label : 'Kein Zimmer zugewiesen' %}
|
||
<div class="grid lg:grid-cols-2 lg:gap-x-8">
|
||
{{ macros.field_or_static(form, 'assignedRoomId', 'Zimmer', roomLabel, bookingDto, participantIndex, {
|
||
'attr': {
|
||
'hx-trigger': 'change',
|
||
'hx-post': path(refreshRouteName, refreshRouteParams|default({index: participantIndex})),
|
||
'hx-target': '#main-content',
|
||
'hx-swap': 'innerHTML'
|
||
}
|
||
}) }}
|
||
{% if form.remarksRoom is defined %}
|
||
{{ form_row(form.remarksRoom) }}
|
||
{% endif %}
|
||
</div>
|
||
{% endif %}
|
||
|
||
{# Service selection #}
|
||
{% if isEligible %}
|
||
{{ macros.service_field(form, 'skiPass', 'Skipass', {
|
||
'attr': {
|
||
'hx-trigger': 'change',
|
||
'hx-post': path(refreshRouteName, refreshRouteParams|default({index: participantIndex})),
|
||
'hx-target': '#main-content', 'hx-swap': 'innerHTML'
|
||
}
|
||
}) }}
|
||
|
||
{{ macros.service_field(form, 'courses', 'Kurse', {
|
||
'attr': {
|
||
'hx-trigger': 'change',
|
||
'hx-post': path(refreshRouteName, refreshRouteParams|default({index: participantIndex})),
|
||
'hx-target': '#main-content', 'hx-swap': 'innerHTML'
|
||
}
|
||
}) }}
|
||
|
||
{{ macros.service_field(form, 'additionalServices', 'Zusatzleistungen', {
|
||
'attr': {
|
||
'hx-trigger': 'change',
|
||
'hx-post': path(refreshRouteName, refreshRouteParams|default({index: participantIndex})),
|
||
'hx-target': '#main-content', 'hx-swap': 'innerHTML'
|
||
}
|
||
}) }}
|
||
|
||
{{ macros.service_field(form, 'board', 'Verpflegung', {
|
||
'attr': {
|
||
'hx-trigger': 'change',
|
||
'hx-post': path(refreshRouteName, refreshRouteParams|default({index: participantIndex})),
|
||
'hx-target': '#main-content', 'hx-swap': 'innerHTML'
|
||
}
|
||
}) }}
|
||
|
||
{% set htmxAttr = {
|
||
'hx-trigger': 'change',
|
||
'hx-post': path(refreshRouteName, refreshRouteParams|default({index: participantIndex})),
|
||
'hx-target': '#main-content',
|
||
'hx-swap': 'innerHTML'
|
||
} %}
|
||
<div class="pb-4">
|
||
<fieldset class="border border-primary-bg">
|
||
<legend class="{{ html_classes('w-full bg-primary-bg p-2 font-semibold uppercase', { 'text-red-500': form.rentals is defined and not form.rentals.vars.valid }) }}">
|
||
Leihmaterial
|
||
{% if form.rentals is defined and form.rentals.vars.required %}<span>*</span>{% endif %}
|
||
</legend>
|
||
|
||
{% if form.rentals is defined %}
|
||
<table class="w-full table-fixed border-collapse">
|
||
{% for child in form.rentals %}
|
||
{% set choiceData = form.rentals.vars.choices[loop.index0].data %}
|
||
{{ macros.service_choice_row(child, choiceData.price|default(null), child.vars.attr['data-description']|default(null), 'Details', htmxAttr) }}
|
||
{% endfor %}
|
||
|
||
{% if form.rentalInsurance is defined %}
|
||
{% set rentalInsuranceService = bookingDto.travel.getAdditionalServicesBySubTypes(constant('App\\BusProNet\\Constants::TOKEN_RENTAL_INSURANCE'), true, true)|first %}
|
||
{{ macros.checkbox_row(
|
||
form.rentalInsurance,
|
||
constant('App\\BusProNet\\Constants::SERVICE_LABELS')[constant('App\\BusProNet\\Constants::TOKEN_RENTAL_INSURANCE')],
|
||
rentalInsuranceService.price|default(null),
|
||
form.rentalInsurance.vars.help|default(null),
|
||
'Info',
|
||
htmxAttr
|
||
) }}
|
||
{% endif %}
|
||
</table>
|
||
{% else %}
|
||
<div class="p-2 text-sm text-gray-500">Bitte zuerst den Skipass auswählen</div>
|
||
{% endif %}
|
||
</fieldset>
|
||
{% if form.rentals is defined %}
|
||
{{ form_errors(form.rentals) }}
|
||
{{ form_help(form.rentals) }}
|
||
{% endif %}
|
||
</div>
|
||
|
||
{% if form.bodyDimensions is defined %}
|
||
<div class="grid lg:grid-cols-3 lg:gap-x-4">
|
||
{{ form_row(form.bodyDimensions.height) }}
|
||
{{ form_row(form.bodyDimensions.shoeSize) }}
|
||
{{ form_row(form.bodyDimensions.weight) }}
|
||
</div>
|
||
{% endif %}
|
||
|
||
{# Insurance display for edit mode (read-only) - only show if insurance is selected #}
|
||
{% if bookingDto.mode == constant('App\\Form\\Model\\BookingDto::MODE_EDIT') %}
|
||
{% if form.vars.data.participant.insurance %}
|
||
{% set editInsurance = form.vars.data.participant.insurance %}
|
||
<div class="pb-4">
|
||
<fieldset class="border border-primary-bg">
|
||
<legend class="w-full bg-primary-bg p-2 font-semibold uppercase">
|
||
Reiseversicherung
|
||
</legend>
|
||
<table class="w-full table-fixed border-collapse">
|
||
<tr>
|
||
<td class="border border-primary-bg p-2 align-top">
|
||
<div>{{ editInsurance.label }}</div>
|
||
{%- if editInsurance.getAllUrlsProductInfo() is not empty -%}
|
||
<div {{ stimulus_controller('tooltip') }}>
|
||
<button type="button" data-tooltip-target="trigger" class="text-sm text-primary-light">
|
||
Details
|
||
</button>
|
||
<template data-tooltip-target="template">
|
||
<div class="text-sm">
|
||
{%- for url in editInsurance.getAllUrlsProductInfo() -%}
|
||
<a href="{{ url }}" target="_blank" rel="noopener noreferrer" class="block">
|
||
Produktinformation {{ editInsurance.getAllUrlsProductInfo()|length > 1 ? loop.index : '' }}
|
||
</a>
|
||
{%- endfor -%}
|
||
</div>
|
||
</template>
|
||
</div>
|
||
{%- endif -%}
|
||
</td>
|
||
<td class="border border-primary-bg p-2 align-top w-24 text-right whitespace-nowrap">
|
||
{% if editInsurance.price and editInsurance.price > 0 %}
|
||
{{ editInsurance.price | format_currency('EUR') }}
|
||
{% endif %}
|
||
</td>
|
||
<td class="border border-primary-bg p-2 align-top w-12"></td>
|
||
</tr>
|
||
</table>
|
||
</fieldset>
|
||
</div>
|
||
{% endif %}
|
||
{# In edit mode without insurance: don't show the section at all #}
|
||
|
||
{# Insurance field OR assigned insurance display for dependent participants (create mode) #}
|
||
{% else %}
|
||
{% set showBulkInsurance = participantIndex > 0 and bookingDto.participants[0].bulkInsuranceBooking %}
|
||
|
||
{% if showBulkInsurance %}
|
||
{% set applicantInsurance = bookingDto.participants[0].insurance %}
|
||
<div class="pb-4">
|
||
<fieldset class="border border-primary-bg">
|
||
<legend class="w-full bg-primary-bg p-2 font-semibold uppercase">
|
||
Reiseversicherung* <span class="font-normal normal-case text-sm text-gray-600 italic">– wie Anmelder</span>
|
||
</legend>
|
||
{% if applicantInsurance %}
|
||
<table class="w-full table-fixed border-collapse">
|
||
<tr>
|
||
<td class="border border-primary-bg p-2 align-top">
|
||
<div>{{ applicantInsurance.label }}</div>
|
||
{%- if applicantInsurance.getAllUrlsProductInfo() is not empty -%}
|
||
<div {{ stimulus_controller('tooltip') }}>
|
||
<button type="button" data-tooltip-target="trigger" class="text-sm text-primary-light">
|
||
Details
|
||
</button>
|
||
<template data-tooltip-target="template">
|
||
<div class="text-sm">
|
||
{%- for url in applicantInsurance.getAllUrlsProductInfo() -%}
|
||
<a href="{{ url }}" target="_blank" rel="noopener noreferrer" class="block">
|
||
Produktinformation {{ applicantInsurance.getAllUrlsProductInfo()|length > 1 ? loop.index : '' }}
|
||
</a>
|
||
{%- endfor -%}
|
||
</div>
|
||
</template>
|
||
</div>
|
||
{%- endif -%}
|
||
</td>
|
||
<td class="border border-primary-bg p-2 align-top w-24 text-right whitespace-nowrap">
|
||
{% if applicantInsurance.price and applicantInsurance.price > 0 %}
|
||
{{ applicantInsurance.price | format_currency('EUR') }}
|
||
{% endif %}
|
||
</td>
|
||
<td class="border border-primary-bg p-2 align-top w-12"></td>
|
||
</tr>
|
||
</table>
|
||
{% else %}
|
||
<table class="w-full table-fixed border-collapse">
|
||
<tr>
|
||
<td class="border border-primary-bg p-2 align-top text-sm text-gray-500 italic">wie Anmelder</td>
|
||
<td class="border border-primary-bg p-2 align-top w-24"></td>
|
||
<td class="border border-primary-bg p-2 align-top w-12"></td>
|
||
</tr>
|
||
</table>
|
||
{% endif %}
|
||
</fieldset>
|
||
</div>
|
||
{% else %}
|
||
<div class="pb-4">
|
||
<fieldset class="border border-primary-bg">
|
||
<legend class="{{ html_classes('w-full bg-primary-bg p-2 font-semibold uppercase', { 'text-red-500': form.insurance is defined and not form.insurance.vars.valid }) }}">
|
||
Reiseversicherung*
|
||
</legend>
|
||
{% if form.insurance is defined %}
|
||
<table class="w-full table-fixed border-collapse">
|
||
{% if form.bulkInsuranceBooking is defined %}
|
||
{{ macros.checkbox_row(
|
||
form.bulkInsuranceBooking,
|
||
form.bulkInsuranceBooking.vars.label,
|
||
null,
|
||
form.bulkInsuranceBooking.vars.attr['data-description']|default(null),
|
||
'Info',
|
||
htmxAttr
|
||
) }}
|
||
{% endif %}
|
||
{% for child in form.insurance %}
|
||
{% set choiceData = form.insurance.vars.choices[loop.index0].data %}
|
||
{{ macros.insurance_choice_row(child, choiceData.price|default(null), choiceData ? choiceData.getAllUrlsProductInfo() : [], htmxAttr) }}
|
||
{% endfor %}
|
||
</table>
|
||
{% else %}
|
||
<div class="p-2 text-sm text-gray-500">Nicht wählbar</div>
|
||
{% endif %}
|
||
</fieldset>
|
||
{% if form.insurance is defined %}
|
||
{{ form_errors(form.insurance) }}
|
||
{{ form_help(form.insurance) }}
|
||
{% endif %}
|
||
</div>
|
||
{% endif %}
|
||
{% endif %}
|
||
|
||
{# Transportation Services Section #}
|
||
<h3 class="font-semibold text-lg mb-4">Anreise</h3>
|
||
{% if form.transportationOutbound is defined %}
|
||
<div class="pb-4">
|
||
<fieldset class="border border-primary-bg">
|
||
<legend class="{{ html_classes('w-full bg-primary-bg p-2 font-semibold uppercase', { 'text-red-500': not form.transportationOutbound.vars.valid }) }}">
|
||
{{ form.transportationOutbound.vars.label }}
|
||
{% if form.transportationOutbound.vars.required %}<span>*</span>{% endif %}
|
||
</legend>
|
||
|
||
<table class="w-full table-fixed border-collapse">
|
||
{% for child in form.transportationOutbound %}
|
||
{% set choiceData = form.transportationOutbound.vars.choices[loop.index0].data %}
|
||
{{ macros.service_choice_row(child, choiceData.price|default(null), child.vars.attr['data-description']|default(null), 'Details', htmxAttr) }}
|
||
{% endfor %}
|
||
|
||
{% if form.parking is defined %}
|
||
{% set parkingService = bookingDto.travel.getAdditionalServicesBySubTypes(constant('App\\BusProNet\\Constants::TOKEN_PARKING'), true)|first %}
|
||
{{ macros.checkbox_row(
|
||
form.parking,
|
||
'Parkplatz',
|
||
parkingService.price|default(null),
|
||
null,
|
||
null,
|
||
htmxAttr
|
||
) }}
|
||
{% endif %}
|
||
</table>
|
||
</fieldset>
|
||
{{ form_errors(form.transportationOutbound) }}
|
||
{{ form_help(form.transportationOutbound) }}
|
||
</div>
|
||
{% endif %}
|
||
{% if form.transportationInbound is defined %}
|
||
{{ form_row(form.transportationInbound, {
|
||
'attr': {
|
||
'hx-trigger': 'change',
|
||
'hx-post': path(refreshRouteName, refreshRouteParams|default({index: participantIndex})),
|
||
'hx-target': '#main-content', 'hx-swap': 'innerHTML'
|
||
}
|
||
}) }}
|
||
{% endif %}
|
||
{% if form.pickup is defined %}
|
||
{{ form_row(form.pickup, {
|
||
'attr': {
|
||
'hx-trigger': 'change',
|
||
'hx-post': path(refreshRouteName, refreshRouteParams|default({index: participantIndex})),
|
||
'hx-target': '#main-content', 'hx-swap': 'innerHTML'
|
||
}
|
||
}) }}
|
||
{% endif %}
|
||
{% if form.licensePlate is defined %}
|
||
{{ form_row(form.licensePlate) }}
|
||
{% endif %}
|
||
{% endif %}
|
||
|
||
{# Voucher fields - available for all participants regardless of eligibility #}
|
||
{% if form.purchaseVoucherCode is defined or form.promoVoucherCode is defined %}
|
||
<h3 class="font-semibold text-lg mb-4">
|
||
Gutscheine
|
||
</h3>
|
||
<div class="grid lg:grid-cols-2 lg:gap-x-4">
|
||
{% if form.purchaseVoucherCode is defined %}
|
||
{{ form_row(form.purchaseVoucherCode) }}
|
||
{% endif %}
|
||
{% if form.promoVoucherCode is defined %}
|
||
{{ form_row(form.promoVoucherCode) }}
|
||
{% endif %}
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
{% endblock %}
|
||
|
||
{# Sidebar summary with conditional OOB swap - only render for HTMX requests #}
|
||
{% if htmx_oob_swap|default(false) %}
|
||
{% block booking_summary %}
|
||
<div id="booking-summary" hx-swap-oob="innerHTML">
|
||
{% include 'booking/_summary.html.twig' with {
|
||
'bookingCreateDto': bookingDto,
|
||
'summaryData': summaryData,
|
||
'mutableData': mutableData|default(null)
|
||
} %}
|
||
</div>
|
||
{% endblock %}
|
||
{% endif %}
|