Replace sx_call() with render_to_sx() across all services
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 2m6s
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 2m6s
Python no longer generates s-expression strings. All SX rendering now goes through render_to_sx() which builds AST from native Python values and evaluates via async_eval_to_sx() — no SX string literals in Python. - Add render_to_sx()/render_to_html() infrastructure in shared/sx/helpers.py - Add (abort status msg) IO primitive in shared/sx/primitives_io.py - Convert all 9 services: ~650 sx_call() invocations replaced - Convert shared helpers (root_header_sx, full_page_sx, etc.) to async - Fix likes service import bug (likes.models → models) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -56,6 +56,6 @@ def register(url_prefix="/"):
|
||||
await g.s.flush()
|
||||
|
||||
from sx.sx_components import render_newsletter_toggle
|
||||
return sx_response(render_newsletter_toggle(un))
|
||||
return sx_response(await render_newsletter_toggle(un))
|
||||
|
||||
return account_bp
|
||||
|
||||
@@ -11,7 +11,7 @@ from typing import Any
|
||||
|
||||
from shared.sx.jinja_bridge import load_service_components
|
||||
from shared.sx.helpers import (
|
||||
sx_call, SxExpr,
|
||||
render_to_sx,
|
||||
root_header_sx, full_page_sx,
|
||||
)
|
||||
|
||||
@@ -28,9 +28,10 @@ async def render_login_page(ctx: dict) -> str:
|
||||
"""Full page: login form."""
|
||||
error = ctx.get("error", "")
|
||||
email = ctx.get("email", "")
|
||||
hdr = root_header_sx(ctx)
|
||||
content = sx_call("account-login-content", error=error or None, email=email)
|
||||
return full_page_sx(ctx, header_rows=hdr,
|
||||
hdr = await root_header_sx(ctx)
|
||||
content = await render_to_sx("account-login-content",
|
||||
error=error or None, email=email)
|
||||
return await full_page_sx(ctx, header_rows=hdr,
|
||||
content=content,
|
||||
meta_html='<title>Login \u2014 Rose Ash</title>')
|
||||
|
||||
@@ -39,18 +40,19 @@ async def render_device_page(ctx: dict) -> str:
|
||||
"""Full page: device authorization form."""
|
||||
error = ctx.get("error", "")
|
||||
code = ctx.get("code", "")
|
||||
hdr = root_header_sx(ctx)
|
||||
content = sx_call("account-device-content", error=error or None, code=code)
|
||||
return full_page_sx(ctx, header_rows=hdr,
|
||||
hdr = await root_header_sx(ctx)
|
||||
content = await render_to_sx("account-device-content",
|
||||
error=error or None, code=code)
|
||||
return await full_page_sx(ctx, header_rows=hdr,
|
||||
content=content,
|
||||
meta_html='<title>Authorize Device \u2014 Rose Ash</title>')
|
||||
|
||||
|
||||
async def render_device_approved_page(ctx: dict) -> str:
|
||||
"""Full page: device approved."""
|
||||
hdr = root_header_sx(ctx)
|
||||
content = sx_call("account-device-approved")
|
||||
return full_page_sx(ctx, header_rows=hdr,
|
||||
hdr = await root_header_sx(ctx)
|
||||
content = await render_to_sx("account-device-approved")
|
||||
return await full_page_sx(ctx, header_rows=hdr,
|
||||
content=content,
|
||||
meta_html='<title>Device Authorized \u2014 Rose Ash</title>')
|
||||
|
||||
@@ -59,10 +61,10 @@ async def render_check_email_page(ctx: dict) -> str:
|
||||
"""Full page: check email after magic link sent."""
|
||||
email = ctx.get("email", "")
|
||||
email_error = ctx.get("email_error")
|
||||
hdr = root_header_sx(ctx)
|
||||
content = sx_call("account-check-email-content",
|
||||
email=email, email_error=email_error)
|
||||
return full_page_sx(ctx, header_rows=hdr,
|
||||
hdr = await root_header_sx(ctx)
|
||||
content = await render_to_sx("account-check-email-content",
|
||||
email=email, email_error=email_error)
|
||||
return await full_page_sx(ctx, header_rows=hdr,
|
||||
content=content,
|
||||
meta_html='<title>Check your email \u2014 Rose Ash</title>')
|
||||
|
||||
@@ -71,7 +73,7 @@ async def render_check_email_page(ctx: dict) -> str:
|
||||
# Public API: Fragment renderers for POST handlers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def render_newsletter_toggle(un) -> str:
|
||||
async def render_newsletter_toggle(un) -> str:
|
||||
"""Render a newsletter toggle switch for POST response."""
|
||||
from shared.browser.app.csrf import generate_csrf_token
|
||||
|
||||
@@ -94,7 +96,7 @@ def render_newsletter_toggle(un) -> str:
|
||||
translate = "translate-x-1"
|
||||
checked = "false"
|
||||
|
||||
return sx_call(
|
||||
return await render_to_sx(
|
||||
"account-newsletter-toggle",
|
||||
id=f"nl-{nid}", url=toggle_url,
|
||||
hdrs=f'{{"X-CSRFToken": "{csrf}"}}',
|
||||
@@ -103,27 +105,3 @@ def render_newsletter_toggle(un) -> str:
|
||||
checked=checked,
|
||||
knob_cls=f"inline-block h-4 w-4 rounded-full bg-white shadow transform transition-transform {translate}",
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Internal helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _fragment_content(frag: object) -> str:
|
||||
"""Convert a fragment response to sx content string.
|
||||
|
||||
SxExpr (from text/sx responses) is embedded as-is; plain strings
|
||||
(from text/html) are wrapped in ``~rich-text``.
|
||||
"""
|
||||
from shared.sx.parser import SxExpr
|
||||
if isinstance(frag, SxExpr):
|
||||
return frag.source
|
||||
s = str(frag) if frag else ""
|
||||
if not s:
|
||||
return ""
|
||||
return f'(~rich-text :html "{_sx_escape(s)}")'
|
||||
|
||||
|
||||
def _sx_escape(s: str) -> str:
|
||||
"""Escape a string for embedding in sx string literals."""
|
||||
return s.replace("\\", "\\\\").replace('"', '\\"')
|
||||
|
||||
@@ -26,43 +26,51 @@ def _register_account_layouts() -> None:
|
||||
register_custom_layout("account", _account_full, _account_oob, _account_mobile)
|
||||
|
||||
|
||||
def _account_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import root_header_sx, header_child_sx, call_url, sx_call, SxExpr
|
||||
async def _account_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import root_header_sx, header_child_sx, render_to_sx
|
||||
|
||||
root_hdr = root_header_sx(ctx)
|
||||
auth_hdr = sx_call("auth-header-row",
|
||||
account_url=call_url(ctx, "account_url", ""),
|
||||
root_hdr = await root_header_sx(ctx)
|
||||
auth_hdr = await render_to_sx("auth-header-row",
|
||||
account_url=_call_url(ctx, "account_url", ""),
|
||||
select_colours=ctx.get("select_colours", ""),
|
||||
account_nav=_as_sx_nav(ctx),
|
||||
)
|
||||
hdr_child = header_child_sx(auth_hdr)
|
||||
hdr_child = await header_child_sx(auth_hdr)
|
||||
return "(<> " + root_hdr + " " + hdr_child + ")"
|
||||
|
||||
|
||||
def _account_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import root_header_sx, call_url, sx_call
|
||||
async def _account_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import root_header_sx, render_to_sx
|
||||
|
||||
auth_hdr = sx_call("auth-header-row",
|
||||
account_url=call_url(ctx, "account_url", ""),
|
||||
auth_hdr = await render_to_sx("auth-header-row",
|
||||
account_url=_call_url(ctx, "account_url", ""),
|
||||
select_colours=ctx.get("select_colours", ""),
|
||||
account_nav=_as_sx_nav(ctx),
|
||||
oob=True,
|
||||
)
|
||||
return "(<> " + auth_hdr + " " + root_header_sx(ctx, oob=True) + ")"
|
||||
return "(<> " + auth_hdr + " " + await root_header_sx(ctx, oob=True) + ")"
|
||||
|
||||
|
||||
def _account_mobile(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import mobile_menu_sx, mobile_root_nav_sx, sx_call, SxExpr, call_url
|
||||
async def _account_mobile(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import mobile_menu_sx, mobile_root_nav_sx, render_to_sx
|
||||
from shared.sx.parser import SxExpr
|
||||
ctx = _inject_account_nav(ctx)
|
||||
nav_items = sx_call("auth-nav-items",
|
||||
account_url=call_url(ctx, "account_url", ""),
|
||||
nav_items = await render_to_sx("auth-nav-items",
|
||||
account_url=_call_url(ctx, "account_url", ""),
|
||||
select_colours=ctx.get("select_colours", ""),
|
||||
account_nav=_as_sx_nav(ctx),
|
||||
)
|
||||
auth_section = sx_call("mobile-menu-section",
|
||||
auth_section = await render_to_sx("mobile-menu-section",
|
||||
label="account", href="/", level=1, colour="sky",
|
||||
items=SxExpr(nav_items))
|
||||
return mobile_menu_sx(auth_section, mobile_root_nav_sx(ctx))
|
||||
return mobile_menu_sx(auth_section, await mobile_root_nav_sx(ctx))
|
||||
|
||||
|
||||
def _call_url(ctx: dict, key: str, path: str = "/") -> str:
|
||||
fn = ctx.get(key)
|
||||
if callable(fn):
|
||||
return fn(path)
|
||||
return str(fn or "") + path
|
||||
|
||||
|
||||
def _inject_account_nav(ctx: dict) -> dict:
|
||||
@@ -75,7 +83,7 @@ def _inject_account_nav(ctx: dict) -> dict:
|
||||
|
||||
|
||||
def _as_sx_nav(ctx: dict) -> Any:
|
||||
"""Convert account_nav fragment to SxExpr for use in sx_call."""
|
||||
"""Convert account_nav fragment to SxExpr for use in component calls."""
|
||||
from shared.sx.helpers import _as_sx
|
||||
ctx = _inject_account_nav(ctx)
|
||||
return _as_sx(ctx.get("account_nav"))
|
||||
@@ -100,8 +108,7 @@ async def _h_newsletters_content(**kw):
|
||||
from sqlalchemy import select
|
||||
from shared.models import UserNewsletter
|
||||
from shared.models.ghost_membership_entities import GhostNewsletter
|
||||
from shared.sx.helpers import sx_call, SxExpr
|
||||
from shared.sx.parser import serialize
|
||||
from shared.sx.helpers import render_to_sx
|
||||
|
||||
result = await g.s.execute(
|
||||
select(GhostNewsletter).order_by(GhostNewsletter.name)
|
||||
@@ -131,8 +138,8 @@ async def _h_newsletters_content(**kw):
|
||||
# Call account_url to get the base URL string
|
||||
account_url_str = account_url("") if callable(account_url) else str(account_url or "")
|
||||
|
||||
return sx_call("account-newsletters-content",
|
||||
newsletter_list=SxExpr(serialize(newsletter_list)),
|
||||
return await render_to_sx("account-newsletters-content",
|
||||
newsletter_list=newsletter_list,
|
||||
account_url=account_url_str)
|
||||
|
||||
|
||||
@@ -148,5 +155,11 @@ async def _h_fragment_content(slug=None, **kw):
|
||||
)
|
||||
if not fragment_html:
|
||||
abort(404)
|
||||
from sx.sx_components import _fragment_content
|
||||
return _fragment_content(fragment_html)
|
||||
from shared.sx.parser import SxExpr
|
||||
if isinstance(fragment_html, SxExpr):
|
||||
return fragment_html.source
|
||||
s = str(fragment_html) if fragment_html else ""
|
||||
if not s:
|
||||
return ""
|
||||
from shared.sx.helpers import render_to_sx
|
||||
return await render_to_sx("rich-text", html=s)
|
||||
|
||||
@@ -235,7 +235,7 @@ def register(url_prefix, title):
|
||||
from shared.sx.page import get_template_context
|
||||
from sx.sx_components import render_new_post_page, render_editor_panel
|
||||
tctx = await get_template_context()
|
||||
tctx["editor_html"] = render_editor_panel(save_error="Invalid JSON in editor content.")
|
||||
tctx["editor_html"] = await render_editor_panel(save_error="Invalid JSON in editor content.")
|
||||
html = await render_new_post_page(tctx)
|
||||
return await make_response(html, 400)
|
||||
|
||||
@@ -244,7 +244,7 @@ def register(url_prefix, title):
|
||||
from shared.sx.page import get_template_context
|
||||
from sx.sx_components import render_new_post_page, render_editor_panel
|
||||
tctx = await get_template_context()
|
||||
tctx["editor_html"] = render_editor_panel(save_error=reason)
|
||||
tctx["editor_html"] = await render_editor_panel(save_error=reason)
|
||||
html = await render_new_post_page(tctx)
|
||||
return await make_response(html, 400)
|
||||
|
||||
@@ -291,7 +291,7 @@ def register(url_prefix, title):
|
||||
from shared.sx.page import get_template_context
|
||||
from sx.sx_components import render_new_post_page, render_editor_panel
|
||||
tctx = await get_template_context()
|
||||
tctx["editor_html"] = render_editor_panel(save_error="Invalid JSON in editor content.", is_page=True)
|
||||
tctx["editor_html"] = await render_editor_panel(save_error="Invalid JSON in editor content.", is_page=True)
|
||||
tctx["is_page"] = True
|
||||
html = await render_new_post_page(tctx)
|
||||
return await make_response(html, 400)
|
||||
@@ -301,7 +301,7 @@ def register(url_prefix, title):
|
||||
from shared.sx.page import get_template_context
|
||||
from sx.sx_components import render_new_post_page, render_editor_panel
|
||||
tctx = await get_template_context()
|
||||
tctx["editor_html"] = render_editor_panel(save_error=reason, is_page=True)
|
||||
tctx["editor_html"] = await render_editor_panel(save_error=reason, is_page=True)
|
||||
tctx["is_page"] = True
|
||||
html = await render_new_post_page(tctx)
|
||||
return await make_response(html, 400)
|
||||
|
||||
@@ -17,10 +17,10 @@ from shared.sx.helpers import sx_response
|
||||
def register():
|
||||
bp = Blueprint("menu_items", __name__, url_prefix='/settings/menu_items')
|
||||
|
||||
def get_menu_items_nav_oob_sync(menu_items):
|
||||
async def get_menu_items_nav_oob_async(menu_items):
|
||||
"""Helper to generate OOB update for root nav menu items"""
|
||||
from sx.sx_components import render_menu_items_nav_oob
|
||||
return render_menu_items_nav_oob(menu_items)
|
||||
return await render_menu_items_nav_oob(menu_items)
|
||||
|
||||
@bp.get("/new/")
|
||||
@require_admin
|
||||
@@ -51,8 +51,8 @@ def register():
|
||||
# Get updated list and nav OOB
|
||||
menu_items = await get_all_menu_items(g.s)
|
||||
from sx.sx_components import render_menu_items_list
|
||||
html = render_menu_items_list(menu_items)
|
||||
nav_oob = get_menu_items_nav_oob_sync(menu_items)
|
||||
html = await render_menu_items_list(menu_items)
|
||||
nav_oob = await get_menu_items_nav_oob_async(menu_items)
|
||||
return sx_response(html + nav_oob)
|
||||
|
||||
except MenuItemError as e:
|
||||
@@ -91,8 +91,8 @@ def register():
|
||||
# Get updated list and nav OOB
|
||||
menu_items = await get_all_menu_items(g.s)
|
||||
from sx.sx_components import render_menu_items_list
|
||||
html = render_menu_items_list(menu_items)
|
||||
nav_oob = get_menu_items_nav_oob_sync(menu_items)
|
||||
html = await render_menu_items_list(menu_items)
|
||||
nav_oob = await get_menu_items_nav_oob_async(menu_items)
|
||||
return sx_response(html + nav_oob)
|
||||
|
||||
except MenuItemError as e:
|
||||
@@ -112,8 +112,8 @@ def register():
|
||||
# Get updated list and nav OOB
|
||||
menu_items = await get_all_menu_items(g.s)
|
||||
from sx.sx_components import render_menu_items_list
|
||||
html = render_menu_items_list(menu_items)
|
||||
nav_oob = get_menu_items_nav_oob_sync(menu_items)
|
||||
html = await render_menu_items_list(menu_items)
|
||||
nav_oob = await get_menu_items_nav_oob_async(menu_items)
|
||||
return sx_response(html + nav_oob)
|
||||
|
||||
@bp.get("/pages/search/")
|
||||
@@ -128,7 +128,7 @@ def register():
|
||||
has_more = (page * per_page) < total
|
||||
|
||||
from sx.sx_components import render_page_search_results
|
||||
return sx_response(render_page_search_results(pages, query, page, has_more))
|
||||
return sx_response(await render_page_search_results(pages, query, page, has_more))
|
||||
|
||||
@bp.post("/reorder/")
|
||||
@require_admin
|
||||
@@ -153,8 +153,8 @@ def register():
|
||||
# Get updated list and nav OOB
|
||||
menu_items = await get_all_menu_items(g.s)
|
||||
from sx.sx_components import render_menu_items_list
|
||||
html = render_menu_items_list(menu_items)
|
||||
nav_oob = get_menu_items_nav_oob_sync(menu_items)
|
||||
html = await render_menu_items_list(menu_items)
|
||||
nav_oob = await get_menu_items_nav_oob_async(menu_items)
|
||||
return sx_response(html + nav_oob)
|
||||
|
||||
return bp
|
||||
|
||||
@@ -90,7 +90,7 @@ def register():
|
||||
features = result.get("features", {})
|
||||
|
||||
from sx.sx_components import render_features_panel
|
||||
html = render_features_panel(
|
||||
html = await render_features_panel(
|
||||
features, post,
|
||||
sumup_configured=result.get("sumup_configured", False),
|
||||
sumup_merchant_code=result.get("sumup_merchant_code") or "",
|
||||
@@ -129,7 +129,7 @@ def register():
|
||||
|
||||
features = result.get("features", {})
|
||||
from sx.sx_components import render_features_panel
|
||||
html = render_features_panel(
|
||||
html = await render_features_panel(
|
||||
features, post,
|
||||
sumup_configured=result.get("sumup_configured", False),
|
||||
sumup_merchant_code=result.get("sumup_merchant_code") or "",
|
||||
@@ -259,8 +259,8 @@ def register():
|
||||
from sx.sx_components import render_associated_entries, render_nav_entries_oob
|
||||
|
||||
post = g.post_data["post"]
|
||||
admin_list = render_associated_entries(all_calendars, associated_entry_ids, post["slug"])
|
||||
nav_entries_html = render_nav_entries_oob(associated_entries, calendars, post)
|
||||
admin_list = await render_associated_entries(all_calendars, associated_entry_ids, post["slug"])
|
||||
nav_entries_html = await render_nav_entries_oob(associated_entries, calendars, post)
|
||||
|
||||
return sx_response(admin_list + nav_entries_html)
|
||||
|
||||
@@ -436,7 +436,7 @@ def register():
|
||||
page_markets = await _fetch_page_markets(post_id)
|
||||
|
||||
from sx.sx_components import render_markets_panel
|
||||
return sx_response(render_markets_panel(page_markets, post))
|
||||
return sx_response(await render_markets_panel(page_markets, post))
|
||||
|
||||
@bp.post("/markets/new/")
|
||||
@require_admin
|
||||
@@ -462,7 +462,7 @@ def register():
|
||||
page_markets = await _fetch_page_markets(post_id)
|
||||
|
||||
from sx.sx_components import render_markets_panel
|
||||
return sx_response(render_markets_panel(page_markets, post))
|
||||
return sx_response(await render_markets_panel(page_markets, post))
|
||||
|
||||
@bp.delete("/markets/<market_slug>/")
|
||||
@require_admin
|
||||
@@ -482,6 +482,6 @@ def register():
|
||||
page_markets = await _fetch_page_markets(post_id)
|
||||
|
||||
from sx.sx_components import render_markets_panel
|
||||
return sx_response(render_markets_panel(page_markets, post))
|
||||
return sx_response(await render_markets_panel(page_markets, post))
|
||||
|
||||
return bp
|
||||
|
||||
@@ -125,7 +125,7 @@ def register():
|
||||
|
||||
# Get post_id from g.post_data
|
||||
if not g.user:
|
||||
return sx_response(render_like_toggle_button(slug, False, like_url), status=403)
|
||||
return sx_response(await render_like_toggle_button(slug, False, like_url), status=403)
|
||||
|
||||
post_id = g.post_data["post"]["id"]
|
||||
user_id = g.user.id
|
||||
@@ -135,7 +135,7 @@ def register():
|
||||
})
|
||||
liked = result["liked"]
|
||||
|
||||
return sx_response(render_like_toggle_button(slug, liked, like_url))
|
||||
return sx_response(await render_like_toggle_button(slug, liked, like_url))
|
||||
|
||||
@bp.get("/w/<widget_domain>/")
|
||||
async def widget_paginate(slug: str, widget_domain: str):
|
||||
|
||||
@@ -47,7 +47,7 @@ def register():
|
||||
|
||||
snippets = await _visible_snippets(g.s)
|
||||
from sx.sx_components import render_snippets_list
|
||||
return sx_response(render_snippets_list(snippets, is_admin))
|
||||
return sx_response(await render_snippets_list(snippets, is_admin))
|
||||
|
||||
@bp.patch("/<int:snippet_id>/visibility/")
|
||||
@require_login
|
||||
@@ -71,6 +71,6 @@ def register():
|
||||
|
||||
snippets = await _visible_snippets(g.s)
|
||||
from sx.sx_components import render_snippets_list
|
||||
return sx_response(render_snippets_list(snippets, True))
|
||||
return sx_response(await render_snippets_list(snippets, True))
|
||||
|
||||
return bp
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -129,148 +129,148 @@ def _register_blog_layouts() -> None:
|
||||
|
||||
# --- Blog layout (root + blog header) ---
|
||||
|
||||
def _blog_full(ctx: dict, **kw: Any) -> str:
|
||||
async def _blog_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import root_header_sx
|
||||
from sx.sx_components import _blog_header_sx
|
||||
root_hdr = root_header_sx(ctx)
|
||||
blog_hdr = _blog_header_sx(ctx)
|
||||
root_hdr = await root_header_sx(ctx)
|
||||
blog_hdr = await _blog_header_sx(ctx)
|
||||
return "(<> " + root_hdr + " " + blog_hdr + ")"
|
||||
|
||||
|
||||
def _blog_oob(ctx: dict, **kw: Any) -> str:
|
||||
async def _blog_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import root_header_sx, oob_header_sx
|
||||
from sx.sx_components import _blog_header_sx
|
||||
root_hdr = root_header_sx(ctx)
|
||||
blog_hdr = _blog_header_sx(ctx)
|
||||
root_hdr = await root_header_sx(ctx)
|
||||
blog_hdr = await _blog_header_sx(ctx)
|
||||
rows = "(<> " + root_hdr + " " + blog_hdr + ")"
|
||||
return oob_header_sx("root-header-child", "blog-header-child", rows)
|
||||
return await oob_header_sx("root-header-child", "blog-header-child", rows)
|
||||
|
||||
|
||||
# --- Settings layout (root + settings header) ---
|
||||
|
||||
def _settings_full(ctx: dict, **kw: Any) -> str:
|
||||
async def _settings_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import root_header_sx
|
||||
from sx.sx_components import _settings_header_sx
|
||||
root_hdr = root_header_sx(ctx)
|
||||
settings_hdr = _settings_header_sx(ctx)
|
||||
root_hdr = await root_header_sx(ctx)
|
||||
settings_hdr = await _settings_header_sx(ctx)
|
||||
return "(<> " + root_hdr + " " + settings_hdr + ")"
|
||||
|
||||
|
||||
def _settings_oob(ctx: dict, **kw: Any) -> str:
|
||||
async def _settings_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import root_header_sx, oob_header_sx
|
||||
from sx.sx_components import _settings_header_sx
|
||||
root_hdr = root_header_sx(ctx)
|
||||
settings_hdr = _settings_header_sx(ctx)
|
||||
root_hdr = await root_header_sx(ctx)
|
||||
settings_hdr = await _settings_header_sx(ctx)
|
||||
rows = "(<> " + root_hdr + " " + settings_hdr + ")"
|
||||
return oob_header_sx("root-header-child", "root-settings-header-child", rows)
|
||||
return await oob_header_sx("root-header-child", "root-settings-header-child", rows)
|
||||
|
||||
|
||||
def _settings_mobile(ctx: dict, **kw: Any) -> str:
|
||||
async def _settings_mobile(ctx: dict, **kw: Any) -> str:
|
||||
from sx.sx_components import _settings_nav_sx
|
||||
return _settings_nav_sx(ctx)
|
||||
return await _settings_nav_sx(ctx)
|
||||
|
||||
|
||||
# --- Sub-settings helpers ---
|
||||
|
||||
def _sub_settings_full(ctx: dict, row_id: str, child_id: str,
|
||||
async def _sub_settings_full(ctx: dict, row_id: str, child_id: str,
|
||||
endpoint: str, icon: str, label: str) -> str:
|
||||
from shared.sx.helpers import root_header_sx
|
||||
from sx.sx_components import _settings_header_sx, _sub_settings_header_sx
|
||||
from quart import url_for as qurl
|
||||
root_hdr = root_header_sx(ctx)
|
||||
settings_hdr = _settings_header_sx(ctx)
|
||||
sub_hdr = _sub_settings_header_sx(row_id, child_id,
|
||||
root_hdr = await root_header_sx(ctx)
|
||||
settings_hdr = await _settings_header_sx(ctx)
|
||||
sub_hdr = await _sub_settings_header_sx(row_id, child_id,
|
||||
qurl(endpoint), icon, label, ctx)
|
||||
return "(<> " + root_hdr + " " + settings_hdr + " " + sub_hdr + ")"
|
||||
|
||||
|
||||
def _sub_settings_oob(ctx: dict, row_id: str, child_id: str,
|
||||
async def _sub_settings_oob(ctx: dict, row_id: str, child_id: str,
|
||||
endpoint: str, icon: str, label: str) -> str:
|
||||
from shared.sx.helpers import oob_header_sx
|
||||
from sx.sx_components import _settings_header_sx, _sub_settings_header_sx
|
||||
from quart import url_for as qurl
|
||||
settings_hdr_oob = _settings_header_sx(ctx, oob=True)
|
||||
sub_hdr = _sub_settings_header_sx(row_id, child_id,
|
||||
settings_hdr_oob = await _settings_header_sx(ctx, oob=True)
|
||||
sub_hdr = await _sub_settings_header_sx(row_id, child_id,
|
||||
qurl(endpoint), icon, label, ctx)
|
||||
sub_oob = oob_header_sx("root-settings-header-child", child_id, sub_hdr)
|
||||
sub_oob = await oob_header_sx("root-settings-header-child", child_id, sub_hdr)
|
||||
return "(<> " + settings_hdr_oob + " " + sub_oob + ")"
|
||||
|
||||
|
||||
# --- Cache ---
|
||||
|
||||
def _cache_full(ctx: dict, **kw: Any) -> str:
|
||||
return _sub_settings_full(ctx, "cache-row", "cache-header-child",
|
||||
async def _cache_full(ctx: dict, **kw: Any) -> str:
|
||||
return await _sub_settings_full(ctx, "cache-row", "cache-header-child",
|
||||
"defpage_cache_page", "refresh", "Cache")
|
||||
|
||||
|
||||
def _cache_oob(ctx: dict, **kw: Any) -> str:
|
||||
return _sub_settings_oob(ctx, "cache-row", "cache-header-child",
|
||||
async def _cache_oob(ctx: dict, **kw: Any) -> str:
|
||||
return await _sub_settings_oob(ctx, "cache-row", "cache-header-child",
|
||||
"defpage_cache_page", "refresh", "Cache")
|
||||
|
||||
|
||||
# --- Snippets ---
|
||||
|
||||
def _snippets_full(ctx: dict, **kw: Any) -> str:
|
||||
return _sub_settings_full(ctx, "snippets-row", "snippets-header-child",
|
||||
async def _snippets_full(ctx: dict, **kw: Any) -> str:
|
||||
return await _sub_settings_full(ctx, "snippets-row", "snippets-header-child",
|
||||
"defpage_snippets_page", "puzzle-piece", "Snippets")
|
||||
|
||||
|
||||
def _snippets_oob(ctx: dict, **kw: Any) -> str:
|
||||
return _sub_settings_oob(ctx, "snippets-row", "snippets-header-child",
|
||||
async def _snippets_oob(ctx: dict, **kw: Any) -> str:
|
||||
return await _sub_settings_oob(ctx, "snippets-row", "snippets-header-child",
|
||||
"defpage_snippets_page", "puzzle-piece", "Snippets")
|
||||
|
||||
|
||||
# --- Menu Items ---
|
||||
|
||||
def _menu_items_full(ctx: dict, **kw: Any) -> str:
|
||||
return _sub_settings_full(ctx, "menu_items-row", "menu_items-header-child",
|
||||
async def _menu_items_full(ctx: dict, **kw: Any) -> str:
|
||||
return await _sub_settings_full(ctx, "menu_items-row", "menu_items-header-child",
|
||||
"defpage_menu_items_page", "bars", "Menu Items")
|
||||
|
||||
|
||||
def _menu_items_oob(ctx: dict, **kw: Any) -> str:
|
||||
return _sub_settings_oob(ctx, "menu_items-row", "menu_items-header-child",
|
||||
async def _menu_items_oob(ctx: dict, **kw: Any) -> str:
|
||||
return await _sub_settings_oob(ctx, "menu_items-row", "menu_items-header-child",
|
||||
"defpage_menu_items_page", "bars", "Menu Items")
|
||||
|
||||
|
||||
# --- Tag Groups ---
|
||||
|
||||
def _tag_groups_full(ctx: dict, **kw: Any) -> str:
|
||||
return _sub_settings_full(ctx, "tag-groups-row", "tag-groups-header-child",
|
||||
async def _tag_groups_full(ctx: dict, **kw: Any) -> str:
|
||||
return await _sub_settings_full(ctx, "tag-groups-row", "tag-groups-header-child",
|
||||
"defpage_tag_groups_page", "tags", "Tag Groups")
|
||||
|
||||
|
||||
def _tag_groups_oob(ctx: dict, **kw: Any) -> str:
|
||||
return _sub_settings_oob(ctx, "tag-groups-row", "tag-groups-header-child",
|
||||
async def _tag_groups_oob(ctx: dict, **kw: Any) -> str:
|
||||
return await _sub_settings_oob(ctx, "tag-groups-row", "tag-groups-header-child",
|
||||
"defpage_tag_groups_page", "tags", "Tag Groups")
|
||||
|
||||
|
||||
# --- Tag Group Edit ---
|
||||
|
||||
def _tag_group_edit_full(ctx: dict, **kw: Any) -> str:
|
||||
async def _tag_group_edit_full(ctx: dict, **kw: Any) -> str:
|
||||
from quart import request
|
||||
g_id = (request.view_args or {}).get("id")
|
||||
from quart import url_for as qurl
|
||||
from shared.sx.helpers import root_header_sx
|
||||
from sx.sx_components import _settings_header_sx, _sub_settings_header_sx
|
||||
root_hdr = root_header_sx(ctx)
|
||||
settings_hdr = _settings_header_sx(ctx)
|
||||
sub_hdr = _sub_settings_header_sx("tag-groups-row", "tag-groups-header-child",
|
||||
root_hdr = await root_header_sx(ctx)
|
||||
settings_hdr = await _settings_header_sx(ctx)
|
||||
sub_hdr = await _sub_settings_header_sx("tag-groups-row", "tag-groups-header-child",
|
||||
qurl("defpage_tag_group_edit", id=g_id),
|
||||
"tags", "Tag Groups", ctx)
|
||||
return "(<> " + root_hdr + " " + settings_hdr + " " + sub_hdr + ")"
|
||||
|
||||
|
||||
def _tag_group_edit_oob(ctx: dict, **kw: Any) -> str:
|
||||
async def _tag_group_edit_oob(ctx: dict, **kw: Any) -> str:
|
||||
from quart import request
|
||||
g_id = (request.view_args or {}).get("id")
|
||||
from quart import url_for as qurl
|
||||
from shared.sx.helpers import oob_header_sx
|
||||
from sx.sx_components import _settings_header_sx, _sub_settings_header_sx
|
||||
settings_hdr_oob = _settings_header_sx(ctx, oob=True)
|
||||
sub_hdr = _sub_settings_header_sx("tag-groups-row", "tag-groups-header-child",
|
||||
settings_hdr_oob = await _settings_header_sx(ctx, oob=True)
|
||||
sub_hdr = await _sub_settings_header_sx("tag-groups-row", "tag-groups-header-child",
|
||||
qurl("defpage_tag_group_edit", id=g_id),
|
||||
"tags", "Tag Groups", ctx)
|
||||
sub_oob = oob_header_sx("root-settings-header-child", "tag-groups-header-child", sub_hdr)
|
||||
sub_oob = await oob_header_sx("root-settings-header-child", "tag-groups-header-child", sub_hdr)
|
||||
return "(<> " + settings_hdr_oob + " " + sub_oob + ")"
|
||||
|
||||
|
||||
@@ -302,12 +302,12 @@ def _register_blog_helpers() -> None:
|
||||
|
||||
async def _h_editor_content(**kw):
|
||||
from sx.sx_components import render_editor_panel
|
||||
return render_editor_panel()
|
||||
return await render_editor_panel()
|
||||
|
||||
|
||||
async def _h_editor_page_content(**kw):
|
||||
from sx.sx_components import render_editor_panel
|
||||
return render_editor_panel(is_page=True)
|
||||
return await render_editor_panel(is_page=True)
|
||||
|
||||
|
||||
# --- Post admin helpers ---
|
||||
@@ -391,7 +391,7 @@ async def _h_post_preview_content(slug=None, **kw):
|
||||
from sx.sx_components import _preview_main_panel_sx
|
||||
tctx = await get_template_context()
|
||||
tctx.update(preview_ctx)
|
||||
return _preview_main_panel_sx(tctx)
|
||||
return await _preview_main_panel_sx(tctx)
|
||||
|
||||
|
||||
async def _h_post_entries_content(slug=None, **kw):
|
||||
@@ -415,7 +415,7 @@ async def _h_post_entries_content(slug=None, **kw):
|
||||
tctx = await get_template_context()
|
||||
tctx["all_calendars"] = all_calendars
|
||||
tctx["associated_entry_ids"] = associated_entry_ids
|
||||
return _post_entries_content_sx(tctx)
|
||||
return await _post_entries_content_sx(tctx)
|
||||
|
||||
|
||||
async def _h_post_settings_content(slug=None, **kw):
|
||||
@@ -468,7 +468,7 @@ async def _h_post_edit_content(slug=None, **kw):
|
||||
tctx["save_success"] = save_success
|
||||
tctx["save_error"] = save_error
|
||||
tctx["newsletters"] = newsletters
|
||||
return _post_edit_content_sx(tctx)
|
||||
return await _post_edit_content_sx(tctx)
|
||||
|
||||
|
||||
# --- Settings helpers ---
|
||||
@@ -484,7 +484,7 @@ async def _h_cache_content(**kw):
|
||||
from shared.sx.page import get_template_context
|
||||
from sx.sx_components import _cache_main_panel_sx
|
||||
tctx = await get_template_context()
|
||||
return _cache_main_panel_sx(tctx)
|
||||
return await _cache_main_panel_sx(tctx)
|
||||
|
||||
|
||||
# --- Snippets helper ---
|
||||
@@ -506,7 +506,7 @@ async def _h_snippets_content(**kw):
|
||||
tctx = await get_template_context()
|
||||
tctx["snippets"] = rows
|
||||
tctx["is_admin"] = is_admin
|
||||
return _snippets_main_panel_sx(tctx)
|
||||
return await _snippets_main_panel_sx(tctx)
|
||||
|
||||
|
||||
# --- Menu Items helper ---
|
||||
@@ -519,7 +519,7 @@ async def _h_menu_items_content(**kw):
|
||||
from sx.sx_components import _menu_items_main_panel_sx
|
||||
tctx = await get_template_context()
|
||||
tctx["menu_items"] = menu_items
|
||||
return _menu_items_main_panel_sx(tctx)
|
||||
return await _menu_items_main_panel_sx(tctx)
|
||||
|
||||
|
||||
# --- Tag Groups helpers ---
|
||||
@@ -539,7 +539,7 @@ async def _h_tag_groups_content(**kw):
|
||||
from sx.sx_components import _tag_groups_main_panel_sx
|
||||
tctx = await get_template_context()
|
||||
tctx.update({"groups": groups, "unassigned_tags": unassigned})
|
||||
return _tag_groups_main_panel_sx(tctx)
|
||||
return await _tag_groups_main_panel_sx(tctx)
|
||||
|
||||
|
||||
async def _h_tag_group_edit_content(id=None, **kw):
|
||||
@@ -571,4 +571,4 @@ async def _h_tag_group_edit_content(id=None, **kw):
|
||||
"all_tags": all_tags,
|
||||
"assigned_tag_ids": set(assigned_rows),
|
||||
})
|
||||
return _tag_groups_edit_main_panel_sx(tctx)
|
||||
return await _tag_groups_edit_main_panel_sx(tctx)
|
||||
|
||||
@@ -49,7 +49,7 @@ def register():
|
||||
from shared.sx.page import get_template_context
|
||||
from sx.sx_components import render_cart_payments_panel
|
||||
ctx = await get_template_context()
|
||||
html = render_cart_payments_panel(ctx)
|
||||
html = await render_cart_payments_panel(ctx)
|
||||
return sx_response(html)
|
||||
|
||||
return bp
|
||||
|
||||
@@ -16,9 +16,9 @@ from shared.sx.helpers import (
|
||||
post_header_sx as _shared_post_header_sx,
|
||||
search_desktop_sx, search_mobile_sx,
|
||||
full_page_sx, oob_page_sx, header_child_sx,
|
||||
sx_call, SxExpr,
|
||||
render_to_sx,
|
||||
)
|
||||
from shared.sx.parser import serialize
|
||||
from shared.sx.parser import SxExpr
|
||||
from shared.infrastructure.urls import cart_url
|
||||
|
||||
# Load cart-specific .sx components + handlers at import time
|
||||
@@ -69,12 +69,12 @@ async def _post_header_sx(ctx: dict, page_post: Any, *, oob: bool = False) -> st
|
||||
"""Build post-level header row from page_post DTO, using shared helper."""
|
||||
ctx = _ensure_post_ctx(ctx, page_post)
|
||||
ctx = await _ensure_container_nav(ctx)
|
||||
return _shared_post_header_sx(ctx, oob=oob)
|
||||
return await _shared_post_header_sx(ctx, oob=oob)
|
||||
|
||||
|
||||
def _cart_header_sx(ctx: dict, *, oob: bool = False) -> str:
|
||||
async def _cart_header_sx(ctx: dict, *, oob: bool = False) -> str:
|
||||
"""Build the cart section header row."""
|
||||
return sx_call(
|
||||
return await render_to_sx(
|
||||
"menu-row-sx",
|
||||
id="cart-row", level=1, colour="sky",
|
||||
link_href=call_url(ctx, "cart_url", "/"),
|
||||
@@ -83,17 +83,17 @@ def _cart_header_sx(ctx: dict, *, oob: bool = False) -> str:
|
||||
)
|
||||
|
||||
|
||||
def _page_cart_header_sx(ctx: dict, page_post: Any, *, oob: bool = False) -> str:
|
||||
async def _page_cart_header_sx(ctx: dict, page_post: Any, *, oob: bool = False) -> str:
|
||||
"""Build the per-page cart header row."""
|
||||
slug = page_post.slug if page_post else ""
|
||||
title = ((page_post.title if page_post else None) or "")[:160]
|
||||
label_parts = []
|
||||
if page_post and page_post.feature_image:
|
||||
label_parts.append(sx_call("cart-page-label-img", src=page_post.feature_image))
|
||||
label_parts.append(await render_to_sx("cart-page-label-img", src=page_post.feature_image))
|
||||
label_parts.append(f'(span "{escape(title)}")')
|
||||
label_sx = "(<> " + " ".join(label_parts) + ")"
|
||||
nav_sx = sx_call("cart-all-carts-link", href=call_url(ctx, "cart_url", "/"))
|
||||
return sx_call(
|
||||
nav_sx = await render_to_sx("cart-all-carts-link", href=call_url(ctx, "cart_url", "/"))
|
||||
return await render_to_sx(
|
||||
"menu-row-sx",
|
||||
id="page-cart-row", level=2, colour="sky",
|
||||
link_href=call_url(ctx, "cart_url", f"/{slug}/"),
|
||||
@@ -102,26 +102,26 @@ def _page_cart_header_sx(ctx: dict, page_post: Any, *, oob: bool = False) -> str
|
||||
)
|
||||
|
||||
|
||||
def _auth_header_sx(ctx: dict, *, oob: bool = False) -> str:
|
||||
async def _auth_header_sx(ctx: dict, *, oob: bool = False) -> str:
|
||||
"""Build the account section header row (for orders)."""
|
||||
return sx_call(
|
||||
return await render_to_sx(
|
||||
"auth-header-row-simple",
|
||||
account_url=call_url(ctx, "account_url", ""),
|
||||
oob=oob,
|
||||
)
|
||||
|
||||
|
||||
def _orders_header_sx(ctx: dict, list_url: str) -> str:
|
||||
async def _orders_header_sx(ctx: dict, list_url: str) -> str:
|
||||
"""Build the orders section header row."""
|
||||
return sx_call("orders-header-row", list_url=list_url)
|
||||
return await render_to_sx("orders-header-row", list_url=list_url)
|
||||
|
||||
|
||||
def _cart_page_admin_header_sx(ctx: dict, page_post: Any, *, oob: bool = False,
|
||||
async def _cart_page_admin_header_sx(ctx: dict, page_post: Any, *, oob: bool = False,
|
||||
selected: str = "") -> str:
|
||||
"""Build the page-level admin header row."""
|
||||
slug = page_post.slug if page_post else ""
|
||||
ctx = _ensure_post_ctx(ctx, page_post)
|
||||
return post_admin_header_sx(ctx, slug, oob=oob, selected=selected)
|
||||
return await post_admin_header_sx(ctx, slug, oob=oob, selected=selected)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -190,24 +190,25 @@ async def render_orders_page(ctx: dict, orders: list, page: int,
|
||||
detail_url_prefix = pfx + url_for_fn("orders.order.order_detail", order_id=0).rsplit("0/", 1)[0]
|
||||
|
||||
order_dicts = [_serialize_order(o) for o in orders]
|
||||
content = sx_call("orders-list-content",
|
||||
orders=SxExpr(serialize(order_dicts)),
|
||||
content = await render_to_sx("orders-list-content",
|
||||
orders=order_dicts,
|
||||
page=page, total_pages=total_pages,
|
||||
rows_url=rows_url, detail_url_prefix=detail_url_prefix)
|
||||
|
||||
hdr = root_header_sx(ctx)
|
||||
auth = _auth_header_sx(ctx)
|
||||
orders_hdr = _orders_header_sx(ctx, list_url)
|
||||
auth_child = sx_call(
|
||||
hdr = await root_header_sx(ctx)
|
||||
auth = await _auth_header_sx(ctx)
|
||||
orders_hdr = await _orders_header_sx(ctx, list_url)
|
||||
auth_child_inner = await render_to_sx("header-child-sx", id="auth-header-child", inner=SxExpr(orders_hdr))
|
||||
auth_child = await render_to_sx(
|
||||
"header-child-sx",
|
||||
inner=SxExpr("(<> " + auth + " " + sx_call("header-child-sx", id="auth-header-child", inner=SxExpr(orders_hdr)) + ")"),
|
||||
inner=SxExpr("(<> " + auth + " " + auth_child_inner + ")"),
|
||||
)
|
||||
header_rows = "(<> " + hdr + " " + auth_child + ")"
|
||||
|
||||
filt = sx_call("order-list-header", search_mobile=SxExpr(search_mobile_sx(ctx)))
|
||||
return full_page_sx(ctx, header_rows=header_rows,
|
||||
filt = await render_to_sx("order-list-header", search_mobile=SxExpr(await search_mobile_sx(ctx)))
|
||||
return await full_page_sx(ctx, header_rows=header_rows,
|
||||
filter=filt,
|
||||
aside=search_desktop_sx(ctx),
|
||||
aside=await search_desktop_sx(ctx),
|
||||
content=content)
|
||||
|
||||
|
||||
@@ -222,20 +223,21 @@ async def render_orders_rows(ctx: dict, orders: list, page: int,
|
||||
detail_url_prefix = pfx + url_for_fn("orders.order.order_detail", order_id=0).rsplit("0/", 1)[0]
|
||||
|
||||
order_dicts = [_serialize_order(o) for o in orders]
|
||||
parts = [sx_call("order-row-pair",
|
||||
order=SxExpr(serialize(od)),
|
||||
detail_url_prefix=detail_url_prefix)
|
||||
for od in order_dicts]
|
||||
parts = []
|
||||
for od in order_dicts:
|
||||
parts.append(await render_to_sx("order-row-pair",
|
||||
order=od,
|
||||
detail_url_prefix=detail_url_prefix))
|
||||
|
||||
if page < total_pages:
|
||||
next_url = list_url + qs_fn(page=page + 1)
|
||||
parts.append(sx_call(
|
||||
parts.append(await render_to_sx(
|
||||
"infinite-scroll",
|
||||
url=next_url, page=page, total_pages=total_pages,
|
||||
id_prefix="orders", colspan=5,
|
||||
))
|
||||
else:
|
||||
parts.append(sx_call("order-end-row"))
|
||||
parts.append(await render_to_sx("order-end-row"))
|
||||
|
||||
return "(<> " + " ".join(parts) + ")"
|
||||
|
||||
@@ -255,24 +257,25 @@ async def render_orders_oob(ctx: dict, orders: list, page: int,
|
||||
detail_url_prefix = pfx + url_for_fn("orders.order.order_detail", order_id=0).rsplit("0/", 1)[0]
|
||||
|
||||
order_dicts = [_serialize_order(o) for o in orders]
|
||||
content = sx_call("orders-list-content",
|
||||
orders=SxExpr(serialize(order_dicts)),
|
||||
content = await render_to_sx("orders-list-content",
|
||||
orders=order_dicts,
|
||||
page=page, total_pages=total_pages,
|
||||
rows_url=rows_url, detail_url_prefix=detail_url_prefix)
|
||||
|
||||
auth_oob = _auth_header_sx(ctx, oob=True)
|
||||
auth_child_oob = sx_call(
|
||||
auth_oob = await _auth_header_sx(ctx, oob=True)
|
||||
orders_hdr = await _orders_header_sx(ctx, list_url)
|
||||
auth_child_oob = await render_to_sx(
|
||||
"oob-header-sx",
|
||||
parent_id="auth-header-child",
|
||||
row=SxExpr(_orders_header_sx(ctx, list_url)),
|
||||
row=SxExpr(orders_hdr),
|
||||
)
|
||||
root_oob = root_header_sx(ctx, oob=True)
|
||||
root_oob = await root_header_sx(ctx, oob=True)
|
||||
oobs = "(<> " + auth_oob + " " + auth_child_oob + " " + root_oob + ")"
|
||||
|
||||
filt = sx_call("order-list-header", search_mobile=SxExpr(search_mobile_sx(ctx)))
|
||||
return oob_page_sx(oobs=oobs,
|
||||
filt = await render_to_sx("order-list-header", search_mobile=SxExpr(await search_mobile_sx(ctx)))
|
||||
return await oob_page_sx(oobs=oobs,
|
||||
filter=filt,
|
||||
aside=search_desktop_sx(ctx),
|
||||
aside=await search_desktop_sx(ctx),
|
||||
content=content)
|
||||
|
||||
|
||||
@@ -296,29 +299,32 @@ async def render_order_page(ctx: dict, order: Any,
|
||||
order_data = _serialize_order(order)
|
||||
cal_data = [_serialize_calendar_entry(e) for e in (calendar_entries or [])]
|
||||
|
||||
main = sx_call("order-detail-content",
|
||||
order=SxExpr(serialize(order_data)),
|
||||
calendar_entries=SxExpr(serialize(cal_data)))
|
||||
filt = sx_call("order-detail-filter-content",
|
||||
order=SxExpr(serialize(order_data)),
|
||||
main = await render_to_sx("order-detail-content",
|
||||
order=order_data,
|
||||
calendar_entries=cal_data)
|
||||
filt = await render_to_sx("order-detail-filter-content",
|
||||
order=order_data,
|
||||
list_url=list_url, recheck_url=recheck_url,
|
||||
pay_url=pay_url, csrf=generate_csrf_token())
|
||||
|
||||
hdr = root_header_sx(ctx)
|
||||
order_row = sx_call(
|
||||
hdr = await root_header_sx(ctx)
|
||||
order_row = await render_to_sx(
|
||||
"menu-row-sx",
|
||||
id="order-row", level=3, colour="sky",
|
||||
link_href=detail_url, link_label=f"Order {order.id}", icon="fa fa-gbp",
|
||||
)
|
||||
order_child = sx_call(
|
||||
auth = await _auth_header_sx(ctx)
|
||||
orders_hdr = await _orders_header_sx(ctx, list_url)
|
||||
orders_child = await render_to_sx("header-child-sx", id="orders-header-child", inner=SxExpr(order_row))
|
||||
auth_inner = "(<> " + orders_hdr + " " + orders_child + ")"
|
||||
auth_child = await render_to_sx("header-child-sx", id="auth-header-child", inner=SxExpr(auth_inner))
|
||||
order_child = await render_to_sx(
|
||||
"header-child-sx",
|
||||
inner=SxExpr("(<> " + _auth_header_sx(ctx) + " " + sx_call("header-child-sx", id="auth-header-child", inner=SxExpr(
|
||||
"(<> " + _orders_header_sx(ctx, list_url) + " " + sx_call("header-child-sx", id="orders-header-child", inner=SxExpr(order_row)) + ")"
|
||||
)) + ")"),
|
||||
inner=SxExpr("(<> " + auth + " " + auth_child + ")"),
|
||||
)
|
||||
header_rows = "(<> " + hdr + " " + order_child + ")"
|
||||
|
||||
return full_page_sx(ctx, header_rows=header_rows, filter=filt, content=main)
|
||||
return await full_page_sx(ctx, header_rows=header_rows, filter=filt, content=main)
|
||||
|
||||
|
||||
async def render_order_oob(ctx: dict, order: Any,
|
||||
@@ -337,27 +343,27 @@ async def render_order_oob(ctx: dict, order: Any,
|
||||
order_data = _serialize_order(order)
|
||||
cal_data = [_serialize_calendar_entry(e) for e in (calendar_entries or [])]
|
||||
|
||||
main = sx_call("order-detail-content",
|
||||
order=SxExpr(serialize(order_data)),
|
||||
calendar_entries=SxExpr(serialize(cal_data)))
|
||||
filt = sx_call("order-detail-filter-content",
|
||||
order=SxExpr(serialize(order_data)),
|
||||
main = await render_to_sx("order-detail-content",
|
||||
order=order_data,
|
||||
calendar_entries=cal_data)
|
||||
filt = await render_to_sx("order-detail-filter-content",
|
||||
order=order_data,
|
||||
list_url=list_url, recheck_url=recheck_url,
|
||||
pay_url=pay_url, csrf=generate_csrf_token())
|
||||
|
||||
order_row_oob = sx_call(
|
||||
order_row_oob = await render_to_sx(
|
||||
"menu-row-sx",
|
||||
id="order-row", level=3, colour="sky",
|
||||
link_href=detail_url, link_label=f"Order {order.id}", icon="fa fa-gbp",
|
||||
oob=True,
|
||||
)
|
||||
orders_child_oob = sx_call("oob-header-sx",
|
||||
orders_child_oob = await render_to_sx("oob-header-sx",
|
||||
parent_id="orders-header-child",
|
||||
row=SxExpr(order_row_oob))
|
||||
root_oob = root_header_sx(ctx, oob=True)
|
||||
root_oob = await root_header_sx(ctx, oob=True)
|
||||
oobs = "(<> " + orders_child_oob + " " + root_oob + ")"
|
||||
|
||||
return oob_page_sx(oobs=oobs, filter=filt, content=main)
|
||||
return await oob_page_sx(oobs=oobs, filter=filt, content=main)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -370,25 +376,25 @@ async def render_checkout_error_page(ctx: dict, error: str | None = None,
|
||||
err_msg = error or "Unexpected error while creating the hosted checkout session."
|
||||
order_sx = None
|
||||
if order:
|
||||
order_sx = sx_call("checkout-error-order-id", oid=f"#{order.id}")
|
||||
order_sx = await render_to_sx("checkout-error-order-id", oid=f"#{order.id}")
|
||||
back_url = cart_url("/")
|
||||
|
||||
hdr = root_header_sx(ctx)
|
||||
filt = sx_call("checkout-error-header")
|
||||
content = sx_call(
|
||||
hdr = await root_header_sx(ctx)
|
||||
filt = await render_to_sx("checkout-error-header")
|
||||
content = await render_to_sx(
|
||||
"checkout-error-content",
|
||||
msg=err_msg,
|
||||
order=SxExpr(order_sx) if order_sx else None,
|
||||
back_url=back_url,
|
||||
)
|
||||
return full_page_sx(ctx, header_rows=hdr, filter=filt, content=content)
|
||||
return await full_page_sx(ctx, header_rows=hdr, filter=filt, content=content)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Public API: POST response renderers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def render_cart_payments_panel(ctx: dict) -> str:
|
||||
async def render_cart_payments_panel(ctx: dict) -> str:
|
||||
"""Render the payments config panel for PUT response."""
|
||||
page_config = ctx.get("page_config")
|
||||
pc_data = None
|
||||
@@ -398,5 +404,5 @@ def render_cart_payments_panel(ctx: dict) -> str:
|
||||
"sumup_merchant_code": getattr(page_config, "sumup_merchant_code", None) or "",
|
||||
"sumup_checkout_prefix": getattr(page_config, "sumup_checkout_prefix", None) or "",
|
||||
}
|
||||
return sx_call("cart-payments-content",
|
||||
page_config=SxExpr(serialize(pc_data)) if pc_data else None)
|
||||
return await render_to_sx("cart-payments-content",
|
||||
page_config=pc_data)
|
||||
|
||||
@@ -27,31 +27,35 @@ def _register_cart_layouts() -> None:
|
||||
register_custom_layout("cart-admin", _cart_admin_full, _cart_admin_oob)
|
||||
|
||||
|
||||
def _cart_page_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import root_header_sx, sx_call, SxExpr
|
||||
async def _cart_page_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import root_header_sx, render_to_sx
|
||||
from shared.sx.parser import SxExpr
|
||||
from sx.sx_components import _cart_header_sx, _page_cart_header_sx
|
||||
|
||||
page_post = ctx.get("page_post")
|
||||
root_hdr = root_header_sx(ctx)
|
||||
child = _cart_header_sx(ctx)
|
||||
page_hdr = _page_cart_header_sx(ctx, page_post)
|
||||
nested = sx_call(
|
||||
root_hdr = await root_header_sx(ctx)
|
||||
child = await _cart_header_sx(ctx)
|
||||
page_hdr = await _page_cart_header_sx(ctx, page_post)
|
||||
inner_child = await render_to_sx("header-child-sx", id="cart-header-child", inner=SxExpr(page_hdr))
|
||||
nested = await render_to_sx(
|
||||
"header-child-sx",
|
||||
inner=SxExpr("(<> " + child + " " + sx_call("header-child-sx", id="cart-header-child", inner=SxExpr(page_hdr)) + ")"),
|
||||
inner=SxExpr("(<> " + child + " " + inner_child + ")"),
|
||||
)
|
||||
return "(<> " + root_hdr + " " + nested + ")"
|
||||
|
||||
|
||||
def _cart_page_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import root_header_sx, sx_call, SxExpr
|
||||
async def _cart_page_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import root_header_sx, render_to_sx
|
||||
from shared.sx.parser import SxExpr
|
||||
from sx.sx_components import _cart_header_sx, _page_cart_header_sx
|
||||
|
||||
page_post = ctx.get("page_post")
|
||||
child_oob = sx_call("oob-header-sx",
|
||||
page_hdr = await _page_cart_header_sx(ctx, page_post)
|
||||
child_oob = await render_to_sx("oob-header-sx",
|
||||
parent_id="cart-header-child",
|
||||
row=SxExpr(_page_cart_header_sx(ctx, page_post)))
|
||||
cart_hdr_oob = _cart_header_sx(ctx, oob=True)
|
||||
root_hdr_oob = root_header_sx(ctx, oob=True)
|
||||
row=SxExpr(page_hdr))
|
||||
cart_hdr_oob = await _cart_header_sx(ctx, oob=True)
|
||||
root_hdr_oob = await root_header_sx(ctx, oob=True)
|
||||
return "(<> " + child_oob + " " + cart_hdr_oob + " " + root_hdr_oob + ")"
|
||||
|
||||
|
||||
@@ -61,9 +65,9 @@ async def _cart_admin_full(ctx: dict, **kw: Any) -> str:
|
||||
|
||||
page_post = ctx.get("page_post")
|
||||
selected = kw.get("selected", "")
|
||||
root_hdr = root_header_sx(ctx)
|
||||
root_hdr = await root_header_sx(ctx)
|
||||
post_hdr = await _post_header_sx(ctx, page_post)
|
||||
admin_hdr = _cart_page_admin_header_sx(ctx, page_post, selected=selected)
|
||||
admin_hdr = await _cart_page_admin_header_sx(ctx, page_post, selected=selected)
|
||||
return "(<> " + root_hdr + " " + post_hdr + " " + admin_hdr + ")"
|
||||
|
||||
|
||||
@@ -72,7 +76,7 @@ async def _cart_admin_oob(ctx: dict, **kw: Any) -> str:
|
||||
|
||||
page_post = ctx.get("page_post")
|
||||
selected = kw.get("selected", "")
|
||||
return _cart_page_admin_header_sx(ctx, page_post, oob=True, selected=selected)
|
||||
return await _cart_page_admin_header_sx(ctx, page_post, oob=True, selected=selected)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -244,22 +248,21 @@ def _build_summary_data(ctx: dict, cart: list, cal_entries: list, tickets: list,
|
||||
|
||||
async def _h_overview_content(**kw):
|
||||
from quart import g
|
||||
from shared.sx.helpers import sx_call, SxExpr
|
||||
from shared.sx.parser import serialize
|
||||
from shared.sx.helpers import render_to_sx
|
||||
from shared.infrastructure.urls import cart_url
|
||||
from bp.cart.services import get_cart_grouped_by_page
|
||||
|
||||
page_groups = await get_cart_grouped_by_page(g.s)
|
||||
grp_dicts = [d for d in (_serialize_page_group(grp) for grp in page_groups) if d]
|
||||
return sx_call("cart-overview-content",
|
||||
page_groups=SxExpr(serialize(grp_dicts)),
|
||||
return await render_to_sx("cart-overview-content",
|
||||
page_groups=grp_dicts,
|
||||
cart_url_base=cart_url(""))
|
||||
|
||||
|
||||
async def _h_page_cart_content(page_slug=None, **kw):
|
||||
from quart import g
|
||||
from shared.sx.helpers import sx_call, SxExpr
|
||||
from shared.sx.parser import serialize
|
||||
from shared.sx.helpers import render_to_sx
|
||||
from shared.sx.parser import SxExpr
|
||||
from shared.sx.page import get_template_context
|
||||
from bp.cart.services import total, calendar_total, ticket_total
|
||||
from bp.cart.services.page_cart import (
|
||||
@@ -277,7 +280,7 @@ async def _h_page_cart_content(page_slug=None, **kw):
|
||||
sd = _build_summary_data(ctx, cart, cal_entries, page_tickets,
|
||||
total, calendar_total, ticket_total)
|
||||
|
||||
summary_sx = sx_call("cart-summary-from-data",
|
||||
summary_sx = await render_to_sx("cart-summary-from-data",
|
||||
item_count=sd["item_count"],
|
||||
grand_total=sd["grand_total"],
|
||||
symbol=sd["symbol"],
|
||||
@@ -286,10 +289,10 @@ async def _h_page_cart_content(page_slug=None, **kw):
|
||||
login_href=sd.get("login_href"),
|
||||
user_email=sd.get("user_email"))
|
||||
|
||||
return sx_call("cart-page-cart-content",
|
||||
cart_items=SxExpr(serialize([_serialize_cart_item(i) for i in cart])),
|
||||
cal_entries=SxExpr(serialize([_serialize_cal_entry(e) for e in cal_entries])),
|
||||
ticket_groups=SxExpr(serialize([_serialize_ticket_group(tg) for tg in ticket_groups])),
|
||||
return await render_to_sx("cart-page-cart-content",
|
||||
cart_items=[_serialize_cart_item(i) for i in cart],
|
||||
cal_entries=[_serialize_cal_entry(e) for e in cal_entries],
|
||||
ticket_groups=[_serialize_ticket_group(tg) for tg in ticket_groups],
|
||||
summary=SxExpr(summary_sx))
|
||||
|
||||
|
||||
@@ -299,8 +302,7 @@ async def _h_cart_admin_content(page_slug=None, **kw):
|
||||
|
||||
async def _h_cart_payments_content(page_slug=None, **kw):
|
||||
from shared.sx.page import get_template_context
|
||||
from shared.sx.helpers import sx_call, SxExpr
|
||||
from shared.sx.parser import serialize
|
||||
from shared.sx.helpers import render_to_sx
|
||||
|
||||
ctx = await get_template_context()
|
||||
page_config = ctx.get("page_config")
|
||||
@@ -311,5 +313,5 @@ async def _h_cart_payments_content(page_slug=None, **kw):
|
||||
"sumup_merchant_code": getattr(page_config, "sumup_merchant_code", None) or "",
|
||||
"sumup_checkout_prefix": getattr(page_config, "sumup_checkout_prefix", None) or "",
|
||||
}
|
||||
return sx_call("cart-payments-content",
|
||||
page_config=SxExpr(serialize(pc_data)) if pc_data else None)
|
||||
return await render_to_sx("cart-payments-content",
|
||||
page_config=pc_data)
|
||||
|
||||
@@ -126,7 +126,7 @@ def register() -> Blueprint:
|
||||
frag_params["session_id"] = ident["session_id"]
|
||||
|
||||
from sx.sx_components import render_ticket_widget
|
||||
widget_html = render_ticket_widget(entry, qty, "/all-tickets/adjust")
|
||||
widget_html = await render_ticket_widget(entry, qty, "/all-tickets/adjust")
|
||||
mini_html = await fetch_fragment("cart", "cart-mini", params=frag_params, required=False)
|
||||
return sx_response(widget_html + (mini_html or ""))
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ def register():
|
||||
@require_admin
|
||||
async def calendar_description_edit(calendar_slug: str, **kwargs):
|
||||
from sx.sx_components import render_calendar_description_edit
|
||||
html = render_calendar_description_edit(g.calendar)
|
||||
html = await render_calendar_description_edit(g.calendar)
|
||||
return sx_response(html)
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ def register():
|
||||
await g.s.flush()
|
||||
|
||||
from sx.sx_components import render_calendar_description
|
||||
html = render_calendar_description(g.calendar, oob=True)
|
||||
html = await render_calendar_description(g.calendar, oob=True)
|
||||
return sx_response(html)
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ def register():
|
||||
@require_admin
|
||||
async def calendar_description_view(calendar_slug: str, **kwargs):
|
||||
from sx.sx_components import render_calendar_description
|
||||
html = render_calendar_description(g.calendar)
|
||||
html = await render_calendar_description(g.calendar)
|
||||
return sx_response(html)
|
||||
|
||||
return bp
|
||||
|
||||
@@ -201,7 +201,7 @@ def register():
|
||||
from shared.sx.page import get_template_context
|
||||
from sx.sx_components import _calendar_admin_main_panel_html
|
||||
ctx = await get_template_context()
|
||||
html = _calendar_admin_main_panel_html(ctx)
|
||||
html = await _calendar_admin_main_panel_html(ctx)
|
||||
return sx_response(html)
|
||||
|
||||
|
||||
@@ -220,7 +220,7 @@ def register():
|
||||
from shared.sx.page import get_template_context
|
||||
from sx.sx_components import render_calendars_list_panel
|
||||
ctx = await get_template_context()
|
||||
html = render_calendars_list_panel(ctx)
|
||||
html = await render_calendars_list_panel(ctx)
|
||||
|
||||
if post_data:
|
||||
from shared.services.entry_associations import get_associated_entries
|
||||
@@ -236,7 +236,7 @@ def register():
|
||||
).scalars().all()
|
||||
|
||||
associated_entries = await get_associated_entries(post_id)
|
||||
nav_oob = render_post_nav_entries_oob(associated_entries, cals, post_data["post"])
|
||||
nav_oob = await render_post_nav_entries_oob(associated_entries, cals, post_data["post"])
|
||||
html = html + nav_oob
|
||||
|
||||
return sx_response(html)
|
||||
|
||||
@@ -259,7 +259,7 @@ def register():
|
||||
}
|
||||
|
||||
from sx.sx_components import render_day_main_panel
|
||||
html = render_day_main_panel(ctx)
|
||||
html = await render_day_main_panel(ctx)
|
||||
mini_html = await fetch_fragment("cart", "cart-mini", params=frag_params, required=False)
|
||||
return sx_response(html + (mini_html or ""))
|
||||
|
||||
@@ -280,12 +280,12 @@ def register():
|
||||
day_slots = list(result.scalars())
|
||||
|
||||
from sx.sx_components import render_entry_add_form
|
||||
return sx_response(render_entry_add_form(g.calendar, day, month, year, day_slots))
|
||||
return sx_response(await render_entry_add_form(g.calendar, day, month, year, day_slots))
|
||||
|
||||
@bp.get("/add-button/")
|
||||
async def add_button(day: int, month: int, year: int, **kwargs):
|
||||
from sx.sx_components import render_entry_add_button
|
||||
return sx_response(render_entry_add_button(g.calendar, day, month, year))
|
||||
return sx_response(await render_entry_add_button(g.calendar, day, month, year))
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -112,7 +112,7 @@ def register():
|
||||
|
||||
# Render OOB nav
|
||||
from sx.sx_components import render_day_entries_nav_oob
|
||||
return render_day_entries_nav_oob(visible.confirmed_entries, calendar, day_date)
|
||||
return await render_day_entries_nav_oob(visible.confirmed_entries, calendar, day_date)
|
||||
|
||||
async def get_post_nav_oob(entry_id: int):
|
||||
"""Helper to generate OOB update for post entries nav when entry state changes"""
|
||||
@@ -149,7 +149,7 @@ def register():
|
||||
|
||||
# Render OOB nav for this post
|
||||
from sx.sx_components import render_post_nav_entries_oob
|
||||
nav_oob = render_post_nav_entries_oob(associated_entries, calendars, post)
|
||||
nav_oob = await render_post_nav_entries_oob(associated_entries, calendars, post)
|
||||
nav_oobs.append(nav_oob)
|
||||
|
||||
return "".join(nav_oobs)
|
||||
@@ -257,7 +257,7 @@ def register():
|
||||
day_slots = list(result.scalars())
|
||||
|
||||
from sx.sx_components import render_entry_edit_form
|
||||
return sx_response(render_entry_edit_form(g.entry, g.calendar, day, month, year, day_slots))
|
||||
return sx_response(await render_entry_edit_form(g.entry, g.calendar, day, month, year, day_slots))
|
||||
|
||||
@bp.put("/")
|
||||
@require_admin
|
||||
@@ -423,7 +423,7 @@ def register():
|
||||
from sx.sx_components import _entry_main_panel_html
|
||||
|
||||
tctx = await get_template_context()
|
||||
html = _entry_main_panel_html(tctx)
|
||||
html = await _entry_main_panel_html(tctx)
|
||||
return sx_response(html + nav_oob)
|
||||
|
||||
|
||||
@@ -449,7 +449,7 @@ def register():
|
||||
# Re-read entry to get updated state
|
||||
await g.s.refresh(g.entry)
|
||||
from sx.sx_components import render_entry_optioned
|
||||
html = render_entry_optioned(g.entry, g.calendar, day, month, year)
|
||||
html = await render_entry_optioned(g.entry, g.calendar, day, month, year)
|
||||
return sx_response(html + day_nav_oob + post_nav_oob)
|
||||
|
||||
@bp.post("/decline/")
|
||||
@@ -474,7 +474,7 @@ def register():
|
||||
# Re-read entry to get updated state
|
||||
await g.s.refresh(g.entry)
|
||||
from sx.sx_components import render_entry_optioned
|
||||
html = render_entry_optioned(g.entry, g.calendar, day, month, year)
|
||||
html = await render_entry_optioned(g.entry, g.calendar, day, month, year)
|
||||
return sx_response(html + day_nav_oob + post_nav_oob)
|
||||
|
||||
@bp.post("/provisional/")
|
||||
@@ -499,7 +499,7 @@ def register():
|
||||
# Re-read entry to get updated state
|
||||
await g.s.refresh(g.entry)
|
||||
from sx.sx_components import render_entry_optioned
|
||||
html = render_entry_optioned(g.entry, g.calendar, day, month, year)
|
||||
html = await render_entry_optioned(g.entry, g.calendar, day, month, year)
|
||||
return sx_response(html + day_nav_oob + post_nav_oob)
|
||||
|
||||
@bp.post("/tickets/")
|
||||
@@ -543,7 +543,7 @@ def register():
|
||||
# Return just the tickets fragment (targeted by hx-target="#entry-tickets-...")
|
||||
await g.s.refresh(g.entry)
|
||||
from sx.sx_components import render_entry_tickets_config
|
||||
html = render_entry_tickets_config(g.entry, g.calendar, request.view_args.get("day"), request.view_args.get("month"), request.view_args.get("year"))
|
||||
html = await render_entry_tickets_config(g.entry, g.calendar, request.view_args.get("day"), request.view_args.get("month"), request.view_args.get("year"))
|
||||
return sx_response(html)
|
||||
|
||||
@bp.get("/posts/search/")
|
||||
@@ -559,7 +559,7 @@ def register():
|
||||
|
||||
va = request.view_args or {}
|
||||
from sx.sx_components import render_post_search_results
|
||||
return sx_response(render_post_search_results(
|
||||
return sx_response(await render_post_search_results(
|
||||
search_posts, query, page, total_pages,
|
||||
g.entry, g.calendar,
|
||||
va.get("day"), va.get("month"), va.get("year"),
|
||||
@@ -594,8 +594,8 @@ def register():
|
||||
# Return updated posts list + OOB nav update
|
||||
from sx.sx_components import render_entry_posts_panel, render_entry_posts_nav_oob
|
||||
va = request.view_args or {}
|
||||
html = render_entry_posts_panel(entry_posts, g.entry, g.calendar, va.get("day"), va.get("month"), va.get("year"))
|
||||
nav_oob = render_entry_posts_nav_oob(entry_posts)
|
||||
html = await render_entry_posts_panel(entry_posts, g.entry, g.calendar, va.get("day"), va.get("month"), va.get("year"))
|
||||
nav_oob = await render_entry_posts_nav_oob(entry_posts)
|
||||
return sx_response(html + nav_oob)
|
||||
|
||||
@bp.delete("/posts/<int:post_id>/")
|
||||
@@ -616,8 +616,8 @@ def register():
|
||||
# Return updated posts list + OOB nav update
|
||||
from sx.sx_components import render_entry_posts_panel, render_entry_posts_nav_oob
|
||||
va = request.view_args or {}
|
||||
html = render_entry_posts_panel(entry_posts, g.entry, g.calendar, va.get("day"), va.get("month"), va.get("year"))
|
||||
nav_oob = render_entry_posts_nav_oob(entry_posts)
|
||||
html = await render_entry_posts_panel(entry_posts, g.entry, g.calendar, va.get("day"), va.get("month"), va.get("year"))
|
||||
nav_oob = await render_entry_posts_nav_oob(entry_posts)
|
||||
return sx_response(html + nav_oob)
|
||||
|
||||
return bp
|
||||
|
||||
@@ -69,7 +69,7 @@ def register():
|
||||
from shared.sx.page import get_template_context
|
||||
from sx.sx_components import render_calendars_list_panel
|
||||
ctx = await get_template_context()
|
||||
html = render_calendars_list_panel(ctx)
|
||||
html = await render_calendars_list_panel(ctx)
|
||||
|
||||
# Blog-embedded mode: also update post nav
|
||||
if post_data:
|
||||
@@ -85,7 +85,7 @@ def register():
|
||||
).scalars().all()
|
||||
|
||||
associated_entries = await get_associated_entries(post_id)
|
||||
nav_oob = render_post_nav_entries_oob(associated_entries, cals, post_data["post"])
|
||||
nav_oob = await render_post_nav_entries_oob(associated_entries, cals, post_data["post"])
|
||||
html = html + nav_oob
|
||||
|
||||
return sx_response(html)
|
||||
|
||||
@@ -44,7 +44,7 @@ def register():
|
||||
from shared.sx.page import get_template_context
|
||||
from sx.sx_components import render_markets_list_panel
|
||||
ctx = await get_template_context()
|
||||
return sx_response(render_markets_list_panel(ctx))
|
||||
return sx_response(await render_markets_list_panel(ctx))
|
||||
|
||||
@bp.delete("/<market_slug>/")
|
||||
@require_admin
|
||||
@@ -57,6 +57,6 @@ def register():
|
||||
from shared.sx.page import get_template_context
|
||||
from sx.sx_components import render_markets_list_panel
|
||||
ctx = await get_template_context()
|
||||
return sx_response(render_markets_list_panel(ctx))
|
||||
return sx_response(await render_markets_list_panel(ctx))
|
||||
|
||||
return bp
|
||||
|
||||
@@ -107,7 +107,7 @@ def register() -> Blueprint:
|
||||
frag_params["session_id"] = ident["session_id"]
|
||||
|
||||
from sx.sx_components import render_ticket_widget
|
||||
widget_html = render_ticket_widget(entry, qty, f"/{g.post_slug}/tickets/adjust")
|
||||
widget_html = await 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 ""))
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ def register():
|
||||
if not slot:
|
||||
return await make_response("Not found", 404)
|
||||
from sx.sx_components import render_slot_edit_form
|
||||
return sx_response(render_slot_edit_form(slot, g.calendar))
|
||||
return sx_response(await render_slot_edit_form(slot, g.calendar))
|
||||
|
||||
@bp.get("/view/")
|
||||
@require_admin
|
||||
@@ -45,7 +45,7 @@ def register():
|
||||
if not slot:
|
||||
return await make_response("Not found", 404)
|
||||
from sx.sx_components import render_slot_main_panel
|
||||
return sx_response(render_slot_main_panel(slot, g.calendar))
|
||||
return sx_response(await render_slot_main_panel(slot, g.calendar))
|
||||
|
||||
@bp.delete("/")
|
||||
@require_admin
|
||||
@@ -54,7 +54,7 @@ def register():
|
||||
await svc_delete_slot(g.s, slot_id)
|
||||
slots = await svc_list_slots(g.s, g.calendar.id)
|
||||
from sx.sx_components import render_slots_table
|
||||
return sx_response(render_slots_table(slots, g.calendar))
|
||||
return sx_response(await render_slots_table(slots, g.calendar))
|
||||
|
||||
@bp.put("/")
|
||||
@require_admin
|
||||
@@ -136,7 +136,7 @@ def register():
|
||||
), 422
|
||||
|
||||
from sx.sx_components import render_slot_main_panel
|
||||
return sx_response(render_slot_main_panel(slot, g.calendar, oob=True))
|
||||
return sx_response(await render_slot_main_panel(slot, g.calendar, oob=True))
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -111,19 +111,19 @@ def register():
|
||||
# Success → re-render the slots table
|
||||
slots = await svc_list_slots(g.s, g.calendar.id)
|
||||
from sx.sx_components import render_slots_table
|
||||
return sx_response(render_slots_table(slots, g.calendar))
|
||||
return sx_response(await render_slots_table(slots, g.calendar))
|
||||
|
||||
|
||||
@bp.get("/add")
|
||||
@require_admin
|
||||
async def add_form(**kwargs):
|
||||
from sx.sx_components import render_slot_add_form
|
||||
return sx_response(render_slot_add_form(g.calendar))
|
||||
return sx_response(await render_slot_add_form(g.calendar))
|
||||
|
||||
@bp.get("/add-button")
|
||||
@require_admin
|
||||
async def add_button(**kwargs):
|
||||
from sx.sx_components import render_slot_add_button
|
||||
return sx_response(render_slot_add_button(g.calendar))
|
||||
return sx_response(await render_slot_add_button(g.calendar))
|
||||
|
||||
return bp
|
||||
|
||||
@@ -54,7 +54,7 @@ def register() -> Blueprint:
|
||||
tickets = await get_tickets_for_entry(g.s, entry_id)
|
||||
|
||||
from sx.sx_components import render_entry_tickets_admin
|
||||
html = render_entry_tickets_admin(entry, tickets)
|
||||
html = await render_entry_tickets_admin(entry, tickets)
|
||||
return sx_response(html)
|
||||
|
||||
@bp.get("/lookup/")
|
||||
@@ -71,9 +71,9 @@ def register() -> Blueprint:
|
||||
ticket = await get_ticket_by_code(g.s, code)
|
||||
from sx.sx_components import render_lookup_result
|
||||
if not ticket:
|
||||
return sx_response(render_lookup_result(None, "Ticket not found"))
|
||||
return sx_response(await render_lookup_result(None, "Ticket not found"))
|
||||
|
||||
return sx_response(render_lookup_result(ticket, None))
|
||||
return sx_response(await render_lookup_result(ticket, None))
|
||||
|
||||
@bp.post("/<code>/checkin/")
|
||||
@require_admin
|
||||
@@ -84,9 +84,9 @@ def register() -> Blueprint:
|
||||
|
||||
from sx.sx_components import render_checkin_result
|
||||
if not success:
|
||||
return sx_response(render_checkin_result(False, error, None))
|
||||
return sx_response(await render_checkin_result(False, error, None))
|
||||
|
||||
ticket = await get_ticket_by_code(g.s, code)
|
||||
return sx_response(render_checkin_result(True, None, ticket))
|
||||
return sx_response(await render_checkin_result(True, None, ticket))
|
||||
|
||||
return bp
|
||||
|
||||
@@ -32,7 +32,7 @@ def register():
|
||||
|
||||
from sx.sx_components import render_ticket_type_edit_form
|
||||
va = request.view_args or {}
|
||||
return sx_response(render_ticket_type_edit_form(
|
||||
return sx_response(await render_ticket_type_edit_form(
|
||||
ticket_type, g.entry, g.calendar,
|
||||
va.get("day"), va.get("month"), va.get("year"),
|
||||
))
|
||||
@@ -47,7 +47,7 @@ def register():
|
||||
|
||||
from sx.sx_components import render_ticket_type_main_panel
|
||||
va = request.view_args or {}
|
||||
return sx_response(render_ticket_type_main_panel(
|
||||
return sx_response(await render_ticket_type_main_panel(
|
||||
ticket_type, g.entry, g.calendar,
|
||||
va.get("day"), va.get("month"), va.get("year"),
|
||||
))
|
||||
@@ -114,7 +114,7 @@ def register():
|
||||
# Return updated view with OOB flag
|
||||
from sx.sx_components import render_ticket_type_main_panel
|
||||
va = request.view_args or {}
|
||||
return sx_response(render_ticket_type_main_panel(
|
||||
return sx_response(await render_ticket_type_main_panel(
|
||||
ticket_type, g.entry, g.calendar,
|
||||
va.get("day"), va.get("month"), va.get("year"),
|
||||
oob=True,
|
||||
@@ -133,7 +133,7 @@ def register():
|
||||
ticket_types = await svc_list_ticket_types(g.s, g.entry.id)
|
||||
from sx.sx_components import render_ticket_types_table
|
||||
va = request.view_args or {}
|
||||
return sx_response(render_ticket_types_table(
|
||||
return sx_response(await render_ticket_types_table(
|
||||
ticket_types, g.entry, g.calendar,
|
||||
va.get("day"), va.get("month"), va.get("year"),
|
||||
))
|
||||
|
||||
@@ -95,7 +95,7 @@ def register():
|
||||
ticket_types = await svc_list_ticket_types(g.s, g.entry.id)
|
||||
from sx.sx_components import render_ticket_types_table
|
||||
va = request.view_args or {}
|
||||
return sx_response(render_ticket_types_table(
|
||||
return sx_response(await render_ticket_types_table(
|
||||
ticket_types, g.entry, g.calendar,
|
||||
va.get("day"), va.get("month"), va.get("year"),
|
||||
))
|
||||
@@ -106,7 +106,7 @@ def register():
|
||||
"""Show the add ticket type form."""
|
||||
from sx.sx_components import render_ticket_type_add_form
|
||||
va = request.view_args or {}
|
||||
return sx_response(render_ticket_type_add_form(
|
||||
return sx_response(await render_ticket_type_add_form(
|
||||
g.entry, g.calendar,
|
||||
va.get("day"), va.get("month"), va.get("year"),
|
||||
))
|
||||
@@ -117,7 +117,7 @@ def register():
|
||||
"""Show the add ticket type button."""
|
||||
from sx.sx_components import render_ticket_type_add_button
|
||||
va = request.view_args or {}
|
||||
return sx_response(render_ticket_type_add_button(
|
||||
return sx_response(await render_ticket_type_add_button(
|
||||
g.entry, g.calendar,
|
||||
va.get("day"), va.get("month"), va.get("year"),
|
||||
))
|
||||
|
||||
@@ -127,7 +127,7 @@ def register() -> Blueprint:
|
||||
cart_count = summary.count + summary.calendar_count + summary.ticket_count
|
||||
|
||||
from sx.sx_components import render_buy_result
|
||||
return sx_response(render_buy_result(entry, created, remaining, cart_count))
|
||||
return sx_response(await render_buy_result(entry, created, remaining, cart_count))
|
||||
|
||||
@bp.post("/adjust/")
|
||||
@clear_cache(tag="calendars", tag_scope="all")
|
||||
@@ -250,7 +250,7 @@ def register() -> Blueprint:
|
||||
cart_count = summary.count + summary.calendar_count + summary.ticket_count
|
||||
|
||||
from sx.sx_components import render_adjust_response
|
||||
return sx_response(render_adjust_response(
|
||||
return sx_response(await render_adjust_response(
|
||||
entry, ticket_remaining, ticket_sold_count,
|
||||
user_ticket_count, user_ticket_counts_by_type, cart_count,
|
||||
))
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -44,11 +44,11 @@ async def _cal_admin_full(ctx: dict, **kw: Any) -> str:
|
||||
)
|
||||
ctx = await _ensure_container_nav(ctx)
|
||||
slug = (ctx.get("post") or {}).get("slug", "")
|
||||
root_hdr = root_header_sx(ctx)
|
||||
post_hdr = _post_header_sx(ctx)
|
||||
admin_hdr = post_admin_header_sx(ctx, slug, selected="calendars")
|
||||
child = admin_hdr + _calendar_header_sx(ctx) + _calendar_admin_header_sx(ctx)
|
||||
return root_hdr + post_hdr + header_child_sx(child)
|
||||
root_hdr = await root_header_sx(ctx)
|
||||
post_hdr = await _post_header_sx(ctx)
|
||||
admin_hdr = await post_admin_header_sx(ctx, slug, selected="calendars")
|
||||
child = admin_hdr + await _calendar_header_sx(ctx) + await _calendar_admin_header_sx(ctx)
|
||||
return root_hdr + post_hdr + await header_child_sx(child)
|
||||
|
||||
|
||||
async def _cal_admin_oob(ctx: dict, **kw: Any) -> str:
|
||||
@@ -59,10 +59,10 @@ async def _cal_admin_oob(ctx: dict, **kw: Any) -> str:
|
||||
)
|
||||
ctx = await _ensure_container_nav(ctx)
|
||||
slug = (ctx.get("post") or {}).get("slug", "")
|
||||
oobs = (post_admin_header_sx(ctx, slug, oob=True, selected="calendars")
|
||||
+ _calendar_header_sx(ctx, oob=True))
|
||||
oobs += oob_header_sx("calendar-header-child", "calendar-admin-header-child",
|
||||
_calendar_admin_header_sx(ctx))
|
||||
oobs = (await post_admin_header_sx(ctx, slug, oob=True, selected="calendars")
|
||||
+ await _calendar_header_sx(ctx, oob=True))
|
||||
oobs += await oob_header_sx("calendar-header-child", "calendar-admin-header-child",
|
||||
await _calendar_admin_header_sx(ctx))
|
||||
oobs += _clear_deeper_oob("post-row", "post-header-child",
|
||||
"post-admin-row", "post-admin-header-child",
|
||||
"calendar-row", "calendar-header-child",
|
||||
@@ -83,8 +83,8 @@ async def _slots_oob(ctx: dict, **kw: Any) -> str:
|
||||
)
|
||||
ctx = await _ensure_container_nav({**ctx, "is_admin_section": True})
|
||||
slug = (ctx.get("post") or {}).get("slug", "")
|
||||
oobs = (post_admin_header_sx(ctx, slug, oob=True, selected="calendars")
|
||||
+ _calendar_admin_header_sx(ctx, oob=True))
|
||||
oobs = (await post_admin_header_sx(ctx, slug, oob=True, selected="calendars")
|
||||
+ await _calendar_admin_header_sx(ctx, oob=True))
|
||||
oobs += _clear_deeper_oob("post-row", "post-header-child",
|
||||
"post-admin-row", "post-admin-header-child",
|
||||
"calendar-row", "calendar-header-child",
|
||||
@@ -102,12 +102,12 @@ async def _slot_full(ctx: dict, **kw: Any) -> str:
|
||||
)
|
||||
ctx = await _ensure_container_nav({**ctx, "is_admin_section": True})
|
||||
slug = (ctx.get("post") or {}).get("slug", "")
|
||||
root_hdr = root_header_sx(ctx)
|
||||
post_hdr = _post_header_sx(ctx)
|
||||
admin_hdr = post_admin_header_sx(ctx, slug, selected="calendars")
|
||||
child = (admin_hdr + _calendar_header_sx(ctx)
|
||||
+ _calendar_admin_header_sx(ctx) + _slot_header_html(ctx))
|
||||
return root_hdr + post_hdr + header_child_sx(child)
|
||||
root_hdr = await root_header_sx(ctx)
|
||||
post_hdr = await _post_header_sx(ctx)
|
||||
admin_hdr = await post_admin_header_sx(ctx, slug, selected="calendars")
|
||||
child = (admin_hdr + await _calendar_header_sx(ctx)
|
||||
+ await _calendar_admin_header_sx(ctx) + await _slot_header_html(ctx))
|
||||
return root_hdr + post_hdr + await header_child_sx(child)
|
||||
|
||||
|
||||
async def _slot_oob(ctx: dict, **kw: Any) -> str:
|
||||
@@ -118,10 +118,10 @@ async def _slot_oob(ctx: dict, **kw: Any) -> str:
|
||||
)
|
||||
ctx = await _ensure_container_nav({**ctx, "is_admin_section": True})
|
||||
slug = (ctx.get("post") or {}).get("slug", "")
|
||||
oobs = (post_admin_header_sx(ctx, slug, oob=True, selected="calendars")
|
||||
+ _calendar_admin_header_sx(ctx, oob=True))
|
||||
oobs += oob_header_sx("calendar-admin-header-child", "slot-header-child",
|
||||
_slot_header_html(ctx))
|
||||
oobs = (await post_admin_header_sx(ctx, slug, oob=True, selected="calendars")
|
||||
+ await _calendar_admin_header_sx(ctx, oob=True))
|
||||
oobs += await oob_header_sx("calendar-admin-header-child", "slot-header-child",
|
||||
await _slot_header_html(ctx))
|
||||
oobs += _clear_deeper_oob("post-row", "post-header-child",
|
||||
"post-admin-row", "post-admin-header-child",
|
||||
"calendar-row", "calendar-header-child",
|
||||
@@ -140,12 +140,12 @@ async def _day_admin_full(ctx: dict, **kw: Any) -> str:
|
||||
)
|
||||
ctx = await _ensure_container_nav(ctx)
|
||||
slug = (ctx.get("post") or {}).get("slug", "")
|
||||
root_hdr = root_header_sx(ctx)
|
||||
post_hdr = _post_header_sx(ctx)
|
||||
admin_hdr = post_admin_header_sx(ctx, slug, selected="calendars")
|
||||
child = (admin_hdr + _calendar_header_sx(ctx) + _day_header_sx(ctx)
|
||||
+ _day_admin_header_sx(ctx))
|
||||
return root_hdr + post_hdr + header_child_sx(child)
|
||||
root_hdr = await root_header_sx(ctx)
|
||||
post_hdr = await _post_header_sx(ctx)
|
||||
admin_hdr = await post_admin_header_sx(ctx, slug, selected="calendars")
|
||||
child = (admin_hdr + await _calendar_header_sx(ctx) + await _day_header_sx(ctx)
|
||||
+ await _day_admin_header_sx(ctx))
|
||||
return root_hdr + post_hdr + await header_child_sx(child)
|
||||
|
||||
|
||||
async def _day_admin_oob(ctx: dict, **kw: Any) -> str:
|
||||
@@ -156,10 +156,10 @@ async def _day_admin_oob(ctx: dict, **kw: Any) -> str:
|
||||
)
|
||||
ctx = await _ensure_container_nav(ctx)
|
||||
slug = (ctx.get("post") or {}).get("slug", "")
|
||||
oobs = (post_admin_header_sx(ctx, slug, oob=True, selected="calendars")
|
||||
+ _calendar_header_sx(ctx, oob=True))
|
||||
oobs += oob_header_sx("day-header-child", "day-admin-header-child",
|
||||
_day_admin_header_sx(ctx))
|
||||
oobs = (await post_admin_header_sx(ctx, slug, oob=True, selected="calendars")
|
||||
+ await _calendar_header_sx(ctx, oob=True))
|
||||
oobs += await oob_header_sx("day-header-child", "day-admin-header-child",
|
||||
await _day_admin_header_sx(ctx))
|
||||
oobs += _clear_deeper_oob("post-row", "post-header-child",
|
||||
"post-admin-row", "post-admin-header-child",
|
||||
"calendar-row", "calendar-header-child",
|
||||
@@ -170,26 +170,26 @@ async def _day_admin_oob(ctx: dict, **kw: Any) -> str:
|
||||
|
||||
# --- Entry layout (root + child(post + cal + day + entry), + menu) ---
|
||||
|
||||
def _entry_full(ctx: dict, **kw: Any) -> str:
|
||||
async def _entry_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import root_header_sx, header_child_sx
|
||||
from sx.sx_components import (
|
||||
_post_header_sx, _calendar_header_sx,
|
||||
_day_header_sx, _entry_header_html,
|
||||
)
|
||||
root_hdr = root_header_sx(ctx)
|
||||
child = (_post_header_sx(ctx) + _calendar_header_sx(ctx)
|
||||
+ _day_header_sx(ctx) + _entry_header_html(ctx))
|
||||
return root_hdr + header_child_sx(child)
|
||||
root_hdr = await root_header_sx(ctx)
|
||||
child = (await _post_header_sx(ctx) + await _calendar_header_sx(ctx)
|
||||
+ await _day_header_sx(ctx) + await _entry_header_html(ctx))
|
||||
return root_hdr + await header_child_sx(child)
|
||||
|
||||
|
||||
def _entry_oob(ctx: dict, **kw: Any) -> str:
|
||||
async def _entry_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import oob_header_sx
|
||||
from sx.sx_components import (
|
||||
_day_header_sx, _entry_header_html, _clear_deeper_oob,
|
||||
)
|
||||
oobs = _day_header_sx(ctx, oob=True)
|
||||
oobs += oob_header_sx("day-header-child", "entry-header-child",
|
||||
_entry_header_html(ctx))
|
||||
oobs = await _day_header_sx(ctx, oob=True)
|
||||
oobs += await oob_header_sx("day-header-child", "entry-header-child",
|
||||
await _entry_header_html(ctx))
|
||||
oobs += _clear_deeper_oob("post-row", "post-header-child",
|
||||
"calendar-row", "calendar-header-child",
|
||||
"day-row", "day-header-child",
|
||||
@@ -208,12 +208,12 @@ async def _entry_admin_full(ctx: dict, **kw: Any) -> str:
|
||||
)
|
||||
ctx = await _ensure_container_nav(ctx)
|
||||
slug = (ctx.get("post") or {}).get("slug", "")
|
||||
root_hdr = root_header_sx(ctx)
|
||||
post_hdr = _post_header_sx(ctx)
|
||||
admin_hdr = post_admin_header_sx(ctx, slug, selected="calendars")
|
||||
child = (admin_hdr + _calendar_header_sx(ctx) + _day_header_sx(ctx)
|
||||
+ _entry_header_html(ctx) + _entry_admin_header_html(ctx))
|
||||
return root_hdr + post_hdr + header_child_sx(child)
|
||||
root_hdr = await root_header_sx(ctx)
|
||||
post_hdr = await _post_header_sx(ctx)
|
||||
admin_hdr = await post_admin_header_sx(ctx, slug, selected="calendars")
|
||||
child = (admin_hdr + await _calendar_header_sx(ctx) + await _day_header_sx(ctx)
|
||||
+ await _entry_header_html(ctx) + await _entry_admin_header_html(ctx))
|
||||
return root_hdr + post_hdr + await header_child_sx(child)
|
||||
|
||||
|
||||
async def _entry_admin_oob(ctx: dict, **kw: Any) -> str:
|
||||
@@ -224,10 +224,10 @@ async def _entry_admin_oob(ctx: dict, **kw: Any) -> str:
|
||||
)
|
||||
ctx = await _ensure_container_nav(ctx)
|
||||
slug = (ctx.get("post") or {}).get("slug", "")
|
||||
oobs = (post_admin_header_sx(ctx, slug, oob=True, selected="calendars")
|
||||
+ _entry_header_html(ctx, oob=True))
|
||||
oobs += oob_header_sx("entry-header-child", "entry-admin-header-child",
|
||||
_entry_admin_header_html(ctx))
|
||||
oobs = (await post_admin_header_sx(ctx, slug, oob=True, selected="calendars")
|
||||
+ await _entry_header_html(ctx, oob=True))
|
||||
oobs += await oob_header_sx("entry-header-child", "entry-admin-header-child",
|
||||
await _entry_admin_header_html(ctx))
|
||||
oobs += _clear_deeper_oob("post-row", "post-header-child",
|
||||
"post-admin-row", "post-admin-header-child",
|
||||
"calendar-row", "calendar-header-child",
|
||||
@@ -239,75 +239,75 @@ async def _entry_admin_oob(ctx: dict, **kw: Any) -> str:
|
||||
|
||||
# --- Ticket types layout (extends entry admin with ticket-types header, + menu) ---
|
||||
|
||||
def _ticket_types_full(ctx: dict, **kw: Any) -> str:
|
||||
async def _ticket_types_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import root_header_sx, header_child_sx
|
||||
from sx.sx_components import (
|
||||
_post_header_sx, _calendar_header_sx, _day_header_sx,
|
||||
_entry_header_html, _entry_admin_header_html,
|
||||
_ticket_types_header_html,
|
||||
)
|
||||
root_hdr = root_header_sx(ctx)
|
||||
child = (_post_header_sx(ctx) + _calendar_header_sx(ctx)
|
||||
+ _day_header_sx(ctx) + _entry_header_html(ctx)
|
||||
+ _entry_admin_header_html(ctx) + _ticket_types_header_html(ctx))
|
||||
return root_hdr + header_child_sx(child)
|
||||
root_hdr = await root_header_sx(ctx)
|
||||
child = (await _post_header_sx(ctx) + await _calendar_header_sx(ctx)
|
||||
+ await _day_header_sx(ctx) + await _entry_header_html(ctx)
|
||||
+ await _entry_admin_header_html(ctx) + await _ticket_types_header_html(ctx))
|
||||
return root_hdr + await header_child_sx(child)
|
||||
|
||||
|
||||
def _ticket_types_oob(ctx: dict, **kw: Any) -> str:
|
||||
async def _ticket_types_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import oob_header_sx
|
||||
from sx.sx_components import (
|
||||
_entry_admin_header_html, _ticket_types_header_html, _clear_deeper_oob,
|
||||
)
|
||||
oobs = _entry_admin_header_html(ctx, oob=True)
|
||||
oobs += oob_header_sx("entry-admin-header-child", "ticket_types-header-child",
|
||||
_ticket_types_header_html(ctx))
|
||||
oobs = await _entry_admin_header_html(ctx, oob=True)
|
||||
oobs += await oob_header_sx("entry-admin-header-child", "ticket_types-header-child",
|
||||
await _ticket_types_header_html(ctx))
|
||||
return oobs
|
||||
|
||||
|
||||
# --- Ticket type detail layout (extends ticket types with ticket-type header, + menu) ---
|
||||
|
||||
def _ticket_type_full(ctx: dict, **kw: Any) -> str:
|
||||
async def _ticket_type_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import root_header_sx, header_child_sx
|
||||
from sx.sx_components import (
|
||||
_post_header_sx, _calendar_header_sx, _day_header_sx,
|
||||
_entry_header_html, _entry_admin_header_html,
|
||||
_ticket_types_header_html, _ticket_type_header_html,
|
||||
)
|
||||
root_hdr = root_header_sx(ctx)
|
||||
child = (_post_header_sx(ctx) + _calendar_header_sx(ctx)
|
||||
+ _day_header_sx(ctx) + _entry_header_html(ctx)
|
||||
+ _entry_admin_header_html(ctx) + _ticket_types_header_html(ctx)
|
||||
+ _ticket_type_header_html(ctx))
|
||||
return root_hdr + header_child_sx(child)
|
||||
root_hdr = await root_header_sx(ctx)
|
||||
child = (await _post_header_sx(ctx) + await _calendar_header_sx(ctx)
|
||||
+ await _day_header_sx(ctx) + await _entry_header_html(ctx)
|
||||
+ await _entry_admin_header_html(ctx) + await _ticket_types_header_html(ctx)
|
||||
+ await _ticket_type_header_html(ctx))
|
||||
return root_hdr + await header_child_sx(child)
|
||||
|
||||
|
||||
def _ticket_type_oob(ctx: dict, **kw: Any) -> str:
|
||||
async def _ticket_type_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import oob_header_sx
|
||||
from sx.sx_components import (
|
||||
_ticket_types_header_html, _ticket_type_header_html,
|
||||
)
|
||||
oobs = _ticket_types_header_html(ctx, oob=True)
|
||||
oobs += oob_header_sx("ticket_types-header-child", "ticket_type-header-child",
|
||||
_ticket_type_header_html(ctx))
|
||||
oobs = await _ticket_types_header_html(ctx, oob=True)
|
||||
oobs += await oob_header_sx("ticket_types-header-child", "ticket_type-header-child",
|
||||
await _ticket_type_header_html(ctx))
|
||||
return oobs
|
||||
|
||||
|
||||
# --- Markets layout (root + child(post + markets)) ---
|
||||
|
||||
def _markets_full(ctx: dict, **kw: Any) -> str:
|
||||
async def _markets_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import root_header_sx, header_child_sx
|
||||
from sx.sx_components import _post_header_sx, _markets_header_sx
|
||||
root_hdr = root_header_sx(ctx)
|
||||
child = _post_header_sx(ctx) + _markets_header_sx(ctx)
|
||||
return root_hdr + header_child_sx(child)
|
||||
root_hdr = await root_header_sx(ctx)
|
||||
child = await _post_header_sx(ctx) + await _markets_header_sx(ctx)
|
||||
return root_hdr + await header_child_sx(child)
|
||||
|
||||
|
||||
def _markets_oob(ctx: dict, **kw: Any) -> str:
|
||||
async def _markets_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import oob_header_sx
|
||||
from sx.sx_components import _post_header_sx, _markets_header_sx
|
||||
oobs = _post_header_sx(ctx, oob=True)
|
||||
oobs += oob_header_sx("post-header-child", "markets-header-child",
|
||||
_markets_header_sx(ctx))
|
||||
oobs = await _post_header_sx(ctx, oob=True)
|
||||
oobs += await oob_header_sx("post-header-child", "markets-header-child",
|
||||
await _markets_header_sx(ctx))
|
||||
return oobs
|
||||
|
||||
|
||||
@@ -518,7 +518,7 @@ async def _h_calendar_admin_content(calendar_slug=None, **kw):
|
||||
from shared.sx.page import get_template_context
|
||||
from sx.sx_components import _calendar_admin_main_panel_html
|
||||
ctx = await get_template_context()
|
||||
return _calendar_admin_main_panel_html(ctx)
|
||||
return await _calendar_admin_main_panel_html(ctx)
|
||||
|
||||
|
||||
async def _h_day_admin_content(calendar_slug=None, year=None, month=None, day=None, **kw):
|
||||
@@ -526,7 +526,7 @@ async def _h_day_admin_content(calendar_slug=None, year=None, month=None, day=No
|
||||
if year is not None:
|
||||
await _ensure_day_data(int(year), int(month), int(day))
|
||||
from sx.sx_components import _day_admin_main_panel_html
|
||||
return _day_admin_main_panel_html({})
|
||||
return await _day_admin_main_panel_html({})
|
||||
|
||||
|
||||
async def _h_slots_content(calendar_slug=None, **kw):
|
||||
@@ -537,7 +537,7 @@ async def _h_slots_content(calendar_slug=None, **kw):
|
||||
slots = await svc_list_slots(g.s, calendar.id) if calendar else []
|
||||
_add_to_defpage_ctx(slots=slots)
|
||||
from sx.sx_components import render_slots_table
|
||||
return render_slots_table(slots, calendar)
|
||||
return await render_slots_table(slots, calendar)
|
||||
|
||||
|
||||
async def _h_slot_content(calendar_slug=None, slot_id=None, **kw):
|
||||
@@ -551,7 +551,7 @@ async def _h_slot_content(calendar_slug=None, slot_id=None, **kw):
|
||||
_add_to_defpage_ctx(slot=slot)
|
||||
calendar = getattr(g, "calendar", None)
|
||||
from sx.sx_components import render_slot_main_panel
|
||||
return render_slot_main_panel(slot, calendar)
|
||||
return await render_slot_main_panel(slot, calendar)
|
||||
|
||||
|
||||
async def _h_entry_content(calendar_slug=None, entry_id=None, **kw):
|
||||
@@ -560,7 +560,7 @@ async def _h_entry_content(calendar_slug=None, entry_id=None, **kw):
|
||||
from shared.sx.page import get_template_context
|
||||
from sx.sx_components import _entry_main_panel_html
|
||||
ctx = await get_template_context()
|
||||
return _entry_main_panel_html(ctx)
|
||||
return await _entry_main_panel_html(ctx)
|
||||
|
||||
|
||||
async def _h_entry_menu(calendar_slug=None, entry_id=None, **kw):
|
||||
@@ -569,7 +569,7 @@ async def _h_entry_menu(calendar_slug=None, entry_id=None, **kw):
|
||||
from shared.sx.page import get_template_context
|
||||
from sx.sx_components import _entry_nav_html
|
||||
ctx = await get_template_context()
|
||||
return _entry_nav_html(ctx)
|
||||
return await _entry_nav_html(ctx)
|
||||
|
||||
|
||||
async def _h_entry_admin_content(calendar_slug=None, entry_id=None, **kw):
|
||||
@@ -578,12 +578,12 @@ async def _h_entry_admin_content(calendar_slug=None, entry_id=None, **kw):
|
||||
from shared.sx.page import get_template_context
|
||||
from sx.sx_components import _entry_admin_main_panel_html
|
||||
ctx = await get_template_context()
|
||||
return _entry_admin_main_panel_html(ctx)
|
||||
return await _entry_admin_main_panel_html(ctx)
|
||||
|
||||
|
||||
def _h_admin_menu():
|
||||
from shared.sx.helpers import sx_call
|
||||
return sx_call("events-admin-placeholder-nav")
|
||||
async def _h_admin_menu():
|
||||
from shared.sx.helpers import render_to_sx
|
||||
return await render_to_sx("events-admin-placeholder-nav")
|
||||
|
||||
|
||||
async def _h_ticket_types_content(calendar_slug=None, entry_id=None,
|
||||
@@ -597,7 +597,7 @@ async def _h_ticket_types_content(calendar_slug=None, entry_id=None,
|
||||
ticket_types = await svc_list_ticket_types(g.s, entry.id) if entry else []
|
||||
_add_to_defpage_ctx(ticket_types=ticket_types)
|
||||
from sx.sx_components import render_ticket_types_table
|
||||
return render_ticket_types_table(ticket_types, entry, calendar, day, month, year)
|
||||
return await render_ticket_types_table(ticket_types, entry, calendar, day, month, year)
|
||||
|
||||
|
||||
async def _h_ticket_type_content(calendar_slug=None, entry_id=None,
|
||||
@@ -614,7 +614,7 @@ async def _h_ticket_type_content(calendar_slug=None, entry_id=None,
|
||||
entry = getattr(g, "entry", None)
|
||||
calendar = getattr(g, "calendar", None)
|
||||
from sx.sx_components import render_ticket_type_main_panel
|
||||
return render_ticket_type_main_panel(ticket_type, entry, calendar, day, month, year)
|
||||
return await render_ticket_type_main_panel(ticket_type, entry, calendar, day, month, year)
|
||||
|
||||
|
||||
async def _h_tickets_content(**kw):
|
||||
@@ -630,7 +630,7 @@ async def _h_tickets_content(**kw):
|
||||
from shared.sx.page import get_template_context
|
||||
from sx.sx_components import _tickets_main_panel_html
|
||||
ctx = await get_template_context()
|
||||
return _tickets_main_panel_html(ctx, tickets)
|
||||
return await _tickets_main_panel_html(ctx, tickets)
|
||||
|
||||
|
||||
async def _h_ticket_detail_content(code=None, **kw):
|
||||
@@ -653,7 +653,7 @@ async def _h_ticket_detail_content(code=None, **kw):
|
||||
from shared.sx.page import get_template_context
|
||||
from sx.sx_components import _ticket_detail_panel_html
|
||||
ctx = await get_template_context()
|
||||
return _ticket_detail_panel_html(ctx, ticket)
|
||||
return await _ticket_detail_panel_html(ctx, ticket)
|
||||
|
||||
|
||||
async def _h_ticket_admin_content(**kw):
|
||||
@@ -693,11 +693,11 @@ async def _h_ticket_admin_content(**kw):
|
||||
from shared.sx.page import get_template_context
|
||||
from sx.sx_components import _ticket_admin_main_panel_html
|
||||
ctx = await get_template_context()
|
||||
return _ticket_admin_main_panel_html(ctx, tickets, stats)
|
||||
return await _ticket_admin_main_panel_html(ctx, tickets, stats)
|
||||
|
||||
|
||||
async def _h_markets_content(**kw):
|
||||
from shared.sx.page import get_template_context
|
||||
from sx.sx_components import _markets_main_panel_html
|
||||
ctx = await get_template_context()
|
||||
return _markets_main_panel_html(ctx)
|
||||
return await _markets_main_panel_html(ctx)
|
||||
|
||||
@@ -156,7 +156,7 @@ def register(url_prefix="/social"):
|
||||
else:
|
||||
list_type = "following"
|
||||
from sx.sx_components import render_actor_card
|
||||
return sx_response(render_actor_card(remote_dto, actor, followed_urls, list_type=list_type))
|
||||
return sx_response(await render_actor_card(remote_dto, actor, followed_urls, list_type=list_type))
|
||||
|
||||
# -- Interactions ----------------------------------------------------------
|
||||
|
||||
@@ -243,7 +243,7 @@ def register(url_prefix="/social"):
|
||||
)).scalar())
|
||||
|
||||
from sx.sx_components import render_interaction_buttons
|
||||
return sx_response(render_interaction_buttons(
|
||||
return sx_response(await render_interaction_buttons(
|
||||
object_id=object_id,
|
||||
author_inbox=author_inbox,
|
||||
like_count=like_count,
|
||||
|
||||
@@ -13,9 +13,8 @@ from typing import Any
|
||||
from markupsafe import escape
|
||||
|
||||
from shared.sx.jinja_bridge import load_service_components
|
||||
from shared.sx.parser import serialize
|
||||
from shared.sx.helpers import (
|
||||
sx_call, SxExpr,
|
||||
render_to_sx,
|
||||
root_header_sx, full_page_sx, header_child_sx,
|
||||
)
|
||||
|
||||
@@ -81,16 +80,16 @@ def _serialize_remote_actor(a) -> dict:
|
||||
# Social page shell
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _social_page(ctx: dict, actor: Any, *, content: str,
|
||||
async def _social_page(ctx: dict, actor: Any, *, content: str,
|
||||
title: str = "Rose Ash", meta_html: str = "") -> str:
|
||||
from shared.sx.parser import SxExpr
|
||||
actor_data = _serialize_actor(actor)
|
||||
nav = sx_call("federation-social-nav",
|
||||
actor=SxExpr(serialize(actor_data)) if actor_data else None)
|
||||
social_hdr = sx_call("federation-social-header", nav=SxExpr(nav))
|
||||
hdr = root_header_sx(ctx)
|
||||
child = header_child_sx(social_hdr)
|
||||
nav = await render_to_sx("federation-social-nav", actor=actor_data)
|
||||
social_hdr = await render_to_sx("federation-social-header", nav=SxExpr(nav))
|
||||
hdr = await root_header_sx(ctx)
|
||||
child = await header_child_sx(social_hdr)
|
||||
header_rows = "(<> " + hdr + " " + child + ")"
|
||||
return full_page_sx(ctx, header_rows=header_rows, content=content,
|
||||
return await full_page_sx(ctx, header_rows=header_rows, content=content,
|
||||
meta_html=meta_html or f'<title>{escape(title)}</title>')
|
||||
|
||||
|
||||
@@ -99,24 +98,24 @@ def _social_page(ctx: dict, actor: Any, *, content: str,
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def render_federation_home(ctx: dict) -> str:
|
||||
hdr = root_header_sx(ctx)
|
||||
return full_page_sx(ctx, header_rows=hdr)
|
||||
hdr = await root_header_sx(ctx)
|
||||
return await full_page_sx(ctx, header_rows=hdr)
|
||||
|
||||
|
||||
async def render_login_page(ctx: dict) -> str:
|
||||
error = ctx.get("error", "")
|
||||
email = ctx.get("email", "")
|
||||
content = sx_call("account-login-content",
|
||||
content = await render_to_sx("account-login-content",
|
||||
error=error or None, email=str(escape(email)))
|
||||
return _social_page(ctx, None, content=content, title="Login \u2014 Rose Ash")
|
||||
return await _social_page(ctx, None, content=content, title="Login \u2014 Rose Ash")
|
||||
|
||||
|
||||
async def render_check_email_page(ctx: dict) -> str:
|
||||
email = ctx.get("email", "")
|
||||
email_error = ctx.get("email_error")
|
||||
content = sx_call("account-check-email-content",
|
||||
content = await render_to_sx("account-check-email-content",
|
||||
email=str(escape(email)), email_error=email_error)
|
||||
return _social_page(ctx, None, content=content,
|
||||
return await _social_page(ctx, None, content=content,
|
||||
title="Check your email \u2014 Rose Ash")
|
||||
|
||||
|
||||
@@ -124,6 +123,7 @@ async def render_choose_username_page(ctx: dict) -> str:
|
||||
from shared.browser.app.csrf import generate_csrf_token
|
||||
from quart import url_for
|
||||
from shared.config import config
|
||||
from shared.sx.parser import SxExpr
|
||||
|
||||
csrf = generate_csrf_token()
|
||||
error = ctx.get("error", "")
|
||||
@@ -132,15 +132,15 @@ async def render_choose_username_page(ctx: dict) -> str:
|
||||
check_url = url_for("identity.check_username")
|
||||
actor = ctx.get("actor")
|
||||
|
||||
error_sx = sx_call("auth-error-banner", error=error) if error else ""
|
||||
content = sx_call(
|
||||
error_sx = await render_to_sx("auth-error-banner", error=error) if error else ""
|
||||
content = await render_to_sx(
|
||||
"federation-choose-username",
|
||||
domain=str(escape(ap_domain)),
|
||||
error=SxExpr(error_sx) if error_sx else None,
|
||||
csrf=csrf, username=str(escape(username)),
|
||||
check_url=check_url,
|
||||
)
|
||||
return _social_page(ctx, actor, content=content,
|
||||
return await _social_page(ctx, actor, content=content,
|
||||
title="Choose Username \u2014 Rose Ash")
|
||||
|
||||
|
||||
@@ -164,10 +164,10 @@ async def render_timeline_items(items: list, timeline_type: str,
|
||||
else:
|
||||
next_url = url_for(f"social.{timeline_type}_timeline_page", before=before)
|
||||
|
||||
return sx_call("federation-timeline-items",
|
||||
items=SxExpr(serialize(item_dicts)),
|
||||
return await render_to_sx("federation-timeline-items",
|
||||
items=item_dicts,
|
||||
timeline_type=timeline_type,
|
||||
actor=SxExpr(serialize(actor_data)) if actor_data else None,
|
||||
actor=actor_data,
|
||||
next_url=next_url)
|
||||
|
||||
|
||||
@@ -178,14 +178,14 @@ async def render_search_results(actors: list, query: str, page: int,
|
||||
actor_data = _serialize_actor(actor)
|
||||
parts = []
|
||||
for ad in actor_dicts:
|
||||
parts.append(sx_call("federation-actor-card-from-data",
|
||||
a=SxExpr(serialize(ad)),
|
||||
actor=SxExpr(serialize(actor_data)) if actor_data else None,
|
||||
followed_urls=SxExpr(serialize(list(followed_urls))),
|
||||
parts.append(await render_to_sx("federation-actor-card-from-data",
|
||||
a=ad,
|
||||
actor=actor_data,
|
||||
followed_urls=list(followed_urls),
|
||||
list_type="search"))
|
||||
if len(actors) >= 20:
|
||||
next_url = url_for("social.search_page", q=query, page=page + 1)
|
||||
parts.append(sx_call("federation-scroll-sentinel", url=next_url))
|
||||
parts.append(await render_to_sx("federation-scroll-sentinel", url=next_url))
|
||||
return "(<> " + " ".join(parts) + ")" if parts else ""
|
||||
|
||||
|
||||
@@ -195,14 +195,14 @@ async def render_following_items(actors: list, page: int, actor: Any) -> str:
|
||||
actor_data = _serialize_actor(actor)
|
||||
parts = []
|
||||
for ad in actor_dicts:
|
||||
parts.append(sx_call("federation-actor-card-from-data",
|
||||
a=SxExpr(serialize(ad)),
|
||||
actor=SxExpr(serialize(actor_data)) if actor_data else None,
|
||||
followed_urls=SxExpr(serialize([])),
|
||||
parts.append(await render_to_sx("federation-actor-card-from-data",
|
||||
a=ad,
|
||||
actor=actor_data,
|
||||
followed_urls=[],
|
||||
list_type="following"))
|
||||
if len(actors) >= 20:
|
||||
next_url = url_for("social.following_list_page", page=page + 1)
|
||||
parts.append(sx_call("federation-scroll-sentinel", url=next_url))
|
||||
parts.append(await render_to_sx("federation-scroll-sentinel", url=next_url))
|
||||
return "(<> " + " ".join(parts) + ")" if parts else ""
|
||||
|
||||
|
||||
@@ -213,14 +213,14 @@ async def render_followers_items(actors: list, page: int,
|
||||
actor_data = _serialize_actor(actor)
|
||||
parts = []
|
||||
for ad in actor_dicts:
|
||||
parts.append(sx_call("federation-actor-card-from-data",
|
||||
a=SxExpr(serialize(ad)),
|
||||
actor=SxExpr(serialize(actor_data)) if actor_data else None,
|
||||
followed_urls=SxExpr(serialize(list(followed_urls))),
|
||||
parts.append(await render_to_sx("federation-actor-card-from-data",
|
||||
a=ad,
|
||||
actor=actor_data,
|
||||
followed_urls=list(followed_urls),
|
||||
list_type="followers"))
|
||||
if len(actors) >= 20:
|
||||
next_url = url_for("social.followers_list_page", page=page + 1)
|
||||
parts.append(sx_call("federation-scroll-sentinel", url=next_url))
|
||||
parts.append(await render_to_sx("federation-scroll-sentinel", url=next_url))
|
||||
return "(<> " + " ".join(parts) + ")" if parts else ""
|
||||
|
||||
|
||||
@@ -233,13 +233,14 @@ async def render_actor_timeline_items(items: list, actor_id: int,
|
||||
# Public API: POST handler fragment renderers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def render_interaction_buttons(object_id: str, author_inbox: str,
|
||||
async def render_interaction_buttons(object_id: str, author_inbox: str,
|
||||
like_count: int, boost_count: int,
|
||||
liked_by_me: bool, boosted_by_me: bool,
|
||||
actor: Any) -> str:
|
||||
"""Render interaction buttons fragment for POST response."""
|
||||
from shared.browser.app.csrf import generate_csrf_token
|
||||
from quart import url_for
|
||||
from shared.sx.parser import SxExpr
|
||||
|
||||
csrf = generate_csrf_token()
|
||||
safe_id = object_id.replace("/", "_").replace(":", "_")
|
||||
@@ -262,31 +263,31 @@ def render_interaction_buttons(object_id: str, author_inbox: str,
|
||||
boost_cls = "hover:text-green-600"
|
||||
|
||||
reply_url = url_for("social.defpage_compose_form", reply_to=object_id) if object_id else ""
|
||||
reply_sx = sx_call("federation-reply-link", url=reply_url) if reply_url else ""
|
||||
reply_sx = await render_to_sx("federation-reply-link", url=reply_url) if reply_url else ""
|
||||
|
||||
like_form = sx_call("federation-like-form",
|
||||
like_form = await render_to_sx("federation-like-form",
|
||||
action=like_action, target=target, oid=object_id, ainbox=author_inbox,
|
||||
csrf=csrf, cls=f"flex items-center gap-1 {like_cls}",
|
||||
icon=like_icon, count=str(like_count))
|
||||
|
||||
boost_form = sx_call("federation-boost-form",
|
||||
boost_form = await render_to_sx("federation-boost-form",
|
||||
action=boost_action, target=target, oid=object_id, ainbox=author_inbox,
|
||||
csrf=csrf, cls=f"flex items-center gap-1 {boost_cls}",
|
||||
count=str(boost_count))
|
||||
|
||||
return sx_call("federation-interaction-buttons",
|
||||
return await render_to_sx("federation-interaction-buttons",
|
||||
like=SxExpr(like_form),
|
||||
boost=SxExpr(boost_form),
|
||||
reply=SxExpr(reply_sx) if reply_sx else None)
|
||||
|
||||
|
||||
def render_actor_card(actor_dto: Any, actor: Any, followed_urls: set,
|
||||
async def render_actor_card(actor_dto: Any, actor: Any, followed_urls: set,
|
||||
*, list_type: str = "following") -> str:
|
||||
"""Render a single actor card fragment for POST response."""
|
||||
actor_data = _serialize_actor(actor)
|
||||
ad = _serialize_remote_actor(actor_dto)
|
||||
return sx_call("federation-actor-card-from-data",
|
||||
a=SxExpr(serialize(ad)),
|
||||
actor=SxExpr(serialize(actor_data)) if actor_data else None,
|
||||
followed_urls=SxExpr(serialize(list(followed_urls))),
|
||||
return await render_to_sx("federation-actor-card-from-data",
|
||||
a=ad,
|
||||
actor=actor_data,
|
||||
followed_urls=list(followed_urls),
|
||||
list_type=list_type)
|
||||
|
||||
@@ -26,33 +26,31 @@ def _register_federation_layouts() -> None:
|
||||
register_custom_layout("social", _social_full, _social_oob)
|
||||
|
||||
|
||||
def _social_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import root_header_sx, header_child_sx, sx_call, SxExpr
|
||||
from shared.sx.parser import serialize
|
||||
async def _social_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import root_header_sx, header_child_sx, render_to_sx
|
||||
from shared.sx.parser import SxExpr
|
||||
|
||||
actor = ctx.get("actor")
|
||||
actor_data = _serialize_actor(actor) if actor else None
|
||||
nav = sx_call("federation-social-nav",
|
||||
actor=SxExpr(serialize(actor_data)) if actor_data else None)
|
||||
social_hdr = sx_call("federation-social-header", nav=SxExpr(nav))
|
||||
root_hdr = root_header_sx(ctx)
|
||||
child = header_child_sx(social_hdr)
|
||||
nav = await render_to_sx("federation-social-nav", actor=actor_data)
|
||||
social_hdr = await render_to_sx("federation-social-header", nav=SxExpr(nav))
|
||||
root_hdr = await root_header_sx(ctx)
|
||||
child = await header_child_sx(social_hdr)
|
||||
return "(<> " + root_hdr + " " + child + ")"
|
||||
|
||||
|
||||
def _social_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import root_header_sx, sx_call, SxExpr
|
||||
from shared.sx.parser import serialize
|
||||
async def _social_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import root_header_sx, render_to_sx
|
||||
from shared.sx.parser import SxExpr
|
||||
|
||||
actor = ctx.get("actor")
|
||||
actor_data = _serialize_actor(actor) if actor else None
|
||||
nav = sx_call("federation-social-nav",
|
||||
actor=SxExpr(serialize(actor_data)) if actor_data else None)
|
||||
social_hdr = sx_call("federation-social-header", nav=SxExpr(nav))
|
||||
child_oob = sx_call("oob-header-sx",
|
||||
nav = await render_to_sx("federation-social-nav", actor=actor_data)
|
||||
social_hdr = await render_to_sx("federation-social-header", nav=SxExpr(nav))
|
||||
child_oob = await render_to_sx("oob-header-sx",
|
||||
parent_id="root-header-child",
|
||||
row=SxExpr(social_hdr))
|
||||
root_hdr_oob = root_header_sx(ctx, oob=True)
|
||||
root_hdr_oob = await root_header_sx(ctx, oob=True)
|
||||
return "(<> " + child_oob + " " + root_hdr_oob + ")"
|
||||
|
||||
|
||||
@@ -145,43 +143,40 @@ def _require_actor():
|
||||
async def _h_home_timeline_content(**kw):
|
||||
from quart import g
|
||||
from shared.services.registry import services
|
||||
from shared.sx.helpers import sx_call, SxExpr
|
||||
from shared.sx.parser import serialize
|
||||
from shared.sx.helpers import render_to_sx
|
||||
actor = _require_actor()
|
||||
items = await services.federation.get_home_timeline(g.s, actor.id)
|
||||
return sx_call("federation-timeline-content",
|
||||
items=SxExpr(serialize([_serialize_timeline_item(i) for i in items])),
|
||||
return await render_to_sx("federation-timeline-content",
|
||||
items=[_serialize_timeline_item(i) for i in items],
|
||||
timeline_type="home",
|
||||
actor=SxExpr(serialize(_serialize_actor(actor))))
|
||||
actor=_serialize_actor(actor))
|
||||
|
||||
|
||||
async def _h_public_timeline_content(**kw):
|
||||
from quart import g
|
||||
from shared.services.registry import services
|
||||
from shared.sx.helpers import sx_call, SxExpr
|
||||
from shared.sx.parser import serialize
|
||||
from shared.sx.helpers import render_to_sx
|
||||
actor = _get_actor()
|
||||
items = await services.federation.get_public_timeline(g.s)
|
||||
return sx_call("federation-timeline-content",
|
||||
items=SxExpr(serialize([_serialize_timeline_item(i) for i in items])),
|
||||
return await render_to_sx("federation-timeline-content",
|
||||
items=[_serialize_timeline_item(i) for i in items],
|
||||
timeline_type="public",
|
||||
actor=SxExpr(serialize(_serialize_actor(actor))) if actor else None)
|
||||
actor=_serialize_actor(actor))
|
||||
|
||||
|
||||
async def _h_compose_content(**kw):
|
||||
from quart import request
|
||||
from shared.sx.helpers import sx_call
|
||||
from shared.sx.helpers import render_to_sx
|
||||
_require_actor()
|
||||
reply_to = request.args.get("reply_to")
|
||||
return sx_call("federation-compose-content",
|
||||
return await render_to_sx("federation-compose-content",
|
||||
reply_to=reply_to or None)
|
||||
|
||||
|
||||
async def _h_search_content(**kw):
|
||||
from quart import g, request
|
||||
from shared.services.registry import services
|
||||
from shared.sx.helpers import sx_call, SxExpr
|
||||
from shared.sx.parser import serialize
|
||||
from shared.sx.helpers import render_to_sx
|
||||
actor = _get_actor()
|
||||
query = request.args.get("q", "").strip()
|
||||
actors_list = []
|
||||
@@ -194,34 +189,32 @@ async def _h_search_content(**kw):
|
||||
g.s, actor.preferred_username, page=1, per_page=1000,
|
||||
)
|
||||
followed_urls = {a.actor_url for a in following}
|
||||
return sx_call("federation-search-content",
|
||||
return await render_to_sx("federation-search-content",
|
||||
query=query,
|
||||
actors=SxExpr(serialize([_serialize_remote_actor(a) for a in actors_list])),
|
||||
actors=[_serialize_remote_actor(a) for a in actors_list],
|
||||
total=total,
|
||||
followed_urls=SxExpr(serialize(list(followed_urls))),
|
||||
actor=SxExpr(serialize(_serialize_actor(actor))) if actor else None)
|
||||
followed_urls=list(followed_urls),
|
||||
actor=_serialize_actor(actor))
|
||||
|
||||
|
||||
async def _h_following_content(**kw):
|
||||
from quart import g
|
||||
from shared.services.registry import services
|
||||
from shared.sx.helpers import sx_call, SxExpr
|
||||
from shared.sx.parser import serialize
|
||||
from shared.sx.helpers import render_to_sx
|
||||
actor = _require_actor()
|
||||
actors_list, total = await services.federation.get_following(
|
||||
g.s, actor.preferred_username,
|
||||
)
|
||||
return sx_call("federation-following-content",
|
||||
actors=SxExpr(serialize([_serialize_remote_actor(a) for a in actors_list])),
|
||||
return await render_to_sx("federation-following-content",
|
||||
actors=[_serialize_remote_actor(a) for a in actors_list],
|
||||
total=total,
|
||||
actor=SxExpr(serialize(_serialize_actor(actor))))
|
||||
actor=_serialize_actor(actor))
|
||||
|
||||
|
||||
async def _h_followers_content(**kw):
|
||||
from quart import g
|
||||
from shared.services.registry import services
|
||||
from shared.sx.helpers import sx_call, SxExpr
|
||||
from shared.sx.parser import serialize
|
||||
from shared.sx.helpers import render_to_sx
|
||||
actor = _require_actor()
|
||||
actors_list, total = await services.federation.get_followers_paginated(
|
||||
g.s, actor.preferred_username,
|
||||
@@ -230,18 +223,17 @@ async def _h_followers_content(**kw):
|
||||
g.s, actor.preferred_username, page=1, per_page=1000,
|
||||
)
|
||||
followed_urls = {a.actor_url for a in following}
|
||||
return sx_call("federation-followers-content",
|
||||
actors=SxExpr(serialize([_serialize_remote_actor(a) for a in actors_list])),
|
||||
return await render_to_sx("federation-followers-content",
|
||||
actors=[_serialize_remote_actor(a) for a in actors_list],
|
||||
total=total,
|
||||
followed_urls=SxExpr(serialize(list(followed_urls))),
|
||||
actor=SxExpr(serialize(_serialize_actor(actor))))
|
||||
followed_urls=list(followed_urls),
|
||||
actor=_serialize_actor(actor))
|
||||
|
||||
|
||||
async def _h_actor_timeline_content(id=None, **kw):
|
||||
from quart import g, abort
|
||||
from shared.services.registry import services
|
||||
from shared.sx.helpers import sx_call, SxExpr
|
||||
from shared.sx.parser import serialize
|
||||
from shared.sx.helpers import render_to_sx
|
||||
actor = _get_actor()
|
||||
actor_id = id
|
||||
from shared.models.federation import RemoteActor
|
||||
@@ -268,18 +260,17 @@ async def _h_actor_timeline_content(id=None, **kw):
|
||||
)
|
||||
).scalar_one_or_none()
|
||||
is_following = existing is not None
|
||||
return sx_call("federation-actor-timeline-content",
|
||||
remote_actor=SxExpr(serialize(_serialize_remote_actor(remote_dto))),
|
||||
items=SxExpr(serialize([_serialize_timeline_item(i) for i in items])),
|
||||
return await render_to_sx("federation-actor-timeline-content",
|
||||
remote_actor=_serialize_remote_actor(remote_dto),
|
||||
items=[_serialize_timeline_item(i) for i in items],
|
||||
is_following=is_following,
|
||||
actor=SxExpr(serialize(_serialize_actor(actor))) if actor else None)
|
||||
actor=_serialize_actor(actor))
|
||||
|
||||
|
||||
async def _h_notifications_content(**kw):
|
||||
from quart import g
|
||||
from shared.services.registry import services
|
||||
from shared.sx.helpers import sx_call, SxExpr
|
||||
from shared.sx.parser import serialize
|
||||
from shared.sx.helpers import render_to_sx
|
||||
actor = _require_actor()
|
||||
items = await services.federation.get_notifications(g.s, actor.id)
|
||||
await services.federation.mark_notifications_read(g.s, actor.id)
|
||||
@@ -298,5 +289,5 @@ async def _h_notifications_content(**kw):
|
||||
"read": getattr(n, "read", True),
|
||||
"app_domain": getattr(n, "app_domain", ""),
|
||||
})
|
||||
return sx_call("federation-notifications-content",
|
||||
notifications=SxExpr(serialize(notif_dicts)))
|
||||
return await render_to_sx("federation-notifications-content",
|
||||
notifications=notif_dicts)
|
||||
|
||||
@@ -31,7 +31,7 @@ def register() -> Blueprint:
|
||||
async def _is_liked():
|
||||
"""Check if a user has liked a specific target."""
|
||||
from sqlalchemy import select
|
||||
from likes.models.like import Like
|
||||
from models.like import Like
|
||||
|
||||
user_id = request.args.get("user_id", type=int)
|
||||
target_type = request.args.get("target_type", "")
|
||||
@@ -62,7 +62,7 @@ def register() -> Blueprint:
|
||||
async def _liked_slugs():
|
||||
"""Return all liked target_slugs for a user + target_type."""
|
||||
from sqlalchemy import select
|
||||
from likes.models.like import Like
|
||||
from models.like import Like
|
||||
|
||||
user_id = request.args.get("user_id", type=int)
|
||||
target_type = request.args.get("target_type", "")
|
||||
@@ -86,7 +86,7 @@ def register() -> Blueprint:
|
||||
async def _liked_ids():
|
||||
"""Return all liked target_ids for a user + target_type."""
|
||||
from sqlalchemy import select
|
||||
from likes.models.like import Like
|
||||
from models.like import Like
|
||||
|
||||
user_id = request.args.get("user_id", type=int)
|
||||
target_type = request.args.get("target_type", "")
|
||||
|
||||
@@ -129,7 +129,7 @@ def register():
|
||||
from sx.sx_components import render_like_toggle_button
|
||||
|
||||
if not g.user:
|
||||
return sx_response(render_like_toggle_button(product_slug, False), status=403)
|
||||
return sx_response(await render_like_toggle_button(product_slug, False), status=403)
|
||||
|
||||
user_id = g.user.id
|
||||
|
||||
@@ -138,7 +138,7 @@ def register():
|
||||
})
|
||||
liked = result["liked"]
|
||||
|
||||
return sx_response(render_like_toggle_button(product_slug, liked))
|
||||
return sx_response(await render_like_toggle_button(product_slug, liked))
|
||||
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@ def register():
|
||||
from sx.sx_components import render_cart_added_response
|
||||
item_data = getattr(g, "item_data", {})
|
||||
d = item_data.get("d", {})
|
||||
return sx_response(render_cart_added_response(g.cart, ci_ns, d))
|
||||
return sx_response(await render_cart_added_response(g.cart, ci_ns, d))
|
||||
|
||||
# normal POST: go to cart page
|
||||
from shared.infrastructure.urls import cart_url
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -27,55 +27,55 @@ def _register_market_layouts() -> None:
|
||||
register_custom_layout("market-admin", _market_admin_full, _market_admin_oob)
|
||||
|
||||
|
||||
def _market_full(ctx: dict, **kw: Any) -> str:
|
||||
async def _market_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import root_header_sx, header_child_sx
|
||||
from sx.sx_components import _post_header_sx, _market_header_sx
|
||||
|
||||
root_hdr = root_header_sx(ctx)
|
||||
child = "(<> " + _post_header_sx(ctx) + " " + _market_header_sx(ctx) + ")"
|
||||
return "(<> " + root_hdr + " " + header_child_sx(child) + ")"
|
||||
root_hdr = await root_header_sx(ctx)
|
||||
child = "(<> " + await _post_header_sx(ctx) + " " + await _market_header_sx(ctx) + ")"
|
||||
return "(<> " + root_hdr + " " + await header_child_sx(child) + ")"
|
||||
|
||||
|
||||
def _market_oob(ctx: dict, **kw: Any) -> str:
|
||||
async def _market_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import oob_header_sx
|
||||
from sx.sx_components import _post_header_sx, _market_header_sx, _clear_deeper_oob
|
||||
|
||||
oobs = oob_header_sx("post-header-child", "market-header-child",
|
||||
_market_header_sx(ctx))
|
||||
oobs = "(<> " + oobs + " " + _post_header_sx(ctx, oob=True) + " "
|
||||
oobs = await oob_header_sx("post-header-child", "market-header-child",
|
||||
await _market_header_sx(ctx))
|
||||
oobs = "(<> " + oobs + " " + await _post_header_sx(ctx, oob=True) + " "
|
||||
oobs += _clear_deeper_oob("post-row", "post-header-child",
|
||||
"market-row", "market-header-child") + ")"
|
||||
return oobs
|
||||
|
||||
|
||||
def _market_mobile(ctx: dict, **kw: Any) -> str:
|
||||
async def _market_mobile(ctx: dict, **kw: Any) -> str:
|
||||
from sx.sx_components import _mobile_nav_panel_sx
|
||||
return _mobile_nav_panel_sx(ctx)
|
||||
return await _mobile_nav_panel_sx(ctx)
|
||||
|
||||
|
||||
def _market_admin_full(ctx: dict, **kw: Any) -> str:
|
||||
async def _market_admin_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import root_header_sx, header_child_sx
|
||||
from sx.sx_components import (
|
||||
_post_header_sx, _market_header_sx, _market_admin_header_sx,
|
||||
)
|
||||
|
||||
selected = kw.get("selected", "")
|
||||
root_hdr = root_header_sx(ctx)
|
||||
child = "(<> " + _post_header_sx(ctx) + " " + _market_header_sx(ctx) + " "
|
||||
child += _market_admin_header_sx(ctx, selected=selected) + ")"
|
||||
return "(<> " + root_hdr + " " + header_child_sx(child) + ")"
|
||||
root_hdr = await root_header_sx(ctx)
|
||||
child = "(<> " + await _post_header_sx(ctx) + " " + await _market_header_sx(ctx) + " "
|
||||
child += await _market_admin_header_sx(ctx, selected=selected) + ")"
|
||||
return "(<> " + root_hdr + " " + await header_child_sx(child) + ")"
|
||||
|
||||
|
||||
def _market_admin_oob(ctx: dict, **kw: Any) -> str:
|
||||
async def _market_admin_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import oob_header_sx
|
||||
from sx.sx_components import (
|
||||
_market_header_sx, _market_admin_header_sx, _clear_deeper_oob,
|
||||
)
|
||||
|
||||
selected = kw.get("selected", "")
|
||||
oobs = "(<> " + _market_header_sx(ctx, oob=True) + " "
|
||||
oobs += oob_header_sx("market-header-child", "market-admin-header-child",
|
||||
_market_admin_header_sx(ctx, selected=selected)) + " "
|
||||
oobs = "(<> " + await _market_header_sx(ctx, oob=True) + " "
|
||||
oobs += await oob_header_sx("market-header-child", "market-admin-header-child",
|
||||
await _market_admin_header_sx(ctx, selected=selected)) + " "
|
||||
oobs += _clear_deeper_oob("post-row", "post-header-child",
|
||||
"market-row", "market-header-child",
|
||||
"market-admin-row", "market-admin-header-child") + ")"
|
||||
@@ -123,14 +123,14 @@ async def _h_all_markets_content(**kw):
|
||||
|
||||
if not markets:
|
||||
from sx.sx_components import _no_markets_sx
|
||||
return _no_markets_sx()
|
||||
return await _no_markets_sx()
|
||||
|
||||
prefix = route_prefix()
|
||||
next_url = prefix + url_for("all_markets.markets_fragment", page=page + 1)
|
||||
|
||||
from sx.sx_components import _market_cards_sx, _markets_grid
|
||||
cards = _market_cards_sx(markets, page_info, page, has_more, next_url)
|
||||
content = _markets_grid(cards)
|
||||
cards = await _market_cards_sx(markets, page_info, page, has_more, next_url)
|
||||
content = await _markets_grid(cards)
|
||||
return "(<> " + content + " " + '(div :class "pb-8")' + ")"
|
||||
|
||||
|
||||
@@ -148,15 +148,15 @@ async def _h_page_markets_content(slug=None, **kw):
|
||||
|
||||
if not markets:
|
||||
from sx.sx_components import _no_markets_sx
|
||||
return _no_markets_sx("No markets for this page")
|
||||
return await _no_markets_sx("No markets for this page")
|
||||
|
||||
prefix = route_prefix()
|
||||
next_url = prefix + url_for("page_markets.markets_fragment", page=page + 1)
|
||||
|
||||
from sx.sx_components import _market_cards_sx, _markets_grid
|
||||
cards = _market_cards_sx(markets, {}, page, has_more, next_url,
|
||||
cards = await _market_cards_sx(markets, {}, page, has_more, next_url,
|
||||
show_page_badge=False, post_slug=post_slug)
|
||||
content = _markets_grid(cards)
|
||||
content = await _markets_grid(cards)
|
||||
return "(<> " + content + " " + '(div :class "pb-8")' + ")"
|
||||
|
||||
|
||||
@@ -168,12 +168,12 @@ async def _h_page_admin_content(slug=None, **kw):
|
||||
return '(div :id "main-panel" ' + content + ')'
|
||||
|
||||
|
||||
def _h_market_home_content(page_slug=None, market_slug=None, **kw):
|
||||
async def _h_market_home_content(page_slug=None, market_slug=None, **kw):
|
||||
from quart import g
|
||||
post_data = getattr(g, "post_data", {})
|
||||
post = post_data.get("post", {})
|
||||
from sx.sx_components import _market_landing_content_sx
|
||||
return _market_landing_content_sx(post)
|
||||
return await _market_landing_content_sx(post)
|
||||
|
||||
|
||||
def _h_market_admin_content(page_slug=None, market_slug=None, **kw):
|
||||
|
||||
@@ -73,8 +73,7 @@ def register(url_prefix: str) -> Blueprint:
|
||||
result = await g.s.execute(stmt)
|
||||
orders = result.scalars().all()
|
||||
|
||||
from shared.sx.helpers import sx_response, sx_call, SxExpr
|
||||
from shared.sx.parser import serialize
|
||||
from shared.sx.helpers import sx_response, render_to_sx
|
||||
from shared.utils import route_prefix
|
||||
|
||||
pfx = route_prefix()
|
||||
@@ -96,16 +95,16 @@ def register(url_prefix: str) -> Blueprint:
|
||||
# Build just the rows fragment (not full table) for infinite scroll
|
||||
parts = []
|
||||
for od in order_dicts:
|
||||
parts.append(sx_call("order-row-pair",
|
||||
order=SxExpr(serialize(od)),
|
||||
parts.append(await render_to_sx("order-row-pair",
|
||||
order=od,
|
||||
detail_url_prefix=detail_prefix))
|
||||
if page < total_pages:
|
||||
parts.append(sx_call("infinite-scroll",
|
||||
parts.append(await render_to_sx("infinite-scroll",
|
||||
url=rows_url + qs_fn(page=page + 1),
|
||||
page=page, total_pages=total_pages,
|
||||
id_prefix="orders", colspan=5))
|
||||
else:
|
||||
parts.append(sx_call("order-end-row"))
|
||||
parts.append(await render_to_sx("order-end-row"))
|
||||
sx_src = "(<> " + " ".join(parts) + ")"
|
||||
|
||||
resp = sx_response(sx_src)
|
||||
|
||||
@@ -12,7 +12,7 @@ from typing import Any
|
||||
|
||||
from shared.sx.jinja_bridge import load_service_components
|
||||
from shared.sx.helpers import (
|
||||
call_url, sx_call, SxExpr,
|
||||
call_url, render_to_sx,
|
||||
root_header_sx, full_page_sx, header_child_sx,
|
||||
)
|
||||
from shared.infrastructure.urls import market_product_url, cart_url
|
||||
@@ -29,24 +29,24 @@ load_service_components(os.path.dirname(os.path.dirname(__file__)),
|
||||
async def render_checkout_error_page(ctx: dict, error: str | None = None, order: Any | None = None) -> str:
|
||||
"""Full page: checkout error (sx wire format)."""
|
||||
account_url = call_url(ctx, "account_url", "")
|
||||
auth_hdr = sx_call("auth-header-row", account_url=account_url)
|
||||
hdr = root_header_sx(ctx)
|
||||
hdr = "(<> " + hdr + " " + header_child_sx(auth_hdr) + ")"
|
||||
filt = sx_call("checkout-error-header")
|
||||
auth_hdr = await render_to_sx("auth-header-row", account_url=account_url)
|
||||
hdr = await root_header_sx(ctx)
|
||||
hdr = "(<> " + hdr + " " + await header_child_sx(auth_hdr) + ")"
|
||||
filt = await render_to_sx("checkout-error-header")
|
||||
|
||||
err_msg = error or "Unexpected error while creating the hosted checkout session."
|
||||
order_sx = ""
|
||||
if order:
|
||||
order_sx = sx_call("checkout-error-order-id", oid=f"#{order.id}")
|
||||
back_url = cart_url("/")
|
||||
content = sx_call(
|
||||
order_sx = await render_to_sx("checkout-error-order-id", oid=f"#{order.id}")
|
||||
from shared.sx.parser import SxExpr
|
||||
content = await render_to_sx(
|
||||
"checkout-error-content",
|
||||
msg=err_msg,
|
||||
order=SxExpr(order_sx) if order_sx else None,
|
||||
back_url=back_url,
|
||||
back_url=cart_url("/"),
|
||||
)
|
||||
|
||||
return full_page_sx(ctx, header_rows=hdr, filter=filt, content=content)
|
||||
return await full_page_sx(ctx, header_rows=hdr, filter=filt, content=content)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -58,12 +58,12 @@ async def render_checkout_return_page(ctx: dict, order: Any | None,
|
||||
calendar_entries: list | None = None,
|
||||
order_tickets: list | None = None) -> str:
|
||||
"""Full page: checkout return after SumUp payment (sx wire format)."""
|
||||
from shared.sx.parser import serialize
|
||||
from shared.sx.parser import SxExpr
|
||||
|
||||
filt = sx_call("checkout-return-header", status=status)
|
||||
filt = await render_to_sx("checkout-return-header", status=status)
|
||||
|
||||
if not order:
|
||||
content = sx_call("checkout-return-missing")
|
||||
content = await render_to_sx("checkout-return-missing")
|
||||
else:
|
||||
# Serialize order data for defcomp
|
||||
order_dict = {
|
||||
@@ -87,7 +87,7 @@ async def render_checkout_return_page(ctx: dict, order: Any | None,
|
||||
],
|
||||
}
|
||||
|
||||
summary = sx_call("order-summary-card",
|
||||
summary = await render_to_sx("order-summary-card",
|
||||
order_id=order.id,
|
||||
created_at=order_dict["created_at_formatted"],
|
||||
description=order.description, status=order.status,
|
||||
@@ -101,19 +101,19 @@ async def render_checkout_return_page(ctx: dict, order: Any | None,
|
||||
item_parts = []
|
||||
for item_d in order_dict["items"]:
|
||||
if item_d["product_image"]:
|
||||
img = sx_call("order-item-image",
|
||||
img = await render_to_sx("order-item-image",
|
||||
src=item_d["product_image"],
|
||||
alt=item_d["product_title"] or "Product image")
|
||||
else:
|
||||
img = sx_call("order-item-no-image")
|
||||
item_parts.append(sx_call("order-item-row",
|
||||
img = await render_to_sx("order-item-no-image")
|
||||
item_parts.append(await render_to_sx("order-item-row",
|
||||
href=item_d["product_url"], img=SxExpr(img),
|
||||
title=item_d["product_title"] or "Unknown product",
|
||||
pid=f"Product ID: {item_d['product_id']}",
|
||||
qty=f"Qty: {item_d['quantity']}",
|
||||
price=f"{item_d['currency'] or order.currency or 'GBP'} {item_d['unit_price_formatted']}",
|
||||
))
|
||||
items = sx_call("order-items-panel",
|
||||
items = await render_to_sx("order-items-panel",
|
||||
items=SxExpr("(<> " + " ".join(item_parts) + ")"))
|
||||
|
||||
# Calendar entries
|
||||
@@ -131,13 +131,13 @@ async def render_checkout_return_page(ctx: dict, order: Any | None,
|
||||
ds = e.start_at.strftime("%-d %b %Y, %H:%M") if e.start_at else ""
|
||||
if e.end_at:
|
||||
ds += f" \u2013 {e.end_at.strftime('%-d %b %Y, %H:%M')}"
|
||||
cal_parts.append(sx_call("order-calendar-entry",
|
||||
cal_parts.append(await render_to_sx("order-calendar-entry",
|
||||
name=e.name,
|
||||
pill=f"inline-flex items-center rounded-full px-2 py-0.5 text-[11px] font-medium {pill}",
|
||||
status=st.capitalize(), date_str=ds,
|
||||
cost=f"\u00a3{e.cost or 0:.2f}",
|
||||
))
|
||||
calendar = sx_call("order-calendar-section",
|
||||
calendar = await render_to_sx("order-calendar-section",
|
||||
items=SxExpr("(<> " + " ".join(cal_parts) + ")"))
|
||||
|
||||
# Tickets
|
||||
@@ -156,24 +156,24 @@ async def render_checkout_return_page(ctx: dict, order: Any | None,
|
||||
ds = tk.entry_start_at.strftime("%-d %b %Y, %H:%M") if tk.entry_start_at else ""
|
||||
if tk.entry_end_at:
|
||||
ds += f" \u2013 {tk.entry_end_at.strftime('%-d %b %Y, %H:%M')}"
|
||||
tk_parts.append(sx_call("checkout-return-ticket",
|
||||
tk_parts.append(await render_to_sx("checkout-return-ticket",
|
||||
name=tk.entry_name, pill=pill_cls,
|
||||
state=st.replace("_", " ").capitalize(),
|
||||
type_name=tk.ticket_type_name or None,
|
||||
date_str=ds, code=tk.code,
|
||||
price=f"\u00a3{tk.price or 0:.2f}",
|
||||
))
|
||||
tickets = sx_call("checkout-return-tickets",
|
||||
tickets = await render_to_sx("checkout-return-tickets",
|
||||
items=SxExpr("(<> " + " ".join(tk_parts) + ")"))
|
||||
|
||||
# Status message
|
||||
status_msg = ""
|
||||
if order.status == "failed":
|
||||
status_msg = sx_call("checkout-return-failed", order_id=order.id)
|
||||
status_msg = await render_to_sx("checkout-return-failed", order_id=order.id)
|
||||
elif order.status == "paid":
|
||||
status_msg = sx_call("checkout-return-paid")
|
||||
status_msg = await render_to_sx("checkout-return-paid")
|
||||
|
||||
content = sx_call("checkout-return-content",
|
||||
content = await render_to_sx("checkout-return-content",
|
||||
summary=SxExpr(summary),
|
||||
items=SxExpr(items) if items else None,
|
||||
calendar=SxExpr(calendar) if calendar else None,
|
||||
@@ -182,8 +182,8 @@ async def render_checkout_return_page(ctx: dict, order: Any | None,
|
||||
)
|
||||
|
||||
account_url = call_url(ctx, "account_url", "")
|
||||
auth_hdr = sx_call("auth-header-row", account_url=account_url)
|
||||
hdr = root_header_sx(ctx)
|
||||
hdr = "(<> " + hdr + " " + header_child_sx(auth_hdr) + ")"
|
||||
auth_hdr = await render_to_sx("auth-header-row", account_url=account_url)
|
||||
hdr = await root_header_sx(ctx)
|
||||
hdr = "(<> " + hdr + " " + await header_child_sx(auth_hdr) + ")"
|
||||
|
||||
return full_page_sx(ctx, header_rows=hdr, filter=filt, content=content)
|
||||
return await full_page_sx(ctx, header_rows=hdr, filter=filt, content=content)
|
||||
|
||||
@@ -28,94 +28,101 @@ def _register_orders_layouts() -> None:
|
||||
register_custom_layout("order-detail", _order_detail_full, _order_detail_oob, _order_detail_mobile)
|
||||
|
||||
|
||||
def _orders_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import root_header_sx, header_child_sx, call_url, sx_call, SxExpr
|
||||
async def _orders_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import root_header_sx, header_child_sx, call_url, render_to_sx
|
||||
|
||||
list_url = kw.get("list_url", "/")
|
||||
account_url = call_url(ctx, "account_url", "")
|
||||
root_hdr = root_header_sx(ctx)
|
||||
auth_hdr = sx_call("auth-header-row",
|
||||
root_hdr = await root_header_sx(ctx)
|
||||
auth_hdr = await render_to_sx("auth-header-row",
|
||||
account_url=account_url,
|
||||
select_colours=ctx.get("select_colours", ""),
|
||||
account_nav=_as_sx_nav(ctx),
|
||||
)
|
||||
orders_hdr = sx_call("orders-header-row", list_url=list_url)
|
||||
orders_hdr = await render_to_sx("orders-header-row", list_url=list_url)
|
||||
inner = "(<> " + auth_hdr + " " + orders_hdr + ")"
|
||||
return "(<> " + root_hdr + " " + header_child_sx(inner) + ")"
|
||||
return "(<> " + root_hdr + " " + await header_child_sx(inner) + ")"
|
||||
|
||||
|
||||
def _orders_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import root_header_sx, sx_call, SxExpr, call_url
|
||||
async def _orders_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import root_header_sx, render_to_sx
|
||||
from shared.sx.helpers import call_url
|
||||
from shared.sx.parser import SxExpr
|
||||
|
||||
list_url = kw.get("list_url", "/")
|
||||
account_url = call_url(ctx, "account_url", "")
|
||||
auth_hdr = sx_call("auth-header-row",
|
||||
auth_hdr = await render_to_sx("auth-header-row",
|
||||
account_url=account_url,
|
||||
select_colours=ctx.get("select_colours", ""),
|
||||
account_nav=_as_sx_nav(ctx),
|
||||
oob=True,
|
||||
)
|
||||
auth_child_oob = sx_call("oob-header-sx",
|
||||
parent_id="auth-header-child",
|
||||
row=SxExpr(sx_call("orders-header-row", list_url=list_url)))
|
||||
root_hdr = root_header_sx(ctx, oob=True)
|
||||
orders_hdr = await render_to_sx("orders-header-row", list_url=list_url)
|
||||
auth_child_oob = await render_to_sx("oob-header-sx",
|
||||
parent_id="auth-header-child",
|
||||
row=SxExpr(orders_hdr))
|
||||
root_hdr = await root_header_sx(ctx, oob=True)
|
||||
return "(<> " + auth_hdr + " " + auth_child_oob + " " + root_hdr + ")"
|
||||
|
||||
|
||||
def _orders_mobile(ctx: dict, **kw: Any) -> str:
|
||||
async def _orders_mobile(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import mobile_menu_sx, mobile_root_nav_sx
|
||||
return mobile_menu_sx(mobile_root_nav_sx(ctx))
|
||||
return mobile_menu_sx(await mobile_root_nav_sx(ctx))
|
||||
|
||||
|
||||
def _order_detail_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import root_header_sx, sx_call, SxExpr, call_url
|
||||
async def _order_detail_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import root_header_sx, render_to_sx
|
||||
from shared.sx.helpers import call_url
|
||||
from shared.sx.parser import SxExpr
|
||||
|
||||
list_url = kw.get("list_url", "/")
|
||||
detail_url = kw.get("detail_url", "/")
|
||||
account_url = call_url(ctx, "account_url", "")
|
||||
root_hdr = root_header_sx(ctx)
|
||||
order_row = sx_call(
|
||||
root_hdr = await root_header_sx(ctx)
|
||||
order_row = await render_to_sx(
|
||||
"menu-row-sx",
|
||||
id="order-row", level=3, colour="sky", link_href=detail_url,
|
||||
link_label="Order", icon="fa fa-gbp",
|
||||
)
|
||||
auth_hdr = sx_call("auth-header-row",
|
||||
auth_hdr = await render_to_sx("auth-header-row",
|
||||
account_url=account_url,
|
||||
select_colours=ctx.get("select_colours", ""),
|
||||
account_nav=_as_sx_nav(ctx),
|
||||
)
|
||||
detail_header = sx_call(
|
||||
orders_hdr = await render_to_sx("orders-header-row", list_url=list_url)
|
||||
detail_header = await render_to_sx(
|
||||
"order-detail-header-stack",
|
||||
auth=SxExpr(auth_hdr),
|
||||
orders=SxExpr(sx_call("orders-header-row", list_url=list_url)),
|
||||
orders=SxExpr(orders_hdr),
|
||||
order=SxExpr(order_row),
|
||||
)
|
||||
return "(<> " + root_hdr + " " + detail_header + ")"
|
||||
|
||||
|
||||
def _order_detail_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import root_header_sx, sx_call, SxExpr
|
||||
async def _order_detail_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import root_header_sx, render_to_sx
|
||||
from shared.sx.parser import SxExpr
|
||||
|
||||
detail_url = kw.get("detail_url", "/")
|
||||
order_row_oob = sx_call(
|
||||
order_row_oob = await render_to_sx(
|
||||
"menu-row-sx",
|
||||
id="order-row", level=3, colour="sky", link_href=detail_url,
|
||||
link_label="Order", icon="fa fa-gbp", oob=True,
|
||||
)
|
||||
header_child_oob = sx_call("oob-header-sx",
|
||||
header_child_oob = await render_to_sx("oob-header-sx",
|
||||
parent_id="orders-header-child",
|
||||
row=SxExpr(order_row_oob))
|
||||
root_hdr = root_header_sx(ctx, oob=True)
|
||||
root_hdr = await root_header_sx(ctx, oob=True)
|
||||
return "(<> " + header_child_oob + " " + root_hdr + ")"
|
||||
|
||||
|
||||
def _order_detail_mobile(ctx: dict, **kw: Any) -> str:
|
||||
async def _order_detail_mobile(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import mobile_menu_sx, mobile_root_nav_sx
|
||||
return mobile_menu_sx(mobile_root_nav_sx(ctx))
|
||||
return mobile_menu_sx(await mobile_root_nav_sx(ctx))
|
||||
|
||||
|
||||
def _as_sx_nav(ctx: dict) -> Any:
|
||||
"""Convert account_nav fragment to SxExpr for use in sx_call."""
|
||||
"""Convert account_nav fragment to SxExpr for use in component calls."""
|
||||
from shared.sx.helpers import _as_sx
|
||||
return _as_sx(ctx.get("account_nav"))
|
||||
|
||||
@@ -279,11 +286,10 @@ async def _ensure_order_detail(order_id):
|
||||
async def _h_orders_list_content(**kw):
|
||||
await _ensure_orders_list()
|
||||
from quart import g
|
||||
from shared.sx.helpers import sx_call, SxExpr
|
||||
from shared.sx.parser import serialize
|
||||
from shared.sx.helpers import render_to_sx
|
||||
d = getattr(g, "orders_page_data", None)
|
||||
if not d:
|
||||
return sx_call("order-empty-state")
|
||||
return await render_to_sx("order-empty-state")
|
||||
|
||||
orders = d["orders"]
|
||||
url_for_fn = d["url_for_fn"]
|
||||
@@ -305,8 +311,8 @@ async def _h_orders_list_content(**kw):
|
||||
detail_prefix = rpfx + url_for_fn("orders.defpage_order_detail", order_id=0).rsplit("0/", 1)[0]
|
||||
rows_url = rpfx + url_for_fn("orders.orders_rows")
|
||||
|
||||
return sx_call("orders-list-content",
|
||||
orders=SxExpr(serialize(order_dicts)),
|
||||
return await render_to_sx("orders-list-content",
|
||||
orders=order_dicts,
|
||||
page=d["page"],
|
||||
total_pages=d["total_pages"],
|
||||
rows_url=rows_url,
|
||||
@@ -316,30 +322,31 @@ async def _h_orders_list_content(**kw):
|
||||
async def _h_orders_list_filter(**kw):
|
||||
await _ensure_orders_list()
|
||||
from quart import g
|
||||
from shared.sx.helpers import sx_call, SxExpr
|
||||
from shared.sx.helpers import render_to_sx
|
||||
from shared.sx.page import SEARCH_HEADERS_MOBILE
|
||||
from shared.sx.parser import SxExpr
|
||||
d = getattr(g, "orders_page_data", None)
|
||||
search = d.get("search", "") if d else ""
|
||||
search_count = d.get("search_count", "") if d else ""
|
||||
search_mobile = sx_call("search-mobile",
|
||||
search_mobile = await render_to_sx("search-mobile",
|
||||
current_local_href="/",
|
||||
search=search or "",
|
||||
search_count=search_count or "",
|
||||
hx_select="#main-panel",
|
||||
search_headers_mobile=SEARCH_HEADERS_MOBILE,
|
||||
)
|
||||
return sx_call("order-list-header", search_mobile=SxExpr(search_mobile))
|
||||
return await render_to_sx("order-list-header", search_mobile=SxExpr(search_mobile))
|
||||
|
||||
|
||||
async def _h_orders_list_aside(**kw):
|
||||
await _ensure_orders_list()
|
||||
from quart import g
|
||||
from shared.sx.helpers import sx_call
|
||||
from shared.sx.helpers import render_to_sx
|
||||
from shared.sx.page import SEARCH_HEADERS_DESKTOP
|
||||
d = getattr(g, "orders_page_data", None)
|
||||
search = d.get("search", "") if d else ""
|
||||
search_count = d.get("search_count", "") if d else ""
|
||||
return sx_call("search-desktop",
|
||||
return await render_to_sx("search-desktop",
|
||||
current_local_href="/",
|
||||
search=search or "",
|
||||
search_count=search_count or "",
|
||||
@@ -358,8 +365,7 @@ async def _h_orders_list_url(**kw):
|
||||
async def _h_order_detail_content(order_id=None, **kw):
|
||||
await _ensure_order_detail(order_id)
|
||||
from quart import g
|
||||
from shared.sx.helpers import sx_call, SxExpr
|
||||
from shared.sx.parser import serialize
|
||||
from shared.sx.helpers import render_to_sx
|
||||
from shared.infrastructure.urls import market_product_url
|
||||
d = getattr(g, "order_detail_data", None)
|
||||
if not d:
|
||||
@@ -402,16 +408,15 @@ async def _h_order_detail_content(order_id=None, **kw):
|
||||
"cost_formatted": f"{e.cost or 0:.2f}",
|
||||
})
|
||||
|
||||
return sx_call("order-detail-content",
|
||||
order=SxExpr(serialize(order_dict)),
|
||||
calendar_entries=SxExpr(serialize(cal_dicts)) if cal_dicts else None)
|
||||
return await render_to_sx("order-detail-content",
|
||||
order=order_dict,
|
||||
calendar_entries=cal_dicts)
|
||||
|
||||
|
||||
async def _h_order_detail_filter(order_id=None, **kw):
|
||||
await _ensure_order_detail(order_id)
|
||||
from quart import g
|
||||
from shared.sx.helpers import sx_call, SxExpr
|
||||
from shared.sx.parser import serialize
|
||||
from shared.sx.helpers import render_to_sx
|
||||
d = getattr(g, "order_detail_data", None)
|
||||
if not d:
|
||||
return ""
|
||||
@@ -422,8 +427,8 @@ async def _h_order_detail_filter(order_id=None, **kw):
|
||||
"created_at_formatted": order.created_at.strftime("%-d %b %Y, %H:%M") if order.created_at else "\u2014",
|
||||
}
|
||||
|
||||
return sx_call("order-detail-filter-content",
|
||||
order=SxExpr(serialize(order_dict)),
|
||||
return await render_to_sx("order-detail-filter-content",
|
||||
order=order_dict,
|
||||
list_url=d["list_url"],
|
||||
recheck_url=d["recheck_url"],
|
||||
pay_url=d["pay_url"],
|
||||
|
||||
@@ -149,22 +149,22 @@ async def _rich_error_page(errnum: str, message: str, image: str | None = None)
|
||||
# Root header (site nav bar)
|
||||
from shared.sx.helpers import (
|
||||
root_header_sx, post_header_sx,
|
||||
header_child_sx, full_page_sx, sx_call,
|
||||
header_child_sx, full_page_sx, render_to_sx,
|
||||
)
|
||||
hdr = root_header_sx(ctx)
|
||||
hdr = await root_header_sx(ctx)
|
||||
|
||||
# Post breadcrumb if we resolved a post
|
||||
post = (post_data or {}).get("post") or ctx.get("post") or {}
|
||||
if post.get("slug"):
|
||||
ctx["post"] = post
|
||||
post_row = post_header_sx(ctx)
|
||||
post_row = await post_header_sx(ctx)
|
||||
if post_row:
|
||||
hdr = "(<> " + hdr + " " + header_child_sx(post_row) + ")"
|
||||
hdr = "(<> " + hdr + " " + await header_child_sx(post_row) + ")"
|
||||
|
||||
# Error content
|
||||
error_content = sx_call("error-content", errnum=errnum, message=message, image=image)
|
||||
error_content = await render_to_sx("error-content", errnum=errnum, message=message, image=image)
|
||||
|
||||
return full_page_sx(ctx, header_rows=hdr, content=error_content)
|
||||
return await full_page_sx(ctx, header_rows=hdr, content=error_content)
|
||||
except Exception:
|
||||
current_app.logger.debug("Rich error page failed, falling back", exc_info=True)
|
||||
return None
|
||||
|
||||
@@ -114,18 +114,18 @@ async def _render_profile_sx(actor, activities, total):
|
||||
# Import federation layout for OOB headers
|
||||
try:
|
||||
from federation.sxc.pages import _social_oob
|
||||
oob_headers = _social_oob(tctx)
|
||||
oob_headers = await _social_oob(tctx)
|
||||
except ImportError:
|
||||
oob_headers = ""
|
||||
return sx_response(oob_page_sx(oobs=oob_headers, content=content))
|
||||
return sx_response(await oob_page_sx(oobs=oob_headers, content=content))
|
||||
else:
|
||||
try:
|
||||
from federation.sxc.pages import _social_full
|
||||
header_rows = _social_full(tctx)
|
||||
header_rows = await _social_full(tctx)
|
||||
except ImportError:
|
||||
from shared.sx.helpers import root_header_sx
|
||||
header_rows = root_header_sx(tctx)
|
||||
return full_page_sx(tctx, header_rows=header_rows, content=content)
|
||||
header_rows = await root_header_sx(tctx)
|
||||
return await full_page_sx(tctx, header_rows=header_rows, content=content)
|
||||
|
||||
|
||||
def create_activitypub_blueprint(app_name: str) -> Blueprint:
|
||||
|
||||
@@ -92,14 +92,14 @@ def create_ap_social_blueprint(app_name: str) -> Blueprint:
|
||||
kw = {"actor": actor}
|
||||
|
||||
if is_htmx_request():
|
||||
oob_headers = _social_oob_headers(tctx, **kw)
|
||||
return sx_response(oob_page_sx(
|
||||
oob_headers = await _social_oob_headers(tctx, **kw)
|
||||
return sx_response(await oob_page_sx(
|
||||
oobs=oob_headers,
|
||||
content=content,
|
||||
))
|
||||
else:
|
||||
header_rows = _social_full_headers(tctx, **kw)
|
||||
return full_page_sx(tctx, header_rows=header_rows, content=content)
|
||||
header_rows = await _social_full_headers(tctx, **kw)
|
||||
return await full_page_sx(tctx, header_rows=header_rows, content=content)
|
||||
|
||||
# -- Index ----------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -14,10 +14,9 @@ from typing import Any
|
||||
from markupsafe import escape
|
||||
|
||||
from shared.sx.helpers import (
|
||||
sx_call, root_header_sx, oob_header_sx,
|
||||
root_header_sx, oob_header_sx,
|
||||
mobile_menu_sx, mobile_root_nav_sx, full_page_sx, oob_page_sx,
|
||||
)
|
||||
from shared.sx.parser import SxExpr
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -91,23 +90,23 @@ def _social_header_row(actor: Any) -> str:
|
||||
)
|
||||
|
||||
|
||||
def _social_full_headers(ctx: dict, **kw: Any) -> str:
|
||||
root_hdr = root_header_sx(ctx)
|
||||
async def _social_full_headers(ctx: dict, **kw: Any) -> str:
|
||||
root_hdr = await root_header_sx(ctx)
|
||||
actor = kw.get("actor")
|
||||
social_row = _social_header_row(actor)
|
||||
return "(<> " + root_hdr + " " + social_row + ")"
|
||||
|
||||
|
||||
def _social_oob_headers(ctx: dict, **kw: Any) -> str:
|
||||
root_hdr = root_header_sx(ctx)
|
||||
async def _social_oob_headers(ctx: dict, **kw: Any) -> str:
|
||||
root_hdr = await root_header_sx(ctx)
|
||||
actor = kw.get("actor")
|
||||
social_row = _social_header_row(actor)
|
||||
rows = "(<> " + root_hdr + " " + social_row + ")"
|
||||
return oob_header_sx("root-header-child", "social-lite-header-child", rows)
|
||||
return await oob_header_sx("root-header-child", "social-lite-header-child", rows)
|
||||
|
||||
|
||||
def _social_mobile(ctx: dict, **kw: Any) -> str:
|
||||
return mobile_menu_sx(mobile_root_nav_sx(ctx))
|
||||
async def _social_mobile(ctx: dict, **kw: Any) -> str:
|
||||
return mobile_menu_sx(await mobile_root_nav_sx(ctx))
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -83,12 +83,12 @@ def _as_sx(val: Any) -> SxExpr | None:
|
||||
return SxExpr(f'(~rich-text :html "{escaped}")')
|
||||
|
||||
|
||||
def root_header_sx(ctx: dict, *, oob: bool = False) -> str:
|
||||
"""Build the root header row as a sx call string."""
|
||||
async def root_header_sx(ctx: dict, *, oob: bool = False) -> str:
|
||||
"""Build the root header row as sx wire format."""
|
||||
rights = ctx.get("rights") or {}
|
||||
is_admin = rights.get("admin") if isinstance(rights, dict) else getattr(rights, "admin", False)
|
||||
settings_url = call_url(ctx, "blog_url", "/settings/") if is_admin else ""
|
||||
return sx_call("header-row-sx",
|
||||
return await render_to_sx("header-row-sx",
|
||||
cart_mini=_as_sx(ctx.get("cart_mini")),
|
||||
blog_url=call_url(ctx, "blog_url", ""),
|
||||
site_title=ctx.get("base_title", ""),
|
||||
@@ -108,13 +108,13 @@ def mobile_menu_sx(*sections: str) -> str:
|
||||
return "(<> " + " ".join(parts) + ")" if parts else ""
|
||||
|
||||
|
||||
def mobile_root_nav_sx(ctx: dict) -> str:
|
||||
async def mobile_root_nav_sx(ctx: dict) -> str:
|
||||
"""Root-level mobile nav via ~mobile-root-nav component."""
|
||||
nav_tree = ctx.get("nav_tree") or ""
|
||||
auth_menu = ctx.get("auth_menu") or ""
|
||||
if not nav_tree and not auth_menu:
|
||||
return ""
|
||||
return sx_call("mobile-root-nav",
|
||||
return await render_to_sx("mobile-root-nav",
|
||||
nav_tree=_as_sx(nav_tree),
|
||||
auth_menu=_as_sx(auth_menu),
|
||||
)
|
||||
@@ -124,7 +124,7 @@ def mobile_root_nav_sx(ctx: dict) -> str:
|
||||
# Shared nav-item builders — used by BOTH desktop headers and mobile menus
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _post_nav_items_sx(ctx: dict) -> str:
|
||||
async def _post_nav_items_sx(ctx: dict) -> str:
|
||||
"""Build post-level nav items (container_nav + admin cog). Shared by
|
||||
``post_header_sx`` (desktop) and ``post_mobile_nav_sx`` (mobile)."""
|
||||
post = ctx.get("post") or {}
|
||||
@@ -135,7 +135,7 @@ def _post_nav_items_sx(ctx: dict) -> str:
|
||||
page_cart_count = ctx.get("page_cart_count", 0)
|
||||
if page_cart_count and page_cart_count > 0:
|
||||
cart_href = call_url(ctx, "cart_url", f"/{slug}/")
|
||||
parts.append(sx_call("page-cart-badge", href=cart_href,
|
||||
parts.append(await render_to_sx("page-cart-badge", href=cart_href,
|
||||
count=str(page_cart_count)))
|
||||
|
||||
container_nav = str(ctx.get("container_nav") or "").strip()
|
||||
@@ -171,7 +171,7 @@ def _post_nav_items_sx(ctx: dict) -> str:
|
||||
return "(<> " + " ".join(parts) + ")" if parts else ""
|
||||
|
||||
|
||||
def _post_admin_nav_items_sx(ctx: dict, slug: str,
|
||||
async def _post_admin_nav_items_sx(ctx: dict, slug: str,
|
||||
selected: str = "") -> str:
|
||||
"""Build post-admin nav items (calendars, markets, etc.). Shared by
|
||||
``post_admin_header_sx`` (desktop) and mobile menu."""
|
||||
@@ -193,7 +193,7 @@ def _post_admin_nav_items_sx(ctx: dict, slug: str,
|
||||
continue
|
||||
href = url_fn(path)
|
||||
is_sel = label == selected
|
||||
parts.append(sx_call("nav-link", href=href, label=label,
|
||||
parts.append(await render_to_sx("nav-link", href=href, label=label,
|
||||
select_colours=select_colours,
|
||||
is_selected=is_sel or None))
|
||||
return "(<> " + " ".join(parts) + ")" if parts else ""
|
||||
@@ -203,15 +203,15 @@ def _post_admin_nav_items_sx(ctx: dict, slug: str,
|
||||
# Mobile menu section builders — wrap shared nav items for hamburger panel
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def post_mobile_nav_sx(ctx: dict) -> str:
|
||||
async def post_mobile_nav_sx(ctx: dict) -> str:
|
||||
"""Post-level mobile menu section."""
|
||||
nav = _post_nav_items_sx(ctx)
|
||||
nav = await _post_nav_items_sx(ctx)
|
||||
if not nav:
|
||||
return ""
|
||||
post = ctx.get("post") or {}
|
||||
slug = post.get("slug", "")
|
||||
title = (post.get("title") or slug)[:40]
|
||||
return sx_call("mobile-menu-section",
|
||||
return await render_to_sx("mobile-menu-section",
|
||||
label=title,
|
||||
href=call_url(ctx, "blog_url", f"/{slug}/"),
|
||||
level=1,
|
||||
@@ -219,22 +219,22 @@ def post_mobile_nav_sx(ctx: dict) -> str:
|
||||
)
|
||||
|
||||
|
||||
def post_admin_mobile_nav_sx(ctx: dict, slug: str,
|
||||
async def post_admin_mobile_nav_sx(ctx: dict, slug: str,
|
||||
selected: str = "") -> str:
|
||||
"""Post-admin mobile menu section."""
|
||||
nav = _post_admin_nav_items_sx(ctx, slug, selected)
|
||||
nav = await _post_admin_nav_items_sx(ctx, slug, selected)
|
||||
if not nav:
|
||||
return ""
|
||||
admin_href = call_url(ctx, "blog_url", f"/{slug}/admin/")
|
||||
return sx_call("mobile-menu-section",
|
||||
return await render_to_sx("mobile-menu-section",
|
||||
label="admin", href=admin_href, level=2,
|
||||
items=SxExpr(nav),
|
||||
)
|
||||
|
||||
|
||||
def search_mobile_sx(ctx: dict) -> str:
|
||||
"""Build mobile search input as sx call string."""
|
||||
return sx_call("search-mobile",
|
||||
async def search_mobile_sx(ctx: dict) -> str:
|
||||
"""Build mobile search input as sx wire format."""
|
||||
return await render_to_sx("search-mobile",
|
||||
current_local_href=ctx.get("current_local_href", "/"),
|
||||
search=ctx.get("search", ""),
|
||||
search_count=ctx.get("search_count", ""),
|
||||
@@ -243,9 +243,9 @@ def search_mobile_sx(ctx: dict) -> str:
|
||||
)
|
||||
|
||||
|
||||
def search_desktop_sx(ctx: dict) -> str:
|
||||
"""Build desktop search input as sx call string."""
|
||||
return sx_call("search-desktop",
|
||||
async def search_desktop_sx(ctx: dict) -> str:
|
||||
"""Build desktop search input as sx wire format."""
|
||||
return await render_to_sx("search-desktop",
|
||||
current_local_href=ctx.get("current_local_href", "/"),
|
||||
search=ctx.get("search", ""),
|
||||
search_count=ctx.get("search_count", ""),
|
||||
@@ -254,8 +254,8 @@ def search_desktop_sx(ctx: dict) -> str:
|
||||
)
|
||||
|
||||
|
||||
def post_header_sx(ctx: dict, *, oob: bool = False, child: str = "") -> str:
|
||||
"""Build the post-level header row as sx call string."""
|
||||
async def post_header_sx(ctx: dict, *, oob: bool = False, child: str = "") -> str:
|
||||
"""Build the post-level header row as sx wire format."""
|
||||
post = ctx.get("post") or {}
|
||||
slug = post.get("slug", "")
|
||||
if not slug:
|
||||
@@ -263,11 +263,11 @@ def post_header_sx(ctx: dict, *, oob: bool = False, child: str = "") -> str:
|
||||
title = (post.get("title") or "")[:160]
|
||||
feature_image = post.get("feature_image")
|
||||
|
||||
label_sx = sx_call("post-label", feature_image=feature_image, title=title)
|
||||
nav_sx = _post_nav_items_sx(ctx) or None
|
||||
label_sx = await render_to_sx("post-label", feature_image=feature_image, title=title)
|
||||
nav_sx = await _post_nav_items_sx(ctx) or None
|
||||
link_href = call_url(ctx, "blog_url", f"/{slug}/")
|
||||
|
||||
return sx_call("menu-row-sx",
|
||||
return await render_to_sx("menu-row-sx",
|
||||
id="post-row", level=1,
|
||||
link_href=link_href,
|
||||
link_label_content=SxExpr(label_sx),
|
||||
@@ -278,22 +278,22 @@ def post_header_sx(ctx: dict, *, oob: bool = False, child: str = "") -> str:
|
||||
)
|
||||
|
||||
|
||||
def post_admin_header_sx(ctx: dict, slug: str, *, oob: bool = False,
|
||||
async def post_admin_header_sx(ctx: dict, slug: str, *, oob: bool = False,
|
||||
selected: str = "", admin_href: str = "") -> str:
|
||||
"""Post admin header row as sx call string."""
|
||||
"""Post admin header row as sx wire format."""
|
||||
# Label
|
||||
label_parts = ['(i :class "fa fa-shield-halved" :aria-hidden "true")', '" admin"']
|
||||
if selected:
|
||||
label_parts.append(f'(span :class "text-white" "{escape(selected)}")')
|
||||
label_sx = "(<> " + " ".join(label_parts) + ")"
|
||||
|
||||
nav_sx = _post_admin_nav_items_sx(ctx, slug, selected) or None
|
||||
nav_sx = await _post_admin_nav_items_sx(ctx, slug, selected) or None
|
||||
|
||||
if not admin_href:
|
||||
blog_fn = ctx.get("blog_url")
|
||||
admin_href = blog_fn(f"/{slug}/admin/") if callable(blog_fn) else f"/{slug}/admin/"
|
||||
|
||||
return sx_call("menu-row-sx",
|
||||
return await render_to_sx("menu-row-sx",
|
||||
id="post-admin-row", level=2,
|
||||
link_href=admin_href,
|
||||
link_label_content=SxExpr(label_sx),
|
||||
@@ -302,29 +302,29 @@ def post_admin_header_sx(ctx: dict, slug: str, *, oob: bool = False,
|
||||
)
|
||||
|
||||
|
||||
def oob_header_sx(parent_id: str, child_id: str, row_sx: str) -> str:
|
||||
async def oob_header_sx(parent_id: str, child_id: str, row_sx: str) -> str:
|
||||
"""Wrap a header row sx in an OOB swap.
|
||||
|
||||
child_id is accepted for call-site compatibility but no longer used —
|
||||
the child placeholder is created by ~menu-row-sx itself.
|
||||
"""
|
||||
return sx_call("oob-header-sx",
|
||||
return await render_to_sx("oob-header-sx",
|
||||
parent_id=parent_id,
|
||||
row=SxExpr(row_sx),
|
||||
)
|
||||
|
||||
|
||||
def header_child_sx(inner_sx: str, *, id: str = "root-header-child") -> str:
|
||||
async def header_child_sx(inner_sx: str, *, id: str = "root-header-child") -> str:
|
||||
"""Wrap inner sx in a header-child div."""
|
||||
return sx_call("header-child-sx",
|
||||
return await render_to_sx("header-child-sx",
|
||||
id=id, inner=SxExpr(f"(<> {inner_sx})"),
|
||||
)
|
||||
|
||||
|
||||
def oob_page_sx(*, oobs: str = "", filter: str = "", aside: str = "",
|
||||
async def oob_page_sx(*, oobs: str = "", filter: str = "", aside: str = "",
|
||||
content: str = "", menu: str = "") -> str:
|
||||
"""Build OOB response as sx call string."""
|
||||
return sx_call("oob-sx",
|
||||
"""Build OOB response as sx wire format."""
|
||||
return await render_to_sx("oob-sx",
|
||||
oobs=SxExpr(f"(<> {oobs})") if oobs else None,
|
||||
filter=SxExpr(filter) if filter else None,
|
||||
aside=SxExpr(aside) if aside else None,
|
||||
@@ -333,7 +333,7 @@ def oob_page_sx(*, oobs: str = "", filter: str = "", aside: str = "",
|
||||
)
|
||||
|
||||
|
||||
def full_page_sx(ctx: dict, *, header_rows: str,
|
||||
async def full_page_sx(ctx: dict, *, header_rows: str,
|
||||
filter: str = "", aside: str = "",
|
||||
content: str = "", menu: str = "",
|
||||
meta_html: str = "", meta: str = "") -> str:
|
||||
@@ -344,8 +344,8 @@ def full_page_sx(ctx: dict, *, header_rows: str,
|
||||
"""
|
||||
# Auto-generate mobile nav from context when no menu provided
|
||||
if not menu:
|
||||
menu = mobile_root_nav_sx(ctx)
|
||||
body_sx = sx_call("app-body",
|
||||
menu = await mobile_root_nav_sx(ctx)
|
||||
body_sx = await render_to_sx("app-body",
|
||||
header_rows=SxExpr(f"(<> {header_rows})") if header_rows else None,
|
||||
filter=SxExpr(filter) if filter else None,
|
||||
aside=SxExpr(aside) if aside else None,
|
||||
@@ -359,6 +359,64 @@ def full_page_sx(ctx: dict, *, header_rows: str,
|
||||
return sx_page(ctx, body_sx, meta_html=meta_html)
|
||||
|
||||
|
||||
def _build_component_ast(__name: str, **kwargs: Any) -> list:
|
||||
"""Build an AST list for a component call from Python kwargs.
|
||||
|
||||
Returns e.g. [Symbol("~card"), Keyword("title"), "hello", Keyword("count"), 3]
|
||||
No SX string generation — values stay as native Python objects.
|
||||
"""
|
||||
from .types import Symbol, Keyword, NIL
|
||||
comp_sym = Symbol(__name if __name.startswith("~") else f"~{__name}")
|
||||
ast: list = [comp_sym]
|
||||
for key, val in kwargs.items():
|
||||
kebab = key.replace("_", "-")
|
||||
ast.append(Keyword(kebab))
|
||||
if val is None:
|
||||
ast.append(NIL)
|
||||
elif isinstance(val, SxExpr):
|
||||
# SxExpr values need to be parsed into AST
|
||||
from .parser import parse
|
||||
ast.append(parse(val.source))
|
||||
else:
|
||||
ast.append(val)
|
||||
return ast
|
||||
|
||||
|
||||
async def render_to_sx(__name: str, **kwargs: Any) -> str:
|
||||
"""Call a defcomp and get SX wire format back. No SX string literals.
|
||||
|
||||
Builds an AST from Python values and evaluates it through the SX
|
||||
evaluator, which resolves IO primitives and serializes component/tag
|
||||
calls as SX wire format.
|
||||
|
||||
await render_to_sx("card", title="hello", count=3)
|
||||
# equivalent to old: sx_call("card", title="hello", count=3)
|
||||
# but values flow as native objects, not serialized strings
|
||||
"""
|
||||
from .jinja_bridge import get_component_env, _get_request_context
|
||||
from .async_eval import async_eval_to_sx
|
||||
|
||||
ast = _build_component_ast(__name, **kwargs)
|
||||
env = dict(get_component_env())
|
||||
ctx = _get_request_context()
|
||||
return await async_eval_to_sx(ast, env, ctx)
|
||||
|
||||
|
||||
async def render_to_html(__name: str, **kwargs: Any) -> str:
|
||||
"""Call a defcomp and get HTML back. No SX string literals.
|
||||
|
||||
Same as render_to_sx() but produces HTML output instead of SX wire
|
||||
format. Used by route renders that need HTML (full pages, fragments).
|
||||
"""
|
||||
from .jinja_bridge import get_component_env, _get_request_context
|
||||
from .async_eval import async_render
|
||||
|
||||
ast = _build_component_ast(__name, **kwargs)
|
||||
env = dict(get_component_env())
|
||||
ctx = _get_request_context()
|
||||
return await async_render(ast, env, ctx)
|
||||
|
||||
|
||||
def sx_call(component_name: str, **kwargs: Any) -> str:
|
||||
"""Build an s-expression component call string from Python kwargs.
|
||||
|
||||
@@ -428,27 +486,19 @@ def components_for_request() -> str:
|
||||
return "\n".join(parts)
|
||||
|
||||
|
||||
def sx_response(source_or_component: str, status: int = 200,
|
||||
headers: dict | None = None, **kwargs: Any):
|
||||
def sx_response(source: str, status: int = 200,
|
||||
headers: dict | None = None):
|
||||
"""Return an s-expression wire-format response.
|
||||
|
||||
Can be called with a raw sx string::
|
||||
Takes a raw sx string::
|
||||
|
||||
return sx_response('(~test-row :nodeid "foo")')
|
||||
|
||||
Or with a component name + kwargs (builds the sx call)::
|
||||
|
||||
return sx_response("test-row", nodeid="foo", outcome="passed")
|
||||
|
||||
For SX requests, missing component definitions are prepended as a
|
||||
``<script type="text/sx" data-components>`` block so the client
|
||||
can process them before rendering OOB content.
|
||||
"""
|
||||
from quart import request, Response
|
||||
if kwargs:
|
||||
source = sx_call(source_or_component, **kwargs)
|
||||
else:
|
||||
source = source_or_component
|
||||
|
||||
body = source
|
||||
# Validate the sx source parses as a single expression
|
||||
|
||||
@@ -87,61 +87,61 @@ def get_layout(name: str) -> Layout | None:
|
||||
# Built-in layouts
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _root_full(ctx: dict, **kw: Any) -> str:
|
||||
return root_header_sx(ctx)
|
||||
async def _root_full(ctx: dict, **kw: Any) -> str:
|
||||
return await root_header_sx(ctx)
|
||||
|
||||
|
||||
def _root_oob(ctx: dict, **kw: Any) -> str:
|
||||
root_hdr = root_header_sx(ctx)
|
||||
return oob_header_sx("root-header-child", "root-header-child", root_hdr)
|
||||
async def _root_oob(ctx: dict, **kw: Any) -> str:
|
||||
root_hdr = await root_header_sx(ctx)
|
||||
return await oob_header_sx("root-header-child", "root-header-child", root_hdr)
|
||||
|
||||
|
||||
def _post_full(ctx: dict, **kw: Any) -> str:
|
||||
root_hdr = root_header_sx(ctx)
|
||||
post_hdr = post_header_sx(ctx)
|
||||
async def _post_full(ctx: dict, **kw: Any) -> str:
|
||||
root_hdr = await root_header_sx(ctx)
|
||||
post_hdr = await post_header_sx(ctx)
|
||||
return "(<> " + root_hdr + " " + post_hdr + ")"
|
||||
|
||||
|
||||
def _post_oob(ctx: dict, **kw: Any) -> str:
|
||||
post_hdr = post_header_sx(ctx, oob=True)
|
||||
async def _post_oob(ctx: dict, **kw: Any) -> str:
|
||||
post_hdr = await post_header_sx(ctx, oob=True)
|
||||
# Also replace #post-header-child (empty — clears any nested admin rows)
|
||||
child_oob = oob_header_sx("post-header-child", "", "")
|
||||
child_oob = await oob_header_sx("post-header-child", "", "")
|
||||
return "(<> " + post_hdr + " " + child_oob + ")"
|
||||
|
||||
|
||||
def _post_admin_full(ctx: dict, **kw: Any) -> str:
|
||||
async def _post_admin_full(ctx: dict, **kw: Any) -> str:
|
||||
slug = ctx.get("post", {}).get("slug", "")
|
||||
selected = kw.get("selected", "")
|
||||
root_hdr = root_header_sx(ctx)
|
||||
admin_hdr = post_admin_header_sx(ctx, slug, selected=selected)
|
||||
post_hdr = post_header_sx(ctx, child=admin_hdr)
|
||||
root_hdr = await root_header_sx(ctx)
|
||||
admin_hdr = await post_admin_header_sx(ctx, slug, selected=selected)
|
||||
post_hdr = await post_header_sx(ctx, child=admin_hdr)
|
||||
return "(<> " + root_hdr + " " + post_hdr + ")"
|
||||
|
||||
|
||||
def _post_admin_oob(ctx: dict, **kw: Any) -> str:
|
||||
async def _post_admin_oob(ctx: dict, **kw: Any) -> str:
|
||||
slug = ctx.get("post", {}).get("slug", "")
|
||||
selected = kw.get("selected", "")
|
||||
post_hdr = post_header_sx(ctx, oob=True)
|
||||
admin_hdr = post_admin_header_sx(ctx, slug, selected=selected)
|
||||
admin_oob = oob_header_sx("post-header-child", "post-admin-header-child", admin_hdr)
|
||||
post_hdr = await post_header_sx(ctx, oob=True)
|
||||
admin_hdr = await post_admin_header_sx(ctx, slug, selected=selected)
|
||||
admin_oob = await oob_header_sx("post-header-child", "post-admin-header-child", admin_hdr)
|
||||
return "(<> " + post_hdr + " " + admin_oob + ")"
|
||||
|
||||
|
||||
def _root_mobile(ctx: dict, **kw: Any) -> str:
|
||||
return mobile_root_nav_sx(ctx)
|
||||
async def _root_mobile(ctx: dict, **kw: Any) -> str:
|
||||
return await mobile_root_nav_sx(ctx)
|
||||
|
||||
|
||||
def _post_mobile(ctx: dict, **kw: Any) -> str:
|
||||
return mobile_menu_sx(post_mobile_nav_sx(ctx), mobile_root_nav_sx(ctx))
|
||||
async def _post_mobile(ctx: dict, **kw: Any) -> str:
|
||||
return mobile_menu_sx(await post_mobile_nav_sx(ctx), await mobile_root_nav_sx(ctx))
|
||||
|
||||
|
||||
def _post_admin_mobile(ctx: dict, **kw: Any) -> str:
|
||||
async def _post_admin_mobile(ctx: dict, **kw: Any) -> str:
|
||||
slug = ctx.get("post", {}).get("slug", "")
|
||||
selected = kw.get("selected", "")
|
||||
return mobile_menu_sx(
|
||||
post_admin_mobile_nav_sx(ctx, slug, selected),
|
||||
post_mobile_nav_sx(ctx),
|
||||
mobile_root_nav_sx(ctx),
|
||||
await post_admin_mobile_nav_sx(ctx, slug, selected),
|
||||
await post_mobile_nav_sx(ctx),
|
||||
await mobile_root_nav_sx(ctx),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -268,7 +268,7 @@ async def execute_page(
|
||||
is_htmx = is_htmx_request()
|
||||
|
||||
if is_htmx:
|
||||
return sx_response(oob_page_sx(
|
||||
return sx_response(await oob_page_sx(
|
||||
oobs=oob_headers if oob_headers else "",
|
||||
filter=filter_sx,
|
||||
aside=aside_sx,
|
||||
@@ -276,7 +276,7 @@ async def execute_page(
|
||||
menu=menu_sx,
|
||||
))
|
||||
else:
|
||||
return full_page_sx(
|
||||
return await full_page_sx(
|
||||
tctx,
|
||||
header_rows=header_rows,
|
||||
filter=filter_sx,
|
||||
|
||||
@@ -42,6 +42,7 @@ IO_PRIMITIVES: frozenset[str] = frozenset({
|
||||
"get-children",
|
||||
"g",
|
||||
"csrf-token",
|
||||
"abort",
|
||||
})
|
||||
|
||||
|
||||
@@ -328,6 +329,22 @@ async def _io_csrf_token(
|
||||
return ""
|
||||
|
||||
|
||||
async def _io_abort(
|
||||
args: list[Any], kwargs: dict[str, Any], ctx: RequestContext
|
||||
) -> Any:
|
||||
"""``(abort 403 "message")`` — raise HTTP error from SX.
|
||||
|
||||
Allows defpages to abort with HTTP error codes for auth/ownership
|
||||
checks without needing a Python page helper.
|
||||
"""
|
||||
if not args:
|
||||
raise ValueError("abort requires a status code")
|
||||
from quart import abort
|
||||
status = int(args[0])
|
||||
message = str(args[1]) if len(args) > 1 else ""
|
||||
abort(status, message)
|
||||
|
||||
|
||||
_IO_HANDLERS: dict[str, Any] = {
|
||||
"frag": _io_frag,
|
||||
"query": _io_query,
|
||||
@@ -341,4 +358,5 @@ _IO_HANDLERS: dict[str, Any] = {
|
||||
"get-children": _io_get_children,
|
||||
"g": _io_g,
|
||||
"csrf-token": _io_csrf_token,
|
||||
"abort": _io_abort,
|
||||
}
|
||||
|
||||
@@ -34,30 +34,30 @@ def _register_sx_layouts() -> None:
|
||||
register_custom_layout("sx-section", _sx_section_full_headers, _sx_section_oob_headers, _sx_section_mobile)
|
||||
|
||||
|
||||
def _sx_full_headers(ctx: dict, **kw: Any) -> str:
|
||||
async def _sx_full_headers(ctx: dict, **kw: Any) -> str:
|
||||
"""Full headers for sx home page: root + sx menu row."""
|
||||
from shared.sx.helpers import root_header_sx
|
||||
from sxc.sx_components import _sx_header_sx, _main_nav_sx
|
||||
|
||||
main_nav = _main_nav_sx(kw.get("section"))
|
||||
root_hdr = root_header_sx(ctx)
|
||||
sx_row = _sx_header_sx(main_nav)
|
||||
main_nav = await _main_nav_sx(kw.get("section"))
|
||||
root_hdr = await root_header_sx(ctx)
|
||||
sx_row = await _sx_header_sx(main_nav)
|
||||
return "(<> " + root_hdr + " " + sx_row + ")"
|
||||
|
||||
|
||||
def _sx_oob_headers(ctx: dict, **kw: Any) -> str:
|
||||
async def _sx_oob_headers(ctx: dict, **kw: Any) -> str:
|
||||
"""OOB headers for sx home page."""
|
||||
from shared.sx.helpers import root_header_sx, oob_header_sx
|
||||
from sxc.sx_components import _sx_header_sx, _main_nav_sx
|
||||
|
||||
root_hdr = root_header_sx(ctx)
|
||||
main_nav = _main_nav_sx(kw.get("section"))
|
||||
sx_row = _sx_header_sx(main_nav)
|
||||
root_hdr = await root_header_sx(ctx)
|
||||
main_nav = await _main_nav_sx(kw.get("section"))
|
||||
sx_row = await _sx_header_sx(main_nav)
|
||||
rows = "(<> " + root_hdr + " " + sx_row + ")"
|
||||
return oob_header_sx("root-header-child", "sx-header-child", rows)
|
||||
return await oob_header_sx("root-header-child", "sx-header-child", rows)
|
||||
|
||||
|
||||
def _sx_section_full_headers(ctx: dict, **kw: Any) -> str:
|
||||
async def _sx_section_full_headers(ctx: dict, **kw: Any) -> str:
|
||||
"""Full headers for sx section pages: root + sx row + sub row."""
|
||||
from shared.sx.helpers import root_header_sx
|
||||
from sxc.sx_components import (
|
||||
@@ -70,14 +70,14 @@ def _sx_section_full_headers(ctx: dict, **kw: Any) -> str:
|
||||
sub_nav = kw.get("sub_nav", "")
|
||||
selected = kw.get("selected", "")
|
||||
|
||||
root_hdr = root_header_sx(ctx)
|
||||
main_nav = _main_nav_sx(section)
|
||||
sub_row = _sub_row_sx(sub_label, sub_href, sub_nav, selected)
|
||||
sx_row = _sx_header_sx(main_nav, child=sub_row)
|
||||
root_hdr = await root_header_sx(ctx)
|
||||
main_nav = await _main_nav_sx(section)
|
||||
sub_row = await _sub_row_sx(sub_label, sub_href, sub_nav, selected)
|
||||
sx_row = await _sx_header_sx(main_nav, child=sub_row)
|
||||
return "(<> " + root_hdr + " " + sx_row + ")"
|
||||
|
||||
|
||||
def _sx_section_oob_headers(ctx: dict, **kw: Any) -> str:
|
||||
async def _sx_section_oob_headers(ctx: dict, **kw: Any) -> str:
|
||||
"""OOB headers for sx section pages."""
|
||||
from shared.sx.helpers import root_header_sx, oob_header_sx
|
||||
from sxc.sx_components import (
|
||||
@@ -90,34 +90,34 @@ def _sx_section_oob_headers(ctx: dict, **kw: Any) -> str:
|
||||
sub_nav = kw.get("sub_nav", "")
|
||||
selected = kw.get("selected", "")
|
||||
|
||||
root_hdr = root_header_sx(ctx)
|
||||
main_nav = _main_nav_sx(section)
|
||||
sub_row = _sub_row_sx(sub_label, sub_href, sub_nav, selected)
|
||||
sx_row = _sx_header_sx(main_nav, child=sub_row)
|
||||
root_hdr = await root_header_sx(ctx)
|
||||
main_nav = await _main_nav_sx(section)
|
||||
sub_row = await _sub_row_sx(sub_label, sub_href, sub_nav, selected)
|
||||
sx_row = await _sx_header_sx(main_nav, child=sub_row)
|
||||
rows = "(<> " + root_hdr + " " + sx_row + ")"
|
||||
return oob_header_sx("root-header-child", "sx-header-child", rows)
|
||||
return await oob_header_sx("root-header-child", "sx-header-child", rows)
|
||||
|
||||
|
||||
def _sx_mobile(ctx: dict, **kw: Any) -> str:
|
||||
async def _sx_mobile(ctx: dict, **kw: Any) -> str:
|
||||
"""Mobile menu for sx home page: main nav + root."""
|
||||
from shared.sx.helpers import (
|
||||
mobile_menu_sx, mobile_root_nav_sx, sx_call, SxExpr,
|
||||
mobile_menu_sx, mobile_root_nav_sx, render_to_sx, SxExpr,
|
||||
)
|
||||
from sxc.sx_components import _main_nav_sx
|
||||
|
||||
main_nav = _main_nav_sx(kw.get("section"))
|
||||
main_nav = await _main_nav_sx(kw.get("section"))
|
||||
return mobile_menu_sx(
|
||||
sx_call("mobile-menu-section",
|
||||
await render_to_sx("mobile-menu-section",
|
||||
label="sx", href="/", level=1, colour="violet",
|
||||
items=SxExpr(main_nav)),
|
||||
mobile_root_nav_sx(ctx),
|
||||
await mobile_root_nav_sx(ctx),
|
||||
)
|
||||
|
||||
|
||||
def _sx_section_mobile(ctx: dict, **kw: Any) -> str:
|
||||
async def _sx_section_mobile(ctx: dict, **kw: Any) -> str:
|
||||
"""Mobile menu for sx section pages: sub nav + main nav + root."""
|
||||
from shared.sx.helpers import (
|
||||
mobile_menu_sx, mobile_root_nav_sx, sx_call, SxExpr,
|
||||
mobile_menu_sx, mobile_root_nav_sx, render_to_sx, SxExpr,
|
||||
)
|
||||
from sxc.sx_components import _main_nav_sx
|
||||
|
||||
@@ -125,17 +125,17 @@ def _sx_section_mobile(ctx: dict, **kw: Any) -> str:
|
||||
sub_label = kw.get("sub_label", section)
|
||||
sub_href = kw.get("sub_href", "/")
|
||||
sub_nav = kw.get("sub_nav", "")
|
||||
main_nav = _main_nav_sx(section)
|
||||
main_nav = await _main_nav_sx(section)
|
||||
|
||||
parts = []
|
||||
if sub_nav:
|
||||
parts.append(sx_call("mobile-menu-section",
|
||||
parts.append(await render_to_sx("mobile-menu-section",
|
||||
label=sub_label, href=sub_href, level=2, colour="violet",
|
||||
items=SxExpr(sub_nav)))
|
||||
parts.append(sx_call("mobile-menu-section",
|
||||
parts.append(await render_to_sx("mobile-menu-section",
|
||||
label="sx", href="/", level=1, colour="violet",
|
||||
items=SxExpr(main_nav)))
|
||||
parts.append(mobile_root_nav_sx(ctx))
|
||||
parts.append(await mobile_root_nav_sx(ctx))
|
||||
return mobile_menu_sx(*parts)
|
||||
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import os
|
||||
|
||||
from shared.sx.jinja_bridge import load_sx_dir, watch_sx_dir
|
||||
from shared.sx.helpers import (
|
||||
sx_call, SxExpr, get_asset_url,
|
||||
render_to_sx, SxExpr, get_asset_url,
|
||||
)
|
||||
from content.highlight import highlight
|
||||
|
||||
@@ -108,11 +108,11 @@ def _full_wire_text(sx_src: str, *comp_names: str) -> str:
|
||||
# Navigation helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _nav_items_sx(items: list[tuple[str, str]], current: str | None = None) -> str:
|
||||
async def _nav_items_sx(items: list[tuple[str, str]], current: str | None = None) -> str:
|
||||
"""Build nav link items as sx."""
|
||||
parts = []
|
||||
for label, href in items:
|
||||
parts.append(sx_call("nav-link",
|
||||
parts.append(await render_to_sx("nav-link",
|
||||
href=href, label=label,
|
||||
is_selected="true" if current == label else None,
|
||||
select_colours="aria-selected:bg-violet-200 aria-selected:text-violet-900",
|
||||
@@ -120,9 +120,9 @@ def _nav_items_sx(items: list[tuple[str, str]], current: str | None = None) -> s
|
||||
return "(<> " + " ".join(parts) + ")"
|
||||
|
||||
|
||||
def _sx_header_sx(nav: str | None = None, *, child: str | None = None) -> str:
|
||||
async def _sx_header_sx(nav: str | None = None, *, child: str | None = None) -> str:
|
||||
"""Build the sx docs menu-row."""
|
||||
return sx_call("menu-row-sx",
|
||||
return await render_to_sx("menu-row-sx",
|
||||
id="sx-row", level=1, colour="violet",
|
||||
link_href="/", link_label="sx",
|
||||
link_label_content=SxExpr('(span :class "font-mono" "(</>) sx")'),
|
||||
@@ -132,40 +132,40 @@ def _sx_header_sx(nav: str | None = None, *, child: str | None = None) -> str:
|
||||
)
|
||||
|
||||
|
||||
def _docs_nav_sx(current: str | None = None) -> str:
|
||||
async def _docs_nav_sx(current: str | None = None) -> str:
|
||||
from content.pages import DOCS_NAV
|
||||
return _nav_items_sx(DOCS_NAV, current)
|
||||
return await _nav_items_sx(DOCS_NAV, current)
|
||||
|
||||
|
||||
def _reference_nav_sx(current: str | None = None) -> str:
|
||||
async def _reference_nav_sx(current: str | None = None) -> str:
|
||||
from content.pages import REFERENCE_NAV
|
||||
return _nav_items_sx(REFERENCE_NAV, current)
|
||||
return await _nav_items_sx(REFERENCE_NAV, current)
|
||||
|
||||
|
||||
def _protocols_nav_sx(current: str | None = None) -> str:
|
||||
async def _protocols_nav_sx(current: str | None = None) -> str:
|
||||
from content.pages import PROTOCOLS_NAV
|
||||
return _nav_items_sx(PROTOCOLS_NAV, current)
|
||||
return await _nav_items_sx(PROTOCOLS_NAV, current)
|
||||
|
||||
|
||||
def _examples_nav_sx(current: str | None = None) -> str:
|
||||
async def _examples_nav_sx(current: str | None = None) -> str:
|
||||
from content.pages import EXAMPLES_NAV
|
||||
return _nav_items_sx(EXAMPLES_NAV, current)
|
||||
return await _nav_items_sx(EXAMPLES_NAV, current)
|
||||
|
||||
|
||||
def _essays_nav_sx(current: str | None = None) -> str:
|
||||
async def _essays_nav_sx(current: str | None = None) -> str:
|
||||
from content.pages import ESSAYS_NAV
|
||||
return _nav_items_sx(ESSAYS_NAV, current)
|
||||
return await _nav_items_sx(ESSAYS_NAV, current)
|
||||
|
||||
|
||||
def _main_nav_sx(current_section: str | None = None) -> str:
|
||||
async def _main_nav_sx(current_section: str | None = None) -> str:
|
||||
from content.pages import MAIN_NAV
|
||||
return _nav_items_sx(MAIN_NAV, current_section)
|
||||
return await _nav_items_sx(MAIN_NAV, current_section)
|
||||
|
||||
|
||||
def _sub_row_sx(sub_label: str, sub_href: str, sub_nav: str,
|
||||
async def _sub_row_sx(sub_label: str, sub_href: str, sub_nav: str,
|
||||
selected: str = "") -> str:
|
||||
"""Build the level-2 sub-section menu-row."""
|
||||
return sx_call("menu-row-sx",
|
||||
return await render_to_sx("menu-row-sx",
|
||||
id="sx-sub-row", level=2, colour="violet",
|
||||
link_href=sub_href, link_label=sub_label,
|
||||
selected=selected or None,
|
||||
@@ -178,22 +178,22 @@ def _sub_row_sx(sub_label: str, sub_href: str, sub_nav: str,
|
||||
# Content builders — return sx source strings
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _doc_nav_sx(items: list[tuple[str, str]], current: str) -> str:
|
||||
async def _doc_nav_sx(items: list[tuple[str, str]], current: str) -> str:
|
||||
"""Build the in-page doc navigation pills."""
|
||||
items_sx = " ".join(
|
||||
f'(list "{label}" "{href}")'
|
||||
for label, href in items
|
||||
)
|
||||
return sx_call("doc-nav", items=SxExpr(f"(list {items_sx})"), current=current)
|
||||
return await render_to_sx("doc-nav", items=SxExpr(f"(list {items_sx})"), current=current)
|
||||
|
||||
|
||||
def _attr_table_sx(title: str, attrs: list[tuple[str, str, bool]]) -> str:
|
||||
async def _attr_table_sx(title: str, attrs: list[tuple[str, str, bool]]) -> str:
|
||||
"""Build an attribute reference table."""
|
||||
from content.pages import ATTR_DETAILS
|
||||
rows = []
|
||||
for attr, desc, exists in attrs:
|
||||
href = f"/reference/attributes/{attr}" if exists and attr in ATTR_DETAILS else None
|
||||
rows.append(sx_call("doc-attr-row", attr=attr, description=desc,
|
||||
rows.append(await render_to_sx("doc-attr-row", attr=attr, description=desc,
|
||||
exists="true" if exists else None,
|
||||
href=href))
|
||||
return (
|
||||
@@ -209,13 +209,13 @@ def _attr_table_sx(title: str, attrs: list[tuple[str, str, bool]]) -> str:
|
||||
)
|
||||
|
||||
|
||||
def _primitives_section_sx() -> str:
|
||||
async def _primitives_section_sx() -> str:
|
||||
"""Build the primitives section."""
|
||||
from content.pages import PRIMITIVES
|
||||
parts = []
|
||||
for category, prims in PRIMITIVES.items():
|
||||
prims_sx = " ".join(f'"{p}"' for p in prims)
|
||||
parts.append(sx_call("doc-primitives-table",
|
||||
parts.append(await render_to_sx("doc-primitives-table",
|
||||
category=category,
|
||||
primitives=SxExpr(f"(list {prims_sx})")))
|
||||
return " ".join(parts)
|
||||
@@ -245,8 +245,9 @@ def _headers_table_sx(title: str, headers: list[tuple[str, str, str]]) -> str:
|
||||
|
||||
|
||||
|
||||
def _docs_content_sx(slug: str) -> str:
|
||||
async def _docs_content_sx(slug: str) -> str:
|
||||
"""Route to the right docs content builder."""
|
||||
import inspect
|
||||
builders = {
|
||||
"introduction": _docs_introduction_sx,
|
||||
"getting-started": _docs_getting_started_sx,
|
||||
@@ -257,7 +258,8 @@ def _docs_content_sx(slug: str) -> str:
|
||||
"server-rendering": _docs_server_rendering_sx,
|
||||
}
|
||||
builder = builders.get(slug, _docs_introduction_sx)
|
||||
return builder()
|
||||
result = builder()
|
||||
return await result if inspect.isawaitable(result) else result
|
||||
|
||||
|
||||
def _docs_introduction_sx() -> str:
|
||||
@@ -379,8 +381,8 @@ def _docs_evaluator_sx() -> str:
|
||||
)
|
||||
|
||||
|
||||
def _docs_primitives_sx() -> str:
|
||||
prims = _primitives_section_sx()
|
||||
async def _docs_primitives_sx() -> str:
|
||||
prims = await _primitives_section_sx()
|
||||
return (
|
||||
f'(~doc-page :title "Primitives"'
|
||||
f' (~doc-section :title "Built-in functions" :id "builtins"'
|
||||
@@ -471,14 +473,16 @@ def _docs_server_rendering_sx() -> str:
|
||||
# Reference pages
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _reference_content_sx(slug: str) -> str:
|
||||
async def _reference_content_sx(slug: str) -> str:
|
||||
import inspect
|
||||
builders = {
|
||||
"attributes": _reference_attrs_sx,
|
||||
"headers": _reference_headers_sx,
|
||||
"events": _reference_events_sx,
|
||||
"js-api": _reference_js_api_sx,
|
||||
}
|
||||
return builders.get(slug or "", _reference_attrs_sx)()
|
||||
result = builders.get(slug or "", _reference_attrs_sx)()
|
||||
return await result if inspect.isawaitable(result) else result
|
||||
|
||||
|
||||
def _reference_index_sx() -> str:
|
||||
@@ -573,18 +577,22 @@ def _reference_attr_detail_sx(slug: str) -> str:
|
||||
)
|
||||
|
||||
|
||||
def _reference_attrs_sx() -> str:
|
||||
async def _reference_attrs_sx() -> str:
|
||||
from content.pages import REQUEST_ATTRS, BEHAVIOR_ATTRS, SX_UNIQUE_ATTRS, HTMX_MISSING_ATTRS
|
||||
req = await _attr_table_sx("Request Attributes", REQUEST_ATTRS)
|
||||
beh = await _attr_table_sx("Behavior Attributes", BEHAVIOR_ATTRS)
|
||||
uniq = await _attr_table_sx("Unique to sx", SX_UNIQUE_ATTRS)
|
||||
missing = await _attr_table_sx("htmx features not yet in sx", HTMX_MISSING_ATTRS)
|
||||
return (
|
||||
f'(~doc-page :title "Attribute Reference"'
|
||||
f' (p :class "text-stone-600 mb-6"'
|
||||
f' "sx attributes mirror htmx where possible. This table shows what exists, '
|
||||
f'what\'s unique to sx, and what\'s not yet implemented.")'
|
||||
f' (div :class "space-y-8"'
|
||||
f' {_attr_table_sx("Request Attributes", REQUEST_ATTRS)}'
|
||||
f' {_attr_table_sx("Behavior Attributes", BEHAVIOR_ATTRS)}'
|
||||
f' {_attr_table_sx("Unique to sx", SX_UNIQUE_ATTRS)}'
|
||||
f' {_attr_table_sx("htmx features not yet in sx", HTMX_MISSING_ATTRS)}))'
|
||||
f' {req}'
|
||||
f' {beh}'
|
||||
f' {uniq}'
|
||||
f' {missing}))'
|
||||
)
|
||||
|
||||
|
||||
@@ -2066,9 +2074,9 @@ def home_content_sx() -> str:
|
||||
)
|
||||
|
||||
|
||||
def docs_content_partial_sx(slug: str) -> str:
|
||||
async def docs_content_partial_sx(slug: str) -> str:
|
||||
"""Docs content as sx wire format."""
|
||||
inner = _docs_content_sx(slug)
|
||||
inner = await _docs_content_sx(slug)
|
||||
return (
|
||||
f'(section :id "main-panel"'
|
||||
f' :class "flex-1 md:h-full md:min-h-0 overflow-y-auto overscroll-contain js-grid-viewport"'
|
||||
@@ -2076,8 +2084,8 @@ def docs_content_partial_sx(slug: str) -> str:
|
||||
)
|
||||
|
||||
|
||||
def reference_content_partial_sx(slug: str) -> str:
|
||||
inner = _reference_content_sx(slug)
|
||||
async def reference_content_partial_sx(slug: str) -> str:
|
||||
inner = await _reference_content_sx(slug)
|
||||
return (
|
||||
f'(section :id "main-panel"'
|
||||
f' :class "flex-1 md:h-full md:min-h-0 overflow-y-auto overscroll-contain js-grid-viewport"'
|
||||
@@ -2085,8 +2093,8 @@ def reference_content_partial_sx(slug: str) -> str:
|
||||
)
|
||||
|
||||
|
||||
def protocol_content_partial_sx(slug: str) -> str:
|
||||
inner = _protocol_content_sx(slug)
|
||||
async def protocol_content_partial_sx(slug: str) -> str:
|
||||
inner = await _protocol_content_sx(slug)
|
||||
return (
|
||||
f'(section :id "main-panel"'
|
||||
f' :class "flex-1 md:h-full md:min-h-0 overflow-y-auto overscroll-contain js-grid-viewport"'
|
||||
@@ -2094,8 +2102,8 @@ def protocol_content_partial_sx(slug: str) -> str:
|
||||
)
|
||||
|
||||
|
||||
def examples_content_partial_sx(slug: str) -> str:
|
||||
inner = _examples_content_sx(slug)
|
||||
async def examples_content_partial_sx(slug: str) -> str:
|
||||
inner = await _examples_content_sx(slug)
|
||||
return (
|
||||
f'(section :id "main-panel"'
|
||||
f' :class "flex-1 md:h-full md:min-h-0 overflow-y-auto overscroll-contain js-grid-viewport"'
|
||||
@@ -2103,8 +2111,8 @@ def examples_content_partial_sx(slug: str) -> str:
|
||||
)
|
||||
|
||||
|
||||
def essay_content_partial_sx(slug: str) -> str:
|
||||
inner = _essay_content_sx(slug)
|
||||
async def essay_content_partial_sx(slug: str) -> str:
|
||||
inner = await _essay_content_sx(slug)
|
||||
return (
|
||||
f'(section :id "main-panel"'
|
||||
f' :class "flex-1 md:h-full md:min-h-0 overflow-y-auto overscroll-contain js-grid-viewport"'
|
||||
|
||||
@@ -64,7 +64,7 @@ def register(url_prefix: str = "/") -> Blueprint:
|
||||
# S-expression wire format — sx.js renders client-side
|
||||
from shared.sx.helpers import sx_response
|
||||
from sx.sx_components import test_detail_sx
|
||||
return sx_response(test_detail_sx(test))
|
||||
return sx_response(await test_detail_sx(test))
|
||||
|
||||
# Full page render (direct navigation / refresh)
|
||||
from shared.sx.page import get_template_context
|
||||
|
||||
@@ -6,7 +6,7 @@ from datetime import datetime
|
||||
|
||||
from shared.sx.jinja_bridge import load_service_components
|
||||
from shared.sx.helpers import (
|
||||
sx_call, SxExpr,
|
||||
render_to_sx, SxExpr,
|
||||
root_header_sx, full_page_sx, header_child_sx,
|
||||
)
|
||||
|
||||
@@ -50,9 +50,9 @@ def _filter_tests(tests: list[dict], active_filter: str | None,
|
||||
# Results partial
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def test_detail_sx(test: dict) -> str:
|
||||
async def test_detail_sx(test: dict) -> str:
|
||||
"""Return s-expression wire format for a test detail view."""
|
||||
inner = sx_call(
|
||||
inner = await render_to_sx(
|
||||
"test-detail",
|
||||
nodeid=test["nodeid"],
|
||||
outcome=test["outcome"],
|
||||
@@ -70,10 +70,10 @@ def test_detail_sx(test: dict) -> str:
|
||||
# Sx-native versions — return sx source (not HTML)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _test_header_sx(ctx: dict, active_service: str | None = None) -> str:
|
||||
async def _test_header_sx(ctx: dict, active_service: str | None = None) -> str:
|
||||
"""Build the Tests menu-row as sx call."""
|
||||
nav = _service_nav_sx(ctx, active_service)
|
||||
return sx_call("menu-row-sx",
|
||||
nav = await _service_nav_sx(ctx, active_service)
|
||||
return await render_to_sx("menu-row-sx",
|
||||
id="test-row", level=1, colour="sky",
|
||||
link_href="/", link_label="Tests", icon="fa fa-flask",
|
||||
nav=SxExpr(nav),
|
||||
@@ -81,17 +81,17 @@ def _test_header_sx(ctx: dict, active_service: str | None = None) -> str:
|
||||
)
|
||||
|
||||
|
||||
def _service_nav_sx(ctx: dict, active_service: str | None = None) -> str:
|
||||
async def _service_nav_sx(ctx: dict, active_service: str | None = None) -> str:
|
||||
"""Service filter nav as sx."""
|
||||
from runner import _SERVICE_ORDER
|
||||
parts = []
|
||||
parts.append(sx_call("nav-link",
|
||||
parts.append(await render_to_sx("nav-link",
|
||||
href="/", label="all",
|
||||
is_selected="true" if not active_service else None,
|
||||
select_colours="aria-selected:bg-sky-200 aria-selected:text-sky-900",
|
||||
))
|
||||
for svc in _SERVICE_ORDER:
|
||||
parts.append(sx_call("nav-link",
|
||||
parts.append(await render_to_sx("nav-link",
|
||||
href=f"/?service={svc}", label=svc,
|
||||
is_selected="true" if active_service == svc else None,
|
||||
select_colours="aria-selected:bg-sky-200 aria-selected:text-sky-900",
|
||||
@@ -99,19 +99,19 @@ def _service_nav_sx(ctx: dict, active_service: str | None = None) -> str:
|
||||
return "(<> " + " ".join(parts) + ")"
|
||||
|
||||
|
||||
def _header_stack_sx(ctx: dict, active_service: str | None = None) -> str:
|
||||
async def _header_stack_sx(ctx: dict, active_service: str | None = None) -> str:
|
||||
"""Full header stack as sx."""
|
||||
hdr = root_header_sx(ctx)
|
||||
inner = _test_header_sx(ctx, active_service)
|
||||
child = header_child_sx(inner)
|
||||
hdr = await root_header_sx(ctx)
|
||||
inner = await _test_header_sx(ctx, active_service)
|
||||
child = await header_child_sx(inner)
|
||||
return "(<> " + hdr + " " + child + ")"
|
||||
|
||||
|
||||
def _test_rows_sx(tests: list[dict]) -> str:
|
||||
async def _test_rows_sx(tests: list[dict]) -> str:
|
||||
"""Render all test result rows as sx."""
|
||||
parts = []
|
||||
for t in tests:
|
||||
parts.append(sx_call("test-row",
|
||||
parts.append(await render_to_sx("test-row",
|
||||
nodeid=t["nodeid"],
|
||||
outcome=t["outcome"],
|
||||
duration=str(t["duration"]),
|
||||
@@ -120,46 +120,46 @@ def _test_rows_sx(tests: list[dict]) -> str:
|
||||
return "(<> " + " ".join(parts) + ")"
|
||||
|
||||
|
||||
def _grouped_rows_sx(tests: list[dict]) -> str:
|
||||
async def _grouped_rows_sx(tests: list[dict]) -> str:
|
||||
"""Test rows grouped by service as sx."""
|
||||
from runner import group_tests_by_service
|
||||
sections = group_tests_by_service(tests)
|
||||
parts = []
|
||||
for sec in sections:
|
||||
parts.append(sx_call("test-service-header",
|
||||
parts.append(await render_to_sx("test-service-header",
|
||||
service=sec["service"],
|
||||
total=str(sec["total"]),
|
||||
passed=str(sec["passed"]),
|
||||
failed=str(sec["failed"]),
|
||||
))
|
||||
parts.append(_test_rows_sx(sec["tests"]))
|
||||
parts.append(await _test_rows_sx(sec["tests"]))
|
||||
return "(<> " + " ".join(parts) + ")"
|
||||
|
||||
|
||||
def _results_partial_sx(result: dict | None, running: bool, csrf: str,
|
||||
async def _results_partial_sx(result: dict | None, running: bool, csrf: str,
|
||||
active_filter: str | None = None,
|
||||
active_service: str | None = None) -> str:
|
||||
"""Results section as sx."""
|
||||
if running and not result:
|
||||
summary = sx_call("test-summary",
|
||||
summary = await render_to_sx("test-summary",
|
||||
status="running", passed="0", failed="0", errors="0",
|
||||
skipped="0", total="0", duration="...",
|
||||
last_run="in progress", running=True, csrf=csrf,
|
||||
active_filter=active_filter,
|
||||
)
|
||||
return "(<> " + summary + " " + sx_call("test-running-indicator") + ")"
|
||||
return "(<> " + summary + " " + await render_to_sx("test-running-indicator") + ")"
|
||||
|
||||
if not result:
|
||||
summary = sx_call("test-summary",
|
||||
summary = await render_to_sx("test-summary",
|
||||
status=None, passed="0", failed="0", errors="0",
|
||||
skipped="0", total="0", duration="0",
|
||||
last_run="never", running=running, csrf=csrf,
|
||||
active_filter=active_filter,
|
||||
)
|
||||
return "(<> " + summary + " " + sx_call("test-no-results") + ")"
|
||||
return "(<> " + summary + " " + await render_to_sx("test-no-results") + ")"
|
||||
|
||||
status = "running" if running else result["status"]
|
||||
summary = sx_call("test-summary",
|
||||
summary = await render_to_sx("test-summary",
|
||||
status=status,
|
||||
passed=str(result["passed"]),
|
||||
failed=str(result["failed"]),
|
||||
@@ -174,16 +174,16 @@ def _results_partial_sx(result: dict | None, running: bool, csrf: str,
|
||||
)
|
||||
|
||||
if running:
|
||||
return "(<> " + summary + " " + sx_call("test-running-indicator") + ")"
|
||||
return "(<> " + summary + " " + await render_to_sx("test-running-indicator") + ")"
|
||||
|
||||
tests = result.get("tests", [])
|
||||
tests = _filter_tests(tests, active_filter, active_service)
|
||||
if not tests:
|
||||
return "(<> " + summary + " " + sx_call("test-no-results") + ")"
|
||||
return "(<> " + summary + " " + await render_to_sx("test-no-results") + ")"
|
||||
|
||||
has_failures = result["failed"] > 0 or result["errors"] > 0
|
||||
rows = _grouped_rows_sx(tests)
|
||||
table = sx_call("test-results-table",
|
||||
rows = await _grouped_rows_sx(tests)
|
||||
table = await render_to_sx("test-results-table",
|
||||
rows=SxExpr(rows),
|
||||
has_failures=str(has_failures).lower(),
|
||||
)
|
||||
@@ -203,10 +203,10 @@ async def render_dashboard_page_sx(ctx: dict, result: dict | None,
|
||||
active_filter: str | None = None,
|
||||
active_service: str | None = None) -> str:
|
||||
"""Full page: test dashboard (sx wire format)."""
|
||||
hdr = _header_stack_sx(ctx, active_service)
|
||||
inner = _results_partial_sx(result, running, csrf, active_filter, active_service)
|
||||
hdr = await _header_stack_sx(ctx, active_service)
|
||||
inner = await _results_partial_sx(result, running, csrf, active_filter, active_service)
|
||||
content = _wrap_results_div_sx(inner, running)
|
||||
return full_page_sx(ctx, header_rows=hdr, content=content)
|
||||
return await full_page_sx(ctx, header_rows=hdr, content=content)
|
||||
|
||||
|
||||
async def render_results_partial_sx(result: dict | None, running: bool,
|
||||
@@ -214,25 +214,27 @@ async def render_results_partial_sx(result: dict | None, running: bool,
|
||||
active_filter: str | None = None,
|
||||
active_service: str | None = None) -> str:
|
||||
"""HTMX partial: results section (sx wire format)."""
|
||||
inner = _results_partial_sx(result, running, csrf, active_filter, active_service)
|
||||
inner = await _results_partial_sx(result, running, csrf, active_filter, active_service)
|
||||
return _wrap_results_div_sx(inner, running)
|
||||
|
||||
|
||||
async def render_test_detail_page_sx(ctx: dict, test: dict) -> str:
|
||||
"""Full page: test detail (sx wire format)."""
|
||||
root_hdr = root_header_sx(ctx)
|
||||
test_row = _test_header_sx(ctx)
|
||||
detail_row = sx_call("menu-row-sx",
|
||||
root_hdr = await root_header_sx(ctx)
|
||||
test_row = await _test_header_sx(ctx)
|
||||
detail_row = await render_to_sx("menu-row-sx",
|
||||
id="test-detail-row", level=2, colour="sky",
|
||||
link_href=f"/test/{test['nodeid']}",
|
||||
link_label=test["nodeid"].rsplit("::", 1)[-1],
|
||||
)
|
||||
inner = "(<> " + test_row + " " + header_child_sx(detail_row, id="test-header-child") + ")"
|
||||
hdr = "(<> " + root_hdr + " " + header_child_sx(inner) + ")"
|
||||
content = sx_call("test-detail",
|
||||
hdr_child_detail = await header_child_sx(detail_row, id="test-header-child")
|
||||
inner = "(<> " + test_row + " " + hdr_child_detail + ")"
|
||||
hdr_child_inner = await header_child_sx(inner)
|
||||
hdr = "(<> " + root_hdr + " " + hdr_child_inner + ")"
|
||||
content = await render_to_sx("test-detail",
|
||||
nodeid=test["nodeid"],
|
||||
outcome=test["outcome"],
|
||||
duration=str(test["duration"]),
|
||||
longrepr=test.get("longrepr", ""),
|
||||
)
|
||||
return full_page_sx(ctx, header_rows=hdr, content=content)
|
||||
return await full_page_sx(ctx, header_rows=hdr, content=content)
|
||||
|
||||
Reference in New Issue
Block a user