195 lines
10 KiB
Twig
195 lines
10 KiB
Twig
{% extends 'layout_admin.html.twig' %}
|
||
|
||
{% block content %}
|
||
{% set back_params = { 'id': booking.id } %}
|
||
{% if app.request.query.get('r') %}
|
||
{% set back_params = back_params | merge({ 'r': app.request.query.get('r') }) %}
|
||
{% endif %}
|
||
|
||
<twig:page:heading>{{ booking.recordLabel }} {{ booking.groupName }}</twig:page:heading>
|
||
<p class="flex items-center gap-3 text-sm text-gray-500 mb-6">
|
||
{{ booking.accommodation.name }}
|
||
{# Only an Entwurf may still move house — see ChangeAccommodationController. #}
|
||
{% if booking.draft %}
|
||
<button type="button"
|
||
hx-get="{{ path('app_admin_accommodationbooking_change_accommodation', back_params) }}"
|
||
hx-target="body"
|
||
hx-swap="beforeend"
|
||
class="button button--secondary button--small">ändern</button>
|
||
{% endif %}
|
||
</p>
|
||
|
||
{% form_theme form 'forms_admin.html.twig' %}
|
||
|
||
{{ form_start(form) }}
|
||
{{ form_errors(form) }}
|
||
{# Ordered by what gets negotiated: the stay first, the contact data last. The sidebar keeps
|
||
status, price and the save button in view on wide screens. #}
|
||
<div class="xl:grid xl:grid-cols-3 xl:gap-6 xl:items-start space-y-6 xl:space-y-0">
|
||
<div class="xl:col-span-2 space-y-6">
|
||
<twig:form:section title="Aufenthalt">
|
||
<div class="grid sm:grid-cols-2 gap-4">
|
||
{{ form_row(form.dateFrom) }}
|
||
{{ form_row(form.dateTo) }}
|
||
</div>
|
||
<div class="grid sm:grid-cols-3 gap-4 pt-4">
|
||
{{ form_row(form.paxCount) }}
|
||
{{ form_row(form.minorsCount) }}
|
||
{{ form_row(form.childrenCount) }}
|
||
</div>
|
||
|
||
{# Saved by their own modals, which reprice the booking on the spot — see SplitPeriodController. #}
|
||
<div class="mt-6 border-t border-gray-200 pt-4">
|
||
<div class="font-bold pb-1">Zeiträume</div>
|
||
<p class="text-xs text-gray-500 pb-2">
|
||
Anreise und Abreise umfassen den ganzen Aufenthalt. Abgeteilte Zeiträume, etwa für ein
|
||
Vorausteam, werden mit eigenen Positionen berechnet; Personen und Leistungen gelten
|
||
nur für den Zeitraum der Gruppe.
|
||
</p>
|
||
<table class="w-full text-sm">
|
||
{% if booking.leadingPeriod %}
|
||
{% include 'admin/accommodation_booking/_period_row.html.twig' with { period: booking.leadingPeriod } %}
|
||
{% endif %}
|
||
<tr class="border-t border-gray-200">
|
||
<td class="py-1 whitespace-nowrap">{{ booking.mainDateFrom | date('d.m.Y') }} – {{ booking.mainDateTo | date('d.m.Y') }}</td>
|
||
<td class="py-1 whitespace-nowrap">{{ booking.mainNights }} {{ booking.mainNights != 1 ? 'Nächte' : 'Nacht' }}</td>
|
||
<td class="py-1" colspan="2">Gruppe</td>
|
||
<td class="py-1 text-right">
|
||
{# One period per side, so only the free sides are offered. #}
|
||
{% if booking.leadingPeriod is null or booking.trailingPeriod is null %}
|
||
<twig:dropdown>
|
||
{% if booking.leadingPeriod is null %}
|
||
<twig:dropdown:hxbutton url="{{ path('app_admin_accommodationbooking_period_split', back_params | merge({ position: 'leading' })) }}">
|
||
am Anfang abteilen
|
||
</twig:dropdown:hxbutton>
|
||
{% endif %}
|
||
{% if booking.trailingPeriod is null %}
|
||
<twig:dropdown:hxbutton url="{{ path('app_admin_accommodationbooking_period_split', back_params | merge({ position: 'trailing' })) }}">
|
||
am Ende abteilen
|
||
</twig:dropdown:hxbutton>
|
||
{% endif %}
|
||
</twig:dropdown>
|
||
{% endif %}
|
||
</td>
|
||
</tr>
|
||
{% if booking.trailingPeriod %}
|
||
{% include 'admin/accommodation_booking/_period_row.html.twig' with { period: booking.trailingPeriod } %}
|
||
{% endif %}
|
||
</table>
|
||
</div>
|
||
</twig:form:section>
|
||
|
||
{% if form.boardService is defined or form.selectedAdditionalServices is defined %}
|
||
<twig:form:section title="Leistungen">
|
||
<div class="space-y-4">
|
||
{% if form.boardService is defined %}
|
||
{{ form_row(form.boardService) }}
|
||
{% endif %}
|
||
{% if form.selectedAdditionalServices is defined %}
|
||
<div>
|
||
{{ form_label(form.selectedAdditionalServices) }}
|
||
{# Rows written out here rather than through the theme's choice_widget_expanded,
|
||
whose first:pt-0 would shift only the first cell of the two columns. #}
|
||
<div class="grid sm:grid-cols-2 gap-x-8">
|
||
{% for child in form.selectedAdditionalServices %}
|
||
<div class="relative flex items-start py-1">
|
||
<div class="min-w-0 flex-1 text-sm leading-4">
|
||
<label for="{{ child.vars.id }}" class="select-none font-medium text-gray-900">
|
||
{{ child.vars.label }}
|
||
</label>
|
||
</div>
|
||
<div class="ml-3 flex h-4 items-center">
|
||
{{ form_widget(child) }}
|
||
</div>
|
||
</div>
|
||
{% endfor %}
|
||
</div>
|
||
{{ form_errors(form.selectedAdditionalServices) }}
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
</twig:form:section>
|
||
{% endif %}
|
||
|
||
<twig:form:section title="Rabatte">
|
||
<div class="grid sm:grid-cols-3 gap-4">
|
||
{{ form_row(form.accommodationDiscount) }}
|
||
{{ form_row(form.boardServiceDiscount) }}
|
||
{{ form_row(form.additionalServicesDiscount) }}
|
||
</div>
|
||
<div class="grid sm:grid-cols-3 gap-4 pt-4">
|
||
{{ form_row(form.totalDiscountLabel, { row_attr: { class: 'sm:col-span-2' } }) }}
|
||
{{ form_row(form.totalDiscountAmount) }}
|
||
</div>
|
||
</twig:form:section>
|
||
|
||
<twig:form:section title="Kontakt">
|
||
<div class="grid sm:grid-cols-2 gap-4">
|
||
{{ form_row(form.groupName, { row_attr: { class: 'sm:col-span-2' } }) }}
|
||
{{ form_row(form.salutation) }}
|
||
<div class="hidden sm:block"></div>
|
||
{{ form_row(form.firstName) }}
|
||
{{ form_row(form.lastName) }}
|
||
{{ form_row(form.email) }}
|
||
{{ form_row(form.phone) }}
|
||
{{ form_row(form.street, { row_attr: { class: 'sm:col-span-2' } }) }}
|
||
{{ form_row(form.zip) }}
|
||
{{ form_row(form.city) }}
|
||
</div>
|
||
</twig:form:section>
|
||
|
||
<twig:form:section title="Bemerkungen">
|
||
{{ form_widget(form.remarks) }}
|
||
{{ form_errors(form.remarks) }}
|
||
</twig:form:section>
|
||
</div>
|
||
|
||
<aside class="space-y-6 xl:sticky xl:top-8">
|
||
<twig:form:section title="Status">
|
||
{# Read-only: the status only ever moves through a named action on the detail page,
|
||
so that it cannot be set without the email and timestamp that belong to it. #}
|
||
<dl class="grid grid-cols-2 gap-y-1 text-sm">
|
||
<dt class="text-gray-500">Status</dt>
|
||
<dd>{% include 'admin/accommodation_booking/_status_label.html.twig' %}</dd>
|
||
<dt class="text-gray-500">Herkunft</dt>
|
||
<dd>{{ booking.origin.label }}</dd>
|
||
{% if booking.acceptedAt is not null %}
|
||
<dt class="text-gray-500">Angenommen</dt>
|
||
<dd>{{ booking.acceptedAt | date('d.m.Y, H:i') }}</dd>
|
||
{% endif %}
|
||
{% if booking.confirmedAt is not null %}
|
||
<dt class="text-gray-500">Bestätigt</dt>
|
||
<dd>{{ booking.confirmedAt | date('d.m.Y, H:i') }}</dd>
|
||
{% endif %}
|
||
</dl>
|
||
{% if form.managedBy is defined %}
|
||
<div class="pt-4">
|
||
{{ form_row(form.managedBy) }}
|
||
</div>
|
||
{% endif %}
|
||
</twig:form:section>
|
||
|
||
{% if priceBreakdown is not null %}
|
||
{% set currency = booking.pricingCurrency ?? priceBreakdown.currency %}
|
||
<twig:form:section title="Preis">
|
||
{% include 'admin/accommodation_booking/_price_breakdown.html.twig' %}
|
||
<p class="text-xs text-gray-500 pt-2">
|
||
Stand der letzten Speicherung. Änderungen oben werden erst beim Speichern neu berechnet.
|
||
</p>
|
||
</twig:form:section>
|
||
{% endif %}
|
||
|
||
<div class="flex flex-col gap-3">
|
||
<button type="submit" class="button button--primary button--small w-full">
|
||
speichern
|
||
</button>
|
||
<a href="{{ path('app_admin_accommodationbooking_show', back_params) }}" class="text-sm text-gray-500 hover:text-gray-900 hover:underline">
|
||
← zurück ohne speichern
|
||
</a>
|
||
</div>
|
||
</aside>
|
||
</div>
|
||
{{ form_rest(form) }}
|
||
{{ form_end(form) }}
|
||
{% endblock %}
|