""" Page summary blueprint — shows upcoming events for a single page's calendars. Routes: GET // — full page scoped to this page GET //entries — HTMX fragment for infinite scroll POST //tickets/adjust — adjust ticket quantity inline """ from __future__ import annotations from quart import Blueprint, g, request, 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.services.registry import services def register() -> Blueprint: bp = Blueprint("page_summary", __name__) async def _load_entries(post_id, page, per_page=20): """Load upcoming entries for this page + pending ticket counts.""" entries, has_more = await services.calendar.upcoming_entries_for_container( g.s, "page", post_id, 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 return entries, has_more, pending_tickets @bp.get("/") async def index(): post = g.post_data["post"] view = request.args.get("view", "list") page = int(request.args.get("page", 1)) entries, has_more, pending_tickets = await _load_entries(post["id"], page) from shared.sx.page import get_template_context from sxc.pages.renders import render_page_summary_page, render_page_summary_oob ctx = await get_template_context() if is_htmx_request(): sx_src = await render_page_summary_oob(ctx, entries, has_more, pending_tickets, {}, page, view) return sx_response(sx_src) else: html = await render_page_summary_page(ctx, entries, has_more, pending_tickets, {}, page, view) return await make_response(html, 200) @bp.get("/entries") async def entries_fragment(): post = g.post_data["post"] view = request.args.get("view", "list") page = int(request.args.get("page", 1)) entries, has_more, pending_tickets = await _load_entries(post["id"], page) from sxc.pages.renders import render_page_summary_cards sx_src = render_page_summary_cards(entries, has_more, pending_tickets, {}, page, view, post) return sx_response(sx_src) @bp.post("/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 sxc.pages.tickets import render_ticket_widget widget_html = render_ticket_widget(entry, qty, f"/{g.post_slug}/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