Add account-nav-item and account-page fragment handlers (Phase 5)
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 53s
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 53s
Events now serves tickets/bookings nav links and page panels as fragments for the account app. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -94,6 +94,37 @@ def register():
|
||||
|
||||
_handlers["container-cards"] = _container_cards_handler
|
||||
|
||||
# --- account-nav-item fragment: tickets + bookings links for account nav -
|
||||
|
||||
async def _account_nav_item_handler():
|
||||
return await render_template("fragments/account_nav_items.html")
|
||||
|
||||
_handlers["account-nav-item"] = _account_nav_item_handler
|
||||
|
||||
# --- account-page fragment: tickets or bookings panel --------------------
|
||||
|
||||
async def _account_page_handler():
|
||||
slug = request.args.get("slug", "")
|
||||
user_id = request.args.get("user_id", type=int)
|
||||
if not user_id:
|
||||
return ""
|
||||
|
||||
if slug == "tickets":
|
||||
tickets = await services.calendar.user_tickets(g.s, user_id=user_id)
|
||||
return await render_template(
|
||||
"fragments/account_page_tickets.html",
|
||||
tickets=tickets,
|
||||
)
|
||||
elif slug == "bookings":
|
||||
bookings = await services.calendar.user_bookings(g.s, user_id=user_id)
|
||||
return await render_template(
|
||||
"fragments/account_page_bookings.html",
|
||||
bookings=bookings,
|
||||
)
|
||||
return ""
|
||||
|
||||
_handlers["account-page"] = _account_page_handler
|
||||
|
||||
bp._fragment_handlers = _handlers
|
||||
|
||||
return bp
|
||||
|
||||
2
shared
2
shared
Submodule shared updated: d2e07e047e...65c4989d08
23
templates/fragments/account_nav_items.html
Normal file
23
templates/fragments/account_nav_items.html
Normal file
@@ -0,0 +1,23 @@
|
||||
{# Account nav items: tickets + bookings links for the account dashboard #}
|
||||
<div class="relative nav-group">
|
||||
<a href="{{ account_url('/tickets/') }}"
|
||||
hx-get="{{ account_url('/tickets/') }}"
|
||||
hx-target="#main-panel"
|
||||
hx-select="{{ hx_select_search }}"
|
||||
hx-swap="outerHTML"
|
||||
hx-push-url="true"
|
||||
class="{{styles.nav_button}}">
|
||||
tickets
|
||||
</a>
|
||||
</div>
|
||||
<div class="relative nav-group">
|
||||
<a href="{{ account_url('/bookings/') }}"
|
||||
hx-get="{{ account_url('/bookings/') }}"
|
||||
hx-target="#main-panel"
|
||||
hx-select="{{ hx_select_search }}"
|
||||
hx-swap="outerHTML"
|
||||
hx-push-url="true"
|
||||
class="{{styles.nav_button}}">
|
||||
bookings
|
||||
</a>
|
||||
</div>
|
||||
44
templates/fragments/account_page_bookings.html
Normal file
44
templates/fragments/account_page_bookings.html
Normal file
@@ -0,0 +1,44 @@
|
||||
<div class="w-full max-w-3xl mx-auto px-4 py-6">
|
||||
<div class="bg-white/70 backdrop-blur rounded-2xl shadow border border-stone-200 p-6 sm:p-8 space-y-6">
|
||||
|
||||
<h1 class="text-xl font-semibold tracking-tight">Bookings</h1>
|
||||
|
||||
{% if bookings %}
|
||||
<div class="divide-y divide-stone-100">
|
||||
{% for booking in bookings %}
|
||||
<div class="py-4 first:pt-0 last:pb-0">
|
||||
<div class="flex items-start justify-between gap-4">
|
||||
<div class="min-w-0 flex-1">
|
||||
<p class="text-sm font-medium text-stone-800">{{ booking.name }}</p>
|
||||
<div class="mt-1 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-stone-500">
|
||||
<span>{{ booking.start_at.strftime('%d %b %Y, %H:%M') }}</span>
|
||||
{% if booking.end_at %}
|
||||
<span>– {{ booking.end_at.strftime('%H:%M') }}</span>
|
||||
{% endif %}
|
||||
{% if booking.calendar_name %}
|
||||
<span>· {{ booking.calendar_name }}</span>
|
||||
{% endif %}
|
||||
{% if booking.cost %}
|
||||
<span>· £{{ booking.cost }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-shrink-0">
|
||||
{% if booking.state == 'confirmed' %}
|
||||
<span class="inline-flex items-center rounded-full bg-emerald-50 border border-emerald-200 px-2.5 py-0.5 text-xs font-medium text-emerald-700">confirmed</span>
|
||||
{% elif booking.state == 'provisional' %}
|
||||
<span class="inline-flex items-center rounded-full bg-amber-50 border border-amber-200 px-2.5 py-0.5 text-xs font-medium text-amber-700">provisional</span>
|
||||
{% else %}
|
||||
<span class="inline-flex items-center rounded-full bg-stone-50 border border-stone-200 px-2.5 py-0.5 text-xs font-medium text-stone-600">{{ booking.state }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="text-sm text-stone-500">No bookings yet.</p>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
44
templates/fragments/account_page_tickets.html
Normal file
44
templates/fragments/account_page_tickets.html
Normal file
@@ -0,0 +1,44 @@
|
||||
<div class="w-full max-w-3xl mx-auto px-4 py-6">
|
||||
<div class="bg-white/70 backdrop-blur rounded-2xl shadow border border-stone-200 p-6 sm:p-8 space-y-6">
|
||||
|
||||
<h1 class="text-xl font-semibold tracking-tight">Tickets</h1>
|
||||
|
||||
{% if tickets %}
|
||||
<div class="divide-y divide-stone-100">
|
||||
{% for ticket in tickets %}
|
||||
<div class="py-4 first:pt-0 last:pb-0">
|
||||
<div class="flex items-start justify-between gap-4">
|
||||
<div class="min-w-0 flex-1">
|
||||
<a href="{{ events_url('/tickets/' ~ ticket.code ~ '/') }}"
|
||||
class="text-sm font-medium text-stone-800 hover:text-emerald-700 transition">
|
||||
{{ ticket.entry_name }}
|
||||
</a>
|
||||
<div class="mt-1 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-stone-500">
|
||||
<span>{{ ticket.entry_start_at.strftime('%d %b %Y, %H:%M') }}</span>
|
||||
{% if ticket.calendar_name %}
|
||||
<span>· {{ ticket.calendar_name }}</span>
|
||||
{% endif %}
|
||||
{% if ticket.ticket_type_name %}
|
||||
<span>· {{ ticket.ticket_type_name }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-shrink-0">
|
||||
{% if ticket.state == 'checked_in' %}
|
||||
<span class="inline-flex items-center rounded-full bg-blue-50 border border-blue-200 px-2.5 py-0.5 text-xs font-medium text-blue-700">checked in</span>
|
||||
{% elif ticket.state == 'confirmed' %}
|
||||
<span class="inline-flex items-center rounded-full bg-emerald-50 border border-emerald-200 px-2.5 py-0.5 text-xs font-medium text-emerald-700">confirmed</span>
|
||||
{% else %}
|
||||
<span class="inline-flex items-center rounded-full bg-amber-50 border border-amber-200 px-2.5 py-0.5 text-xs font-medium text-amber-700">{{ ticket.state }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="text-sm text-stone-500">No tickets yet.</p>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user