SxExpr is now a str subclass so it works everywhere a plain string does (join, isinstance, f-strings) while serialize() still emits it unquoted. sx_call() and all internal render functions (_render_to_sx, async_eval_to_sx, etc.) return SxExpr, eliminating the "forgot to wrap" bug class that caused the sx_content leak and list serialization bugs. - Phase 0: SxExpr(str) with .source property, __add__/__radd__ - Phase 1: sx_call returns SxExpr (drop-in, all 200+ sites unchanged) - Phase 2: async_eval_to_sx, async_eval_slot_to_sx, _render_to_sx, mobile_menu_sx return SxExpr; remove isinstance(str) workaround - Phase 3: Remove ~150 redundant SxExpr() wrappings across 45 files - Phase 4: serialize() docstring, handler return docs, ;; returns: sx Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
134 lines
4.9 KiB
Python
134 lines
4.9 KiB
Python
"""Cart layout registration and header builders."""
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
|
|
def _register_cart_layouts() -> None:
|
|
from shared.sx.layouts import register_custom_layout
|
|
register_custom_layout("cart-page", _cart_page_full, _cart_page_oob)
|
|
register_custom_layout("cart-admin", _cart_admin_full, _cart_admin_oob)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Header helpers
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def _ensure_post_ctx(ctx: dict, page_post: Any) -> dict:
|
|
"""Ensure ctx has a 'post' dict from page_post DTO."""
|
|
if ctx.get("post") or not page_post:
|
|
return ctx
|
|
return {**ctx, "post": {
|
|
"id": getattr(page_post, "id", None),
|
|
"slug": getattr(page_post, "slug", ""),
|
|
"title": getattr(page_post, "title", ""),
|
|
"feature_image": getattr(page_post, "feature_image", None),
|
|
}}
|
|
|
|
|
|
async def _ensure_container_nav(ctx: dict) -> dict:
|
|
"""Fetch container_nav if not already present."""
|
|
if ctx.get("container_nav"):
|
|
return ctx
|
|
post = ctx.get("post") or {}
|
|
post_id = post.get("id")
|
|
if not post_id:
|
|
return ctx
|
|
slug = post.get("slug", "")
|
|
from shared.infrastructure.fragments import fetch_fragments
|
|
nav_params = {
|
|
"container_type": "page",
|
|
"container_id": str(post_id),
|
|
"post_slug": slug,
|
|
}
|
|
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}
|
|
|
|
|
|
async def _post_header_sx(ctx: dict, page_post: Any, *, oob: bool = False) -> str:
|
|
from shared.sx.helpers import post_header_sx as _shared_post_header_sx
|
|
ctx = _ensure_post_ctx(ctx, page_post)
|
|
ctx = await _ensure_container_nav(ctx)
|
|
return await _shared_post_header_sx(ctx, oob=oob)
|
|
|
|
|
|
def _cart_header_sx(ctx: dict, *, oob: bool = False) -> str:
|
|
from shared.sx.helpers import sx_call, call_url
|
|
return sx_call(
|
|
"menu-row-sx",
|
|
id="cart-row", level=1, colour="sky",
|
|
link_href=call_url(ctx, "cart_url", "/"),
|
|
link_label="cart", icon="fa fa-shopping-cart",
|
|
child_id="cart-header-child", oob=oob,
|
|
)
|
|
|
|
|
|
def _page_cart_header_sx(ctx: dict, page_post: Any, *, oob: bool = False) -> str:
|
|
from shared.sx.helpers import sx_call, call_url
|
|
slug = page_post.slug if page_post else ""
|
|
title = ((page_post.title if page_post else None) or "")[:160]
|
|
label_sx = sx_call("cart-page-label",
|
|
feature_image=page_post.feature_image if page_post else None,
|
|
title=title)
|
|
nav_sx = sx_call("cart-all-carts-link", href=call_url(ctx, "cart_url", "/"))
|
|
return sx_call(
|
|
"menu-row-sx",
|
|
id="page-cart-row", level=2, colour="sky",
|
|
link_href=call_url(ctx, "cart_url", f"/{slug}/"),
|
|
link_label_content=label_sx,
|
|
nav=nav_sx, oob=oob,
|
|
)
|
|
|
|
|
|
async def _cart_page_admin_header_sx(ctx: dict, page_post: Any, *, oob: bool = False,
|
|
selected: str = "") -> str:
|
|
from shared.sx.helpers import post_admin_header_sx
|
|
slug = page_post.slug if page_post else ""
|
|
ctx = _ensure_post_ctx(ctx, page_post)
|
|
return await post_admin_header_sx(ctx, slug, oob=oob, selected=selected)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Layout functions
|
|
# ---------------------------------------------------------------------------
|
|
|
|
async def _cart_page_full(ctx: dict, **kw: Any) -> str:
|
|
from shared.sx.helpers import render_to_sx_with_env
|
|
page_post = ctx.get("page_post")
|
|
env = {}
|
|
return await render_to_sx_with_env("cart-page-layout-full", env,
|
|
cart_row=_cart_header_sx(ctx),
|
|
page_cart_row=_page_cart_header_sx(ctx, page_post),
|
|
)
|
|
|
|
|
|
async def _cart_page_oob(ctx: dict, **kw: Any) -> str:
|
|
from shared.sx.helpers import render_to_sx_with_env, root_header_sx
|
|
page_post = ctx.get("page_post")
|
|
env = {}
|
|
return await render_to_sx_with_env("cart-page-layout-oob", env,
|
|
root_header_oob=await root_header_sx(ctx, oob=True),
|
|
cart_row_oob=_cart_header_sx(ctx, oob=True),
|
|
page_cart_row=_page_cart_header_sx(ctx, page_post),
|
|
)
|
|
|
|
|
|
async def _cart_admin_full(ctx: dict, **kw: Any) -> str:
|
|
from shared.sx.helpers import render_to_sx_with_env
|
|
page_post = ctx.get("page_post")
|
|
selected = kw.get("selected", "")
|
|
env = {}
|
|
return await render_to_sx_with_env("cart-admin-layout-full", env,
|
|
post_header=await _post_header_sx(ctx, page_post),
|
|
admin_header=await _cart_page_admin_header_sx(ctx, page_post, selected=selected),
|
|
)
|
|
|
|
|
|
async def _cart_admin_oob(ctx: dict, **kw: Any) -> str:
|
|
page_post = ctx.get("page_post")
|
|
selected = kw.get("selected", "")
|
|
return await _cart_page_admin_header_sx(ctx, page_post, oob=True, selected=selected)
|