chore: adopt namespace pattern for accommodation booking controllers
This commit is contained in:
@@ -1,33 +0,0 @@
|
||||
{% extends 'htmx_modal.html.twig' %}
|
||||
|
||||
{% block title %}Angebot verbindlich buchen{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% from '_partials/_validation_errors.html.twig' import validation_alert %}
|
||||
{# Generic title: both fields can fail, and each renders its own message inline. #}
|
||||
{{ validation_alert(confirmationForm, null, false) }}
|
||||
|
||||
<p class="mb-6">
|
||||
Möchtest du dieses Angebot jetzt verbindlich buchen?
|
||||
</p>
|
||||
|
||||
{{ form_start(confirmationForm, {
|
||||
'action': path('app_groups_booking_offer_confirm', { uuid: booking.uuid }),
|
||||
'attr': {
|
||||
'hx-target': '#htmx-modal',
|
||||
'hx-swap': 'outerHTML',
|
||||
}
|
||||
}) }}
|
||||
{{ form_row(confirmationForm.remarks) }}
|
||||
{{ form_row(confirmationForm.termsAccepted) }}
|
||||
|
||||
<div class="mt-6 flex flex-col space-y-4 md:flex-row md:space-y-0 justify-between">
|
||||
<button type="button" class="button button--secondary" {{ stimulus_action('modal', 'close') }}>
|
||||
Abbrechen
|
||||
</button>
|
||||
<button type="submit" class="button button--primary">
|
||||
Jetzt verbindlich buchen
|
||||
</button>
|
||||
</div>
|
||||
{{ form_end(confirmationForm) }}
|
||||
{% endblock %}
|
||||
@@ -1,181 +0,0 @@
|
||||
{% block booking_summary %}
|
||||
<div class="bg-white h-full flex flex-col lg:border-l lg:border-primary-bg"
|
||||
id="booking-summary">
|
||||
{# Header #}
|
||||
<div class="shrink-0 px-4 lg:px-8 py-4 shadow-md relative z-10">
|
||||
{% if ctx.priceBreakdown is not null %}
|
||||
<div class="flex items-center justify-between pb-2">
|
||||
<span class="block uppercase font-semibold">Gesamtpreis</span>
|
||||
<span class="font-semibold">{{ (ctx.priceBreakdown.discountedTotal / 100)|format_currency(currency) }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="block uppercase font-semibold">Übersicht</span>
|
||||
<button type="button"
|
||||
class="lg:hidden button button--small bg-primary-light"
|
||||
data-action="toggle#toggle">
|
||||
<svg class="w-4 h-4 text-white transition-transform"
|
||||
data-toggle-target="icon"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 256 256">
|
||||
<rect width="256" height="256" fill="none"/>
|
||||
<polyline points="208 96 128 176 48 96" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Content #}
|
||||
<div class="hidden lg:block flex-1 min-h-0 bg-white shadow-lg lg:shadow-none overflow-y-auto scroll-stable px-4 lg:px-8 py-8"
|
||||
data-toggle-target="content">
|
||||
|
||||
{# Accommodation info #}
|
||||
{% set hasImage = ctx.hotelCmsData is not null
|
||||
and ctx.hotelCmsData.images is not null
|
||||
and ctx.hotelCmsData.images.resized is defined
|
||||
and ctx.hotelCmsData.images.resized.l is defined
|
||||
and ctx.hotelCmsData.images.resized.l[0] is defined %}
|
||||
<div class="border border-primary-bg mb-4">
|
||||
<div class="p-2 bg-primary-bg uppercase font-semibold">Unterkunft</div>
|
||||
<div class="grid grid-cols-3 gap-x-4 p-2">
|
||||
{% if hasImage %}
|
||||
<img src="{{ ctx.hotelCmsData.images.resized.l[0].url }}"
|
||||
alt="{{ ctx.hotelCmsData.images.resized.l[0].alt ?? ctx.accommodation.name }}"
|
||||
class="block w-full h-auto">
|
||||
{% endif %}
|
||||
<div class="{{ hasImage ? 'col-span-2' : 'col-span-3' }}">
|
||||
<span class="block font-semibold">{{ ctx.accommodation.name }}</span>
|
||||
{% if ctx.hotelCmsData is not null and ctx.hotelCmsData.address is not null %}
|
||||
<div class="text-sm text-gray-500 mt-1">{{ ctx.hotelCmsData.address|nl2br }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Price breakdown #}
|
||||
{% if ctx.priceBreakdown is not null %}
|
||||
<div class="border border-primary-bg mt-4">
|
||||
<div class="p-2 bg-primary-bg uppercase font-semibold">Preise</div>
|
||||
<table class="w-full table-fixed text-sm">
|
||||
<tr>
|
||||
<td class="p-2">
|
||||
Zeitraum
|
||||
</td>
|
||||
<td class="p-2 text-right">
|
||||
{{ booking.dateFrom|date('d.m.Y') }} – {{ booking.dateTo|date('d.m.Y') }},
|
||||
<br>
|
||||
{{ booking.nights }} {{ booking.nights != 1 ? 'Nächte' : 'Nacht' }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="p-2">
|
||||
Anzahl Personen
|
||||
</td>
|
||||
<td class="p-2 text-right">
|
||||
{{ booking.paxCount }}
|
||||
</td>
|
||||
</tr>
|
||||
{% if booking.minorsCount > 0 %}
|
||||
<tr>
|
||||
<td class="p-2">davon Kinder (0–3 J.)</td>
|
||||
<td class="p-2 text-right">{{ booking.minorsCount }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if booking.childrenCount > 0 %}
|
||||
<tr>
|
||||
<td class="p-2">davon Kinder (4–{{ ctx.accommodation.maxAdolescentAge }} J.)</td>
|
||||
<td class="p-2 text-right">{{ booking.childrenCount }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
|
||||
{% if ctx.priceBreakdown.basePrice > 0 %}
|
||||
<tr>
|
||||
<td class="p-2">
|
||||
Basispreis
|
||||
<p class="text-xs text-gray-500">für {{ ctx.priceBreakdown.includedPax }} Pers.</p>
|
||||
</td>
|
||||
<td class="p-2 text-right whitespace-nowrap">{{ (ctx.priceBreakdown.basePrice / 100)|format_currency(currency) }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if ctx.priceBreakdown.additionalPersonsPrice > 0 %}
|
||||
<tr>
|
||||
<td class="p-2">
|
||||
Aufpreis Personenzahl
|
||||
<p class="text-xs text-gray-500">für {{ ctx.priceBreakdown.effectivePax - ctx.priceBreakdown.includedPax }} Pers.</p>
|
||||
</td>
|
||||
<td class="p-2 text-right whitespace-nowrap">{{ (ctx.priceBreakdown.additionalPersonsPrice / 100)|format_currency(currency) }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if ctx.priceBreakdown.shortTermSurcharge > 0 %}
|
||||
<tr>
|
||||
<td class="p-2">Aufpreis Kurzzeit</td>
|
||||
<td class="p-2 text-right whitespace-nowrap">{{ (ctx.priceBreakdown.shortTermSurcharge / 100)|format_currency(currency) }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if ctx.priceBreakdown.boardPrice > 0 %}
|
||||
<tr>
|
||||
<td class="p-2">Verpflegung{% if booking.boardServiceLabel %} ({{ booking.boardServiceLabel }}){% endif %}</td>
|
||||
<td class="p-2 text-right whitespace-nowrap">{{ (ctx.priceBreakdown.boardPrice / 100)|format_currency(currency) }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if ctx.priceBreakdown.undersubscriptionSurcharge > 0 %}
|
||||
<tr>
|
||||
<td class="p-2">
|
||||
Verpflegungs-Aufschlag
|
||||
<p class="text-xs text-gray-500">für Gruppen unter {{ ctx.priceBreakdown.undersubscriptionThreshold }} Pers.</p>
|
||||
</td>
|
||||
<td class="p-2 text-right whitespace-nowrap">{{ (ctx.priceBreakdown.undersubscriptionSurcharge / 100)|format_currency(currency) }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% for service in ctx.priceBreakdown.serviceDetails %}
|
||||
{% if service.price > 0 %}
|
||||
<tr>
|
||||
<td class="p-2">{{ service.label }}</td>
|
||||
<td class="p-2 text-right whitespace-nowrap">{{ (service.price / 100)|format_currency(currency) }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if ctx.priceBreakdown.runningCosts > 0 %}
|
||||
<tr>
|
||||
<td class="p-2">Strom- und Abfallgebühren</td>
|
||||
<td class="p-2 text-right whitespace-nowrap">{{ (ctx.priceBreakdown.runningCosts / 100)|format_currency(currency) }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
<tr class="border-t border-primary-bg font-semibold text-base">
|
||||
<td class="p-2">
|
||||
Gesamtpreis
|
||||
<p class="text-xs font-bold">zzgl. ortsabhängiger Gebühren</p>
|
||||
</td>
|
||||
<td class="p-2 text-right whitespace-nowrap">
|
||||
{{ (ctx.priceBreakdown.total / 100)|format_currency(currency) }}
|
||||
</td>
|
||||
</tr>
|
||||
{% for discount in ctx.priceBreakdown.discounts %}
|
||||
<tr class="text-green-700">
|
||||
<td class="p-2">Rabatt {{ discount.label }} {{ discount.percent }} %</td>
|
||||
<td class="p-2 text-right whitespace-nowrap">– {{ (discount.amount / 100)|format_currency(currency) }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% if ctx.priceBreakdown.discounts is not empty %}
|
||||
<tr class="border-t border-primary-bg font-bold text-base text-green-700">
|
||||
<td class="p-2">Gesamtpreis nach Rabatt</td>
|
||||
<td class="p-2 text-right whitespace-nowrap">{{ (ctx.priceBreakdown.discountedTotal / 100)|format_currency(currency) }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if booking.remarks %}
|
||||
<div class="border border-primary-bg mt-4">
|
||||
<div class="p-2 bg-primary-bg uppercase font-semibold">Anmerkungen</div>
|
||||
<div class="p-2 text-sm">
|
||||
{{ booking.remarks | nl2br }}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -1,31 +0,0 @@
|
||||
<div class="border border-primary-bg mb-4">
|
||||
<div class="p-2 bg-primary-bg uppercase font-semibold">Persönliche Daten</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-x-4 gap-y-2 p-2 text-sm">
|
||||
<div>
|
||||
<span class="text-gray-600 font-semibold">Gruppenname:</span>
|
||||
{{ dto.groupName }}
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-gray-600 font-semibold">Name:</span>
|
||||
{{ dto.salutation }} {{ dto.firstName }} {{ dto.lastName }}
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-gray-600 font-semibold">E-Mail:</span>
|
||||
{{ dto.email }}
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-gray-600 font-semibold">Telefon:</span>
|
||||
{{ dto.phone }}
|
||||
</div>
|
||||
<div class="md:col-span-2">
|
||||
<span class="text-gray-600 font-semibold">Adresse:</span>
|
||||
{{ dto.street }}, {{ dto.zip }} {{ dto.city }}
|
||||
</div>
|
||||
{% if dto.remarks %}
|
||||
<div class="md:col-span-2">
|
||||
<span class="text-gray-600 font-semibold">Anmerkungen:</span>
|
||||
{{ dto.remarks }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,42 +0,0 @@
|
||||
{% set selectedBoardService = null %}
|
||||
{% for service in ctx.boardServices %}
|
||||
{% if service.id == dto.selectedBoardServiceId %}
|
||||
{% set selectedBoardService = service %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% set selectedAdditionalServices = ctx.additionalServices|filter(service => service.id in dto.selectedAdditionalServiceIds) %}
|
||||
|
||||
<div class="border border-primary-bg mb-4">
|
||||
<div class="p-2 bg-primary-bg uppercase font-semibold">Ausgewählte Leistungen</div>
|
||||
<div class="p-2 text-sm">
|
||||
<div class="mb-2">
|
||||
<span class="text-gray-600 font-semibold">Verpflegung:</span>
|
||||
{% if selectedBoardService %}
|
||||
{{ selectedBoardService.label }}
|
||||
{% if selectedBoardService.price > 0 %}
|
||||
— {{ (selectedBoardService.price / 100)|format_currency(ctx.accommodation.currency) }} pro Person und Nacht
|
||||
{% endif %}
|
||||
{% if selectedBoardService.description %}
|
||||
<div class="text-xs text-gray-500">{{ selectedBoardService.description|nl2br }}</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
Selbstversorgung
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if selectedAdditionalServices is not empty %}
|
||||
<div>
|
||||
<span class="text-gray-600 font-semibold">Zusatzleistungen:</span>
|
||||
<ul class="list-disc list-inside">
|
||||
{% for service in selectedAdditionalServices %}
|
||||
<li>
|
||||
{{ service.label }}
|
||||
— {{ (service.price / 100)|format_currency(ctx.accommodation.currency) }} {{ service.type.label|trans }}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,230 +0,0 @@
|
||||
{% extends 'layout_booking.html.twig' %}
|
||||
|
||||
{% macro imageGallery(images, thumbImages, defaultAlt) %}
|
||||
{% if images|length > 0 %}
|
||||
<div class="grid md:grid-cols-2 gap-8" {{ stimulus_controller('gallery') }}>
|
||||
{% for image in images %}
|
||||
<div class="relative block w-full h-auto cursor-pointer{{ loop.index0 >= 2 ? ' hidden' : '' }}"
|
||||
data-gallery-target="item"
|
||||
data-gallery-href="{{ image.url }}"
|
||||
data-gallery-alt="{{ image.alt ?? defaultAlt }}">
|
||||
<img src="{{ thumbImages[loop.index0].url ?? image.url }}"
|
||||
alt="{{ image.alt ?? defaultAlt }}"
|
||||
class="block w-full h-auto">
|
||||
{% if loop.index0 == 1 and images|length > 2 %}
|
||||
<div class="absolute inset-0 bg-black/50 flex items-center justify-center text-white font-bold text-lg">
|
||||
+{{ images|length - 2 }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{% import _self as macros %}
|
||||
|
||||
{% block title %}{{ booking.acceptedAt is not null ? 'Deine Buchung' : 'Dein Angebot' }}{% endblock %}
|
||||
|
||||
{% block background %}bg-outer bg-outer--summer{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% set currency = booking.pricingCurrency ?? priceBreakdown.currency %}
|
||||
|
||||
<div class="flex-1 flex flex-col lg:grid lg:grid-cols-5 min-h-0 relative">
|
||||
|
||||
{# Sidebar summary #}
|
||||
<div id="booking-summary-wrapper"
|
||||
class="order-1 lg:order-2 lg:flex-1 lg:min-h-0 lg:col-span-2"
|
||||
{{ stimulus_controller('toggle', { 'open': false }, { 'closed': 'hidden', 'open': 'absolute inset-0 z-20', 'iconOpen': 'rotate-180' }) }}>
|
||||
{% include 'groups/booking/_offer_summary.html.twig' with {
|
||||
booking: booking,
|
||||
ctx: ctx,
|
||||
currency: currency,
|
||||
} %}
|
||||
</div>
|
||||
|
||||
{# Main area #}
|
||||
<div class="flex-1 order-2 lg:order-1 lg:col-span-3 bg-white flex flex-col gap-y-8 min-h-0 overflow-y-auto scroll-stable px-4 lg:px-8 py-8">
|
||||
{% include '_partials/_flashes.html.twig' %}
|
||||
|
||||
<hgroup>
|
||||
<h1 class="text-2xl lg:text-4xl font-bold mb-2">
|
||||
{{ booking.acceptedAt is not null ? 'Deine Buchung' : 'Dein Angebot' }}
|
||||
</h1>
|
||||
<p class="text-2xl">
|
||||
{{ ctx.accommodation.name }}
|
||||
</p>
|
||||
</hgroup>
|
||||
|
||||
{# Booked services #}
|
||||
{% if booking.boardServiceLabel or booking.additionalServices | length > 0 %}
|
||||
<div>
|
||||
<h2 class="text-xl font-bold mb-2">
|
||||
{{ booking.acceptedAt is not null ? 'Gebuchte' : 'Angebotene' }} Leistungen
|
||||
</h2>
|
||||
<ul class="list-disc pl-4">
|
||||
{% if booking.boardServiceLabel %}
|
||||
<li>{{ booking.boardServiceLabel }}</li>
|
||||
{% endif %}
|
||||
{% for service in booking.additionalServices %}
|
||||
<li>{{ service.label }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if booking.open %}
|
||||
<div>
|
||||
<button type="button"
|
||||
class="button button--primary w-full"
|
||||
hx-get="{{ path('app_groups_booking_offer_confirm', { uuid: booking.uuid }) }}"
|
||||
hx-target="body"
|
||||
hx-swap="beforeend">
|
||||
Angebot verbindlich buchen
|
||||
</button>
|
||||
</div>
|
||||
{% elseif booking.confirmedAt is not null %}
|
||||
<div class="flex items-center space-x-2">
|
||||
{{ icon('info', 'w-5 h-5') }}
|
||||
<div class="text-gray-500">
|
||||
Buchung bestätigt am {{ booking.confirmedAt | date('d.m.Y') }}
|
||||
</div>
|
||||
</div>
|
||||
{% elseif booking.acceptedAt is not null %}
|
||||
<div class="flex items-center space-x-2">
|
||||
{{ icon('info', 'w-5 h-5') }}
|
||||
<div class="text-gray-500">
|
||||
Am {{ booking.acceptedAt | date('d.m.Y') }} bei uns eingegangen — wir prüfen deine
|
||||
Buchung und melden uns mit der Bestätigung bei dir.
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{# hotel description #}
|
||||
{% if ctx.hotelCmsData is not null %}
|
||||
{% embed '_partials/_collapsible.html.twig' with {
|
||||
heading: 'Die Unterkunft',
|
||||
id: 'accommodation',
|
||||
} %}
|
||||
{% block body %}
|
||||
{% set icons = ctx.hotelCmsData.icons %}
|
||||
{% set iconMap = {
|
||||
bedsCount: { icon: 'bed', label: 'Anzahl Betten' },
|
||||
parkingCount: { icon: 'parking', label: 'Anzahl Parkplätze' },
|
||||
altitude: { icon: 'mountain', label: 'Höhe' },
|
||||
partyroom: { icon: 'party', label: 'Partyraum' },
|
||||
gamesroom: { icon: 'dice', label: 'Spieleraum' },
|
||||
distanceBus: { icon: 'bus', label: 'Entfernung Bus' },
|
||||
distanceLift: { icon: 'lift', label: 'Entfernung Lifte' },
|
||||
sauna: { icon: 'sauna', label: 'Sauna' },
|
||||
shower: { icon: 'shower', label: 'Dusche' },
|
||||
wifi: { icon: 'wifi', label: 'WiFi' },
|
||||
} %}
|
||||
{% if icons is not empty %}
|
||||
<ul class="grid grid-cols-2 md:grid-cols-3 gap-2">
|
||||
{% for key, value in icons %}
|
||||
{% set mapping = iconMap[key] ?? null %}
|
||||
{% set isBool = value is same as(true) or value is same as(false) %}
|
||||
<li class="flex items-center gap-2">
|
||||
<div class="border-2 border-gray-900 rounded-full w-16 h-16 shrink-0 flex flex-col items-center justify-center">
|
||||
{% if mapping %}
|
||||
{{ icon(mapping.icon, 'w-8 h-8 shrink-0 text-gray-900') }}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="text-sm font-bold">
|
||||
{% if isBool %}
|
||||
{{ mapping ? mapping.label : '' }}
|
||||
{% else %}
|
||||
{{ mapping ? mapping.label ~ ': ' ~ value : value }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
{{ ctx.hotelCmsData.description | raw }}
|
||||
|
||||
{% if ctx.hotelCmsData.features|striptags|trim is not empty %}
|
||||
<h2>
|
||||
Details zur Unterkunft
|
||||
</h2>
|
||||
{{ ctx.hotelCmsData.features | raw }}
|
||||
{% endif %}
|
||||
|
||||
{% if ctx.hotelCmsData.roomTypes|striptags|trim is not empty %}
|
||||
<h2>
|
||||
Die Zimmer
|
||||
</h2>
|
||||
{{ ctx.hotelCmsData.roomTypes | raw }}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% endembed %}
|
||||
|
||||
{{ macros.imageGallery(ctx.hotelCmsData.images.resized.l, ctx.hotelCmsData.images.resized.m, ctx.accommodation.name) }}
|
||||
{% endif %}
|
||||
|
||||
{# additional information #}
|
||||
{% if ctx.hotelCmsData is not null and ctx.hotelCmsData.additionalInformation|striptags|trim is not empty %}
|
||||
{% embed '_partials/_collapsible.html.twig' with {
|
||||
heading: 'Zusatzinformationen',
|
||||
id: 'additional-information',
|
||||
} %}
|
||||
{% block body %}
|
||||
{{ ctx.hotelCmsData.additionalInformation | raw }}
|
||||
{% endblock %}
|
||||
{% endembed %}
|
||||
{% endif %}
|
||||
|
||||
{# region description #}
|
||||
{% if ctx.hotelCmsData is not null and ctx.hotelCmsData.region is not null %}
|
||||
{% set region = ctx.hotelCmsData.region %}
|
||||
{% embed '_partials/_collapsible.html.twig' with {
|
||||
heading: 'Die Region ' ~ region.name,
|
||||
id: 'region',
|
||||
region: region,
|
||||
} %}
|
||||
{% block body %}
|
||||
{% if region.altitude or region.length or region.lifts %}
|
||||
<ul class="flex flex-wrap gap-x-6 text-sm not-prose">
|
||||
{% if region.altitude %}
|
||||
<li>{{ region.altitude }} m Höhe</li>
|
||||
{% endif %}
|
||||
{% if region.length %}
|
||||
<li>{{ region.length }} km Pisten</li>
|
||||
{% endif %}
|
||||
{% if region.lifts %}
|
||||
<li>{{ region.lifts }} Lifte</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% if region.webcam %}
|
||||
<p>
|
||||
<a href="{{ region.webcam }}" target="_blank" rel="noopener">Webcam der Region</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
{{ region.description | default('') | raw }}
|
||||
|
||||
{{ region.skiArea | default('') | raw }}
|
||||
{{ region.skiAreaExtended | default('') | raw }}
|
||||
|
||||
{% if region.news %}
|
||||
<h2>Aktuelles aus der Region</h2>
|
||||
{{ region.news | raw }}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% endembed %}
|
||||
|
||||
{% if region.regionMaps is not null and region.regionMaps | length > 0 %}
|
||||
{% set regionMapImages = region.regionMaps | map(url => {url: url}) %}
|
||||
{{ macros.imageGallery(regionMapImages, [], region.name) }}
|
||||
{% endif %}
|
||||
|
||||
{{ macros.imageGallery(region.images.resized.l ?? [], region.images.resized.m ?? [], region.name) }}
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -1,13 +0,0 @@
|
||||
{% extends 'layout.html.twig' %}
|
||||
|
||||
{% block title %}Link nicht verfügbar{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 max-w-lg w-full">
|
||||
{% include '_partials/_alert.html.twig' with {
|
||||
level: 'error',
|
||||
title: 'Dieser Link ist nicht (mehr) gültig',
|
||||
messages: ['Bitte wende dich an uns, damit wir dir einen neuen Link zusenden können.'],
|
||||
} %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -40,8 +40,80 @@
|
||||
} %}
|
||||
{% endif %}
|
||||
|
||||
{% include 'groups/booking/_step4_services_recap.html.twig' with { 'dto': dto, 'ctx': ctx } %}
|
||||
{% include 'groups/booking/_step4_personal_data_recap.html.twig' with { 'dto': dto } %}
|
||||
{% set selectedBoardService = null %}
|
||||
{% for service in ctx.boardServices %}
|
||||
{% if service.id == dto.selectedBoardServiceId %}
|
||||
{% set selectedBoardService = service %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% set selectedAdditionalServices = ctx.additionalServices|filter(service => service.id in dto.selectedAdditionalServiceIds) %}
|
||||
|
||||
<div class="border border-primary-bg mb-4">
|
||||
<div class="p-2 bg-primary-bg uppercase font-semibold">Ausgewählte Leistungen</div>
|
||||
<div class="p-2 text-sm">
|
||||
<div class="mb-2">
|
||||
<span class="text-gray-600 font-semibold">Verpflegung:</span>
|
||||
{% if selectedBoardService %}
|
||||
{{ selectedBoardService.label }}
|
||||
{% if selectedBoardService.price > 0 %}
|
||||
— {{ (selectedBoardService.price / 100)|format_currency(ctx.accommodation.currency) }} pro Person und Nacht
|
||||
{% endif %}
|
||||
{% if selectedBoardService.description %}
|
||||
<div class="text-xs text-gray-500">{{ selectedBoardService.description|nl2br }}</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
Selbstversorgung
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if selectedAdditionalServices is not empty %}
|
||||
<div>
|
||||
<span class="text-gray-600 font-semibold">Zusatzleistungen:</span>
|
||||
<ul class="list-disc list-inside">
|
||||
{% for service in selectedAdditionalServices %}
|
||||
<li>
|
||||
{{ service.label }}
|
||||
— {{ (service.price / 100)|format_currency(ctx.accommodation.currency) }} {{ service.type.label|trans }}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="border border-primary-bg mb-4">
|
||||
<div class="p-2 bg-primary-bg uppercase font-semibold">Persönliche Daten</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-x-4 gap-y-2 p-2 text-sm">
|
||||
<div>
|
||||
<span class="text-gray-600 font-semibold">Gruppenname:</span>
|
||||
{{ dto.groupName }}
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-gray-600 font-semibold">Name:</span>
|
||||
{{ dto.salutation }} {{ dto.firstName }} {{ dto.lastName }}
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-gray-600 font-semibold">E-Mail:</span>
|
||||
{{ dto.email }}
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-gray-600 font-semibold">Telefon:</span>
|
||||
{{ dto.phone }}
|
||||
</div>
|
||||
<div class="md:col-span-2">
|
||||
<span class="text-gray-600 font-semibold">Adresse:</span>
|
||||
{{ dto.street }}, {{ dto.zip }} {{ dto.city }}
|
||||
</div>
|
||||
{% if dto.remarks %}
|
||||
<div class="md:col-span-2">
|
||||
<span class="text-gray-600 font-semibold">Anmerkungen:</span>
|
||||
{{ dto.remarks }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user