"""Events app fragment endpoints. Exposes sx fragments at ``/internal/fragments/`` for consumption by other coop apps via the fragment client. Most handlers are defined declaratively in .sx files under ``events/sx/handlers/`` and dispatched via the sx handler registry. Jinja HTML handlers (container-cards, account-page) remain as Python because they return ``text/html`` templates, not sx source. """ from __future__ import annotations from quart import Blueprint, Response, g, render_template, request from shared.infrastructure.fragments import FRAGMENT_HEADER from shared.services.registry import services from shared.sx.handlers import get_handler, execute_handler def register(): bp = Blueprint("fragments", __name__, url_prefix="/internal/fragments") _handlers: dict[str, object] = {} # Fragment types that return HTML (Jinja templates) _html_types = {"container-cards", "account-page"} @bp.before_request async def _require_fragment_header(): if not request.headers.get(FRAGMENT_HEADER): return Response("", status=403) @bp.get("/") async def get_fragment(fragment_type: str): # 1. Check Python handlers first (Jinja HTML types) handler = _handlers.get(fragment_type) if handler is not None: result = await handler() ct = "text/html" if fragment_type in _html_types else "text/sx" return Response(result, status=200, content_type=ct) # 2. Check sx handler registry handler_def = get_handler("events", fragment_type) if handler_def is not None: result = await execute_handler( handler_def, "events", args=dict(request.args), ) return Response(result, status=200, content_type="text/sx") return Response("", status=200, content_type="text/sx") # --- container-cards fragment: entries for blog listing cards (Jinja HTML) -- async def _container_cards_handler(): post_ids_raw = request.args.get("post_ids", "") post_slugs_raw = request.args.get("post_slugs", "") post_ids = [int(x) for x in post_ids_raw.split(",") if x.strip()] post_slugs = [x.strip() for x in post_slugs_raw.split(",") if x.strip()] if not post_ids: return "" slug_map = {} for i, pid in enumerate(post_ids): slug_map[pid] = post_slugs[i] if i < len(post_slugs) else "" batch = await services.calendar.confirmed_entries_for_posts(g.s, post_ids) return await render_template( "fragments/container_cards_entries.html", batch=batch, post_ids=post_ids, slug_map=slug_map, ) _handlers["container-cards"] = _container_cards_handler # --- account-page fragment: tickets or bookings panel (Jinja HTML) ------ 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