""" All-events blueprint — shows upcoming events across ALL pages' calendars. Mounted at / (root of events app). No slug context — works independently of the post/slug machinery. Routes: GET / — full page with first page of entries GET /all-entries — HTMX fragment for infinite scroll POST /all-tickets/adjust — adjust ticket quantity inline """ from __future__ import annotations from quart import Blueprint, g, request, render_template, render_template_string, make_response from shared.browser.app.utils.htmx import is_htmx_request from shared.infrastructure.cart_identity import current_cart_identity from shared.services.registry import services def register() -> Blueprint: bp = Blueprint("all_events", __name__) async def _load_entries(page, per_page=20): """Load all upcoming entries + pending ticket counts + page info.""" entries, has_more = await services.calendar.upcoming_entries_for_container( g.s, page=page, per_page=per_page, ) # Pending ticket counts keyed by entry_id ident = current_cart_identity() pending_tickets = {} if entries: tickets = await services.calendar.pending_tickets( g.s, user_id=ident["user_id"], session_id=ident["session_id"], ) for t in tickets: if t.entry_id is not None: pending_tickets[t.entry_id] = pending_tickets.get(t.entry_id, 0) + 1 # Batch-load page info for container_ids page_info = {} # {post_id: {title, slug}} if entries: post_ids = list({ e.calendar_container_id for e in entries if e.calendar_container_type == "page" and e.calendar_container_id }) if post_ids: posts = await services.blog.get_posts_by_ids(g.s, post_ids) for p in posts: page_info[p.id] = {"title": p.title, "slug": p.slug} return entries, has_more, pending_tickets, page_info @bp.get("/") async def index(): view = request.args.get("view", "list") page = int(request.args.get("page", 1)) entries, has_more, pending_tickets, page_info = await _load_entries(page) ctx = dict( entries=entries, has_more=has_more, pending_tickets=pending_tickets, page_info=page_info, page=page, view=view, ) if is_htmx_request(): html = await render_template("_types/all_events/_main_panel.html", **ctx) else: html = await render_template("_types/all_events/index.html", **ctx) return await make_response(html, 200) @bp.get("/all-entries") async def entries_fragment(): view = request.args.get("view", "list") page = int(request.args.get("page", 1)) entries, has_more, pending_tickets, page_info = await _load_entries(page) html = await render_template( "_types/all_events/_cards.html", entries=entries, has_more=has_more, pending_tickets=pending_tickets, page_info=page_info, page=page, view=view, ) return await make_response(html, 200) @bp.post("/all-tickets/adjust") async def adjust_ticket(): """Adjust ticket quantity, return updated widget + OOB cart-mini.""" ident = current_cart_identity() form = await request.form entry_id = int(form.get("entry_id", 0)) count = max(int(form.get("count", 0)), 0) tt_raw = (form.get("ticket_type_id") or "").strip() ticket_type_id = int(tt_raw) if tt_raw else None await services.calendar.adjust_ticket_quantity( g.s, entry_id, count, user_id=ident["user_id"], session_id=ident["session_id"], ticket_type_id=ticket_type_id, ) # Get updated ticket count for this entry tickets = await services.calendar.pending_tickets( g.s, user_id=ident["user_id"], session_id=ident["session_id"], ) qty = sum(1 for t in tickets if t.entry_id == entry_id) # Load entry DTO for the widget template entry = await services.calendar.entry_by_id(g.s, entry_id) # Updated cart count for OOB mini-cart summary = await services.cart.cart_summary( g.s, user_id=ident["user_id"], session_id=ident["session_id"], ) cart_count = summary.count + summary.calendar_count + summary.ticket_count # Render widget + OOB cart-mini widget_html = await render_template( "_types/page_summary/_ticket_widget.html", entry=entry, qty=qty, ticket_url="/all-tickets/adjust", ) mini_html = await render_template_string( '{% from "_types/cart/_mini.html" import mini with context %}' '{{ mini(oob="true") }}', cart_count=cart_count, ) return await make_response(widget_html + mini_html, 200) return bp