feat: show my bookings on a month calendar
This commit is contained in:
1 parent
5a8227f4d9
commit
4517b84023
12 files changed
+891
-21
No files matched your search
@@ -0,0 +1,106 @@
|
||||
{% set weekdays = ['Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa', 'So'] %}
|
||||
{% set visible_per_day = 2 %}
|
||||
|
||||
<div class="rounded-md border border-gray-200 p-4">
|
||||
<div class="flex items-center justify-between pb-4">
|
||||
{% if has_prev %}
|
||||
<button type="button"
|
||||
class="p-1 rounded-md hover:bg-gray-100"
|
||||
aria-label="Voriger Monat"
|
||||
hx-get="{{ path('app_booking_month', { month: prev_month|date('Y-m') }) }}"
|
||||
hx-swap="none">
|
||||
{{ ux_icon('lucide:chevron-left', { class: 'w-5 h-5' }) }}
|
||||
</button>
|
||||
{% else %}
|
||||
<span class="p-1 text-gray-300" aria-hidden="true">{{ ux_icon('lucide:chevron-left', { class: 'w-5 h-5' }) }}</span>
|
||||
{% endif %}
|
||||
|
||||
<div class="font-bold">
|
||||
{{ month_start|format_datetime(pattern: 'LLLL y') }}
|
||||
</div>
|
||||
|
||||
{% if has_next %}
|
||||
<button type="button"
|
||||
class="p-1 rounded-md hover:bg-gray-100"
|
||||
aria-label="Nächster Monat"
|
||||
hx-get="{{ path('app_booking_month', { month: next_month|date('Y-m') }) }}"
|
||||
hx-swap="none">
|
||||
{{ ux_icon('lucide:chevron-right', { class: 'w-5 h-5' }) }}
|
||||
</button>
|
||||
{% else %}
|
||||
<span class="p-1 text-gray-300" aria-hidden="true"
|
||||
title="Weiter voraus kann nicht gebucht werden">{{ ux_icon('lucide:chevron-right', { class: 'w-5 h-5' }) }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-7 gap-1 text-center text-xs text-gray-500 pb-1">
|
||||
{% for weekday in weekdays %}<div>{{ weekday }}</div>{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-7 gap-1">
|
||||
{% for week in weeks %}
|
||||
{% for day in week %}
|
||||
{% set classes = 'flex flex-col gap-0.5 rounded-md border p-1 text-left h-16 sm:h-24 overflow-hidden' %}
|
||||
{% if not day.in_month %}
|
||||
{% set classes = classes ~ ' border-transparent text-gray-300' %}
|
||||
{% elseif day.is_past %}
|
||||
{% set classes = classes ~ ' border-dashed border-gray-200 text-gray-400' %}
|
||||
{% elseif day.is_weekend %}
|
||||
{% set classes = classes ~ ' border-transparent bg-gray-50 text-gray-400' %}
|
||||
{% else %}
|
||||
{% set classes = classes ~ ' border-gray-200' %}
|
||||
{% endif %}
|
||||
{% if day.is_today %}
|
||||
{% set classes = classes ~ ' ring-1 ring-primary' %}
|
||||
{% endif %}
|
||||
|
||||
<div class="{{ classes }}" data-date="{{ day.date|date('Y-m-d') }}">
|
||||
<div class="text-xs leading-none">{{ day.date|date('j') }}</div>
|
||||
|
||||
{% if day.bookings is not empty %}
|
||||
<div class="flex flex-wrap gap-0.5 sm:hidden">
|
||||
{% for booking in day.bookings %}
|
||||
<span class="w-1.5 h-1.5 rounded-full {{ day.is_past ? 'bg-gray-300' : 'bg-primary' }}"></span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="hidden sm:flex sm:flex-col sm:gap-0.5 sm:min-w-0">
|
||||
{% for booking in day.bookings|slice(0, visible_per_day) %}
|
||||
<a class="flex items-center gap-1 rounded px-1 py-0.5 text-[10px] leading-tight min-w-0
|
||||
{{ day.is_past ? 'bg-gray-100 text-gray-500' : 'bg-primary-bg text-primary-dark hover:bg-primary hover:text-white' }}"
|
||||
href="{{ path('app_board', { type: booking.resource.type.value, date: booking.date|date('Y-m-d') }) }}"
|
||||
title="{{ booking.resource.type.label }} · {{ booking.resource.label }} · {{ booking.timeRange }}{{ booking.partOfSeries ? ' · wiederkehrend' : '' }}{{ booking.overridden ? ' · abweichend' : '' }}">
|
||||
{{ ux_icon(booking.resource.type.icon, { class: 'w-3 h-3 shrink-0' }) }}
|
||||
<span class="truncate">
|
||||
{{- booking.timeRange.allDay ? booking.resource.label : booking.timeRange.start ~ ' ' ~ booking.resource.label -}}
|
||||
</span>
|
||||
{% if booking.partOfSeries %}
|
||||
{{ ux_icon('lucide:repeat', { class: 'w-2.5 h-2.5 shrink-0 opacity-60' }) }}
|
||||
{% endif %}
|
||||
{% if booking.overridden %}
|
||||
<span class="w-1.5 h-1.5 shrink-0 rounded-full bg-amber-500"></span>
|
||||
{% endif %}
|
||||
</a>
|
||||
{% endfor %}
|
||||
|
||||
{% if day.bookings|length > visible_per_day %}
|
||||
<span class="text-[10px] leading-tight text-gray-500">+{{ day.bookings|length - visible_per_day }} weitere</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="pt-4 text-xs text-gray-500 space-y-1">
|
||||
<div class="flex items-center gap-2">
|
||||
{{ ux_icon('lucide:repeat', { class: 'w-3 h-3' }) }} Teil einer Wiederholung
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="w-1.5 h-1.5 rounded-full bg-amber-500"></span> Zeit weicht von der Wiederholung ab
|
||||
</div>
|
||||
<div>gestrichelt = vergangen, nicht mehr buchbar</div>
|
||||
<div>Klick auf einen Termin öffnet den Plan des Tages</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -8,6 +8,17 @@
|
||||
<h1 class="pt-2">Meine Buchungen</h1>
|
||||
<p class="subline">Alles, was ab heute für dich reserviert ist.</p>
|
||||
|
||||
{#
|
||||
Swapped out of band by Booking\MonthController when you page the month. It owns a stable id
|
||||
and emits hx-swap-oob only when rendered through htmxOobResponse -- rendered normally it is
|
||||
plain markup. Same idiom as board/index.html.twig.
|
||||
#}
|
||||
{% block calendar %}
|
||||
<div id="booking-calendar" class="mt-8"{% if htmx_oob_swap is defined and htmx_oob_swap %} hx-swap-oob="true"{% endif %}>
|
||||
{% include 'booking/_calendar.html.twig' %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% if series is not empty %}
|
||||
<h2 class="pt-8">Wiederkehrende Buchungen</h2>
|
||||
<ul class="divide-y divide-gray-200 border-y border-gray-200">
|
||||
|
||||
Reference in new issue
Block a user