Continues the pattern of eliminating Python sx_call tree-building in favour of data-driven .sx defcomps. POST/PUT/DELETE routes now pass plain data (dicts, lists, scalars) and let .sx handle iteration, conditionals, and layout via map/let/when/if. Single response components wrap OOB swaps. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
139 lines
4.6 KiB
Python
139 lines
4.6 KiB
Python
"""Badge helpers, OOB helpers, formatting utilities, list containers."""
|
|
from __future__ import annotations
|
|
|
|
from shared.sx.helpers import sx_call
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# OOB header helper — delegates to shared
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Post header helpers — thin wrapper over shared post_header_sx
|
|
# ---------------------------------------------------------------------------
|
|
|
|
async def _ensure_container_nav(ctx: dict) -> dict:
|
|
"""Fetch container_nav if not already present (for post header row)."""
|
|
if ctx.get("container_nav"):
|
|
return ctx
|
|
post = ctx.get("post") or {}
|
|
post_id = post.get("id")
|
|
slug = post.get("slug", "")
|
|
if not post_id:
|
|
return ctx
|
|
from quart import g
|
|
from shared.infrastructure.fragments import fetch_fragments
|
|
current_cal = getattr(g, "calendar_slug", "") or ""
|
|
nav_params = {
|
|
"container_type": "page",
|
|
"container_id": str(post_id),
|
|
"post_slug": slug,
|
|
"current_calendar": current_cal,
|
|
}
|
|
events_nav, market_nav = await fetch_fragments([
|
|
("events", "container-nav", nav_params),
|
|
("market", "container-nav", nav_params),
|
|
], required=False)
|
|
return {**ctx, "container_nav": events_nav + market_nav}
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Utility
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def _list_container(ctx: dict) -> str:
|
|
styles = ctx.get("styles") or {}
|
|
return getattr(styles, "list_container", "") if hasattr(styles, "list_container") else styles.get("list_container", "")
|
|
|
|
|
|
def _entry_state_badge_html(state: str) -> str:
|
|
"""Render an entry state badge."""
|
|
state_classes = {
|
|
"confirmed": "bg-emerald-100 text-emerald-800",
|
|
"provisional": "bg-amber-100 text-amber-800",
|
|
"ordered": "bg-sky-100 text-sky-800",
|
|
"pending": "bg-stone-100 text-stone-700",
|
|
"declined": "bg-red-100 text-red-800",
|
|
}
|
|
cls = state_classes.get(state, "bg-stone-100 text-stone-700")
|
|
label = state.replace("_", " ").capitalize()
|
|
return sx_call("badge", cls=cls, label=label)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# View toggle + SVG caching
|
|
# ---------------------------------------------------------------------------
|
|
|
|
_LIST_SVG = None
|
|
_TILE_SVG = None
|
|
|
|
|
|
def _get_list_svg():
|
|
global _LIST_SVG
|
|
if _LIST_SVG is None:
|
|
_LIST_SVG = sx_call("list-svg")
|
|
return _LIST_SVG
|
|
|
|
|
|
def _get_tile_svg():
|
|
global _TILE_SVG
|
|
if _TILE_SVG is None:
|
|
_TILE_SVG = sx_call("tile-svg")
|
|
return _TILE_SVG
|
|
|
|
|
|
def _view_toggle_html(ctx: dict, view: str) -> str:
|
|
"""Render the list/tile view toggle bar."""
|
|
from shared.utils import route_prefix
|
|
prefix = route_prefix()
|
|
clh = ctx.get("current_local_href", "/")
|
|
hx_select = ctx.get("hx_select_search", "#main-panel")
|
|
|
|
list_href = prefix + str(clh)
|
|
tile_href = prefix + str(clh)
|
|
if "?" in list_href:
|
|
list_href = list_href.split("?")[0]
|
|
if "?" in tile_href:
|
|
tile_href = tile_href.split("?")[0] + "?view=tile"
|
|
else:
|
|
tile_href = tile_href + "?view=tile"
|
|
|
|
list_active = 'bg-stone-200 text-stone-800' if view != 'tile' else 'text-stone-400 hover:text-stone-600'
|
|
tile_active = 'bg-stone-200 text-stone-800' if view == 'tile' else 'text-stone-400 hover:text-stone-600'
|
|
|
|
return sx_call("view-toggle",
|
|
list_href=list_href, tile_href=tile_href,
|
|
hx_select=hx_select, list_cls=list_active,
|
|
tile_cls=tile_active, storage_key="events_view",
|
|
list_svg=_get_list_svg(), tile_svg=_get_tile_svg())
|
|
|
|
|
|
def _cart_icon_oob(count: int) -> str:
|
|
"""Render the OOB cart icon/badge swap."""
|
|
ctx = _cart_icon_ctx(count)
|
|
return sx_call("events-cart-icon", **ctx)
|
|
|
|
|
|
def _cart_icon_ctx(count: int) -> dict:
|
|
"""Return data dict for the ~events-cart-icon component."""
|
|
from quart import g
|
|
|
|
blog_url_fn = getattr(g, "blog_url", None)
|
|
cart_url_fn = getattr(g, "cart_url", None)
|
|
site_fn = getattr(g, "site", None)
|
|
logo = ""
|
|
if site_fn:
|
|
site_obj = site_fn() if callable(site_fn) else site_fn
|
|
logo = getattr(site_obj, "logo", "") if site_obj else ""
|
|
|
|
blog_href = blog_url_fn("/") if blog_url_fn else "/"
|
|
cart_href = cart_url_fn("/") if cart_url_fn else "/"
|
|
return {
|
|
"cart_count": count,
|
|
"blog_href": blog_href,
|
|
"cart_href": cart_href,
|
|
"logo": logo,
|
|
}
|