"""Events app fragment endpoints. Exposes HTML fragments at ``/internal/fragments/`` for consumption by other coop apps via the fragment client. """ 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 def register(): bp = Blueprint("fragments", __name__, url_prefix="/internal/fragments") _handlers: dict[str, object] = {} @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): handler = _handlers.get(fragment_type) if handler is None: return Response("", status=200, content_type="text/html") html = await handler() return Response(html, status=200, content_type="text/html") # --- container-nav fragment: calendar entries + calendar links ----------- async def _container_nav_handler(): container_type = request.args.get("container_type", "page") container_id = int(request.args.get("container_id", 0)) post_slug = request.args.get("post_slug", "") paginate_url_base = request.args.get("paginate_url", "") page = int(request.args.get("page", 1)) exclude = request.args.get("exclude", "") excludes = [e.strip() for e in exclude.split(",") if e.strip()] html_parts = [] # Calendar entries nav if not any(e.startswith("calendar") for e in excludes): entries, has_more = await services.calendar.associated_entries( g.s, container_type, container_id, page, ) if entries: html_parts.append(await render_template( "fragments/container_nav_entries.html", entries=entries, has_more=has_more, page=page, post_slug=post_slug, paginate_url_base=paginate_url_base, )) # Calendar links nav if not any(e.startswith("calendar") for e in excludes): calendars = await services.calendar.calendars_for_container( g.s, container_type, container_id, ) if calendars: html_parts.append(await render_template( "fragments/container_nav_calendars.html", calendars=calendars, post_slug=post_slug, )) return "\n".join(html_parts) _handlers["container-nav"] = _container_nav_handler # --- container-cards fragment: entries for blog listing cards ------------ 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 "" # Build post_id -> slug mapping 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-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