""" 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, make_response from shared.browser.app.utils.htmx import is_htmx_request from shared.sx.helpers import sx_response from shared.infrastructure.cart_identity import current_cart_identity from shared.infrastructure.data_client import fetch_data from shared.contracts.dtos import PostDTO, dto_from_dict 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: raw_posts = await fetch_data("blog", "posts-by-ids", params={"ids": ",".join(str(i) for i in post_ids)}, required=False) or [] for raw_p in raw_posts: p = dto_from_dict(PostDTO, raw_p) 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) from shared.sx.page import get_template_context from sx.sx_components import render_all_events_page, render_all_events_oob ctx = await get_template_context() if is_htmx_request(): sx_src = await render_all_events_oob(ctx, entries, has_more, pending_tickets, page_info, page, view) return sx_response(sx_src) else: html = await render_all_events_page(ctx, entries, has_more, pending_tickets, page_info, page, view) 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) from sx.sx_components import render_all_events_cards sx_src = await render_all_events_cards(entries, has_more, pending_tickets, page_info, page, view) return sx_response(sx_src) @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) # Commit so cross-service calls see the updated tickets await g.tx.commit() g.tx = await g.s.begin() from shared.infrastructure.fragments import fetch_fragment frag_params = {"oob": "1"} if ident["user_id"] is not None: frag_params["user_id"] = str(ident["user_id"]) if ident["session_id"] is not None: frag_params["session_id"] = ident["session_id"] from sx.sx_components import render_ticket_widget widget_html = render_ticket_widget(entry, qty, "/all-tickets/adjust") mini_html = await fetch_fragment("cart", "cart-mini", params=frag_params, required=False) return sx_response(widget_html + (mini_html or "")) return bp