Remove render_to_sx from public API: enforce sx_call for all service code
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m44s
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m44s
Replace ~250 render_to_sx calls across all services with sync sx_call, converting many async functions to sync where no other awaits remained. Make render_to_sx/render_to_sx_with_env private (_render_to_sx). Add (post-header-ctx) IO primitive and shared post/post-admin defmacros. Convert built-in post/post-admin layouts from Python to register_sx_layout with .sx defcomps. Remove dead post_admin_mobile_nav_sx. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -13,7 +13,7 @@ from sqlalchemy import select
|
||||
|
||||
from shared.models import UserNewsletter
|
||||
from shared.infrastructure.fragments import fetch_fragments
|
||||
from shared.sx.helpers import sx_response, render_to_sx
|
||||
from shared.sx.helpers import sx_response, sx_call
|
||||
|
||||
|
||||
def register(url_prefix="/"):
|
||||
@@ -66,7 +66,7 @@ def register(url_prefix="/"):
|
||||
translate = "translate-x-6" if un.subscribed else "translate-x-1"
|
||||
checked = "true" if un.subscribed else "false"
|
||||
|
||||
return sx_response(await render_to_sx(
|
||||
return sx_response(sx_call(
|
||||
"account-newsletter-toggle",
|
||||
id=f"nl-{nid}", url=toggle_url,
|
||||
hdrs=f'{{"X-CSRFToken": "{csrf}"}}',
|
||||
|
||||
@@ -47,11 +47,11 @@ ACCOUNT_SESSION_KEY = "account_sid"
|
||||
|
||||
async def _render_auth_page(component: str, title: str, **kwargs) -> str:
|
||||
"""Render an auth page with root layout — replaces sx_components helpers."""
|
||||
from shared.sx.helpers import render_to_sx, full_page_sx, root_header_sx
|
||||
from shared.sx.helpers import sx_call, full_page_sx, root_header_sx
|
||||
from shared.sx.page import get_template_context
|
||||
ctx = await get_template_context()
|
||||
hdr = await root_header_sx(ctx)
|
||||
content = await render_to_sx(component, **{k: v for k, v in kwargs.items() if v})
|
||||
content = sx_call(component, **{k: v for k, v in kwargs.items() if v})
|
||||
return await full_page_sx(ctx, header_rows=hdr, content=content,
|
||||
meta_html=f"<title>{title}</title>")
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ from .services.pages_data import pages_data
|
||||
from shared.browser.app.redis_cacher import cache_page, invalidate_tag_cache
|
||||
from shared.browser.app.utils.htmx import is_htmx_request
|
||||
from shared.browser.app.authz import require_admin
|
||||
from shared.sx.helpers import sx_response, render_to_sx
|
||||
from shared.sx.helpers import sx_response, sx_call
|
||||
from shared.utils import host_url
|
||||
|
||||
def register(url_prefix, title):
|
||||
@@ -67,7 +67,7 @@ def register(url_prefix, title):
|
||||
from shared.sx.helpers import root_header_sx, full_page_sx
|
||||
from shared.sx.parser import SxExpr
|
||||
root_hdr = await root_header_sx(tctx)
|
||||
blog_hdr = await render_to_sx("menu-row-sx",
|
||||
blog_hdr = sx_call("menu-row-sx",
|
||||
id="blog-row", level=1,
|
||||
link_label_content=SxExpr("(div)"),
|
||||
child_id="blog-header-child")
|
||||
@@ -132,7 +132,7 @@ def register(url_prefix, title):
|
||||
|
||||
from shared.sx.page import get_template_context
|
||||
from shared.sx.helpers import (
|
||||
render_to_sx, root_header_sx, full_page_sx, oob_page_sx,
|
||||
sx_call, root_header_sx, full_page_sx, oob_page_sx,
|
||||
post_header_sx, oob_header_sx, mobile_menu_sx,
|
||||
post_mobile_nav_sx, mobile_root_nav_sx,
|
||||
)
|
||||
@@ -143,11 +143,11 @@ def register(url_prefix, title):
|
||||
tctx.update(ctx)
|
||||
|
||||
post = ctx.get("post", {})
|
||||
content = await render_to_sx("blog-home-main",
|
||||
content = sx_call("blog-home-main",
|
||||
html_content=post.get("html", ""),
|
||||
sx_content=SxExpr(post.get("sx_content", "")) if post.get("sx_content") else None)
|
||||
meta_data = services.blog_page.post_meta_data(post, ctx.get("base_title", ""))
|
||||
meta = await render_to_sx("blog-meta", **meta_data)
|
||||
meta = sx_call("blog-meta", **meta_data)
|
||||
|
||||
if not is_htmx_request():
|
||||
root_hdr = await root_header_sx(tctx)
|
||||
@@ -171,12 +171,12 @@ def register(url_prefix, title):
|
||||
"""Blog listing — moved from / to /index."""
|
||||
from shared.services.registry import services
|
||||
from shared.sx.helpers import (
|
||||
render_to_sx, root_header_sx, full_page_sx, oob_page_sx, oob_header_sx,
|
||||
sx_call, root_header_sx, full_page_sx, oob_page_sx, oob_header_sx,
|
||||
)
|
||||
from shared.sx.parser import SxExpr
|
||||
|
||||
async def _blog_hdr(ctx, oob=False):
|
||||
return await render_to_sx("menu-row-sx",
|
||||
def _blog_hdr(ctx, oob=False):
|
||||
return sx_call("menu-row-sx",
|
||||
id="blog-row", level=1,
|
||||
link_label_content=SxExpr("(div)"),
|
||||
child_id="blog-header-child", oob=oob)
|
||||
@@ -184,16 +184,16 @@ def register(url_prefix, title):
|
||||
data = await services.blog_page.index_data(g.s)
|
||||
|
||||
# Render content, aside, and filter via .sx defcomps
|
||||
content = await render_to_sx("blog-index-main-content", **data)
|
||||
aside = await render_to_sx("blog-index-aside-content", **data)
|
||||
filter_sx = await render_to_sx("blog-index-filter-content", **data)
|
||||
content = sx_call("blog-index-main-content", **data)
|
||||
aside = sx_call("blog-index-aside-content", **data)
|
||||
filter_sx = sx_call("blog-index-filter-content", **data)
|
||||
|
||||
from shared.sx.page import get_template_context
|
||||
tctx = await get_template_context()
|
||||
|
||||
if not is_htmx_request():
|
||||
root_hdr = await root_header_sx(tctx)
|
||||
blog_hdr = await _blog_hdr(tctx)
|
||||
blog_hdr = _blog_hdr(tctx)
|
||||
header_rows = "(<> " + root_hdr + " " + blog_hdr + ")"
|
||||
html = await full_page_sx(tctx, header_rows=header_rows,
|
||||
content=content, aside=aside, filter=filter_sx)
|
||||
@@ -203,7 +203,7 @@ def register(url_prefix, title):
|
||||
return sx_response(content)
|
||||
else:
|
||||
root_hdr = await root_header_sx(tctx)
|
||||
blog_hdr = await _blog_hdr(tctx)
|
||||
blog_hdr = _blog_hdr(tctx)
|
||||
rows = "(<> " + root_hdr + " " + blog_hdr + ")"
|
||||
header_oob = await oob_header_sx("root-header-child", "blog-header-child", rows)
|
||||
sx_src = await oob_page_sx(oobs=header_oob, content=content,
|
||||
@@ -231,7 +231,7 @@ def register(url_prefix, title):
|
||||
from shared.sx.page import get_template_context
|
||||
from sxc.pages.renders import render_editor_panel
|
||||
tctx = await get_template_context()
|
||||
tctx["editor_html"] = await render_editor_panel(save_error="Invalid JSON in editor content.")
|
||||
tctx["editor_html"] = render_editor_panel(save_error="Invalid JSON in editor content.")
|
||||
html = await _render_new_post_page(tctx)
|
||||
return await make_response(html, 400)
|
||||
|
||||
@@ -240,7 +240,7 @@ def register(url_prefix, title):
|
||||
from shared.sx.page import get_template_context
|
||||
from sxc.pages.renders import render_editor_panel
|
||||
tctx = await get_template_context()
|
||||
tctx["editor_html"] = await render_editor_panel(save_error=reason)
|
||||
tctx["editor_html"] = render_editor_panel(save_error=reason)
|
||||
html = await _render_new_post_page(tctx)
|
||||
return await make_response(html, 400)
|
||||
|
||||
@@ -287,7 +287,7 @@ def register(url_prefix, title):
|
||||
from shared.sx.page import get_template_context
|
||||
from sxc.pages.renders import render_editor_panel
|
||||
tctx = await get_template_context()
|
||||
tctx["editor_html"] = await render_editor_panel(save_error="Invalid JSON in editor content.", is_page=True)
|
||||
tctx["editor_html"] = 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)
|
||||
@@ -297,7 +297,7 @@ def register(url_prefix, title):
|
||||
from shared.sx.page import get_template_context
|
||||
from sxc.pages.renders import render_editor_panel
|
||||
tctx = await get_template_context()
|
||||
tctx["editor_html"] = await render_editor_panel(save_error=reason, is_page=True)
|
||||
tctx["editor_html"] = 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)
|
||||
|
||||
@@ -13,12 +13,12 @@ from .services.menu_items import (
|
||||
MenuItemError,
|
||||
)
|
||||
from markupsafe import escape
|
||||
from shared.sx.helpers import sx_response, render_to_sx
|
||||
from shared.sx.helpers import sx_response, sx_call
|
||||
from shared.sx.parser import SxExpr
|
||||
from shared.browser.app.csrf import generate_csrf_token
|
||||
|
||||
|
||||
async def _render_menu_items_list(menu_items):
|
||||
def _render_menu_items_list(menu_items):
|
||||
"""Serialize ORM menu items and render via .sx defcomp."""
|
||||
csrf = generate_csrf_token()
|
||||
items = []
|
||||
@@ -32,8 +32,8 @@ async def _render_menu_items_list(menu_items):
|
||||
"delete_url": url_for("menu_items.delete_menu_item_route", item_id=item.id),
|
||||
})
|
||||
new_url = url_for("menu_items.new_menu_item")
|
||||
return await render_to_sx("blog-menu-items-content",
|
||||
menu_items=items, new_url=new_url, csrf=csrf)
|
||||
return sx_call("blog-menu-items-content",
|
||||
menu_items=items, new_url=new_url, csrf=csrf)
|
||||
|
||||
|
||||
def _render_menu_item_form(menu_item=None) -> str:
|
||||
@@ -120,16 +120,16 @@ document.addEventListener('click', function(e) {{
|
||||
return html
|
||||
|
||||
|
||||
async def _render_page_search_results(pages, query, page, has_more) -> str:
|
||||
def _render_page_search_results(pages, query, page, has_more) -> str:
|
||||
"""Render page search results."""
|
||||
if not pages and query:
|
||||
return await render_to_sx("page-search-empty", query=query)
|
||||
return sx_call("page-search-empty", query=query)
|
||||
if not pages:
|
||||
return ""
|
||||
|
||||
items = []
|
||||
for post in pages:
|
||||
items.append(await render_to_sx("page-search-item",
|
||||
items.append(sx_call("page-search-item",
|
||||
id=post.id, title=post.title,
|
||||
slug=post.slug,
|
||||
feature_image=post.feature_image or None))
|
||||
@@ -137,22 +137,22 @@ async def _render_page_search_results(pages, query, page, has_more) -> str:
|
||||
sentinel = ""
|
||||
if has_more:
|
||||
search_url = url_for("menu_items.search_pages_route")
|
||||
sentinel = await render_to_sx("page-search-sentinel",
|
||||
sentinel = sx_call("page-search-sentinel",
|
||||
url=search_url, query=query,
|
||||
next_page=page + 1)
|
||||
|
||||
items_sx = "(<> " + " ".join(items) + ")"
|
||||
return await render_to_sx("page-search-results",
|
||||
return sx_call("page-search-results",
|
||||
items=SxExpr(items_sx),
|
||||
sentinel=SxExpr(sentinel) if sentinel else None)
|
||||
|
||||
|
||||
async def _render_menu_items_nav_oob(menu_items) -> str:
|
||||
def _render_menu_items_nav_oob(menu_items) -> str:
|
||||
"""Render OOB nav update for menu items."""
|
||||
from quart import request as qrequest
|
||||
|
||||
if not menu_items:
|
||||
return await render_to_sx("blog-nav-empty", wrapper_id="menu-items-nav-wrapper")
|
||||
return sx_call("blog-nav-empty", wrapper_id="menu-items-nav-wrapper")
|
||||
|
||||
first_seg = qrequest.path.strip("/").split("/")[0] if qrequest else ""
|
||||
|
||||
@@ -185,23 +185,23 @@ async def _render_menu_items_nav_oob(menu_items) -> str:
|
||||
href = f"/{item_slug}/"
|
||||
selected = "true" if item_slug == first_seg else "false"
|
||||
|
||||
img_sx = await render_to_sx("img-or-placeholder", src=fi, alt=label,
|
||||
img_sx = sx_call("img-or-placeholder", src=fi, alt=label,
|
||||
size_cls="w-8 h-8 rounded-full object-cover flex-shrink-0")
|
||||
|
||||
if item_slug != "cart":
|
||||
item_parts.append(await render_to_sx("blog-nav-item-link",
|
||||
item_parts.append(sx_call("blog-nav-item-link",
|
||||
href=href, hx_get=f"/{item_slug}/", selected=selected,
|
||||
nav_cls=nav_button_cls, img=SxExpr(img_sx), label=label,
|
||||
))
|
||||
else:
|
||||
item_parts.append(await render_to_sx("blog-nav-item-plain",
|
||||
item_parts.append(sx_call("blog-nav-item-plain",
|
||||
href=href, selected=selected, nav_cls=nav_button_cls,
|
||||
img=SxExpr(img_sx), label=label,
|
||||
))
|
||||
|
||||
items_sx = "(<> " + " ".join(item_parts) + ")" if item_parts else ""
|
||||
|
||||
return await render_to_sx("scroll-nav-wrapper",
|
||||
return sx_call("scroll-nav-wrapper",
|
||||
wrapper_id="menu-items-nav-wrapper", container_id=container_id,
|
||||
arrow_cls=arrow_cls,
|
||||
left_hs=f"on click set #{container_id}.scrollLeft to #{container_id}.scrollLeft - 200",
|
||||
@@ -214,9 +214,9 @@ async def _render_menu_items_nav_oob(menu_items) -> str:
|
||||
def register():
|
||||
bp = Blueprint("menu_items", __name__, url_prefix='/settings/menu_items')
|
||||
|
||||
async def get_menu_items_nav_oob_async(menu_items):
|
||||
def get_menu_items_nav_oob_sync(menu_items):
|
||||
"""Helper to generate OOB update for root nav menu items"""
|
||||
return await _render_menu_items_nav_oob(menu_items)
|
||||
return _render_menu_items_nav_oob(menu_items)
|
||||
|
||||
@bp.get("/new/")
|
||||
@require_admin
|
||||
@@ -245,8 +245,8 @@ def register():
|
||||
|
||||
# Get updated list and nav OOB
|
||||
menu_items = await get_all_menu_items(g.s)
|
||||
html = await _render_menu_items_list(menu_items)
|
||||
nav_oob = await get_menu_items_nav_oob_async(menu_items)
|
||||
html = _render_menu_items_list(menu_items)
|
||||
nav_oob = get_menu_items_nav_oob_sync(menu_items)
|
||||
return sx_response(html + nav_oob)
|
||||
|
||||
except MenuItemError as e:
|
||||
@@ -283,8 +283,8 @@ def register():
|
||||
|
||||
# Get updated list and nav OOB
|
||||
menu_items = await get_all_menu_items(g.s)
|
||||
html = await _render_menu_items_list(menu_items)
|
||||
nav_oob = await get_menu_items_nav_oob_async(menu_items)
|
||||
html = _render_menu_items_list(menu_items)
|
||||
nav_oob = get_menu_items_nav_oob_sync(menu_items)
|
||||
return sx_response(html + nav_oob)
|
||||
|
||||
except MenuItemError as e:
|
||||
@@ -303,8 +303,8 @@ def register():
|
||||
|
||||
# Get updated list and nav OOB
|
||||
menu_items = await get_all_menu_items(g.s)
|
||||
html = await _render_menu_items_list(menu_items)
|
||||
nav_oob = await get_menu_items_nav_oob_async(menu_items)
|
||||
html = _render_menu_items_list(menu_items)
|
||||
nav_oob = get_menu_items_nav_oob_sync(menu_items)
|
||||
return sx_response(html + nav_oob)
|
||||
|
||||
@bp.get("/pages/search/")
|
||||
@@ -318,7 +318,7 @@ def register():
|
||||
pages, total = await search_pages(g.s, query, page, per_page)
|
||||
has_more = (page * per_page) < total
|
||||
|
||||
return sx_response(await _render_page_search_results(pages, query, page, has_more))
|
||||
return sx_response(_render_page_search_results(pages, query, page, has_more))
|
||||
|
||||
@bp.post("/reorder/")
|
||||
@require_admin
|
||||
@@ -342,8 +342,8 @@ def register():
|
||||
|
||||
# Get updated list and nav OOB
|
||||
menu_items = await get_all_menu_items(g.s)
|
||||
html = await _render_menu_items_list(menu_items)
|
||||
nav_oob = await get_menu_items_nav_oob_async(menu_items)
|
||||
html = _render_menu_items_list(menu_items)
|
||||
nav_oob = get_menu_items_nav_oob_sync(menu_items)
|
||||
return sx_response(html + nav_oob)
|
||||
|
||||
return bp
|
||||
|
||||
@@ -11,7 +11,7 @@ from quart import (
|
||||
)
|
||||
from shared.browser.app.authz import require_admin, require_post_author
|
||||
from markupsafe import escape
|
||||
from shared.sx.helpers import sx_response, render_to_sx
|
||||
from shared.sx.helpers import sx_response, sx_call
|
||||
from shared.sx.parser import SxExpr, serialize as sx_serialize
|
||||
from shared.utils import host_url
|
||||
|
||||
@@ -60,10 +60,10 @@ def _post_to_edit_dict(post) -> dict:
|
||||
return d
|
||||
|
||||
|
||||
async def _render_features(features, post, result):
|
||||
def _render_features(features, post, result):
|
||||
"""Render features panel via .sx defcomp."""
|
||||
slug = post.get("slug", "")
|
||||
return await render_to_sx("blog-features-panel-content",
|
||||
return sx_call("blog-features-panel-content",
|
||||
features_url=host_url(url_for("blog.post.admin.update_features", slug=slug)),
|
||||
calendar_checked=bool(features.get("calendar")),
|
||||
market_checked=bool(features.get("market")),
|
||||
@@ -187,7 +187,7 @@ def _render_calendar_view(
|
||||
return _raw_html_sx(html)
|
||||
|
||||
|
||||
async def _render_associated_entries(all_calendars, associated_entry_ids, post_slug: str) -> str:
|
||||
def _render_associated_entries(all_calendars, associated_entry_ids, post_slug: str) -> str:
|
||||
"""Render the associated entries panel."""
|
||||
from shared.browser.app.csrf import generate_csrf_token
|
||||
from quart import url_for as qurl
|
||||
@@ -216,13 +216,13 @@ async def _render_associated_entries(all_calendars, associated_entry_ids, post_s
|
||||
|
||||
toggle_url = host_url(qurl("blog.post.admin.toggle_entry", slug=post_slug, entry_id=e_id))
|
||||
|
||||
img_sx = await render_to_sx("blog-entry-image", src=cal_fi, title=cal_title)
|
||||
img_sx = sx_call("blog-entry-image", src=cal_fi, title=cal_title)
|
||||
|
||||
date_str = e_start.strftime("%A, %B %d, %Y at %H:%M") if e_start else ""
|
||||
if e_end:
|
||||
date_str += f" \u2013 {e_end.strftime('%H:%M')}"
|
||||
|
||||
entry_items.append(await render_to_sx("blog-associated-entry",
|
||||
entry_items.append(sx_call("blog-associated-entry",
|
||||
confirm_text=f"This will remove {e_name} from this post",
|
||||
toggle_url=toggle_url,
|
||||
hx_headers=f'{{"X-CSRFToken": "{csrf}"}}',
|
||||
@@ -231,16 +231,16 @@ async def _render_associated_entries(all_calendars, associated_entry_ids, post_s
|
||||
))
|
||||
|
||||
if has_entries:
|
||||
content_sx = await render_to_sx("blog-associated-entries-content",
|
||||
content_sx = sx_call("blog-associated-entries-content",
|
||||
items=SxExpr("(<> " + " ".join(entry_items) + ")"),
|
||||
)
|
||||
else:
|
||||
content_sx = await render_to_sx("blog-associated-entries-empty")
|
||||
content_sx = sx_call("blog-associated-entries-empty")
|
||||
|
||||
return await render_to_sx("blog-associated-entries-panel", content=SxExpr(content_sx))
|
||||
return sx_call("blog-associated-entries-panel", content=SxExpr(content_sx))
|
||||
|
||||
|
||||
async def _render_nav_entries_oob(associated_entries, calendars, post: dict) -> str:
|
||||
def _render_nav_entries_oob(associated_entries, calendars, post: dict) -> str:
|
||||
"""Render the OOB nav entries swap."""
|
||||
entries_list = []
|
||||
if associated_entries and hasattr(associated_entries, "entries"):
|
||||
@@ -249,7 +249,7 @@ async def _render_nav_entries_oob(associated_entries, calendars, post: dict) ->
|
||||
has_items = bool(entries_list or calendars)
|
||||
|
||||
if not has_items:
|
||||
return await render_to_sx("blog-nav-entries-empty")
|
||||
return sx_call("blog-nav-entries-empty")
|
||||
|
||||
select_colours = (
|
||||
"[.hover-capable_&]:hover:bg-yellow-300"
|
||||
@@ -291,7 +291,7 @@ async def _render_nav_entries_oob(associated_entries, calendars, post: dict) ->
|
||||
entry_path = f"/{post_slug}/{cal_slug}/"
|
||||
date_str = ""
|
||||
|
||||
item_parts.append(await render_to_sx("calendar-entry-nav",
|
||||
item_parts.append(sx_call("calendar-entry-nav",
|
||||
href=entry_path, nav_class=nav_cls, name=e_name, date_str=date_str,
|
||||
))
|
||||
|
||||
@@ -300,13 +300,13 @@ async def _render_nav_entries_oob(associated_entries, calendars, post: dict) ->
|
||||
cal_slug = getattr(calendar, "slug", "")
|
||||
cal_path = f"/{post_slug}/{cal_slug}/"
|
||||
|
||||
item_parts.append(await render_to_sx("blog-nav-calendar-item",
|
||||
item_parts.append(sx_call("blog-nav-calendar-item",
|
||||
href=cal_path, nav_cls=nav_cls, name=cal_name,
|
||||
))
|
||||
|
||||
items_sx = "(<> " + " ".join(item_parts) + ")" if item_parts else ""
|
||||
|
||||
return await render_to_sx("scroll-nav-wrapper",
|
||||
return sx_call("scroll-nav-wrapper",
|
||||
wrapper_id="entries-calendars-nav-wrapper", container_id="associated-items-container",
|
||||
arrow_cls="entries-nav-arrow",
|
||||
left_hs="on click set #associated-items-container.scrollLeft to #associated-items-container.scrollLeft - 200",
|
||||
@@ -353,7 +353,7 @@ def register():
|
||||
})
|
||||
|
||||
features = result.get("features", {})
|
||||
html = await _render_features(features, post, result)
|
||||
html = _render_features(features, post, result)
|
||||
return sx_response(html)
|
||||
|
||||
@bp.put("/admin/sumup/")
|
||||
@@ -386,7 +386,7 @@ def register():
|
||||
result = await call_action("blog", "update-page-config", payload=payload)
|
||||
|
||||
features = result.get("features", {})
|
||||
html = await _render_features(features, post, result)
|
||||
html = _render_features(features, post, result)
|
||||
return sx_response(html)
|
||||
|
||||
@bp.get("/entries/calendar/<int:calendar_id>/")
|
||||
@@ -508,8 +508,8 @@ def register():
|
||||
|
||||
# Return the associated entries admin list + OOB update for nav entries
|
||||
post = g.post_data["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)
|
||||
admin_list = _render_associated_entries(all_calendars, associated_entry_ids, post["slug"])
|
||||
nav_entries_html = _render_nav_entries_oob(associated_entries, calendars, post)
|
||||
|
||||
return sx_response(admin_list + nav_entries_html)
|
||||
|
||||
@@ -686,7 +686,7 @@ def register():
|
||||
|
||||
slug = post.get("slug", "")
|
||||
create_url = host_url(url_for("blog.post.admin.create_market", slug=slug))
|
||||
html = await render_to_sx("blog-markets-panel-content",
|
||||
html = sx_call("blog-markets-panel-content",
|
||||
markets=_serialize_markets(page_markets, slug), create_url=create_url)
|
||||
return sx_response(html)
|
||||
|
||||
@@ -715,7 +715,7 @@ def register():
|
||||
|
||||
slug = post.get("slug", "")
|
||||
create_url = host_url(url_for("blog.post.admin.create_market", slug=slug))
|
||||
html = await render_to_sx("blog-markets-panel-content",
|
||||
html = sx_call("blog-markets-panel-content",
|
||||
markets=_serialize_markets(page_markets, slug), create_url=create_url)
|
||||
return sx_response(html)
|
||||
|
||||
@@ -738,7 +738,7 @@ def register():
|
||||
|
||||
slug = post.get("slug", "")
|
||||
create_url = host_url(url_for("blog.post.admin.create_market", slug=slug))
|
||||
html = await render_to_sx("blog-markets-panel-content",
|
||||
html = sx_call("blog-markets-panel-content",
|
||||
markets=_serialize_markets(page_markets, slug), create_url=create_url)
|
||||
return sx_response(html)
|
||||
|
||||
|
||||
@@ -106,7 +106,7 @@ def register():
|
||||
async def post_detail(slug: str):
|
||||
from shared.sx.page import get_template_context
|
||||
from shared.sx.helpers import (
|
||||
render_to_sx, root_header_sx, full_page_sx, oob_page_sx,
|
||||
sx_call, root_header_sx, full_page_sx, oob_page_sx,
|
||||
post_header_sx, oob_header_sx, mobile_menu_sx,
|
||||
post_mobile_nav_sx, mobile_root_nav_sx,
|
||||
)
|
||||
@@ -124,9 +124,9 @@ def register():
|
||||
csrf = generate_csrf_token()
|
||||
svc = services.blog_page
|
||||
detail_data = svc.post_detail_data(post, user, rights, csrf, blog_url_base)
|
||||
content = await render_to_sx("blog-post-detail-content", **detail_data)
|
||||
content = sx_call("blog-post-detail-content", **detail_data)
|
||||
meta_data = svc.post_meta_data(post, tctx.get("base_title", ""))
|
||||
meta = await render_to_sx("blog-meta", **meta_data)
|
||||
meta = sx_call("blog-meta", **meta_data)
|
||||
|
||||
if not is_htmx_request():
|
||||
root_hdr = await root_header_sx(tctx)
|
||||
@@ -149,24 +149,24 @@ def register():
|
||||
@clear_cache(tag="post.post_detail", tag_scope="user")
|
||||
async def like_toggle(slug: str):
|
||||
from shared.utils import host_url
|
||||
from shared.sx.helpers import render_to_sx
|
||||
from shared.sx.helpers import sx_call
|
||||
from shared.browser.app.csrf import generate_csrf_token
|
||||
|
||||
like_url = host_url(url_for('blog.post.like_toggle', slug=slug))
|
||||
csrf = generate_csrf_token()
|
||||
|
||||
async def _like_btn(liked):
|
||||
def _like_btn(liked):
|
||||
if liked:
|
||||
colour, icon, label = "text-red-600", "fa-solid fa-heart", "Unlike this post"
|
||||
else:
|
||||
colour, icon, label = "text-stone-300", "fa-regular fa-heart", "Like this post"
|
||||
return await render_to_sx("market-like-toggle-button",
|
||||
return sx_call("market-like-toggle-button",
|
||||
colour=colour, action=like_url,
|
||||
hx_headers=f'{{"X-CSRFToken": "{csrf}"}}',
|
||||
label=label, icon_cls=icon)
|
||||
|
||||
if not g.user:
|
||||
return sx_response(await _like_btn(False), status=403)
|
||||
return sx_response(_like_btn(False), status=403)
|
||||
|
||||
post_id = g.post_data["post"]["id"]
|
||||
user_id = g.user.id
|
||||
@@ -175,7 +175,7 @@ def register():
|
||||
"user_id": user_id, "target_type": "post", "target_id": post_id,
|
||||
})
|
||||
|
||||
return sx_response(await _like_btn(result["liked"]))
|
||||
return sx_response(_like_btn(result["liked"]))
|
||||
|
||||
@bp.get("/w/<widget_domain>/")
|
||||
async def widget_paginate(slug: str, widget_domain: str):
|
||||
|
||||
@@ -3,7 +3,7 @@ from __future__ import annotations
|
||||
from quart import Blueprint, request, g, abort
|
||||
|
||||
from shared.browser.app.authz import require_login
|
||||
from shared.sx.helpers import sx_response, render_to_sx
|
||||
from shared.sx.helpers import sx_response, sx_call
|
||||
from models import Snippet
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ async def _render_snippets():
|
||||
"""Render snippets list via service data + .sx defcomp."""
|
||||
from shared.services.registry import services
|
||||
data = await services.blog_page.snippets_data(g.s)
|
||||
return await render_to_sx("blog-snippets-content", **data)
|
||||
return sx_call("blog-snippets-content", **data)
|
||||
|
||||
|
||||
def register():
|
||||
|
||||
@@ -126,22 +126,22 @@ def _register_blog_helpers() -> None:
|
||||
|
||||
# --- Editor helpers ---
|
||||
|
||||
async def _h_editor_content(**kw):
|
||||
def _h_editor_content(**kw):
|
||||
from .renders import render_editor_panel
|
||||
return await render_editor_panel()
|
||||
return render_editor_panel()
|
||||
|
||||
|
||||
async def _h_editor_page_content(**kw):
|
||||
def _h_editor_page_content(**kw):
|
||||
from .renders import render_editor_panel
|
||||
return await render_editor_panel(is_page=True)
|
||||
return render_editor_panel(is_page=True)
|
||||
|
||||
|
||||
# --- Post admin helpers ---
|
||||
|
||||
async def _h_post_admin_content(slug=None, **kw):
|
||||
await _ensure_post_data(slug)
|
||||
from shared.sx.helpers import render_to_sx
|
||||
return await render_to_sx("blog-admin-placeholder")
|
||||
from shared.sx.helpers import sx_call
|
||||
return sx_call("blog-admin-placeholder")
|
||||
|
||||
|
||||
async def _h_post_data_content(slug=None, **kw):
|
||||
@@ -264,32 +264,32 @@ async def _h_post_preview_content(slug=None, **kw):
|
||||
await _ensure_post_data(slug)
|
||||
from quart import g
|
||||
from shared.services.registry import services
|
||||
from shared.sx.helpers import render_to_sx
|
||||
from shared.sx.helpers import sx_call
|
||||
from shared.sx.parser import SxExpr
|
||||
|
||||
preview = await services.blog_page.preview_data(g.s)
|
||||
|
||||
sections: list[str] = []
|
||||
if preview.get("sx_pretty"):
|
||||
sections.append(await render_to_sx("blog-preview-section",
|
||||
sections.append(sx_call("blog-preview-section",
|
||||
title="S-Expression Source", content=SxExpr(preview["sx_pretty"])))
|
||||
if preview.get("json_pretty"):
|
||||
sections.append(await render_to_sx("blog-preview-section",
|
||||
sections.append(sx_call("blog-preview-section",
|
||||
title="Lexical JSON", content=SxExpr(preview["json_pretty"])))
|
||||
if preview.get("sx_rendered"):
|
||||
rendered_sx = await render_to_sx("blog-preview-rendered", html=preview["sx_rendered"])
|
||||
sections.append(await render_to_sx("blog-preview-section",
|
||||
rendered_sx = sx_call("blog-preview-rendered", html=preview["sx_rendered"])
|
||||
sections.append(sx_call("blog-preview-section",
|
||||
title="SX Rendered", content=SxExpr(rendered_sx)))
|
||||
if preview.get("lex_rendered"):
|
||||
rendered_sx = await render_to_sx("blog-preview-rendered", html=preview["lex_rendered"])
|
||||
sections.append(await render_to_sx("blog-preview-section",
|
||||
rendered_sx = sx_call("blog-preview-rendered", html=preview["lex_rendered"])
|
||||
sections.append(sx_call("blog-preview-section",
|
||||
title="Lexical Rendered", content=SxExpr(rendered_sx)))
|
||||
|
||||
if not sections:
|
||||
return await render_to_sx("blog-preview-empty")
|
||||
return sx_call("blog-preview-empty")
|
||||
|
||||
inner = " ".join(sections)
|
||||
return await render_to_sx("blog-preview-panel", sections=SxExpr(f"(<> {inner})"))
|
||||
return sx_call("blog-preview-panel", sections=SxExpr(f"(<> {inner})"))
|
||||
|
||||
|
||||
async def _h_post_entries_content(slug=None, **kw):
|
||||
@@ -315,7 +315,7 @@ async def _h_post_entries_content(slug=None, **kw):
|
||||
await g.s.refresh(calendar, ["entries", "post"])
|
||||
|
||||
# Associated entries list
|
||||
assoc_html = await _render_associated_entries(all_calendars, associated_entry_ids, post_slug)
|
||||
assoc_html = _render_associated_entries(all_calendars, associated_entry_ids, post_slug)
|
||||
|
||||
# Calendar browser
|
||||
cal_items: list[str] = []
|
||||
@@ -505,7 +505,7 @@ async def _h_post_edit_content(slug=None, **kw):
|
||||
from sqlalchemy.orm import selectinload
|
||||
from shared.infrastructure.data_client import fetch_data
|
||||
from shared.browser.app.csrf import generate_csrf_token
|
||||
from shared.sx.helpers import render_to_sx
|
||||
from shared.sx.helpers import sx_call
|
||||
from shared.sx.parser import SxExpr, serialize as sx_serialize
|
||||
from bp.post.admin.routes import _post_to_edit_dict
|
||||
|
||||
@@ -584,9 +584,9 @@ async def _h_post_edit_content(slug=None, **kw):
|
||||
parts: list[str] = []
|
||||
|
||||
if save_error:
|
||||
parts.append(await render_to_sx("blog-editor-error", error=save_error))
|
||||
parts.append(sx_call("blog-editor-error", error=save_error))
|
||||
|
||||
parts.append(await render_to_sx("blog-editor-edit-form",
|
||||
parts.append(sx_call("blog-editor-edit-form",
|
||||
csrf=csrf,
|
||||
updated_at=str(updated_at),
|
||||
title_val=title_val,
|
||||
@@ -603,9 +603,9 @@ async def _h_post_edit_content(slug=None, **kw):
|
||||
footer_extra=footer_extra_sx,
|
||||
))
|
||||
|
||||
parts.append(await render_to_sx("blog-editor-publish-js", already_emailed=already_emailed))
|
||||
parts.append(await render_to_sx("blog-editor-styles", css_href=editor_css))
|
||||
parts.append(await render_to_sx("sx-editor-styles"))
|
||||
parts.append(sx_call("blog-editor-publish-js", already_emailed=already_emailed))
|
||||
parts.append(sx_call("blog-editor-styles", css_href=editor_css))
|
||||
parts.append(sx_call("sx-editor-styles"))
|
||||
|
||||
init_js = (
|
||||
'(function() {'
|
||||
@@ -705,10 +705,10 @@ async def _h_post_edit_content(slug=None, **kw):
|
||||
' }, 50); }'
|
||||
'})();'
|
||||
)
|
||||
parts.append(await render_to_sx("blog-editor-scripts",
|
||||
parts.append(sx_call("blog-editor-scripts",
|
||||
js_src=editor_js,
|
||||
sx_editor_js_src=sx_editor_js,
|
||||
init_js=init_js))
|
||||
|
||||
return await render_to_sx("blog-editor-panel",
|
||||
return sx_call("blog-editor-panel",
|
||||
parts=SxExpr("(<> " + " ".join(parts) + ")"))
|
||||
|
||||
@@ -5,19 +5,19 @@ from typing import Any
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Header helpers (moved from sx_components — thin render_to_sx wrappers)
|
||||
# Header helpers (moved from sx_components — thin sx_call wrappers)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def _settings_header_sx(ctx: dict, *, oob: bool = False) -> str:
|
||||
from shared.sx.helpers import render_to_sx
|
||||
def _settings_header_sx(ctx: dict, *, oob: bool = False) -> str:
|
||||
from shared.sx.helpers import sx_call
|
||||
from shared.sx.parser import SxExpr
|
||||
from quart import url_for as qurl
|
||||
|
||||
settings_href = qurl("settings.defpage_settings_home")
|
||||
label_sx = await render_to_sx("blog-admin-label")
|
||||
nav_sx = await _settings_nav_sx(ctx)
|
||||
label_sx = sx_call("blog-admin-label")
|
||||
nav_sx = _settings_nav_sx(ctx)
|
||||
|
||||
return await render_to_sx("menu-row-sx",
|
||||
return sx_call("menu-row-sx",
|
||||
id="root-settings-row", level=1,
|
||||
link_href=settings_href,
|
||||
link_label_content=SxExpr(label_sx),
|
||||
@@ -25,20 +25,20 @@ async def _settings_header_sx(ctx: dict, *, oob: bool = False) -> str:
|
||||
child_id="root-settings-header-child", oob=oob)
|
||||
|
||||
|
||||
async def _settings_nav_sx(ctx: dict) -> str:
|
||||
from shared.sx.helpers import render_to_sx
|
||||
return await render_to_sx("blog-settings-nav")
|
||||
def _settings_nav_sx(ctx: dict) -> str:
|
||||
from shared.sx.helpers import sx_call
|
||||
return sx_call("blog-settings-nav")
|
||||
|
||||
|
||||
async def _sub_settings_header_sx(row_id: str, child_id: str, href: str,
|
||||
icon: str, label: str, ctx: dict,
|
||||
*, oob: bool = False, nav_sx: str = "") -> str:
|
||||
from shared.sx.helpers import render_to_sx
|
||||
def _sub_settings_header_sx(row_id: str, child_id: str, href: str,
|
||||
icon: str, label: str, ctx: dict,
|
||||
*, oob: bool = False, nav_sx: str = "") -> str:
|
||||
from shared.sx.helpers import sx_call
|
||||
from shared.sx.parser import SxExpr
|
||||
|
||||
label_sx = await render_to_sx("blog-sub-settings-label",
|
||||
label_sx = sx_call("blog-sub-settings-label",
|
||||
icon=f"fa fa-{icon}", label=label)
|
||||
return await render_to_sx("menu-row-sx",
|
||||
return sx_call("menu-row-sx",
|
||||
id=row_id, level=2,
|
||||
link_href=href,
|
||||
link_label_content=SxExpr(label_sx),
|
||||
@@ -82,19 +82,19 @@ async def _settings_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env
|
||||
from shared.sx.parser import SxExpr
|
||||
return await render_to_sx_with_env("settings-layout-full", {},
|
||||
settings_header=SxExpr(await _settings_header_sx(ctx)))
|
||||
settings_header=SxExpr(_settings_header_sx(ctx)))
|
||||
|
||||
|
||||
async def _settings_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env, oob_header_sx
|
||||
from shared.sx.parser import SxExpr
|
||||
rows = await render_to_sx_with_env("settings-layout-full", {},
|
||||
settings_header=SxExpr(await _settings_header_sx(ctx)))
|
||||
settings_header=SxExpr(_settings_header_sx(ctx)))
|
||||
return await oob_header_sx("root-header-child", "root-settings-header-child", rows)
|
||||
|
||||
|
||||
async def _settings_mobile(ctx: dict, **kw: Any) -> str:
|
||||
return await _settings_nav_sx(ctx)
|
||||
def _settings_mobile(ctx: dict, **kw: Any) -> str:
|
||||
return _settings_nav_sx(ctx)
|
||||
|
||||
|
||||
# --- Sub-settings helpers ---
|
||||
@@ -105,21 +105,21 @@ async def _sub_settings_full(ctx: dict, row_id: str, child_id: str,
|
||||
from shared.sx.parser import SxExpr
|
||||
from quart import url_for as qurl
|
||||
return await render_to_sx_with_env("sub-settings-layout-full", {},
|
||||
settings_header=SxExpr(await _settings_header_sx(ctx)),
|
||||
sub_header=SxExpr(await _sub_settings_header_sx(
|
||||
settings_header=SxExpr(_settings_header_sx(ctx)),
|
||||
sub_header=SxExpr(_sub_settings_header_sx(
|
||||
row_id, child_id, qurl(endpoint), icon, label, ctx)))
|
||||
|
||||
|
||||
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, render_to_sx
|
||||
from shared.sx.helpers import oob_header_sx, sx_call
|
||||
from shared.sx.parser import SxExpr
|
||||
from quart import url_for as qurl
|
||||
settings_hdr_oob = await _settings_header_sx(ctx, oob=True)
|
||||
sub_hdr = await _sub_settings_header_sx(
|
||||
settings_hdr_oob = _settings_header_sx(ctx, oob=True)
|
||||
sub_hdr = _sub_settings_header_sx(
|
||||
row_id, child_id, qurl(endpoint), icon, label, ctx)
|
||||
sub_oob = await oob_header_sx("root-settings-header-child", child_id, sub_hdr)
|
||||
return await render_to_sx("sub-settings-layout-oob",
|
||||
return sx_call("sub-settings-layout-oob",
|
||||
settings_header_oob=SxExpr(settings_hdr_oob),
|
||||
sub_header_oob=SxExpr(sub_oob))
|
||||
|
||||
@@ -180,8 +180,8 @@ async def _tag_group_edit_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.parser import SxExpr
|
||||
g_id = (request.view_args or {}).get("id")
|
||||
return await render_to_sx_with_env("sub-settings-layout-full", {},
|
||||
settings_header=SxExpr(await _settings_header_sx(ctx)),
|
||||
sub_header=SxExpr(await _sub_settings_header_sx(
|
||||
settings_header=SxExpr(_settings_header_sx(ctx)),
|
||||
sub_header=SxExpr(_sub_settings_header_sx(
|
||||
"tag-groups-row", "tag-groups-header-child",
|
||||
qurl("defpage_tag_group_edit", id=g_id),
|
||||
"tags", "Tag Groups", ctx)))
|
||||
@@ -189,15 +189,15 @@ async def _tag_group_edit_full(ctx: dict, **kw: Any) -> str:
|
||||
|
||||
async def _tag_group_edit_oob(ctx: dict, **kw: Any) -> str:
|
||||
from quart import request, url_for as qurl
|
||||
from shared.sx.helpers import oob_header_sx, render_to_sx
|
||||
from shared.sx.helpers import oob_header_sx, sx_call
|
||||
from shared.sx.parser import SxExpr
|
||||
g_id = (request.view_args or {}).get("id")
|
||||
settings_hdr_oob = await _settings_header_sx(ctx, oob=True)
|
||||
sub_hdr = await _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",
|
||||
qurl("defpage_tag_group_edit", id=g_id),
|
||||
"tags", "Tag Groups", ctx)
|
||||
sub_oob = await oob_header_sx("root-settings-header-child", "tag-groups-header-child", sub_hdr)
|
||||
return await render_to_sx("sub-settings-layout-oob",
|
||||
return sx_call("sub-settings-layout-oob",
|
||||
settings_header_oob=SxExpr(settings_hdr_oob),
|
||||
sub_header_oob=SxExpr(sub_oob))
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
async def render_editor_panel(save_error: str | None = None, is_page: bool = False) -> str:
|
||||
def render_editor_panel(save_error: str | None = None, is_page: bool = False) -> str:
|
||||
"""Build the WYSIWYG editor panel HTML for new post/page creation."""
|
||||
import os
|
||||
from quart import url_for as qurl, current_app
|
||||
from shared.browser.app.csrf import generate_csrf_token
|
||||
from shared.sx.helpers import render_to_sx
|
||||
from shared.sx.helpers import sx_call
|
||||
|
||||
csrf = generate_csrf_token()
|
||||
asset_url_fn = current_app.jinja_env.globals.get("asset_url", lambda p: "")
|
||||
@@ -28,15 +28,15 @@ async def render_editor_panel(save_error: str | None = None, is_page: bool = Fal
|
||||
parts: list[str] = []
|
||||
|
||||
if save_error:
|
||||
parts.append(await render_to_sx("blog-editor-error", error=str(save_error)))
|
||||
parts.append(sx_call("blog-editor-error", error=str(save_error)))
|
||||
|
||||
parts.append(await render_to_sx("blog-editor-form",
|
||||
parts.append(sx_call("blog-editor-form",
|
||||
csrf=csrf, title_placeholder=title_placeholder,
|
||||
create_label=create_label,
|
||||
))
|
||||
|
||||
parts.append(await render_to_sx("blog-editor-styles", css_href=editor_css))
|
||||
parts.append(await render_to_sx("sx-editor-styles"))
|
||||
parts.append(sx_call("blog-editor-styles", css_href=editor_css))
|
||||
parts.append(sx_call("sx-editor-styles"))
|
||||
|
||||
init_js = (
|
||||
"console.log('[EDITOR-DEBUG] init script running');\n"
|
||||
@@ -167,11 +167,11 @@ async def render_editor_panel(save_error: str | None = None, is_page: bool = Fal
|
||||
" }\n"
|
||||
"})();\n"
|
||||
)
|
||||
parts.append(await render_to_sx("blog-editor-scripts",
|
||||
parts.append(sx_call("blog-editor-scripts",
|
||||
js_src=editor_js,
|
||||
sx_editor_js_src=sx_editor_js,
|
||||
init_js=init_js))
|
||||
|
||||
from shared.sx.parser import SxExpr
|
||||
return await render_to_sx("blog-editor-panel",
|
||||
return sx_call("blog-editor-panel",
|
||||
parts=SxExpr("(<> " + " ".join(parts) + ")")) if parts else ""
|
||||
|
||||
@@ -154,7 +154,7 @@ def register(url_prefix: str) -> Blueprint:
|
||||
)
|
||||
resp = await make_response(html)
|
||||
elif page > 1:
|
||||
sx_src = await render_orders_rows(
|
||||
sx_src = render_orders_rows(
|
||||
ctx, orders, page, total_pages, url_for, qs_fn,
|
||||
)
|
||||
resp = sx_response(sx_src)
|
||||
|
||||
@@ -49,7 +49,7 @@ def register():
|
||||
from shared.sx.page import get_template_context
|
||||
from sxc.pages.renders import render_cart_payments_panel
|
||||
ctx = await get_template_context()
|
||||
html = await render_cart_payments_panel(ctx)
|
||||
html = render_cart_payments_panel(ctx)
|
||||
return sx_response(html)
|
||||
|
||||
return bp
|
||||
|
||||
@@ -57,9 +57,9 @@ async def _post_header_sx(ctx: dict, page_post: Any, *, oob: bool = False) -> st
|
||||
return await _shared_post_header_sx(ctx, oob=oob)
|
||||
|
||||
|
||||
async def _cart_header_sx(ctx: dict, *, oob: bool = False) -> str:
|
||||
from shared.sx.helpers import render_to_sx, call_url
|
||||
return await render_to_sx(
|
||||
def _cart_header_sx(ctx: dict, *, oob: bool = False) -> str:
|
||||
from shared.sx.helpers import sx_call, call_url
|
||||
return sx_call(
|
||||
"menu-row-sx",
|
||||
id="cart-row", level=1, colour="sky",
|
||||
link_href=call_url(ctx, "cart_url", "/"),
|
||||
@@ -68,15 +68,15 @@ async def _cart_header_sx(ctx: dict, *, oob: bool = False) -> str:
|
||||
)
|
||||
|
||||
|
||||
async def _page_cart_header_sx(ctx: dict, page_post: Any, *, oob: bool = False) -> str:
|
||||
from shared.sx.helpers import render_to_sx, call_url
|
||||
def _page_cart_header_sx(ctx: dict, page_post: Any, *, oob: bool = False) -> str:
|
||||
from shared.sx.helpers import sx_call, call_url
|
||||
slug = page_post.slug if page_post else ""
|
||||
title = ((page_post.title if page_post else None) or "")[:160]
|
||||
label_sx = await render_to_sx("cart-page-label",
|
||||
label_sx = sx_call("cart-page-label",
|
||||
feature_image=page_post.feature_image if page_post else None,
|
||||
title=title)
|
||||
nav_sx = await render_to_sx("cart-all-carts-link", href=call_url(ctx, "cart_url", "/"))
|
||||
return await render_to_sx(
|
||||
nav_sx = sx_call("cart-all-carts-link", href=call_url(ctx, "cart_url", "/"))
|
||||
return sx_call(
|
||||
"menu-row-sx",
|
||||
id="page-cart-row", level=2, colour="sky",
|
||||
link_href=call_url(ctx, "cart_url", f"/{slug}/"),
|
||||
@@ -102,8 +102,8 @@ async def _cart_page_full(ctx: dict, **kw: Any) -> str:
|
||||
page_post = ctx.get("page_post")
|
||||
env = {}
|
||||
return await render_to_sx_with_env("cart-page-layout-full", env,
|
||||
cart_row=SxExpr(await _cart_header_sx(ctx)),
|
||||
page_cart_row=SxExpr(await _page_cart_header_sx(ctx, page_post)),
|
||||
cart_row=SxExpr(_cart_header_sx(ctx)),
|
||||
page_cart_row=SxExpr(_page_cart_header_sx(ctx, page_post)),
|
||||
)
|
||||
|
||||
|
||||
@@ -113,8 +113,8 @@ async def _cart_page_oob(ctx: dict, **kw: Any) -> str:
|
||||
env = {}
|
||||
return await render_to_sx_with_env("cart-page-layout-oob", env,
|
||||
root_header_oob=SxExpr(await root_header_sx(ctx, oob=True)),
|
||||
cart_row_oob=SxExpr(await _cart_header_sx(ctx, oob=True)),
|
||||
page_cart_row=SxExpr(await _page_cart_header_sx(ctx, page_post)),
|
||||
cart_row_oob=SxExpr(_cart_header_sx(ctx, oob=True)),
|
||||
page_cart_row=SxExpr(_page_cart_header_sx(ctx, page_post)),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ from .utils import _serialize_order, _serialize_calendar_entry
|
||||
|
||||
|
||||
async def render_orders_page(ctx, orders, page, total_pages, search, search_count, url_for_fn, qs_fn):
|
||||
from shared.sx.helpers import render_to_sx, render_to_sx_with_env, search_desktop_sx, search_mobile_sx, full_page_sx
|
||||
from shared.sx.helpers import sx_call, render_to_sx_with_env, search_desktop_sx, search_mobile_sx, full_page_sx
|
||||
from shared.utils import route_prefix
|
||||
ctx["search"] = search
|
||||
ctx["search_count"] = search_count
|
||||
@@ -15,18 +15,18 @@ async def render_orders_page(ctx, orders, page, total_pages, search, search_coun
|
||||
list_url = pfx + url_for_fn("orders.list_orders")
|
||||
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 = await render_to_sx("orders-list-content", orders=order_dicts,
|
||||
content = sx_call("orders-list-content", orders=order_dicts,
|
||||
page=page, total_pages=total_pages, rows_url=list_url, detail_url_prefix=detail_url_prefix)
|
||||
header_rows = await render_to_sx_with_env("cart-orders-layout-full", {},
|
||||
list_url=list_url,
|
||||
)
|
||||
filt = await render_to_sx("order-list-header", search_mobile=SxExpr(await search_mobile_sx(ctx)))
|
||||
filt = sx_call("order-list-header", search_mobile=SxExpr(await search_mobile_sx(ctx)))
|
||||
return await full_page_sx(ctx, header_rows=header_rows, filter=filt,
|
||||
aside=await search_desktop_sx(ctx), content=content)
|
||||
|
||||
|
||||
async def render_orders_rows(ctx, orders, page, total_pages, url_for_fn, qs_fn):
|
||||
from shared.sx.helpers import render_to_sx
|
||||
def render_orders_rows(ctx, orders, page, total_pages, url_for_fn, qs_fn):
|
||||
from shared.sx.helpers import sx_call
|
||||
from shared.utils import route_prefix
|
||||
pfx = route_prefix()
|
||||
list_url = pfx + url_for_fn("orders.list_orders")
|
||||
@@ -34,22 +34,22 @@ async def render_orders_rows(ctx, orders, page, total_pages, url_for_fn, qs_fn):
|
||||
order_dicts = [_serialize_order(o) for o in orders]
|
||||
parts = []
|
||||
for od in order_dicts:
|
||||
parts.append(await render_to_sx("order-row-pair", order=od, detail_url_prefix=detail_url_prefix))
|
||||
parts.append(sx_call("order-row-pair", order=od, detail_url_prefix=detail_url_prefix))
|
||||
next_scroll = ""
|
||||
if page < total_pages:
|
||||
next_url = list_url + qs_fn(page=page + 1)
|
||||
next_scroll = await render_to_sx("infinite-scroll", url=next_url, page=page,
|
||||
next_scroll = sx_call("infinite-scroll", url=next_url, page=page,
|
||||
total_pages=total_pages, id_prefix="orders", colspan=5)
|
||||
else:
|
||||
next_scroll = await render_to_sx("order-end-row")
|
||||
return await render_to_sx("cart-orders-rows",
|
||||
next_scroll = sx_call("order-end-row")
|
||||
return sx_call("cart-orders-rows",
|
||||
rows=SxExpr("(<> " + " ".join(parts) + ")"),
|
||||
next_scroll=SxExpr(next_scroll),
|
||||
)
|
||||
|
||||
|
||||
async def render_orders_oob(ctx, orders, page, total_pages, search, search_count, url_for_fn, qs_fn):
|
||||
from shared.sx.helpers import render_to_sx, render_to_sx_with_env, search_desktop_sx, search_mobile_sx, oob_page_sx
|
||||
from shared.sx.helpers import sx_call, render_to_sx_with_env, search_desktop_sx, search_mobile_sx, oob_page_sx
|
||||
from shared.utils import route_prefix
|
||||
ctx["search"] = search
|
||||
ctx["search_count"] = search_count
|
||||
@@ -57,17 +57,17 @@ async def render_orders_oob(ctx, orders, page, total_pages, search, search_count
|
||||
list_url = pfx + url_for_fn("orders.list_orders")
|
||||
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 = await render_to_sx("orders-list-content", orders=order_dicts,
|
||||
content = sx_call("orders-list-content", orders=order_dicts,
|
||||
page=page, total_pages=total_pages, rows_url=list_url, detail_url_prefix=detail_url_prefix)
|
||||
oobs = await render_to_sx_with_env("cart-orders-layout-oob", {},
|
||||
list_url=list_url,
|
||||
)
|
||||
filt = await render_to_sx("order-list-header", search_mobile=SxExpr(await search_mobile_sx(ctx)))
|
||||
filt = sx_call("order-list-header", search_mobile=SxExpr(await search_mobile_sx(ctx)))
|
||||
return await oob_page_sx(oobs=oobs, filter=filt, aside=await search_desktop_sx(ctx), content=content)
|
||||
|
||||
|
||||
async def render_order_page(ctx, order, calendar_entries, url_for_fn):
|
||||
from shared.sx.helpers import render_to_sx, render_to_sx_with_env, full_page_sx
|
||||
from shared.sx.helpers import sx_call, render_to_sx_with_env, full_page_sx
|
||||
from shared.utils import route_prefix
|
||||
from shared.browser.app.csrf import generate_csrf_token
|
||||
pfx = route_prefix()
|
||||
@@ -77,8 +77,8 @@ async def render_order_page(ctx, order, calendar_entries, url_for_fn):
|
||||
pay_url = pfx + url_for_fn("orders.order.order_pay", order_id=order.id)
|
||||
order_data = _serialize_order(order)
|
||||
cal_data = [_serialize_calendar_entry(e) for e in (calendar_entries or [])]
|
||||
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,
|
||||
main = sx_call("order-detail-content", order=order_data, calendar_entries=cal_data)
|
||||
filt = sx_call("order-detail-filter-content", order=order_data,
|
||||
list_url=list_url, recheck_url=recheck_url, pay_url=pay_url, csrf=generate_csrf_token())
|
||||
header_rows = await render_to_sx_with_env("cart-order-detail-layout-full", {},
|
||||
list_url=list_url, detail_url=detail_url,
|
||||
@@ -88,7 +88,7 @@ async def render_order_page(ctx, order, calendar_entries, url_for_fn):
|
||||
|
||||
|
||||
async def render_order_oob(ctx, order, calendar_entries, url_for_fn):
|
||||
from shared.sx.helpers import render_to_sx, render_to_sx_with_env, oob_page_sx
|
||||
from shared.sx.helpers import sx_call, render_to_sx_with_env, oob_page_sx
|
||||
from shared.utils import route_prefix
|
||||
from shared.browser.app.csrf import generate_csrf_token
|
||||
pfx = route_prefix()
|
||||
@@ -98,8 +98,8 @@ async def render_order_oob(ctx, order, calendar_entries, url_for_fn):
|
||||
pay_url = pfx + url_for_fn("orders.order.order_pay", order_id=order.id)
|
||||
order_data = _serialize_order(order)
|
||||
cal_data = [_serialize_calendar_entry(e) for e in (calendar_entries or [])]
|
||||
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,
|
||||
main = sx_call("order-detail-content", order=order_data, calendar_entries=cal_data)
|
||||
filt = sx_call("order-detail-filter-content", order=order_data,
|
||||
list_url=list_url, recheck_url=recheck_url, pay_url=pay_url, csrf=generate_csrf_token())
|
||||
oobs = await render_to_sx_with_env("cart-order-detail-layout-oob", {},
|
||||
detail_url=detail_url,
|
||||
@@ -109,19 +109,19 @@ async def render_order_oob(ctx, order, calendar_entries, url_for_fn):
|
||||
|
||||
|
||||
async def render_checkout_error_page(ctx, error=None, order=None):
|
||||
from shared.sx.helpers import render_to_sx, render_to_sx_with_env, full_page_sx
|
||||
from shared.sx.helpers import sx_call, render_to_sx_with_env, full_page_sx
|
||||
from shared.infrastructure.urls import cart_url
|
||||
err_msg = error or "Unexpected error while creating the hosted checkout session."
|
||||
order_sx = await render_to_sx("checkout-error-order-id", oid=f"#{order.id}") if order else None
|
||||
order_sx = sx_call("checkout-error-order-id", oid=f"#{order.id}") if order else None
|
||||
hdr = await render_to_sx_with_env("layout-root-full", {})
|
||||
filt = await render_to_sx("checkout-error-header")
|
||||
content = await render_to_sx("checkout-error-content", msg=err_msg,
|
||||
filt = sx_call("checkout-error-header")
|
||||
content = sx_call("checkout-error-content", msg=err_msg,
|
||||
order=SxExpr(order_sx) if order_sx else None, back_url=cart_url("/"))
|
||||
return await full_page_sx(ctx, header_rows=hdr, filter=filt, content=content)
|
||||
|
||||
|
||||
async def render_cart_payments_panel(ctx):
|
||||
from shared.sx.helpers import render_to_sx
|
||||
def render_cart_payments_panel(ctx):
|
||||
from shared.sx.helpers import sx_call
|
||||
page_config = ctx.get("page_config")
|
||||
pc_data = None
|
||||
if page_config:
|
||||
@@ -130,4 +130,4 @@ async def render_cart_payments_panel(ctx):
|
||||
"sumup_merchant_code": getattr(page_config, "sumup_merchant_code", None) or "",
|
||||
"sumup_checkout_prefix": getattr(page_config, "sumup_checkout_prefix", None) or "",
|
||||
}
|
||||
return await render_to_sx("cart-payments-content", page_config=pc_data)
|
||||
return sx_call("cart-payments-content", page_config=pc_data)
|
||||
|
||||
@@ -85,7 +85,7 @@ def register() -> Blueprint:
|
||||
entries, has_more, pending_tickets, page_info = await _load_entries(page)
|
||||
|
||||
from sxc.pages.renders import render_all_events_cards
|
||||
sx_src = await render_all_events_cards(entries, has_more, pending_tickets, page_info, page, view)
|
||||
sx_src = render_all_events_cards(entries, has_more, pending_tickets, page_info, page, view)
|
||||
return sx_response(sx_src)
|
||||
|
||||
@bp.post("/all-tickets/adjust")
|
||||
@@ -126,7 +126,7 @@ def register() -> Blueprint:
|
||||
frag_params["session_id"] = ident["session_id"]
|
||||
|
||||
from sxc.pages.tickets import render_ticket_widget
|
||||
widget_html = await render_ticket_widget(entry, qty, "/all-tickets/adjust")
|
||||
widget_html = 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 sxc.pages.renders import render_calendar_description_edit
|
||||
html = await render_calendar_description_edit(g.calendar)
|
||||
html = render_calendar_description_edit(g.calendar)
|
||||
return sx_response(html)
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ def register():
|
||||
await g.s.flush()
|
||||
|
||||
from sxc.pages.renders import render_calendar_description
|
||||
html = await render_calendar_description(g.calendar, oob=True)
|
||||
html = 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 sxc.pages.renders import render_calendar_description
|
||||
html = await render_calendar_description(g.calendar)
|
||||
html = 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 sxc.pages.calendar import _calendar_admin_main_panel_html
|
||||
ctx = await get_template_context()
|
||||
html = await _calendar_admin_main_panel_html(ctx)
|
||||
html = _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 sxc.pages.renders import render_calendars_list_panel
|
||||
ctx = await get_template_context()
|
||||
html = await render_calendars_list_panel(ctx)
|
||||
html = 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 = await render_post_nav_entries_oob(associated_entries, cals, post_data["post"])
|
||||
nav_oob = 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 sxc.pages.renders import render_day_main_panel
|
||||
html = await render_day_main_panel(ctx)
|
||||
html = 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 sxc.pages.entries import render_entry_add_form
|
||||
return sx_response(await render_entry_add_form(g.calendar, day, month, year, day_slots))
|
||||
return sx_response(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 sxc.pages.entries import render_entry_add_button
|
||||
return sx_response(await render_entry_add_button(g.calendar, day, month, year))
|
||||
return sx_response(render_entry_add_button(g.calendar, day, month, year))
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -112,7 +112,7 @@ def register():
|
||||
|
||||
# Render OOB nav
|
||||
from sxc.pages.entries import render_day_entries_nav_oob
|
||||
return await render_day_entries_nav_oob(visible.confirmed_entries, calendar, day_date)
|
||||
return 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 sxc.pages.entries import render_post_nav_entries_oob
|
||||
nav_oob = await render_post_nav_entries_oob(associated_entries, calendars, post)
|
||||
nav_oob = 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 sxc.pages.entries import render_entry_edit_form
|
||||
return sx_response(await render_entry_edit_form(g.entry, g.calendar, day, month, year, day_slots))
|
||||
return sx_response(render_entry_edit_form(g.entry, g.calendar, day, month, year, day_slots))
|
||||
|
||||
@bp.put("/")
|
||||
@require_admin
|
||||
@@ -423,7 +423,7 @@ def register():
|
||||
from sxc.pages.entries import _entry_main_panel_html
|
||||
|
||||
tctx = await get_template_context()
|
||||
html = await _entry_main_panel_html(tctx)
|
||||
html = _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 sxc.pages.entries import render_entry_optioned
|
||||
html = await render_entry_optioned(g.entry, g.calendar, day, month, year)
|
||||
html = 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 sxc.pages.entries import render_entry_optioned
|
||||
html = await render_entry_optioned(g.entry, g.calendar, day, month, year)
|
||||
html = 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 sxc.pages.entries import render_entry_optioned
|
||||
html = await render_entry_optioned(g.entry, g.calendar, day, month, year)
|
||||
html = 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 sxc.pages.entries import render_entry_tickets_config
|
||||
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"))
|
||||
html = 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 sxc.pages.entries import render_post_search_results
|
||||
return sx_response(await render_post_search_results(
|
||||
return sx_response(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 sxc.pages.entries import render_entry_posts_panel, render_entry_posts_nav_oob
|
||||
va = request.view_args or {}
|
||||
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)
|
||||
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)
|
||||
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 sxc.pages.entries import render_entry_posts_panel, render_entry_posts_nav_oob
|
||||
va = request.view_args or {}
|
||||
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)
|
||||
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)
|
||||
return sx_response(html + nav_oob)
|
||||
|
||||
return bp
|
||||
|
||||
@@ -69,7 +69,7 @@ def register():
|
||||
from shared.sx.page import get_template_context
|
||||
from sxc.pages.renders import render_calendars_list_panel
|
||||
ctx = await get_template_context()
|
||||
html = await render_calendars_list_panel(ctx)
|
||||
html = 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 = await render_post_nav_entries_oob(associated_entries, cals, post_data["post"])
|
||||
nav_oob = 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 sxc.pages.renders import render_markets_list_panel
|
||||
ctx = await get_template_context()
|
||||
return sx_response(await render_markets_list_panel(ctx))
|
||||
return sx_response(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 sxc.pages.renders import render_markets_list_panel
|
||||
ctx = await get_template_context()
|
||||
return sx_response(await render_markets_list_panel(ctx))
|
||||
return sx_response(render_markets_list_panel(ctx))
|
||||
|
||||
return bp
|
||||
|
||||
@@ -66,7 +66,7 @@ def register() -> Blueprint:
|
||||
entries, has_more, pending_tickets = await _load_entries(post["id"], page)
|
||||
|
||||
from sxc.pages.renders import render_page_summary_cards
|
||||
sx_src = await render_page_summary_cards(entries, has_more, pending_tickets, {}, page, view, post)
|
||||
sx_src = render_page_summary_cards(entries, has_more, pending_tickets, {}, page, view, post)
|
||||
return sx_response(sx_src)
|
||||
|
||||
@bp.post("/tickets/adjust")
|
||||
@@ -107,7 +107,7 @@ def register() -> Blueprint:
|
||||
frag_params["session_id"] = ident["session_id"]
|
||||
|
||||
from sxc.pages.tickets import render_ticket_widget
|
||||
widget_html = await render_ticket_widget(entry, qty, f"/{g.post_slug}/tickets/adjust")
|
||||
widget_html = 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 sxc.pages.slots import render_slot_edit_form
|
||||
return sx_response(await render_slot_edit_form(slot, g.calendar))
|
||||
return sx_response(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 sxc.pages.slots import render_slot_main_panel
|
||||
return sx_response(await render_slot_main_panel(slot, g.calendar))
|
||||
return sx_response(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 sxc.pages.slots import render_slots_table
|
||||
return sx_response(await render_slots_table(slots, g.calendar))
|
||||
return sx_response(render_slots_table(slots, g.calendar))
|
||||
|
||||
@bp.put("/")
|
||||
@require_admin
|
||||
@@ -136,7 +136,7 @@ def register():
|
||||
), 422
|
||||
|
||||
from sxc.pages.slots import render_slot_main_panel
|
||||
return sx_response(await render_slot_main_panel(slot, g.calendar, oob=True))
|
||||
return sx_response(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 sxc.pages.slots import render_slots_table
|
||||
return sx_response(await render_slots_table(slots, g.calendar))
|
||||
return sx_response(render_slots_table(slots, g.calendar))
|
||||
|
||||
|
||||
@bp.get("/add")
|
||||
@require_admin
|
||||
async def add_form(**kwargs):
|
||||
from sxc.pages.slots import render_slot_add_form
|
||||
return sx_response(await render_slot_add_form(g.calendar))
|
||||
return sx_response(render_slot_add_form(g.calendar))
|
||||
|
||||
@bp.get("/add-button")
|
||||
@require_admin
|
||||
async def add_button(**kwargs):
|
||||
from sxc.pages.slots import render_slot_add_button
|
||||
return sx_response(await render_slot_add_button(g.calendar))
|
||||
return sx_response(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 sxc.pages.tickets import render_entry_tickets_admin
|
||||
html = await render_entry_tickets_admin(entry, tickets)
|
||||
html = 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 sxc.pages.tickets import render_lookup_result
|
||||
if not ticket:
|
||||
return sx_response(await render_lookup_result(None, "Ticket not found"))
|
||||
return sx_response(render_lookup_result(None, "Ticket not found"))
|
||||
|
||||
return sx_response(await render_lookup_result(ticket, None))
|
||||
return sx_response(render_lookup_result(ticket, None))
|
||||
|
||||
@bp.post("/<code>/checkin/")
|
||||
@require_admin
|
||||
@@ -84,9 +84,9 @@ def register() -> Blueprint:
|
||||
|
||||
from sxc.pages.tickets import render_checkin_result
|
||||
if not success:
|
||||
return sx_response(await render_checkin_result(False, error, None))
|
||||
return sx_response(render_checkin_result(False, error, None))
|
||||
|
||||
ticket = await get_ticket_by_code(g.s, code)
|
||||
return sx_response(await render_checkin_result(True, None, ticket))
|
||||
return sx_response(render_checkin_result(True, None, ticket))
|
||||
|
||||
return bp
|
||||
|
||||
@@ -32,7 +32,7 @@ def register():
|
||||
|
||||
from sxc.pages.tickets import render_ticket_type_edit_form
|
||||
va = request.view_args or {}
|
||||
return sx_response(await render_ticket_type_edit_form(
|
||||
return sx_response(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 sxc.pages.tickets import render_ticket_type_main_panel
|
||||
va = request.view_args or {}
|
||||
return sx_response(await render_ticket_type_main_panel(
|
||||
return sx_response(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 sxc.pages.tickets import render_ticket_type_main_panel
|
||||
va = request.view_args or {}
|
||||
return sx_response(await render_ticket_type_main_panel(
|
||||
return sx_response(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 sxc.pages.tickets import render_ticket_types_table
|
||||
va = request.view_args or {}
|
||||
return sx_response(await render_ticket_types_table(
|
||||
return sx_response(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 sxc.pages.tickets import render_ticket_types_table
|
||||
va = request.view_args or {}
|
||||
return sx_response(await render_ticket_types_table(
|
||||
return sx_response(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 sxc.pages.tickets import render_ticket_type_add_form
|
||||
va = request.view_args or {}
|
||||
return sx_response(await render_ticket_type_add_form(
|
||||
return sx_response(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 sxc.pages.tickets import render_ticket_type_add_button
|
||||
va = request.view_args or {}
|
||||
return sx_response(await render_ticket_type_add_button(
|
||||
return sx_response(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 sxc.pages.tickets import render_buy_result
|
||||
return sx_response(await render_buy_result(entry, created, remaining, cart_count))
|
||||
return sx_response(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 sxc.pages.tickets import render_adjust_response
|
||||
return sx_response(await render_adjust_response(
|
||||
return sx_response(render_adjust_response(
|
||||
entry, ticket_remaining, ticket_sold_count,
|
||||
user_ticket_count, user_ticket_counts_by_type, cart_count,
|
||||
))
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from shared.sx.helpers import (
|
||||
call_url, render_to_sx, render_to_sx_with_env,
|
||||
call_url, sx_call, render_to_sx_with_env,
|
||||
post_admin_header_sx,
|
||||
)
|
||||
from shared.sx.parser import SxExpr
|
||||
@@ -23,7 +23,7 @@ async def _post_header_sx(ctx: dict, *, oob: bool = False) -> str:
|
||||
return await post_header_sx(ctx, oob=oob)
|
||||
|
||||
|
||||
async def _post_nav_sx(ctx: dict) -> str:
|
||||
def _post_nav_sx(ctx: dict) -> str:
|
||||
"""Post desktop nav: calendar links + container nav (markets, etc.)."""
|
||||
from quart import url_for, g
|
||||
|
||||
@@ -37,7 +37,7 @@ async def _post_nav_sx(ctx: dict) -> str:
|
||||
cal_name = getattr(cal, "name", "") if hasattr(cal, "name") else cal.get("name", "")
|
||||
href = url_for("calendar.get", calendar_slug=cal_slug)
|
||||
is_sel = (cal_slug == current_cal_slug)
|
||||
parts.append(await render_to_sx("nav-link", href=href, icon="fa fa-calendar",
|
||||
parts.append(sx_call("nav-link", href=href, icon="fa fa-calendar",
|
||||
label=cal_name, select_colours=select_colours,
|
||||
is_selected=is_sel))
|
||||
# Container nav fragments (markets, etc.)
|
||||
@@ -72,13 +72,13 @@ async def _post_nav_sx(ctx: dict) -> str:
|
||||
# Calendars header
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def _calendars_header_sx(ctx: dict, *, oob: bool = False) -> str:
|
||||
def _calendars_header_sx(ctx: dict, *, oob: bool = False) -> str:
|
||||
"""Build the calendars section header row."""
|
||||
from quart import url_for
|
||||
link_href = url_for("calendars.home")
|
||||
return await render_to_sx("menu-row-sx", id="calendars-row", level=3,
|
||||
return sx_call("menu-row-sx", id="calendars-row", level=3,
|
||||
link_href=link_href,
|
||||
link_label_content=SxExpr(await render_to_sx("events-calendars-label")),
|
||||
link_label_content=SxExpr(sx_call("events-calendars-label")),
|
||||
child_id="calendars-header-child", oob=oob)
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ async def _calendars_header_sx(ctx: dict, *, oob: bool = False) -> str:
|
||||
# Calendar header
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def _calendar_header_sx(ctx: dict, *, oob: bool = False) -> str:
|
||||
def _calendar_header_sx(ctx: dict, *, oob: bool = False) -> str:
|
||||
"""Build a single calendar's header row."""
|
||||
from quart import url_for
|
||||
calendar = ctx.get("calendar")
|
||||
@@ -97,18 +97,18 @@ async def _calendar_header_sx(ctx: dict, *, oob: bool = False) -> str:
|
||||
cal_desc = getattr(calendar, "description", "") or ""
|
||||
|
||||
link_href = url_for("calendar.get", calendar_slug=cal_slug)
|
||||
label_html = await render_to_sx("events-calendar-label",
|
||||
label_html = sx_call("events-calendar-label",
|
||||
name=cal_name, description=cal_desc)
|
||||
|
||||
# Desktop nav: slots + admin
|
||||
nav_html = await _calendar_nav_sx(ctx)
|
||||
nav_html = _calendar_nav_sx(ctx)
|
||||
|
||||
return await render_to_sx("menu-row-sx", id="calendar-row", level=3,
|
||||
return sx_call("menu-row-sx", id="calendar-row", level=3,
|
||||
link_href=link_href, link_label_content=SxExpr(label_html),
|
||||
nav=SxExpr(nav_html) if nav_html else None, child_id="calendar-header-child", oob=oob)
|
||||
|
||||
|
||||
async def _calendar_nav_sx(ctx: dict) -> str:
|
||||
def _calendar_nav_sx(ctx: dict) -> str:
|
||||
"""Calendar desktop nav: Slots + admin link."""
|
||||
from quart import url_for
|
||||
calendar = ctx.get("calendar")
|
||||
@@ -121,11 +121,11 @@ async def _calendar_nav_sx(ctx: dict) -> str:
|
||||
|
||||
parts = []
|
||||
slots_href = url_for("defpage_slots_listing", calendar_slug=cal_slug)
|
||||
parts.append(await render_to_sx("nav-link", href=slots_href, icon="fa fa-clock",
|
||||
parts.append(sx_call("nav-link", href=slots_href, icon="fa fa-clock",
|
||||
label="Slots", select_colours=select_colours))
|
||||
if is_admin:
|
||||
admin_href = url_for("defpage_calendar_admin", calendar_slug=cal_slug)
|
||||
parts.append(await render_to_sx("nav-link", href=admin_href, icon="fa fa-cog",
|
||||
parts.append(sx_call("nav-link", href=admin_href, icon="fa fa-cog",
|
||||
select_colours=select_colours))
|
||||
return "(<> " + " ".join(parts) + ")" if parts else ""
|
||||
|
||||
@@ -134,7 +134,7 @@ async def _calendar_nav_sx(ctx: dict) -> str:
|
||||
# Day header
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def _day_header_sx(ctx: dict, *, oob: bool = False) -> str:
|
||||
def _day_header_sx(ctx: dict, *, oob: bool = False) -> str:
|
||||
"""Build day detail header row."""
|
||||
from quart import url_for
|
||||
calendar = ctx.get("calendar")
|
||||
@@ -152,17 +152,17 @@ async def _day_header_sx(ctx: dict, *, oob: bool = False) -> str:
|
||||
month=day_date.month,
|
||||
day=day_date.day,
|
||||
)
|
||||
label_html = await render_to_sx("events-day-label",
|
||||
label_html = sx_call("events-day-label",
|
||||
date_str=day_date.strftime("%A %d %B %Y"))
|
||||
|
||||
nav_html = await _day_nav_sx(ctx)
|
||||
nav_html = _day_nav_sx(ctx)
|
||||
|
||||
return await render_to_sx("menu-row-sx", id="day-row", level=4,
|
||||
return sx_call("menu-row-sx", id="day-row", level=4,
|
||||
link_href=link_href, link_label_content=SxExpr(label_html),
|
||||
nav=SxExpr(nav_html) if nav_html else None, child_id="day-header-child", oob=oob)
|
||||
|
||||
|
||||
async def _day_nav_sx(ctx: dict) -> str:
|
||||
def _day_nav_sx(ctx: dict) -> str:
|
||||
"""Day desktop nav: confirmed entries scrolling menu + admin link."""
|
||||
from quart import url_for
|
||||
calendar = ctx.get("calendar")
|
||||
@@ -189,11 +189,11 @@ async def _day_nav_sx(ctx: dict) -> str:
|
||||
)
|
||||
start = entry.start_at.strftime("%H:%M") if entry.start_at else ""
|
||||
end = f" \u2013 {entry.end_at.strftime('%H:%M')}" if entry.end_at else ""
|
||||
entry_links.append(await render_to_sx("events-day-entry-link",
|
||||
entry_links.append(sx_call("events-day-entry-link",
|
||||
href=href, name=entry.name,
|
||||
time_str=f"{start}{end}"))
|
||||
inner = "".join(entry_links)
|
||||
parts.append(await render_to_sx("events-day-entries-nav", inner=SxExpr(inner)))
|
||||
parts.append(sx_call("events-day-entries-nav", inner=SxExpr(inner)))
|
||||
|
||||
if is_admin and day_date:
|
||||
admin_href = url_for(
|
||||
@@ -203,7 +203,7 @@ async def _day_nav_sx(ctx: dict) -> str:
|
||||
month=day_date.month,
|
||||
day=day_date.day,
|
||||
)
|
||||
parts.append(await render_to_sx("nav-link", href=admin_href, icon="fa fa-cog"))
|
||||
parts.append(sx_call("nav-link", href=admin_href, icon="fa fa-cog"))
|
||||
return "".join(parts)
|
||||
|
||||
|
||||
@@ -211,7 +211,7 @@ async def _day_nav_sx(ctx: dict) -> str:
|
||||
# Day admin header
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def _day_admin_header_sx(ctx: dict, *, oob: bool = False) -> str:
|
||||
def _day_admin_header_sx(ctx: dict, *, oob: bool = False) -> str:
|
||||
"""Build day admin header row."""
|
||||
from quart import url_for
|
||||
calendar = ctx.get("calendar")
|
||||
@@ -229,7 +229,7 @@ async def _day_admin_header_sx(ctx: dict, *, oob: bool = False) -> str:
|
||||
month=day_date.month,
|
||||
day=day_date.day,
|
||||
)
|
||||
return await render_to_sx("menu-row-sx", id="day-admin-row", level=5,
|
||||
return sx_call("menu-row-sx", id="day-admin-row", level=5,
|
||||
link_href=link_href, link_label="admin", icon="fa fa-cog",
|
||||
child_id="day-admin-header-child", oob=oob)
|
||||
|
||||
@@ -238,7 +238,7 @@ async def _day_admin_header_sx(ctx: dict, *, oob: bool = False) -> str:
|
||||
# Calendar admin header
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def _calendar_admin_header_sx(ctx: dict, *, oob: bool = False) -> str:
|
||||
def _calendar_admin_header_sx(ctx: dict, *, oob: bool = False) -> str:
|
||||
"""Build calendar admin header row with nav links."""
|
||||
from quart import url_for
|
||||
calendar = ctx.get("calendar")
|
||||
@@ -252,11 +252,11 @@ async def _calendar_admin_header_sx(ctx: dict, *, oob: bool = False) -> str:
|
||||
("calendar.admin.calendar_description_edit", "description"),
|
||||
]:
|
||||
href = url_for(endpoint, calendar_slug=cal_slug)
|
||||
nav_parts.append(await render_to_sx("nav-link", href=href, label=label,
|
||||
nav_parts.append(sx_call("nav-link", href=href, label=label,
|
||||
select_colours=select_colours))
|
||||
|
||||
nav_html = "".join(nav_parts)
|
||||
return await render_to_sx("menu-row-sx", id="calendar-admin-row", level=4,
|
||||
return sx_call("menu-row-sx", id="calendar-admin-row", level=4,
|
||||
link_label="admin", icon="fa fa-cog",
|
||||
nav=SxExpr(nav_html) if nav_html else None, child_id="calendar-admin-header-child", oob=oob)
|
||||
|
||||
@@ -265,13 +265,13 @@ async def _calendar_admin_header_sx(ctx: dict, *, oob: bool = False) -> str:
|
||||
# Markets header
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def _markets_header_sx(ctx: dict, *, oob: bool = False) -> str:
|
||||
def _markets_header_sx(ctx: dict, *, oob: bool = False) -> str:
|
||||
"""Build the markets section header row."""
|
||||
from quart import url_for
|
||||
link_href = url_for("defpage_events_markets")
|
||||
return await render_to_sx("menu-row-sx", id="markets-row", level=3,
|
||||
return sx_call("menu-row-sx", id="markets-row", level=3,
|
||||
link_href=link_href,
|
||||
link_label_content=SxExpr(await render_to_sx("events-markets-label")),
|
||||
link_label_content=SxExpr(sx_call("events-markets-label")),
|
||||
child_id="markets-header-child", oob=oob)
|
||||
|
||||
|
||||
@@ -279,7 +279,7 @@ async def _markets_header_sx(ctx: dict, *, oob: bool = False) -> str:
|
||||
# Calendars main panel
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def _calendars_main_panel_sx(ctx: dict) -> str:
|
||||
def _calendars_main_panel_sx(ctx: dict) -> str:
|
||||
"""Render the calendars list + create form panel."""
|
||||
from quart import url_for
|
||||
rights = ctx.get("rights") or {}
|
||||
@@ -294,18 +294,18 @@ async def _calendars_main_panel_sx(ctx: dict) -> str:
|
||||
form_html = ""
|
||||
if can_create:
|
||||
create_url = url_for("calendars.create_calendar")
|
||||
form_html = await render_to_sx("crud-create-form",
|
||||
form_html = sx_call("crud-create-form",
|
||||
create_url=create_url, csrf=csrf,
|
||||
errors_id="cal-create-errors", list_id="calendars-list",
|
||||
placeholder="e.g. Events, Gigs, Meetings", btn_label="Add calendar")
|
||||
|
||||
list_html = await _calendars_list_sx(ctx, calendars)
|
||||
return await render_to_sx("crud-panel",
|
||||
list_html = _calendars_list_sx(ctx, calendars)
|
||||
return sx_call("crud-panel",
|
||||
form=SxExpr(form_html), list=SxExpr(list_html),
|
||||
list_id="calendars-list")
|
||||
|
||||
|
||||
async def _calendars_list_sx(ctx: dict, calendars: list) -> str:
|
||||
def _calendars_list_sx(ctx: dict, calendars: list) -> str:
|
||||
"""Render the calendars list items."""
|
||||
from quart import url_for
|
||||
from shared.utils import route_prefix
|
||||
@@ -314,7 +314,7 @@ async def _calendars_list_sx(ctx: dict, calendars: list) -> str:
|
||||
prefix = route_prefix()
|
||||
|
||||
if not calendars:
|
||||
return await render_to_sx("empty-state", message="No calendars yet. Create one above.",
|
||||
return sx_call("empty-state", message="No calendars yet. Create one above.",
|
||||
cls="text-gray-500 mt-4")
|
||||
|
||||
parts = []
|
||||
@@ -324,7 +324,7 @@ async def _calendars_list_sx(ctx: dict, calendars: list) -> str:
|
||||
href = prefix + url_for("calendar.get", calendar_slug=cal_slug)
|
||||
del_url = url_for("calendar.delete", calendar_slug=cal_slug)
|
||||
csrf_hdr = f'{{"X-CSRFToken":"{csrf}"}}'
|
||||
parts.append(await render_to_sx("crud-item",
|
||||
parts.append(sx_call("crud-item",
|
||||
href=href, name=cal_name, slug=cal_slug,
|
||||
del_url=del_url, csrf_hdr=csrf_hdr,
|
||||
list_id="calendars-list",
|
||||
@@ -337,7 +337,7 @@ async def _calendars_list_sx(ctx: dict, calendars: list) -> str:
|
||||
# Calendar month grid
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def _calendar_main_panel_html(ctx: dict) -> str:
|
||||
def _calendar_main_panel_html(ctx: dict) -> str:
|
||||
"""Render the calendar month grid."""
|
||||
from quart import url_for
|
||||
from quart import session as qsession
|
||||
@@ -375,10 +375,10 @@ async def _calendar_main_panel_html(ctx: dict) -> str:
|
||||
("\u2039", prev_month_year, prev_month),
|
||||
]:
|
||||
href = nav_link(yr, mn)
|
||||
nav_arrows.append(await render_to_sx("events-calendar-nav-arrow",
|
||||
nav_arrows.append(sx_call("events-calendar-nav-arrow",
|
||||
pill_cls=pill_cls, href=href, label=label))
|
||||
|
||||
nav_arrows.append(await render_to_sx("events-calendar-month-label",
|
||||
nav_arrows.append(sx_call("events-calendar-month-label",
|
||||
month_name=month_name, year=str(year)))
|
||||
|
||||
for label, yr, mn in [
|
||||
@@ -386,13 +386,13 @@ async def _calendar_main_panel_html(ctx: dict) -> str:
|
||||
("\u00bb", next_year, month),
|
||||
]:
|
||||
href = nav_link(yr, mn)
|
||||
nav_arrows.append(await render_to_sx("events-calendar-nav-arrow",
|
||||
nav_arrows.append(sx_call("events-calendar-nav-arrow",
|
||||
pill_cls=pill_cls, href=href, label=label))
|
||||
|
||||
# Weekday headers
|
||||
wd_parts = []
|
||||
for wd in weekday_names:
|
||||
wd_parts.append(await render_to_sx("events-calendar-weekday", name=wd))
|
||||
wd_parts.append(sx_call("events-calendar-weekday", name=wd))
|
||||
wd_html = "".join(wd_parts)
|
||||
|
||||
# Day cells
|
||||
@@ -423,9 +423,9 @@ async def _calendar_main_panel_html(ctx: dict) -> str:
|
||||
calendar_slug=cal_slug,
|
||||
year=day_date.year, month=day_date.month, day=day_date.day,
|
||||
)
|
||||
day_short_html = await render_to_sx("events-calendar-day-short",
|
||||
day_short_html = sx_call("events-calendar-day-short",
|
||||
day_str=day_date.strftime("%a"))
|
||||
day_num_html = await render_to_sx("events-calendar-day-num",
|
||||
day_num_html = sx_call("events-calendar-day-num",
|
||||
pill_cls=pill_cls, href=day_href,
|
||||
num=str(day_date.day))
|
||||
|
||||
@@ -443,12 +443,12 @@ async def _calendar_main_panel_html(ctx: dict) -> str:
|
||||
else:
|
||||
bg_cls = "bg-sky-100 text-sky-800" if is_mine else "bg-stone-100 text-stone-700"
|
||||
state_label = (e.state or "pending").replace("_", " ")
|
||||
entry_badges.append(await render_to_sx("events-calendar-entry-badge",
|
||||
entry_badges.append(sx_call("events-calendar-entry-badge",
|
||||
bg_cls=bg_cls, name=e.name,
|
||||
state_label=state_label))
|
||||
|
||||
badges_html = "(<> " + "".join(entry_badges) + ")" if entry_badges else ""
|
||||
cells.append(await render_to_sx("events-calendar-cell",
|
||||
cells.append(sx_call("events-calendar-cell",
|
||||
cell_cls=cell_cls, day_short=SxExpr(day_short_html),
|
||||
day_num=SxExpr(day_num_html),
|
||||
badges=SxExpr(badges_html) if badges_html else None))
|
||||
@@ -456,7 +456,7 @@ async def _calendar_main_panel_html(ctx: dict) -> str:
|
||||
cells_html = "(<> " + "".join(cells) + ")"
|
||||
arrows_html = "(<> " + "".join(nav_arrows) + ")"
|
||||
wd_html = "(<> " + wd_html + ")"
|
||||
return await render_to_sx("events-calendar-grid",
|
||||
return sx_call("events-calendar-grid",
|
||||
arrows=SxExpr(arrows_html), weekdays=SxExpr(wd_html),
|
||||
cells=SxExpr(cells_html))
|
||||
|
||||
@@ -465,7 +465,7 @@ async def _calendar_main_panel_html(ctx: dict) -> str:
|
||||
# Day main panel
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def _day_main_panel_html(ctx: dict) -> str:
|
||||
def _day_main_panel_html(ctx: dict) -> str:
|
||||
"""Render the day entries table + add button."""
|
||||
from quart import url_for
|
||||
|
||||
@@ -488,10 +488,10 @@ async def _day_main_panel_html(ctx: dict) -> str:
|
||||
if day_entries:
|
||||
row_parts = []
|
||||
for entry in day_entries:
|
||||
row_parts.append(await _day_row_html(ctx, entry))
|
||||
row_parts.append(_day_row_html(ctx, entry))
|
||||
rows_html = "".join(row_parts)
|
||||
else:
|
||||
rows_html = await render_to_sx("events-day-empty-row")
|
||||
rows_html = sx_call("events-day-empty-row")
|
||||
|
||||
add_url = url_for(
|
||||
"calendar.day.calendar_entries.add_form",
|
||||
@@ -499,12 +499,12 @@ async def _day_main_panel_html(ctx: dict) -> str:
|
||||
day=day, month=month, year=year,
|
||||
)
|
||||
|
||||
return await render_to_sx("events-day-table",
|
||||
return sx_call("events-day-table",
|
||||
list_container=list_container, rows=SxExpr(rows_html),
|
||||
pre_action=pre_action, add_url=add_url)
|
||||
|
||||
|
||||
async def _day_row_html(ctx: dict, entry) -> str:
|
||||
def _day_row_html(ctx: dict, entry) -> str:
|
||||
"""Render a single day table row."""
|
||||
from quart import url_for
|
||||
calendar = ctx.get("calendar")
|
||||
@@ -523,7 +523,7 @@ async def _day_row_html(ctx: dict, entry) -> str:
|
||||
)
|
||||
|
||||
# Name
|
||||
name_html = await render_to_sx("events-day-row-name",
|
||||
name_html = sx_call("events-day-row-name",
|
||||
href=entry_href, pill_cls=pill_cls, name=entry.name)
|
||||
|
||||
# Slot/Time
|
||||
@@ -532,38 +532,38 @@ async def _day_row_html(ctx: dict, entry) -> str:
|
||||
slot_href = url_for("defpage_slot_detail", calendar_slug=cal_slug, slot_id=slot.id)
|
||||
time_start = slot.time_start.strftime("%H:%M") if slot.time_start else ""
|
||||
time_end = f" \u2192 {slot.time_end.strftime('%H:%M')}" if slot.time_end else ""
|
||||
slot_html = await render_to_sx("events-day-row-slot",
|
||||
slot_html = sx_call("events-day-row-slot",
|
||||
href=slot_href, pill_cls=pill_cls, slot_name=slot.name,
|
||||
time_str=f"({time_start}{time_end})")
|
||||
else:
|
||||
start = entry.start_at.strftime("%H:%M") if entry.start_at else ""
|
||||
end = f" \u2192 {entry.end_at.strftime('%H:%M')}" if entry.end_at else ""
|
||||
slot_html = await render_to_sx("events-day-row-time", start=start, end=end)
|
||||
slot_html = sx_call("events-day-row-time", start=start, end=end)
|
||||
|
||||
# State
|
||||
state = getattr(entry, "state", "pending") or "pending"
|
||||
state_badge = await _entry_state_badge_html(state)
|
||||
state_td = await render_to_sx("events-day-row-state",
|
||||
state_badge = _entry_state_badge_html(state)
|
||||
state_td = sx_call("events-day-row-state",
|
||||
state_id=f"entry-state-{entry.id}", badge=SxExpr(state_badge))
|
||||
|
||||
# Cost
|
||||
cost = getattr(entry, "cost", None)
|
||||
cost_str = f"\u00a3{cost:.2f}" if cost is not None else "\u00a30.00"
|
||||
cost_td = await render_to_sx("events-day-row-cost", cost_str=cost_str)
|
||||
cost_td = sx_call("events-day-row-cost", cost_str=cost_str)
|
||||
|
||||
# Tickets
|
||||
tp = getattr(entry, "ticket_price", None)
|
||||
if tp is not None:
|
||||
tc = getattr(entry, "ticket_count", None)
|
||||
tc_str = f"{tc} tickets" if tc is not None else "Unlimited"
|
||||
tickets_td = await render_to_sx("events-day-row-tickets",
|
||||
tickets_td = sx_call("events-day-row-tickets",
|
||||
price_str=f"\u00a3{tp:.2f}", count_str=tc_str)
|
||||
else:
|
||||
tickets_td = await render_to_sx("events-day-row-no-tickets")
|
||||
tickets_td = sx_call("events-day-row-no-tickets")
|
||||
|
||||
actions_td = await render_to_sx("events-day-row-actions")
|
||||
actions_td = sx_call("events-day-row-actions")
|
||||
|
||||
return await render_to_sx("events-day-row",
|
||||
return sx_call("events-day-row",
|
||||
tr_cls=tr_cls, name=SxExpr(name_html), slot=SxExpr(slot_html),
|
||||
state=SxExpr(state_td), cost=SxExpr(cost_td),
|
||||
tickets=SxExpr(tickets_td), actions=SxExpr(actions_td))
|
||||
@@ -573,16 +573,16 @@ async def _day_row_html(ctx: dict, entry) -> str:
|
||||
# Day admin main panel
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def _day_admin_main_panel_html(ctx: dict) -> str:
|
||||
def _day_admin_main_panel_html(ctx: dict) -> str:
|
||||
"""Render day admin panel (placeholder nav)."""
|
||||
return await render_to_sx("events-day-admin-panel")
|
||||
return sx_call("events-day-admin-panel")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Calendar admin main panel
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def _calendar_admin_main_panel_html(ctx: dict) -> str:
|
||||
def _calendar_admin_main_panel_html(ctx: dict) -> str:
|
||||
"""Render calendar admin config panel with description editor."""
|
||||
from quart import url_for
|
||||
calendar = ctx.get("calendar")
|
||||
@@ -595,17 +595,17 @@ async def _calendar_admin_main_panel_html(ctx: dict) -> str:
|
||||
hx_select = ctx.get("hx_select_search", "#main-panel")
|
||||
|
||||
desc_edit_url = url_for("calendar.admin.calendar_description_edit", calendar_slug=cal_slug)
|
||||
description_html = await _calendar_description_display_html(calendar, desc_edit_url)
|
||||
description_html = _calendar_description_display_html(calendar, desc_edit_url)
|
||||
|
||||
return await render_to_sx("events-calendar-admin-panel",
|
||||
return sx_call("events-calendar-admin-panel",
|
||||
description_content=SxExpr(description_html), csrf=csrf,
|
||||
description=desc)
|
||||
|
||||
|
||||
async def _calendar_description_display_html(calendar, edit_url: str) -> str:
|
||||
def _calendar_description_display_html(calendar, edit_url: str) -> str:
|
||||
"""Render calendar description display with edit button."""
|
||||
desc = getattr(calendar, "description", "") or ""
|
||||
return await render_to_sx("events-calendar-description-display",
|
||||
return sx_call("events-calendar-description-display",
|
||||
description=desc, edit_url=edit_url)
|
||||
|
||||
|
||||
@@ -613,7 +613,7 @@ async def _calendar_description_display_html(calendar, edit_url: str) -> str:
|
||||
# Markets main panel
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def _markets_main_panel_html(ctx: dict) -> str:
|
||||
def _markets_main_panel_html(ctx: dict) -> str:
|
||||
"""Render markets list + create form panel."""
|
||||
from quart import url_for
|
||||
rights = ctx.get("rights") or {}
|
||||
@@ -627,18 +627,18 @@ async def _markets_main_panel_html(ctx: dict) -> str:
|
||||
form_html = ""
|
||||
if can_create:
|
||||
create_url = url_for("markets.create_market")
|
||||
form_html = await render_to_sx("crud-create-form",
|
||||
form_html = sx_call("crud-create-form",
|
||||
create_url=create_url, csrf=csrf,
|
||||
errors_id="market-create-errors", list_id="markets-list",
|
||||
placeholder="e.g. Farm Shop, Bakery", btn_label="Add market")
|
||||
|
||||
list_html = await _markets_list_html(ctx, markets)
|
||||
return await render_to_sx("crud-panel",
|
||||
list_html = _markets_list_html(ctx, markets)
|
||||
return sx_call("crud-panel",
|
||||
form=SxExpr(form_html), list=SxExpr(list_html),
|
||||
list_id="markets-list")
|
||||
|
||||
|
||||
async def _markets_list_html(ctx: dict, markets: list) -> str:
|
||||
def _markets_list_html(ctx: dict, markets: list) -> str:
|
||||
"""Render markets list items."""
|
||||
from quart import url_for
|
||||
csrf_token = ctx.get("csrf_token")
|
||||
@@ -647,7 +647,7 @@ async def _markets_list_html(ctx: dict, markets: list) -> str:
|
||||
slug = post.get("slug", "")
|
||||
|
||||
if not markets:
|
||||
return await render_to_sx("empty-state", message="No markets yet. Create one above.",
|
||||
return sx_call("empty-state", message="No markets yet. Create one above.",
|
||||
cls="text-gray-500 mt-4")
|
||||
|
||||
parts = []
|
||||
@@ -657,7 +657,7 @@ async def _markets_list_html(ctx: dict, markets: list) -> str:
|
||||
market_href = call_url(ctx, "market_url", f"/{slug}/{m_slug}/")
|
||||
del_url = url_for("markets.delete_market", market_slug=m_slug)
|
||||
csrf_hdr = f'{{"X-CSRFToken":"{csrf}"}}'
|
||||
parts.append(await render_to_sx("crud-item",
|
||||
parts.append(sx_call("crud-item",
|
||||
href=market_href, name=m_name,
|
||||
slug=m_slug, del_url=del_url,
|
||||
csrf_hdr=csrf_hdr,
|
||||
|
||||
@@ -3,7 +3,7 @@ from __future__ import annotations
|
||||
|
||||
from markupsafe import escape
|
||||
|
||||
from shared.sx.helpers import render_to_sx, render_to_sx_with_env
|
||||
from shared.sx.helpers import sx_call
|
||||
from shared.sx.parser import SxExpr
|
||||
|
||||
from .utils import (
|
||||
@@ -16,7 +16,7 @@ from .utils import (
|
||||
# All events / page summary entry cards
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def _entry_card_html(entry, page_info: dict, pending_tickets: dict,
|
||||
def _entry_card_html(entry, page_info: dict, pending_tickets: dict,
|
||||
ticket_url: str, events_url_fn, *, is_page_scoped: bool = False,
|
||||
post: dict | None = None) -> str:
|
||||
"""Render a list card for one event entry."""
|
||||
@@ -35,36 +35,36 @@ async def _entry_card_html(entry, page_info: dict, pending_tickets: dict,
|
||||
|
||||
# Title (linked or plain)
|
||||
if entry_href:
|
||||
title_html = await render_to_sx("events-entry-title-linked",
|
||||
title_html = sx_call("events-entry-title-linked",
|
||||
href=entry_href, name=entry.name)
|
||||
else:
|
||||
title_html = await render_to_sx("events-entry-title-plain", name=entry.name)
|
||||
title_html = sx_call("events-entry-title-plain", name=entry.name)
|
||||
|
||||
# Badges
|
||||
badges_html = ""
|
||||
if page_title and (not is_page_scoped or page_title != (post or {}).get("title")):
|
||||
page_href = events_url_fn(f"/{page_slug}/")
|
||||
badges_html += await render_to_sx("events-entry-page-badge",
|
||||
badges_html += sx_call("events-entry-page-badge",
|
||||
href=page_href, title=page_title)
|
||||
cal_name = getattr(entry, "calendar_name", "")
|
||||
if cal_name:
|
||||
badges_html += await render_to_sx("events-entry-cal-badge", name=cal_name)
|
||||
badges_html += sx_call("events-entry-cal-badge", name=cal_name)
|
||||
|
||||
# Time line
|
||||
time_parts = ""
|
||||
if day_href and not is_page_scoped:
|
||||
time_parts += await render_to_sx("events-entry-time-linked",
|
||||
time_parts += sx_call("events-entry-time-linked",
|
||||
href=day_href,
|
||||
date_str=entry.start_at.strftime("%a %-d %b"))
|
||||
elif not is_page_scoped:
|
||||
time_parts += await render_to_sx("events-entry-time-plain",
|
||||
time_parts += sx_call("events-entry-time-plain",
|
||||
date_str=entry.start_at.strftime("%a %-d %b"))
|
||||
time_parts += entry.start_at.strftime("%H:%M")
|
||||
if entry.end_at:
|
||||
time_parts += f' \u2013 {entry.end_at.strftime("%H:%M")}'
|
||||
|
||||
cost = getattr(entry, "cost", None)
|
||||
cost_html = await render_to_sx("events-entry-cost",
|
||||
cost_html = sx_call("events-entry-cost",
|
||||
cost=f"\u00a3{cost:.2f}") if cost else ""
|
||||
|
||||
# Ticket widget
|
||||
@@ -72,16 +72,16 @@ async def _entry_card_html(entry, page_info: dict, pending_tickets: dict,
|
||||
widget_html = ""
|
||||
if tp is not None:
|
||||
qty = pending_tickets.get(entry.id, 0)
|
||||
widget_html = await render_to_sx("events-entry-widget-wrapper",
|
||||
widget=SxExpr(await _ticket_widget_html(entry, qty, ticket_url, ctx={})))
|
||||
widget_html = sx_call("events-entry-widget-wrapper",
|
||||
widget=SxExpr(_ticket_widget_html(entry, qty, ticket_url, ctx={})))
|
||||
|
||||
return await render_to_sx("events-entry-card",
|
||||
return sx_call("events-entry-card",
|
||||
title=SxExpr(title_html), badges=SxExpr(badges_html),
|
||||
time_parts=SxExpr(time_parts), cost=SxExpr(cost_html),
|
||||
widget=SxExpr(widget_html))
|
||||
|
||||
|
||||
async def _entry_card_tile_html(entry, page_info: dict, pending_tickets: dict,
|
||||
def _entry_card_tile_html(entry, page_info: dict, pending_tickets: dict,
|
||||
ticket_url: str, events_url_fn, *, is_page_scoped: bool = False,
|
||||
post: dict | None = None) -> str:
|
||||
"""Render a tile card for one event entry."""
|
||||
@@ -100,25 +100,25 @@ async def _entry_card_tile_html(entry, page_info: dict, pending_tickets: dict,
|
||||
|
||||
# Title
|
||||
if entry_href:
|
||||
title_html = await render_to_sx("events-entry-title-tile-linked",
|
||||
title_html = sx_call("events-entry-title-tile-linked",
|
||||
href=entry_href, name=entry.name)
|
||||
else:
|
||||
title_html = await render_to_sx("events-entry-title-tile-plain", name=entry.name)
|
||||
title_html = sx_call("events-entry-title-tile-plain", name=entry.name)
|
||||
|
||||
# Badges
|
||||
badges_html = ""
|
||||
if page_title and (not is_page_scoped or page_title != (post or {}).get("title")):
|
||||
page_href = events_url_fn(f"/{page_slug}/")
|
||||
badges_html += await render_to_sx("events-entry-page-badge",
|
||||
badges_html += sx_call("events-entry-page-badge",
|
||||
href=page_href, title=page_title)
|
||||
cal_name = getattr(entry, "calendar_name", "")
|
||||
if cal_name:
|
||||
badges_html += await render_to_sx("events-entry-cal-badge", name=cal_name)
|
||||
badges_html += sx_call("events-entry-cal-badge", name=cal_name)
|
||||
|
||||
# Time
|
||||
time_html = ""
|
||||
if day_href:
|
||||
time_html += (await render_to_sx("events-entry-time-linked",
|
||||
time_html += (sx_call("events-entry-time-linked",
|
||||
href=day_href,
|
||||
date_str=entry.start_at.strftime("%a %-d %b"))).replace(" · ", "")
|
||||
else:
|
||||
@@ -128,7 +128,7 @@ async def _entry_card_tile_html(entry, page_info: dict, pending_tickets: dict,
|
||||
time_html += f' \u2013 {entry.end_at.strftime("%H:%M")}'
|
||||
|
||||
cost = getattr(entry, "cost", None)
|
||||
cost_html = await render_to_sx("events-entry-cost",
|
||||
cost_html = sx_call("events-entry-cost",
|
||||
cost=f"\u00a3{cost:.2f}") if cost else ""
|
||||
|
||||
# Ticket widget
|
||||
@@ -136,16 +136,16 @@ async def _entry_card_tile_html(entry, page_info: dict, pending_tickets: dict,
|
||||
widget_html = ""
|
||||
if tp is not None:
|
||||
qty = pending_tickets.get(entry.id, 0)
|
||||
widget_html = await render_to_sx("events-entry-tile-widget-wrapper",
|
||||
widget=SxExpr(await _ticket_widget_html(entry, qty, ticket_url, ctx={})))
|
||||
widget_html = sx_call("events-entry-tile-widget-wrapper",
|
||||
widget=SxExpr(_ticket_widget_html(entry, qty, ticket_url, ctx={})))
|
||||
|
||||
return await render_to_sx("events-entry-card-tile",
|
||||
return sx_call("events-entry-card-tile",
|
||||
title=SxExpr(title_html), badges=SxExpr(badges_html),
|
||||
time=SxExpr(time_html), cost=SxExpr(cost_html),
|
||||
widget=SxExpr(widget_html))
|
||||
|
||||
|
||||
async def _entry_cards_html(entries, page_info, pending_tickets, ticket_url,
|
||||
def _entry_cards_html(entries, page_info, pending_tickets, ticket_url,
|
||||
events_url_fn, view, page, has_more, next_url,
|
||||
*, is_page_scoped=False, post=None) -> str:
|
||||
"""Render entry cards (list or tile) with sentinel."""
|
||||
@@ -153,23 +153,23 @@ async def _entry_cards_html(entries, page_info, pending_tickets, ticket_url,
|
||||
last_date = None
|
||||
for entry in entries:
|
||||
if view == "tile":
|
||||
parts.append(await _entry_card_tile_html(
|
||||
parts.append(_entry_card_tile_html(
|
||||
entry, page_info, pending_tickets, ticket_url, events_url_fn,
|
||||
is_page_scoped=is_page_scoped, post=post,
|
||||
))
|
||||
else:
|
||||
entry_date = entry.start_at.strftime("%A %-d %B %Y") if entry.start_at else ""
|
||||
if entry_date != last_date:
|
||||
parts.append(await render_to_sx("events-date-separator",
|
||||
parts.append(sx_call("events-date-separator",
|
||||
date_str=entry_date))
|
||||
last_date = entry_date
|
||||
parts.append(await _entry_card_html(
|
||||
parts.append(_entry_card_html(
|
||||
entry, page_info, pending_tickets, ticket_url, events_url_fn,
|
||||
is_page_scoped=is_page_scoped, post=post,
|
||||
))
|
||||
|
||||
if has_more:
|
||||
parts.append(await render_to_sx("sentinel-simple",
|
||||
parts.append(sx_call("sentinel-simple",
|
||||
id=f"sentinel-{page}", next_url=next_url))
|
||||
return "".join(parts)
|
||||
|
||||
@@ -178,27 +178,27 @@ async def _entry_cards_html(entries, page_info, pending_tickets, ticket_url,
|
||||
# All events / page summary main panels
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def _events_main_panel_html(ctx: dict, entries, has_more, pending_tickets, page_info,
|
||||
def _events_main_panel_html(ctx: dict, entries, has_more, pending_tickets, page_info,
|
||||
page, view, ticket_url, next_url, events_url_fn,
|
||||
*, is_page_scoped=False, post=None) -> str:
|
||||
"""Render the events main panel with view toggle + cards."""
|
||||
toggle = await _view_toggle_html(ctx, view)
|
||||
toggle = _view_toggle_html(ctx, view)
|
||||
|
||||
if entries:
|
||||
cards = await _entry_cards_html(
|
||||
cards = _entry_cards_html(
|
||||
entries, page_info, pending_tickets, ticket_url, events_url_fn,
|
||||
view, page, has_more, next_url,
|
||||
is_page_scoped=is_page_scoped, post=post,
|
||||
)
|
||||
grid_cls = ("max-w-full px-3 py-3 grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4"
|
||||
if view == "tile" else "max-w-full px-3 py-3 space-y-3")
|
||||
body = await render_to_sx("events-grid", grid_cls=grid_cls, cards=SxExpr(cards))
|
||||
body = sx_call("events-grid", grid_cls=grid_cls, cards=SxExpr(cards))
|
||||
else:
|
||||
body = await render_to_sx("empty-state", icon="fa fa-calendar-xmark",
|
||||
body = sx_call("empty-state", icon="fa fa-calendar-xmark",
|
||||
message="No upcoming events",
|
||||
cls="px-3 py-12 text-center text-stone-400")
|
||||
|
||||
return await render_to_sx("events-main-panel-body",
|
||||
return sx_call("events-main-panel-body",
|
||||
toggle=SxExpr(toggle), body=SxExpr(body))
|
||||
|
||||
|
||||
@@ -206,7 +206,7 @@ async def _events_main_panel_html(ctx: dict, entries, has_more, pending_tickets,
|
||||
# Entry main panel
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def _entry_main_panel_html(ctx: dict) -> str:
|
||||
def _entry_main_panel_html(ctx: dict) -> str:
|
||||
"""Render the entry detail panel (name, slot, time, state, cost, tickets,
|
||||
buy form, date, posts, options + edit button)."""
|
||||
from quart import url_for
|
||||
@@ -228,63 +228,63 @@ async def _entry_main_panel_html(ctx: dict) -> str:
|
||||
eid = entry.id
|
||||
state = getattr(entry, "state", "pending") or "pending"
|
||||
|
||||
async def _field(label, content_html):
|
||||
return await render_to_sx("events-entry-field", label=label, content=SxExpr(content_html))
|
||||
def _field(label, content_html):
|
||||
return sx_call("events-entry-field", label=label, content=SxExpr(content_html))
|
||||
|
||||
# Name
|
||||
name_html = await _field("Name", await render_to_sx("events-entry-name-field", name=entry.name))
|
||||
name_html = _field("Name", sx_call("events-entry-name-field", name=entry.name))
|
||||
|
||||
# Slot
|
||||
slot = getattr(entry, "slot", None)
|
||||
if slot:
|
||||
flex_label = "(flexible)" if getattr(slot, "flexible", False) else "(fixed)"
|
||||
slot_inner = await render_to_sx("events-entry-slot-assigned",
|
||||
slot_inner = sx_call("events-entry-slot-assigned",
|
||||
slot_name=slot.name, flex_label=flex_label)
|
||||
else:
|
||||
slot_inner = await render_to_sx("events-entry-slot-none")
|
||||
slot_html = await _field("Slot", slot_inner)
|
||||
slot_inner = sx_call("events-entry-slot-none")
|
||||
slot_html = _field("Slot", slot_inner)
|
||||
|
||||
# Time Period
|
||||
start_str = entry.start_at.strftime("%H:%M") if entry.start_at else ""
|
||||
end_str = f" \u2013 {entry.end_at.strftime('%H:%M')}" if entry.end_at else " \u2013 open-ended"
|
||||
time_html = await _field("Time Period", await render_to_sx("events-entry-time-field",
|
||||
time_html = _field("Time Period", sx_call("events-entry-time-field",
|
||||
time_str=start_str + end_str))
|
||||
|
||||
# State
|
||||
state_html = await _field("State", await render_to_sx("events-entry-state-field",
|
||||
state_html = _field("State", sx_call("events-entry-state-field",
|
||||
entry_id=str(eid),
|
||||
badge=SxExpr(await _entry_state_badge_html(state))))
|
||||
badge=SxExpr(_entry_state_badge_html(state))))
|
||||
|
||||
# Cost
|
||||
cost = getattr(entry, "cost", None)
|
||||
cost_str = f"{cost:.2f}" if cost is not None else "0.00"
|
||||
cost_html = await _field("Cost", await render_to_sx("events-entry-cost-field",
|
||||
cost_html = _field("Cost", sx_call("events-entry-cost-field",
|
||||
cost=f"\u00a3{cost_str}"))
|
||||
|
||||
# Ticket Configuration (admin)
|
||||
tickets_html = await _field("Tickets", await render_to_sx("events-entry-tickets-field",
|
||||
tickets_html = _field("Tickets", sx_call("events-entry-tickets-field",
|
||||
entry_id=str(eid),
|
||||
tickets_config=SxExpr(await render_entry_tickets_config(entry, calendar, day, month, year))))
|
||||
tickets_config=SxExpr(render_entry_tickets_config(entry, calendar, day, month, year))))
|
||||
|
||||
# Buy Tickets (public-facing)
|
||||
ticket_remaining = ctx.get("ticket_remaining")
|
||||
ticket_sold_count = ctx.get("ticket_sold_count", 0)
|
||||
user_ticket_count = ctx.get("user_ticket_count", 0)
|
||||
user_ticket_counts_by_type = ctx.get("user_ticket_counts_by_type") or {}
|
||||
buy_html = await render_buy_form(
|
||||
buy_html = render_buy_form(
|
||||
entry, ticket_remaining, ticket_sold_count,
|
||||
user_ticket_count, user_ticket_counts_by_type,
|
||||
)
|
||||
|
||||
# Date
|
||||
date_str = entry.start_at.strftime("%A, %B %d, %Y") if entry.start_at else ""
|
||||
date_html = await _field("Date", await render_to_sx("events-entry-date-field", date_str=date_str))
|
||||
date_html = _field("Date", sx_call("events-entry-date-field", date_str=date_str))
|
||||
|
||||
# Associated Posts
|
||||
entry_posts = ctx.get("entry_posts") or []
|
||||
posts_html = await _field("Associated Posts", await render_to_sx("events-entry-posts-field",
|
||||
posts_html = _field("Associated Posts", sx_call("events-entry-posts-field",
|
||||
entry_id=str(eid),
|
||||
posts_panel=SxExpr(await render_entry_posts_panel(entry_posts, entry, calendar, day, month, year))))
|
||||
posts_panel=SxExpr(render_entry_posts_panel(entry_posts, entry, calendar, day, month, year))))
|
||||
|
||||
# Options and Edit Button
|
||||
edit_url = url_for(
|
||||
@@ -293,14 +293,14 @@ async def _entry_main_panel_html(ctx: dict) -> str:
|
||||
day=day, month=month, year=year,
|
||||
)
|
||||
|
||||
return await render_to_sx("events-entry-panel",
|
||||
return sx_call("events-entry-panel",
|
||||
entry_id=str(eid), list_container=list_container,
|
||||
name=SxExpr(name_html), slot=SxExpr(slot_html),
|
||||
time=SxExpr(time_html), state=SxExpr(state_html),
|
||||
cost=SxExpr(cost_html), tickets=SxExpr(tickets_html),
|
||||
buy=SxExpr(buy_html), date=SxExpr(date_html),
|
||||
posts=SxExpr(posts_html),
|
||||
options=SxExpr(await _entry_options_html(entry, calendar, day, month, year)),
|
||||
options=SxExpr(_entry_options_html(entry, calendar, day, month, year)),
|
||||
pre_action=pre_action, edit_url=edit_url)
|
||||
|
||||
|
||||
@@ -308,7 +308,7 @@ async def _entry_main_panel_html(ctx: dict) -> str:
|
||||
# Entry header row
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def _entry_header_html(ctx: dict, *, oob: bool = False) -> str:
|
||||
def _entry_header_html(ctx: dict, *, oob: bool = False) -> str:
|
||||
"""Build entry detail header row."""
|
||||
from quart import url_for
|
||||
|
||||
@@ -329,19 +329,19 @@ async def _entry_header_html(ctx: dict, *, oob: bool = False) -> str:
|
||||
year=year, month=month, day=day,
|
||||
entry_id=entry.id,
|
||||
)
|
||||
label_html = await render_to_sx("events-entry-label",
|
||||
label_html = sx_call("events-entry-label",
|
||||
entry_id=str(entry.id),
|
||||
title=SxExpr(await _entry_title_html(entry)),
|
||||
times=SxExpr(await _entry_times_html(entry)))
|
||||
title=SxExpr(_entry_title_html(entry)),
|
||||
times=SxExpr(_entry_times_html(entry)))
|
||||
|
||||
nav_html = await _entry_nav_html(ctx)
|
||||
nav_html = _entry_nav_html(ctx)
|
||||
|
||||
return await render_to_sx("menu-row-sx", id="entry-row", level=5,
|
||||
return sx_call("menu-row-sx", id="entry-row", level=5,
|
||||
link_href=link_href, link_label_content=SxExpr(label_html),
|
||||
nav=SxExpr(nav_html) if nav_html else None, child_id="entry-header-child", oob=oob)
|
||||
|
||||
|
||||
async def _entry_times_html(entry) -> str:
|
||||
def _entry_times_html(entry) -> str:
|
||||
"""Render entry times label."""
|
||||
start = entry.start_at
|
||||
end = entry.end_at
|
||||
@@ -349,14 +349,14 @@ async def _entry_times_html(entry) -> str:
|
||||
return ""
|
||||
start_str = start.strftime("%H:%M")
|
||||
end_str = f" \u2192 {end.strftime('%H:%M')}" if end else ""
|
||||
return await render_to_sx("events-entry-times", time_str=start_str + end_str)
|
||||
return sx_call("events-entry-times", time_str=start_str + end_str)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Entry nav (desktop + admin link)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def _entry_nav_html(ctx: dict) -> str:
|
||||
def _entry_nav_html(ctx: dict) -> str:
|
||||
"""Entry desktop nav: associated posts scrolling menu + admin link."""
|
||||
from quart import url_for
|
||||
|
||||
@@ -387,12 +387,12 @@ async def _entry_nav_html(ctx: dict) -> str:
|
||||
feat = getattr(ep, "feature_image", None)
|
||||
href = blog_url_fn(f"/{slug}/") if blog_url_fn else f"/{slug}/"
|
||||
if feat:
|
||||
img_html = await render_to_sx("events-post-img", src=feat, alt=title)
|
||||
img_html = sx_call("events-post-img", src=feat, alt=title)
|
||||
else:
|
||||
img_html = await render_to_sx("events-post-img-placeholder")
|
||||
post_links += await render_to_sx("events-entry-nav-post-link",
|
||||
img_html = sx_call("events-post-img-placeholder")
|
||||
post_links += sx_call("events-entry-nav-post-link",
|
||||
href=href, img=SxExpr(img_html), title=title)
|
||||
parts.append((await render_to_sx("events-entry-posts-nav-oob",
|
||||
parts.append((sx_call("events-entry-posts-nav-oob",
|
||||
items=SxExpr(post_links))).replace(' :hx-swap-oob "true"', ''))
|
||||
|
||||
# Admin link
|
||||
@@ -403,7 +403,7 @@ async def _entry_nav_html(ctx: dict) -> str:
|
||||
day=day, month=month, year=year,
|
||||
entry_id=entry.id,
|
||||
)
|
||||
parts.append(await render_to_sx("events-entry-admin-link", href=admin_url))
|
||||
parts.append(sx_call("events-entry-admin-link", href=admin_url))
|
||||
|
||||
return "".join(parts)
|
||||
|
||||
@@ -412,26 +412,26 @@ async def _entry_nav_html(ctx: dict) -> str:
|
||||
# Entry optioned (confirm/decline/provisional response)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def render_entry_optioned(entry, calendar, day, month, year) -> str:
|
||||
def render_entry_optioned(entry, calendar, day, month, year) -> str:
|
||||
"""Render entry options buttons + OOB title & state swaps."""
|
||||
options = await _entry_options_html(entry, calendar, day, month, year)
|
||||
title = await _entry_title_html(entry)
|
||||
state = await _entry_state_badge_html(getattr(entry, "state", "pending") or "pending")
|
||||
options = _entry_options_html(entry, calendar, day, month, year)
|
||||
title = _entry_title_html(entry)
|
||||
state = _entry_state_badge_html(getattr(entry, "state", "pending") or "pending")
|
||||
|
||||
return options + await render_to_sx("events-entry-optioned-oob",
|
||||
return options + sx_call("events-entry-optioned-oob",
|
||||
entry_id=str(entry.id),
|
||||
title=SxExpr(title), state=SxExpr(state))
|
||||
|
||||
|
||||
async def _entry_title_html(entry) -> str:
|
||||
def _entry_title_html(entry) -> str:
|
||||
"""Render entry title (icon + name + state badge)."""
|
||||
state = getattr(entry, "state", "pending") or "pending"
|
||||
return await render_to_sx("events-entry-title",
|
||||
return sx_call("events-entry-title",
|
||||
name=entry.name,
|
||||
badge=SxExpr(await _entry_state_badge_html(state)))
|
||||
badge=SxExpr(_entry_state_badge_html(state)))
|
||||
|
||||
|
||||
async def _entry_options_html(entry, calendar, day, month, year) -> str:
|
||||
def _entry_options_html(entry, calendar, day, month, year) -> str:
|
||||
"""Render confirm/decline/provisional buttons based on entry state."""
|
||||
from quart import url_for, g
|
||||
from shared.browser.app.csrf import generate_csrf_token
|
||||
@@ -445,13 +445,13 @@ async def _entry_options_html(entry, calendar, day, month, year) -> str:
|
||||
state = getattr(entry, "state", "pending") or "pending"
|
||||
target = f"#calendar_entry_options_{eid}"
|
||||
|
||||
async def _make_button(action_name, label, confirm_title, confirm_text, *, trigger_type="submit"):
|
||||
def _make_button(action_name, label, confirm_title, confirm_text, *, trigger_type="submit"):
|
||||
url = url_for(
|
||||
f"calendar.day.calendar_entries.calendar_entry.{action_name}",
|
||||
calendar_slug=cal_slug, day=day, month=month, year=year, entry_id=eid,
|
||||
)
|
||||
btn_type = "button" if trigger_type == "button" else "submit"
|
||||
return await render_to_sx("events-entry-option-button",
|
||||
return sx_call("events-entry-option-button",
|
||||
url=url, target=target, csrf=csrf, btn_type=btn_type,
|
||||
action_btn=action_btn, confirm_title=confirm_title,
|
||||
confirm_text=confirm_text, label=label,
|
||||
@@ -459,22 +459,22 @@ async def _entry_options_html(entry, calendar, day, month, year) -> str:
|
||||
|
||||
buttons_html = ""
|
||||
if state == "provisional":
|
||||
buttons_html += await _make_button(
|
||||
buttons_html += _make_button(
|
||||
"confirm_entry", "confirm",
|
||||
"Confirm entry?", "Are you sure you want to confirm this entry?",
|
||||
)
|
||||
buttons_html += await _make_button(
|
||||
buttons_html += _make_button(
|
||||
"decline_entry", "decline",
|
||||
"Decline entry?", "Are you sure you want to decline this entry?",
|
||||
)
|
||||
elif state == "confirmed":
|
||||
buttons_html += await _make_button(
|
||||
buttons_html += _make_button(
|
||||
"provisional_entry", "provisional",
|
||||
"Provisional entry?", "Are you sure you want to provisional this entry?",
|
||||
trigger_type="button",
|
||||
)
|
||||
|
||||
return await render_to_sx("events-entry-options",
|
||||
return sx_call("events-entry-options",
|
||||
entry_id=str(eid), buttons=SxExpr(buttons_html))
|
||||
|
||||
|
||||
@@ -482,7 +482,7 @@ async def _entry_options_html(entry, calendar, day, month, year) -> str:
|
||||
# Entry tickets config (display + form)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def render_entry_tickets_config(entry, calendar, day, month, year) -> str:
|
||||
def render_entry_tickets_config(entry, calendar, day, month, year) -> str:
|
||||
"""Render ticket config display + edit form for admin entry view."""
|
||||
from quart import url_for
|
||||
from shared.browser.app.csrf import generate_csrf_token
|
||||
@@ -499,11 +499,11 @@ async def render_entry_tickets_config(entry, calendar, day, month, year) -> str:
|
||||
|
||||
if tp is not None:
|
||||
tc_str = f"{tc} tickets" if tc is not None else "Unlimited"
|
||||
display_html = await render_to_sx("events-ticket-config-display",
|
||||
display_html = sx_call("events-ticket-config-display",
|
||||
price_str=f"\u00a3{tp:.2f}",
|
||||
count_str=tc_str, show_js=show_js)
|
||||
else:
|
||||
display_html = await render_to_sx("events-ticket-config-none", show_js=show_js)
|
||||
display_html = sx_call("events-ticket-config-none", show_js=show_js)
|
||||
|
||||
update_url = url_for(
|
||||
"calendar.day.calendar_entries.calendar_entry.update_tickets",
|
||||
@@ -513,7 +513,7 @@ async def render_entry_tickets_config(entry, calendar, day, month, year) -> str:
|
||||
tp_val = f"{tp:.2f}" if tp is not None else ""
|
||||
tc_val = str(tc) if tc is not None else ""
|
||||
|
||||
form_html = await render_to_sx("events-ticket-config-form",
|
||||
form_html = sx_call("events-ticket-config-form",
|
||||
entry_id=eid_s, hidden_cls=hidden_cls,
|
||||
update_url=update_url, csrf=csrf,
|
||||
price_val=tp_val, count_val=tc_val, hide_js=hide_js)
|
||||
@@ -524,7 +524,7 @@ async def render_entry_tickets_config(entry, calendar, day, month, year) -> str:
|
||||
# Entry posts panel
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def render_entry_posts_panel(entry_posts, entry, calendar, day, month, year) -> str:
|
||||
def render_entry_posts_panel(entry_posts, entry, calendar, day, month, year) -> str:
|
||||
"""Render associated posts list with remove buttons and search input."""
|
||||
from quart import url_for
|
||||
from shared.browser.app.csrf import generate_csrf_token
|
||||
@@ -541,28 +541,28 @@ async def render_entry_posts_panel(entry_posts, entry, calendar, day, month, yea
|
||||
ep_title = getattr(ep, "title", "")
|
||||
ep_id = getattr(ep, "id", 0)
|
||||
feat = getattr(ep, "feature_image", None)
|
||||
img_html = (await render_to_sx("events-post-img", src=feat, alt=ep_title)
|
||||
if feat else await render_to_sx("events-post-img-placeholder"))
|
||||
img_html = (sx_call("events-post-img", src=feat, alt=ep_title)
|
||||
if feat else sx_call("events-post-img-placeholder"))
|
||||
|
||||
del_url = url_for(
|
||||
"calendar.day.calendar_entries.calendar_entry.remove_post",
|
||||
calendar_slug=cal_slug, day=day, month=month, year=year,
|
||||
entry_id=eid, post_id=ep_id,
|
||||
)
|
||||
items += await render_to_sx("events-entry-post-item",
|
||||
items += sx_call("events-entry-post-item",
|
||||
img=SxExpr(img_html), title=ep_title,
|
||||
del_url=del_url, entry_id=eid_s,
|
||||
csrf_hdr=f'{{"X-CSRFToken": "{csrf}"}}')
|
||||
posts_html = await render_to_sx("events-entry-posts-list", items=SxExpr(items))
|
||||
posts_html = sx_call("events-entry-posts-list", items=SxExpr(items))
|
||||
else:
|
||||
posts_html = await render_to_sx("events-entry-posts-none")
|
||||
posts_html = sx_call("events-entry-posts-none")
|
||||
|
||||
search_url = url_for(
|
||||
"calendar.day.calendar_entries.calendar_entry.search_posts",
|
||||
calendar_slug=cal_slug, day=day, month=month, year=year, entry_id=eid,
|
||||
)
|
||||
|
||||
return await render_to_sx("events-entry-posts-panel",
|
||||
return sx_call("events-entry-posts-panel",
|
||||
posts=SxExpr(posts_html), search_url=search_url,
|
||||
entry_id=eid_s)
|
||||
|
||||
@@ -571,7 +571,7 @@ async def render_entry_posts_panel(entry_posts, entry, calendar, day, month, yea
|
||||
# Entry posts nav OOB
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def render_entry_posts_nav_oob(entry_posts) -> str:
|
||||
def render_entry_posts_nav_oob(entry_posts) -> str:
|
||||
"""Render OOB nav for entry posts (scrolling menu)."""
|
||||
from quart import g
|
||||
styles = getattr(g, "styles", None) or {}
|
||||
@@ -579,7 +579,7 @@ async def render_entry_posts_nav_oob(entry_posts) -> str:
|
||||
blog_url_fn = getattr(g, "blog_url", None)
|
||||
|
||||
if not entry_posts:
|
||||
return await render_to_sx("events-entry-posts-nav-oob-empty")
|
||||
return sx_call("events-entry-posts-nav-oob-empty")
|
||||
|
||||
items = ""
|
||||
for ep in entry_posts:
|
||||
@@ -587,20 +587,20 @@ async def render_entry_posts_nav_oob(entry_posts) -> str:
|
||||
title = getattr(ep, "title", "")
|
||||
feat = getattr(ep, "feature_image", None)
|
||||
href = blog_url_fn(f"/{slug}/") if blog_url_fn else f"/{slug}/"
|
||||
img_html = (await render_to_sx("events-post-img", src=feat, alt=title)
|
||||
if feat else await render_to_sx("events-post-img-placeholder"))
|
||||
items += await render_to_sx("events-entry-nav-post",
|
||||
img_html = (sx_call("events-post-img", src=feat, alt=title)
|
||||
if feat else sx_call("events-post-img-placeholder"))
|
||||
items += sx_call("events-entry-nav-post",
|
||||
href=href, nav_btn=nav_btn,
|
||||
img=SxExpr(img_html), title=title)
|
||||
|
||||
return await render_to_sx("events-entry-posts-nav-oob", items=SxExpr(items))
|
||||
return sx_call("events-entry-posts-nav-oob", items=SxExpr(items))
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Day entries nav OOB
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def render_day_entries_nav_oob(confirmed_entries, calendar, day_date) -> str:
|
||||
def render_day_entries_nav_oob(confirmed_entries, calendar, day_date) -> str:
|
||||
"""Render OOB nav for confirmed entries in a day."""
|
||||
from quart import url_for, g
|
||||
|
||||
@@ -609,7 +609,7 @@ async def render_day_entries_nav_oob(confirmed_entries, calendar, day_date) -> s
|
||||
cal_slug = getattr(calendar, "slug", "")
|
||||
|
||||
if not confirmed_entries:
|
||||
return await render_to_sx("events-day-entries-nav-oob-empty")
|
||||
return sx_call("events-day-entries-nav-oob-empty")
|
||||
|
||||
items = ""
|
||||
for entry in confirmed_entries:
|
||||
@@ -621,18 +621,18 @@ async def render_day_entries_nav_oob(confirmed_entries, calendar, day_date) -> s
|
||||
)
|
||||
start = entry.start_at.strftime("%H:%M") if entry.start_at else ""
|
||||
end = f" \u2013 {entry.end_at.strftime('%H:%M')}" if entry.end_at else ""
|
||||
items += await render_to_sx("events-day-nav-entry",
|
||||
items += sx_call("events-day-nav-entry",
|
||||
href=href, nav_btn=nav_btn,
|
||||
name=entry.name, time_str=start + end)
|
||||
|
||||
return await render_to_sx("events-day-entries-nav-oob", items=SxExpr(items))
|
||||
return sx_call("events-day-entries-nav-oob", items=SxExpr(items))
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Post nav entries OOB
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def render_post_nav_entries_oob(associated_entries, calendars, post) -> str:
|
||||
def render_post_nav_entries_oob(associated_entries, calendars, post) -> str:
|
||||
"""Render OOB nav for associated entries and calendars of a post."""
|
||||
from quart import g
|
||||
from shared.infrastructure.urls import events_url
|
||||
@@ -644,7 +644,7 @@ async def render_post_nav_entries_oob(associated_entries, calendars, post) -> st
|
||||
has_items = has_entries or calendars
|
||||
|
||||
if not has_items:
|
||||
return await render_to_sx("events-post-nav-oob-empty")
|
||||
return sx_call("events-post-nav-oob-empty")
|
||||
|
||||
slug = post.get("slug", "") if isinstance(post, dict) else getattr(post, "slug", "")
|
||||
|
||||
@@ -659,7 +659,7 @@ async def render_post_nav_entries_oob(associated_entries, calendars, post) -> st
|
||||
href = events_url(entry_path)
|
||||
time_str = entry.start_at.strftime("%b %d, %Y at %H:%M")
|
||||
end_str = f" \u2013 {entry.end_at.strftime('%H:%M')}" if entry.end_at else ""
|
||||
items += await render_to_sx("events-post-nav-entry",
|
||||
items += sx_call("events-post-nav-entry",
|
||||
href=href, nav_btn=nav_btn,
|
||||
name=entry.name, time_str=time_str + end_str)
|
||||
|
||||
@@ -667,7 +667,7 @@ async def render_post_nav_entries_oob(associated_entries, calendars, post) -> st
|
||||
for cal in calendars:
|
||||
cs = getattr(cal, "slug", "")
|
||||
local_href = events_url(f"/{slug}/{cs}/")
|
||||
items += await render_to_sx("events-post-nav-calendar",
|
||||
items += sx_call("events-post-nav-calendar",
|
||||
href=local_href, nav_btn=nav_btn, name=cal.name)
|
||||
|
||||
hs = ("on load or scroll "
|
||||
@@ -675,7 +675,7 @@ async def render_post_nav_entries_oob(associated_entries, calendars, post) -> st
|
||||
"remove .hidden from .entries-nav-arrow add .flex to .entries-nav-arrow "
|
||||
"else add .hidden to .entries-nav-arrow remove .flex from .entries-nav-arrow end")
|
||||
|
||||
return await render_to_sx("events-post-nav-wrapper",
|
||||
return sx_call("events-post-nav-wrapper",
|
||||
items=SxExpr(items), hyperscript=hs)
|
||||
|
||||
|
||||
@@ -683,23 +683,23 @@ async def render_post_nav_entries_oob(associated_entries, calendars, post) -> st
|
||||
# Calendar description display + edit form
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def render_calendar_description(calendar, *, oob: bool = False) -> str:
|
||||
def render_calendar_description(calendar, *, oob: bool = False) -> str:
|
||||
"""Render calendar description display with edit button, optionally with OOB title."""
|
||||
from quart import url_for
|
||||
from .calendar import _calendar_description_display_html
|
||||
|
||||
cal_slug = getattr(calendar, "slug", "")
|
||||
edit_url = url_for("calendar.admin.calendar_description_edit", calendar_slug=cal_slug)
|
||||
html = await _calendar_description_display_html(calendar, edit_url)
|
||||
html = _calendar_description_display_html(calendar, edit_url)
|
||||
|
||||
if oob:
|
||||
desc = getattr(calendar, "description", "") or ""
|
||||
html += await render_to_sx("events-calendar-description-title-oob",
|
||||
html += sx_call("events-calendar-description-title-oob",
|
||||
description=desc)
|
||||
return html
|
||||
|
||||
|
||||
async def render_calendar_description_edit(calendar) -> str:
|
||||
def render_calendar_description_edit(calendar) -> str:
|
||||
"""Render calendar description edit form."""
|
||||
from quart import url_for
|
||||
from shared.browser.app.csrf import generate_csrf_token
|
||||
@@ -710,7 +710,7 @@ async def render_calendar_description_edit(calendar) -> str:
|
||||
save_url = url_for("calendar.admin.calendar_description_save", calendar_slug=cal_slug)
|
||||
cancel_url = url_for("calendar.admin.calendar_description_view", calendar_slug=cal_slug)
|
||||
|
||||
return await render_to_sx("events-calendar-description-edit-form",
|
||||
return sx_call("events-calendar-description-edit-form",
|
||||
save_url=save_url, cancel_url=cancel_url,
|
||||
csrf=csrf, description=desc)
|
||||
|
||||
@@ -719,7 +719,7 @@ async def render_calendar_description_edit(calendar) -> str:
|
||||
# Entry admin page / OOB
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def _entry_admin_header_html(ctx: dict, *, oob: bool = False) -> str:
|
||||
def _entry_admin_header_html(ctx: dict, *, oob: bool = False) -> str:
|
||||
"""Build the entry admin header row."""
|
||||
from quart import url_for
|
||||
|
||||
@@ -739,14 +739,14 @@ async def _entry_admin_header_html(ctx: dict, *, oob: bool = False) -> str:
|
||||
calendar_slug=cal_slug, day=day, month=month, year=year, entry_id=entry.id,
|
||||
)
|
||||
# Nav: ticket_types link
|
||||
nav_html = await _entry_admin_nav_html(ctx)
|
||||
nav_html = _entry_admin_nav_html(ctx)
|
||||
|
||||
return await render_to_sx("menu-row-sx", id="entry-admin-row", level=6,
|
||||
return sx_call("menu-row-sx", id="entry-admin-row", level=6,
|
||||
link_href=link_href, link_label="admin", icon="fa fa-cog",
|
||||
nav=SxExpr(nav_html) if nav_html else None, child_id="entry-admin-header-child", oob=oob)
|
||||
|
||||
|
||||
async def _entry_admin_nav_html(ctx: dict) -> str:
|
||||
def _entry_admin_nav_html(ctx: dict) -> str:
|
||||
"""Entry admin nav: ticket_types link."""
|
||||
from quart import url_for
|
||||
|
||||
@@ -765,11 +765,11 @@ async def _entry_admin_nav_html(ctx: dict) -> str:
|
||||
href = url_for("calendar.day.calendar_entries.calendar_entry.ticket_types.get",
|
||||
calendar_slug=cal_slug, entry_id=entry.id,
|
||||
year=year, month=month, day=day)
|
||||
return await render_to_sx("nav-link", href=href, label="ticket_types",
|
||||
return sx_call("nav-link", href=href, label="ticket_types",
|
||||
select_colours=select_colours)
|
||||
|
||||
|
||||
async def _entry_admin_main_panel_html(ctx: dict) -> str:
|
||||
def _entry_admin_main_panel_html(ctx: dict) -> str:
|
||||
"""Entry admin main panel: just a ticket_types link."""
|
||||
from quart import url_for
|
||||
|
||||
@@ -789,7 +789,7 @@ async def _entry_admin_main_panel_html(ctx: dict) -> str:
|
||||
href = url_for("calendar.day.calendar_entries.calendar_entry.ticket_types.get",
|
||||
calendar_slug=cal_slug, entry_id=entry.id,
|
||||
year=year, month=month, day=day)
|
||||
return await render_to_sx("nav-link", href=href, label="ticket_types",
|
||||
return sx_call("nav-link", href=href, label="ticket_types",
|
||||
select_colours=select_colours, aclass=nav_btn,
|
||||
is_selected=False)
|
||||
|
||||
@@ -798,7 +798,7 @@ async def _entry_admin_main_panel_html(ctx: dict) -> str:
|
||||
# Post search results
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def render_post_search_results(search_posts, search_query, page, total_pages,
|
||||
def render_post_search_results(search_posts, search_query, page, total_pages,
|
||||
entry, calendar, day, month, year) -> str:
|
||||
"""Render post search results (replaces _types/entry/_post_search_results.html)."""
|
||||
from quart import url_for
|
||||
@@ -816,11 +816,11 @@ async def render_post_search_results(search_posts, search_query, page, total_pag
|
||||
feat = getattr(sp, "feature_image", None)
|
||||
title = getattr(sp, "title", "")
|
||||
if feat:
|
||||
img_html = await render_to_sx("events-post-img", src=feat, alt=title)
|
||||
img_html = sx_call("events-post-img", src=feat, alt=title)
|
||||
else:
|
||||
img_html = await render_to_sx("events-post-img-placeholder")
|
||||
img_html = sx_call("events-post-img-placeholder")
|
||||
|
||||
parts.append(await render_to_sx("events-post-search-item",
|
||||
parts.append(sx_call("events-post-search-item",
|
||||
post_url=post_url, entry_id=str(eid), csrf=csrf,
|
||||
post_id=str(sp.id), img=SxExpr(img_html), title=title))
|
||||
|
||||
@@ -830,10 +830,10 @@ async def render_post_search_results(search_posts, search_query, page, total_pag
|
||||
next_url = url_for("calendar.day.calendar_entries.calendar_entry.search_posts",
|
||||
calendar_slug=cal_slug, day=day, month=month, year=year,
|
||||
entry_id=eid, q=search_query, page=page + 1)
|
||||
result += await render_to_sx("events-post-search-sentinel",
|
||||
result += sx_call("events-post-search-sentinel",
|
||||
page=str(page), next_url=next_url)
|
||||
elif search_posts:
|
||||
result += await render_to_sx("events-post-search-end")
|
||||
result += sx_call("events-post-search-end")
|
||||
|
||||
return result
|
||||
|
||||
@@ -842,7 +842,7 @@ async def render_post_search_results(search_posts, search_query, page, total_pag
|
||||
# Entry edit form
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def render_entry_edit_form(entry, calendar, day, month, year, day_slots) -> str:
|
||||
def render_entry_edit_form(entry, calendar, day, month, year, day_slots) -> str:
|
||||
"""Render entry edit form (replaces _types/entry/_edit.html)."""
|
||||
from quart import url_for, g
|
||||
from shared.browser.app.csrf import generate_csrf_token
|
||||
@@ -863,11 +863,11 @@ async def render_entry_edit_form(entry, calendar, day, month, year, day_slots) -
|
||||
|
||||
# Slot picker
|
||||
if day_slots:
|
||||
options_html = await _slot_options_html(day_slots, selected_slot_id=getattr(entry, "slot_id", None))
|
||||
slot_picker_html = await render_to_sx("events-slot-picker",
|
||||
options_html = _slot_options_html(day_slots, selected_slot_id=getattr(entry, "slot_id", None))
|
||||
slot_picker_html = sx_call("events-slot-picker",
|
||||
id=f"entry-slot-{eid}", options=SxExpr(options_html))
|
||||
else:
|
||||
slot_picker_html = await render_to_sx("events-no-slots")
|
||||
slot_picker_html = sx_call("events-no-slots")
|
||||
|
||||
# Values
|
||||
start_val = entry.start_at.strftime("%H:%M") if entry.start_at else ""
|
||||
@@ -879,7 +879,7 @@ async def render_entry_edit_form(entry, calendar, day, month, year, day_slots) -
|
||||
tp_val = f"{tp:.2f}" if tp is not None else ""
|
||||
tc_val = str(tc) if tc is not None else ""
|
||||
|
||||
html = await render_to_sx("events-entry-edit-form",
|
||||
html = sx_call("events-entry-edit-form",
|
||||
entry_id=str(eid), list_container=list_container,
|
||||
put_url=put_url, cancel_url=cancel_url, csrf=csrf,
|
||||
name_val=entry.name or "", slot_picker=SxExpr(slot_picker_html),
|
||||
@@ -893,7 +893,7 @@ async def render_entry_edit_form(entry, calendar, day, month, year, day_slots) -
|
||||
# Entry add form / button
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def render_entry_add_form(calendar, day, month, year, day_slots) -> str:
|
||||
def render_entry_add_form(calendar, day, month, year, day_slots) -> str:
|
||||
"""Render entry add form (replaces _types/day/_add.html)."""
|
||||
from quart import url_for, g
|
||||
from shared.browser.app.csrf import generate_csrf_token
|
||||
@@ -912,13 +912,13 @@ async def render_entry_add_form(calendar, day, month, year, day_slots) -> str:
|
||||
|
||||
# Slot picker
|
||||
if day_slots:
|
||||
options_html = await _slot_options_html(day_slots)
|
||||
slot_picker_html = await render_to_sx("events-slot-picker",
|
||||
options_html = _slot_options_html(day_slots)
|
||||
slot_picker_html = sx_call("events-slot-picker",
|
||||
id="entry-slot-new", options=SxExpr(options_html))
|
||||
else:
|
||||
slot_picker_html = await render_to_sx("events-no-slots")
|
||||
slot_picker_html = sx_call("events-no-slots")
|
||||
|
||||
html = await render_to_sx("events-entry-add-form",
|
||||
html = sx_call("events-entry-add-form",
|
||||
post_url=post_url, csrf=csrf,
|
||||
slot_picker=SxExpr(slot_picker_html),
|
||||
action_btn=action_btn, cancel_btn=cancel_btn,
|
||||
@@ -926,7 +926,7 @@ async def render_entry_add_form(calendar, day, month, year, day_slots) -> str:
|
||||
return html + _SLOT_PICKER_JS
|
||||
|
||||
|
||||
async def render_entry_add_button(calendar, day, month, year) -> str:
|
||||
def render_entry_add_button(calendar, day, month, year) -> str:
|
||||
"""Render entry add button (replaces _types/day/_add_button.html)."""
|
||||
from quart import url_for, g
|
||||
|
||||
@@ -936,14 +936,14 @@ async def render_entry_add_button(calendar, day, month, year) -> str:
|
||||
add_url = url_for("calendar.day.calendar_entries.add_form",
|
||||
calendar_slug=cal_slug, day=day, month=month, year=year)
|
||||
|
||||
return await render_to_sx("events-entry-add-button", pre_action=pre_action, add_url=add_url)
|
||||
return sx_call("events-entry-add-button", pre_action=pre_action, add_url=add_url)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Fragment: container cards entries
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def render_fragment_container_cards(batch, post_ids, slug_map) -> str:
|
||||
def render_fragment_container_cards(batch, post_ids, slug_map) -> str:
|
||||
"""Render container cards entries (replaces fragments/container_cards_entries.html)."""
|
||||
from shared.infrastructure.urls import events_url
|
||||
|
||||
@@ -963,12 +963,12 @@ async def render_fragment_container_cards(batch, post_ids, slug_map) -> str:
|
||||
time_str = entry.start_at.strftime("%H:%M")
|
||||
if entry.end_at:
|
||||
time_str += f" \u2013 {entry.end_at.strftime('%H:%M')}"
|
||||
cards_html += await render_to_sx("events-frag-entry-card",
|
||||
cards_html += sx_call("events-frag-entry-card",
|
||||
href=events_url(_entry_path),
|
||||
name=entry.name,
|
||||
date_str=entry.start_at.strftime("%a, %b %d"),
|
||||
time_str=time_str)
|
||||
parts.append(await render_to_sx("events-frag-entries-widget", cards=SxExpr(cards_html)))
|
||||
parts.append(sx_call("events-frag-entries-widget", cards=SxExpr(cards_html)))
|
||||
parts.append(f"<!-- /card-widget:{post_id} -->")
|
||||
|
||||
return "\n".join(parts)
|
||||
@@ -978,7 +978,7 @@ async def render_fragment_container_cards(batch, post_ids, slug_map) -> str:
|
||||
# Fragment: account page tickets
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def render_fragment_account_tickets(tickets) -> str:
|
||||
def render_fragment_account_tickets(tickets) -> str:
|
||||
"""Render account page tickets (replaces fragments/account_page_tickets.html)."""
|
||||
from shared.infrastructure.urls import events_url
|
||||
|
||||
@@ -993,25 +993,25 @@ async def render_fragment_account_tickets(tickets) -> str:
|
||||
type_name = ""
|
||||
if getattr(ticket, "ticket_type_name", None):
|
||||
type_name = f'<span>· {escape(ticket.ticket_type_name)}</span>'
|
||||
badge_html = await render_to_sx("status-pill",
|
||||
badge_html = sx_call("status-pill",
|
||||
status=getattr(ticket, "state", ""))
|
||||
items_html += await render_to_sx("events-frag-ticket-item",
|
||||
items_html += sx_call("events-frag-ticket-item",
|
||||
href=href, entry_name=ticket.entry_name,
|
||||
date_str=date_str, calendar_name=cal_name,
|
||||
type_name=type_name, badge=SxExpr(badge_html))
|
||||
body = await render_to_sx("events-frag-tickets-list", items=SxExpr(items_html))
|
||||
body = sx_call("events-frag-tickets-list", items=SxExpr(items_html))
|
||||
else:
|
||||
body = await render_to_sx("empty-state", message="No tickets yet.",
|
||||
body = sx_call("empty-state", message="No tickets yet.",
|
||||
cls="text-sm text-stone-500")
|
||||
|
||||
return await render_to_sx("events-frag-tickets-panel", items=SxExpr(body))
|
||||
return sx_call("events-frag-tickets-panel", items=SxExpr(body))
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Fragment: account page bookings
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def render_fragment_account_bookings(bookings) -> str:
|
||||
def render_fragment_account_bookings(bookings) -> str:
|
||||
"""Render account page bookings (replaces fragments/account_page_bookings.html)."""
|
||||
if bookings:
|
||||
items_html = ""
|
||||
@@ -1027,16 +1027,16 @@ async def render_fragment_account_bookings(bookings) -> str:
|
||||
cost_str = ""
|
||||
if getattr(booking, "cost", None):
|
||||
cost_str = f'<span>· £{escape(str(booking.cost))}</span>'
|
||||
badge_html = await render_to_sx("status-pill",
|
||||
badge_html = sx_call("status-pill",
|
||||
status=getattr(booking, "state", ""))
|
||||
items_html += await render_to_sx("events-frag-booking-item",
|
||||
items_html += sx_call("events-frag-booking-item",
|
||||
name=booking.name,
|
||||
date_str=date_str + date_str_extra,
|
||||
calendar_name=cal_name, cost_str=cost_str,
|
||||
badge=SxExpr(badge_html))
|
||||
body = await render_to_sx("events-frag-bookings-list", items=SxExpr(items_html))
|
||||
body = sx_call("events-frag-bookings-list", items=SxExpr(items_html))
|
||||
else:
|
||||
body = await render_to_sx("empty-state", message="No bookings yet.",
|
||||
body = sx_call("empty-state", message="No bookings yet.",
|
||||
cls="text-sm text-stone-500")
|
||||
|
||||
return await render_to_sx("events-frag-bookings-panel", items=SxExpr(body))
|
||||
return sx_call("events-frag-bookings-panel", items=SxExpr(body))
|
||||
|
||||
@@ -3,7 +3,7 @@ from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from shared.sx.helpers import render_to_sx
|
||||
from shared.sx.helpers import sx_call
|
||||
|
||||
from .utils import _clear_deeper_oob, _ensure_container_nav
|
||||
from .calendar import (
|
||||
@@ -235,8 +235,8 @@ async def _cal_admin_full(ctx: dict, **kw: Any) -> str:
|
||||
return await render_to_sx_with_env("events-cal-admin-layout-full", {},
|
||||
post_header=SxExpr(await _post_header_sx(ctx)),
|
||||
admin_header=SxExpr(await post_admin_header_sx(ctx, slug, selected="calendars")),
|
||||
calendar_header=SxExpr(await _calendar_header_sx(ctx)),
|
||||
calendar_admin_header=SxExpr(await _calendar_admin_header_sx(ctx)),
|
||||
calendar_header=SxExpr(_calendar_header_sx(ctx)),
|
||||
calendar_admin_header=SxExpr(_calendar_admin_header_sx(ctx)),
|
||||
)
|
||||
|
||||
|
||||
@@ -247,9 +247,9 @@ async def _cal_admin_oob(ctx: dict, **kw: Any) -> str:
|
||||
slug = (ctx.get("post") or {}).get("slug", "")
|
||||
return await render_to_sx_with_env("events-cal-admin-layout-oob", {},
|
||||
admin_oob=SxExpr(await post_admin_header_sx(ctx, slug, oob=True, selected="calendars")),
|
||||
cal_oob=SxExpr(await _calendar_header_sx(ctx, oob=True)),
|
||||
cal_oob=SxExpr(_calendar_header_sx(ctx, oob=True)),
|
||||
cal_admin_oob_wrap=SxExpr(await oob_header_sx("calendar-header-child",
|
||||
"calendar-admin-header-child", await _calendar_admin_header_sx(ctx))),
|
||||
"calendar-admin-header-child", _calendar_admin_header_sx(ctx))),
|
||||
clear_oob=SxExpr(_clear_deeper_oob("post-row", "post-header-child",
|
||||
"post-admin-row", "post-admin-header-child",
|
||||
"calendar-row", "calendar-header-child",
|
||||
@@ -270,7 +270,7 @@ async def _slots_oob(ctx: dict, **kw: Any) -> str:
|
||||
slug = (ctx.get("post") or {}).get("slug", "")
|
||||
return await render_to_sx_with_env("events-slots-layout-oob", {},
|
||||
admin_oob=SxExpr(await post_admin_header_sx(ctx, slug, oob=True, selected="calendars")),
|
||||
cal_admin_oob=SxExpr(await _calendar_admin_header_sx(ctx, oob=True)),
|
||||
cal_admin_oob=SxExpr(_calendar_admin_header_sx(ctx, oob=True)),
|
||||
clear_oob=SxExpr(_clear_deeper_oob("post-row", "post-header-child",
|
||||
"post-admin-row", "post-admin-header-child",
|
||||
"calendar-row", "calendar-header-child",
|
||||
@@ -288,9 +288,9 @@ async def _slot_full(ctx: dict, **kw: Any) -> str:
|
||||
return await render_to_sx_with_env("events-slot-layout-full", {},
|
||||
post_header=SxExpr(await _post_header_sx(ctx)),
|
||||
admin_header=SxExpr(await post_admin_header_sx(ctx, slug, selected="calendars")),
|
||||
calendar_header=SxExpr(await _calendar_header_sx(ctx)),
|
||||
calendar_admin_header=SxExpr(await _calendar_admin_header_sx(ctx)),
|
||||
slot_header=SxExpr(await _slot_header_html(ctx)),
|
||||
calendar_header=SxExpr(_calendar_header_sx(ctx)),
|
||||
calendar_admin_header=SxExpr(_calendar_admin_header_sx(ctx)),
|
||||
slot_header=SxExpr(_slot_header_html(ctx)),
|
||||
)
|
||||
|
||||
|
||||
@@ -301,9 +301,9 @@ async def _slot_oob(ctx: dict, **kw: Any) -> str:
|
||||
slug = (ctx.get("post") or {}).get("slug", "")
|
||||
return await render_to_sx_with_env("events-slot-layout-oob", {},
|
||||
admin_oob=SxExpr(await post_admin_header_sx(ctx, slug, oob=True, selected="calendars")),
|
||||
cal_admin_oob=SxExpr(await _calendar_admin_header_sx(ctx, oob=True)),
|
||||
cal_admin_oob=SxExpr(_calendar_admin_header_sx(ctx, oob=True)),
|
||||
slot_oob_wrap=SxExpr(await oob_header_sx("calendar-admin-header-child",
|
||||
"slot-header-child", await _slot_header_html(ctx))),
|
||||
"slot-header-child", _slot_header_html(ctx))),
|
||||
clear_oob=SxExpr(_clear_deeper_oob("post-row", "post-header-child",
|
||||
"post-admin-row", "post-admin-header-child",
|
||||
"calendar-row", "calendar-header-child",
|
||||
@@ -322,9 +322,9 @@ async def _day_admin_full(ctx: dict, **kw: Any) -> str:
|
||||
return await render_to_sx_with_env("events-day-admin-layout-full", {},
|
||||
post_header=SxExpr(await _post_header_sx(ctx)),
|
||||
admin_header=SxExpr(await post_admin_header_sx(ctx, slug, selected="calendars")),
|
||||
calendar_header=SxExpr(await _calendar_header_sx(ctx)),
|
||||
day_header=SxExpr(await _day_header_sx(ctx)),
|
||||
day_admin_header=SxExpr(await _day_admin_header_sx(ctx)),
|
||||
calendar_header=SxExpr(_calendar_header_sx(ctx)),
|
||||
day_header=SxExpr(_day_header_sx(ctx)),
|
||||
day_admin_header=SxExpr(_day_admin_header_sx(ctx)),
|
||||
)
|
||||
|
||||
|
||||
@@ -335,9 +335,9 @@ async def _day_admin_oob(ctx: dict, **kw: Any) -> str:
|
||||
slug = (ctx.get("post") or {}).get("slug", "")
|
||||
return await render_to_sx_with_env("events-day-admin-layout-oob", {},
|
||||
admin_oob=SxExpr(await post_admin_header_sx(ctx, slug, oob=True, selected="calendars")),
|
||||
cal_oob=SxExpr(await _calendar_header_sx(ctx, oob=True)),
|
||||
cal_oob=SxExpr(_calendar_header_sx(ctx, oob=True)),
|
||||
day_admin_oob_wrap=SxExpr(await oob_header_sx("day-header-child",
|
||||
"day-admin-header-child", await _day_admin_header_sx(ctx))),
|
||||
"day-admin-header-child", _day_admin_header_sx(ctx))),
|
||||
clear_oob=SxExpr(_clear_deeper_oob("post-row", "post-header-child",
|
||||
"post-admin-row", "post-admin-header-child",
|
||||
"calendar-row", "calendar-header-child",
|
||||
@@ -353,9 +353,9 @@ async def _entry_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.parser import SxExpr
|
||||
return await render_to_sx_with_env("events-entry-layout-full", {},
|
||||
post_header=SxExpr(await _post_header_sx(ctx)),
|
||||
calendar_header=SxExpr(await _calendar_header_sx(ctx)),
|
||||
day_header=SxExpr(await _day_header_sx(ctx)),
|
||||
entry_header=SxExpr(await _entry_header_html(ctx)),
|
||||
calendar_header=SxExpr(_calendar_header_sx(ctx)),
|
||||
day_header=SxExpr(_day_header_sx(ctx)),
|
||||
entry_header=SxExpr(_entry_header_html(ctx)),
|
||||
)
|
||||
|
||||
|
||||
@@ -363,9 +363,9 @@ async def _entry_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env, oob_header_sx
|
||||
from shared.sx.parser import SxExpr
|
||||
return await render_to_sx_with_env("events-entry-layout-oob", {},
|
||||
day_oob=SxExpr(await _day_header_sx(ctx, oob=True)),
|
||||
day_oob=SxExpr(_day_header_sx(ctx, oob=True)),
|
||||
entry_oob_wrap=SxExpr(await oob_header_sx("day-header-child",
|
||||
"entry-header-child", await _entry_header_html(ctx))),
|
||||
"entry-header-child", _entry_header_html(ctx))),
|
||||
clear_oob=SxExpr(_clear_deeper_oob("post-row", "post-header-child",
|
||||
"calendar-row", "calendar-header-child",
|
||||
"day-row", "day-header-child",
|
||||
@@ -383,10 +383,10 @@ async def _entry_admin_full(ctx: dict, **kw: Any) -> str:
|
||||
return await render_to_sx_with_env("events-entry-admin-layout-full", {},
|
||||
post_header=SxExpr(await _post_header_sx(ctx)),
|
||||
admin_header=SxExpr(await post_admin_header_sx(ctx, slug, selected="calendars")),
|
||||
calendar_header=SxExpr(await _calendar_header_sx(ctx)),
|
||||
day_header=SxExpr(await _day_header_sx(ctx)),
|
||||
entry_header=SxExpr(await _entry_header_html(ctx)),
|
||||
entry_admin_header=SxExpr(await _entry_admin_header_html(ctx)),
|
||||
calendar_header=SxExpr(_calendar_header_sx(ctx)),
|
||||
day_header=SxExpr(_day_header_sx(ctx)),
|
||||
entry_header=SxExpr(_entry_header_html(ctx)),
|
||||
entry_admin_header=SxExpr(_entry_admin_header_html(ctx)),
|
||||
)
|
||||
|
||||
|
||||
@@ -397,9 +397,9 @@ async def _entry_admin_oob(ctx: dict, **kw: Any) -> str:
|
||||
slug = (ctx.get("post") or {}).get("slug", "")
|
||||
return await render_to_sx_with_env("events-entry-admin-layout-oob", {},
|
||||
admin_oob=SxExpr(await post_admin_header_sx(ctx, slug, oob=True, selected="calendars")),
|
||||
entry_oob=SxExpr(await _entry_header_html(ctx, oob=True)),
|
||||
entry_oob=SxExpr(_entry_header_html(ctx, oob=True)),
|
||||
entry_admin_oob_wrap=SxExpr(await oob_header_sx("entry-header-child",
|
||||
"entry-admin-header-child", await _entry_admin_header_html(ctx))),
|
||||
"entry-admin-header-child", _entry_admin_header_html(ctx))),
|
||||
clear_oob=SxExpr(_clear_deeper_oob("post-row", "post-header-child",
|
||||
"post-admin-row", "post-admin-header-child",
|
||||
"calendar-row", "calendar-header-child",
|
||||
@@ -416,11 +416,11 @@ async def _ticket_types_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.parser import SxExpr
|
||||
return await render_to_sx_with_env("events-ticket-types-layout-full", {},
|
||||
post_header=SxExpr(await _post_header_sx(ctx)),
|
||||
calendar_header=SxExpr(await _calendar_header_sx(ctx)),
|
||||
day_header=SxExpr(await _day_header_sx(ctx)),
|
||||
entry_header=SxExpr(await _entry_header_html(ctx)),
|
||||
entry_admin_header=SxExpr(await _entry_admin_header_html(ctx)),
|
||||
ticket_types_header=SxExpr(await _ticket_types_header_html(ctx)),
|
||||
calendar_header=SxExpr(_calendar_header_sx(ctx)),
|
||||
day_header=SxExpr(_day_header_sx(ctx)),
|
||||
entry_header=SxExpr(_entry_header_html(ctx)),
|
||||
entry_admin_header=SxExpr(_entry_admin_header_html(ctx)),
|
||||
ticket_types_header=SxExpr(_ticket_types_header_html(ctx)),
|
||||
)
|
||||
|
||||
|
||||
@@ -428,9 +428,9 @@ async def _ticket_types_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env, oob_header_sx
|
||||
from shared.sx.parser import SxExpr
|
||||
return await render_to_sx_with_env("events-ticket-types-layout-oob", {},
|
||||
entry_admin_oob=SxExpr(await _entry_admin_header_html(ctx, oob=True)),
|
||||
entry_admin_oob=SxExpr(_entry_admin_header_html(ctx, oob=True)),
|
||||
ticket_types_oob_wrap=SxExpr(await oob_header_sx("entry-admin-header-child",
|
||||
"ticket_types-header-child", await _ticket_types_header_html(ctx))),
|
||||
"ticket_types-header-child", _ticket_types_header_html(ctx))),
|
||||
)
|
||||
|
||||
|
||||
@@ -441,12 +441,12 @@ async def _ticket_type_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.parser import SxExpr
|
||||
return await render_to_sx_with_env("events-ticket-type-layout-full", {},
|
||||
post_header=SxExpr(await _post_header_sx(ctx)),
|
||||
calendar_header=SxExpr(await _calendar_header_sx(ctx)),
|
||||
day_header=SxExpr(await _day_header_sx(ctx)),
|
||||
entry_header=SxExpr(await _entry_header_html(ctx)),
|
||||
entry_admin_header=SxExpr(await _entry_admin_header_html(ctx)),
|
||||
ticket_types_header=SxExpr(await _ticket_types_header_html(ctx)),
|
||||
ticket_type_header=SxExpr(await _ticket_type_header_html(ctx)),
|
||||
calendar_header=SxExpr(_calendar_header_sx(ctx)),
|
||||
day_header=SxExpr(_day_header_sx(ctx)),
|
||||
entry_header=SxExpr(_entry_header_html(ctx)),
|
||||
entry_admin_header=SxExpr(_entry_admin_header_html(ctx)),
|
||||
ticket_types_header=SxExpr(_ticket_types_header_html(ctx)),
|
||||
ticket_type_header=SxExpr(_ticket_type_header_html(ctx)),
|
||||
)
|
||||
|
||||
|
||||
@@ -454,9 +454,9 @@ async def _ticket_type_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env, oob_header_sx
|
||||
from shared.sx.parser import SxExpr
|
||||
return await render_to_sx_with_env("events-ticket-type-layout-oob", {},
|
||||
ticket_types_oob=SxExpr(await _ticket_types_header_html(ctx, oob=True)),
|
||||
ticket_types_oob=SxExpr(_ticket_types_header_html(ctx, oob=True)),
|
||||
ticket_type_oob_wrap=SxExpr(await oob_header_sx("ticket_types-header-child",
|
||||
"ticket_type-header-child", await _ticket_type_header_html(ctx))),
|
||||
"ticket_type-header-child", _ticket_type_header_html(ctx))),
|
||||
)
|
||||
|
||||
|
||||
@@ -467,7 +467,7 @@ async def _markets_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.parser import SxExpr
|
||||
return await render_to_sx_with_env("events-markets-layout-full", {},
|
||||
post_header=SxExpr(await _post_header_sx(ctx)),
|
||||
markets_header=SxExpr(await _markets_header_sx(ctx)),
|
||||
markets_header=SxExpr(_markets_header_sx(ctx)),
|
||||
)
|
||||
|
||||
|
||||
@@ -477,7 +477,7 @@ async def _markets_oob(ctx: dict, **kw: Any) -> str:
|
||||
return await render_to_sx_with_env("events-markets-layout-oob", {},
|
||||
post_oob=SxExpr(await _post_header_sx(ctx, oob=True)),
|
||||
markets_oob_wrap=SxExpr(await oob_header_sx("post-header-child",
|
||||
"markets-header-child", await _markets_header_sx(ctx))),
|
||||
"markets-header-child", _markets_header_sx(ctx))),
|
||||
)
|
||||
|
||||
|
||||
@@ -510,14 +510,14 @@ async def _h_calendar_admin_content(calendar_slug=None, **kw):
|
||||
await _ensure_calendar(calendar_slug)
|
||||
from shared.sx.page import get_template_context
|
||||
ctx = await get_template_context()
|
||||
return await _calendar_admin_main_panel_html(ctx)
|
||||
return _calendar_admin_main_panel_html(ctx)
|
||||
|
||||
|
||||
async def _h_day_admin_content(calendar_slug=None, year=None, month=None, day=None, **kw):
|
||||
await _ensure_calendar(calendar_slug)
|
||||
if year is not None:
|
||||
await _ensure_day_data(int(year), int(month), int(day))
|
||||
return await _day_admin_main_panel_html({})
|
||||
return _day_admin_main_panel_html({})
|
||||
|
||||
|
||||
async def _h_slots_content(calendar_slug=None, **kw):
|
||||
@@ -527,7 +527,7 @@ async def _h_slots_content(calendar_slug=None, **kw):
|
||||
from bp.slots.services.slots import list_slots as svc_list_slots
|
||||
slots = await svc_list_slots(g.s, calendar.id) if calendar else []
|
||||
_add_to_defpage_ctx(slots=slots)
|
||||
return await render_slots_table(slots, calendar)
|
||||
return render_slots_table(slots, calendar)
|
||||
|
||||
|
||||
async def _h_slot_content(calendar_slug=None, slot_id=None, **kw):
|
||||
@@ -540,7 +540,7 @@ async def _h_slot_content(calendar_slug=None, slot_id=None, **kw):
|
||||
g.slot = slot
|
||||
_add_to_defpage_ctx(slot=slot)
|
||||
calendar = getattr(g, "calendar", None)
|
||||
return await render_slot_main_panel(slot, calendar)
|
||||
return render_slot_main_panel(slot, calendar)
|
||||
|
||||
|
||||
async def _h_entry_content(calendar_slug=None, entry_id=None, **kw):
|
||||
@@ -548,7 +548,7 @@ async def _h_entry_content(calendar_slug=None, entry_id=None, **kw):
|
||||
await _ensure_entry_context(entry_id)
|
||||
from shared.sx.page import get_template_context
|
||||
ctx = await get_template_context()
|
||||
return await _entry_main_panel_html(ctx)
|
||||
return _entry_main_panel_html(ctx)
|
||||
|
||||
|
||||
async def _h_entry_menu(calendar_slug=None, entry_id=None, **kw):
|
||||
@@ -556,7 +556,7 @@ async def _h_entry_menu(calendar_slug=None, entry_id=None, **kw):
|
||||
await _ensure_entry_context(entry_id)
|
||||
from shared.sx.page import get_template_context
|
||||
ctx = await get_template_context()
|
||||
return await _entry_nav_html(ctx)
|
||||
return _entry_nav_html(ctx)
|
||||
|
||||
|
||||
async def _h_entry_admin_content(calendar_slug=None, entry_id=None, **kw):
|
||||
@@ -564,12 +564,11 @@ async def _h_entry_admin_content(calendar_slug=None, entry_id=None, **kw):
|
||||
await _ensure_entry_context(entry_id)
|
||||
from shared.sx.page import get_template_context
|
||||
ctx = await get_template_context()
|
||||
return await _entry_admin_main_panel_html(ctx)
|
||||
return _entry_admin_main_panel_html(ctx)
|
||||
|
||||
|
||||
async def _h_admin_menu():
|
||||
from shared.sx.helpers import render_to_sx
|
||||
return await render_to_sx("events-admin-placeholder-nav")
|
||||
def _h_admin_menu():
|
||||
return sx_call("events-admin-placeholder-nav")
|
||||
|
||||
|
||||
async def _h_ticket_types_content(calendar_slug=None, entry_id=None,
|
||||
@@ -582,7 +581,7 @@ async def _h_ticket_types_content(calendar_slug=None, entry_id=None,
|
||||
from bp.ticket_types.services.tickets import list_ticket_types as svc_list_ticket_types
|
||||
ticket_types = await svc_list_ticket_types(g.s, entry.id) if entry else []
|
||||
_add_to_defpage_ctx(ticket_types=ticket_types)
|
||||
return await render_ticket_types_table(ticket_types, entry, calendar, day, month, year)
|
||||
return render_ticket_types_table(ticket_types, entry, calendar, day, month, year)
|
||||
|
||||
|
||||
async def _h_ticket_type_content(calendar_slug=None, entry_id=None,
|
||||
@@ -598,7 +597,7 @@ async def _h_ticket_type_content(calendar_slug=None, entry_id=None,
|
||||
_add_to_defpage_ctx(ticket_type=ticket_type)
|
||||
entry = getattr(g, "entry", None)
|
||||
calendar = getattr(g, "calendar", None)
|
||||
return await render_ticket_type_main_panel(ticket_type, entry, calendar, day, month, year)
|
||||
return render_ticket_type_main_panel(ticket_type, entry, calendar, day, month, year)
|
||||
|
||||
|
||||
async def _h_tickets_content(**kw):
|
||||
@@ -613,7 +612,7 @@ async def _h_tickets_content(**kw):
|
||||
)
|
||||
from shared.sx.page import get_template_context
|
||||
ctx = await get_template_context()
|
||||
return await _tickets_main_panel_html(ctx, tickets)
|
||||
return _tickets_main_panel_html(ctx, tickets)
|
||||
|
||||
|
||||
async def _h_ticket_detail_content(code=None, **kw):
|
||||
@@ -635,7 +634,7 @@ async def _h_ticket_detail_content(code=None, **kw):
|
||||
abort(404)
|
||||
from shared.sx.page import get_template_context
|
||||
ctx = await get_template_context()
|
||||
return await _ticket_detail_panel_html(ctx, ticket)
|
||||
return _ticket_detail_panel_html(ctx, ticket)
|
||||
|
||||
|
||||
async def _h_ticket_admin_content(**kw):
|
||||
@@ -674,10 +673,10 @@ async def _h_ticket_admin_content(**kw):
|
||||
|
||||
from shared.sx.page import get_template_context
|
||||
ctx = await get_template_context()
|
||||
return await _ticket_admin_main_panel_html(ctx, tickets, stats)
|
||||
return _ticket_admin_main_panel_html(ctx, tickets, stats)
|
||||
|
||||
|
||||
async def _h_markets_content(**kw):
|
||||
from shared.sx.page import get_template_context
|
||||
ctx = await get_template_context()
|
||||
return await _markets_main_panel_html(ctx)
|
||||
return _markets_main_panel_html(ctx)
|
||||
|
||||
@@ -42,8 +42,8 @@ async def _cal_admin_full(ctx: dict, **kw: Any) -> str:
|
||||
return await render_to_sx_with_env("events-cal-admin-layout-full", {},
|
||||
post_header=SxExpr(await _post_header_sx(ctx)),
|
||||
admin_header=SxExpr(await post_admin_header_sx(ctx, slug, selected="calendars")),
|
||||
calendar_header=SxExpr(await _calendar_header_sx(ctx)),
|
||||
calendar_admin_header=SxExpr(await _calendar_admin_header_sx(ctx)),
|
||||
calendar_header=SxExpr(_calendar_header_sx(ctx)),
|
||||
calendar_admin_header=SxExpr(_calendar_admin_header_sx(ctx)),
|
||||
)
|
||||
|
||||
|
||||
@@ -54,9 +54,9 @@ async def _cal_admin_oob(ctx: dict, **kw: Any) -> str:
|
||||
slug = (ctx.get("post") or {}).get("slug", "")
|
||||
return await render_to_sx_with_env("events-cal-admin-layout-oob", {},
|
||||
admin_oob=SxExpr(await post_admin_header_sx(ctx, slug, oob=True, selected="calendars")),
|
||||
cal_oob=SxExpr(await _calendar_header_sx(ctx, oob=True)),
|
||||
cal_oob=SxExpr(_calendar_header_sx(ctx, oob=True)),
|
||||
cal_admin_oob_wrap=SxExpr(await oob_header_sx("calendar-header-child",
|
||||
"calendar-admin-header-child", await _calendar_admin_header_sx(ctx))),
|
||||
"calendar-admin-header-child", _calendar_admin_header_sx(ctx))),
|
||||
clear_oob=SxExpr(_clear_deeper_oob("post-row", "post-header-child",
|
||||
"post-admin-row", "post-admin-header-child",
|
||||
"calendar-row", "calendar-header-child",
|
||||
@@ -77,7 +77,7 @@ async def _slots_oob(ctx: dict, **kw: Any) -> str:
|
||||
slug = (ctx.get("post") or {}).get("slug", "")
|
||||
return await render_to_sx_with_env("events-slots-layout-oob", {},
|
||||
admin_oob=SxExpr(await post_admin_header_sx(ctx, slug, oob=True, selected="calendars")),
|
||||
cal_admin_oob=SxExpr(await _calendar_admin_header_sx(ctx, oob=True)),
|
||||
cal_admin_oob=SxExpr(_calendar_admin_header_sx(ctx, oob=True)),
|
||||
clear_oob=SxExpr(_clear_deeper_oob("post-row", "post-header-child",
|
||||
"post-admin-row", "post-admin-header-child",
|
||||
"calendar-row", "calendar-header-child",
|
||||
@@ -95,9 +95,9 @@ async def _slot_full(ctx: dict, **kw: Any) -> str:
|
||||
return await render_to_sx_with_env("events-slot-layout-full", {},
|
||||
post_header=SxExpr(await _post_header_sx(ctx)),
|
||||
admin_header=SxExpr(await post_admin_header_sx(ctx, slug, selected="calendars")),
|
||||
calendar_header=SxExpr(await _calendar_header_sx(ctx)),
|
||||
calendar_admin_header=SxExpr(await _calendar_admin_header_sx(ctx)),
|
||||
slot_header=SxExpr(await _slot_header_html(ctx)),
|
||||
calendar_header=SxExpr(_calendar_header_sx(ctx)),
|
||||
calendar_admin_header=SxExpr(_calendar_admin_header_sx(ctx)),
|
||||
slot_header=SxExpr(_slot_header_html(ctx)),
|
||||
)
|
||||
|
||||
|
||||
@@ -108,9 +108,9 @@ async def _slot_oob(ctx: dict, **kw: Any) -> str:
|
||||
slug = (ctx.get("post") or {}).get("slug", "")
|
||||
return await render_to_sx_with_env("events-slot-layout-oob", {},
|
||||
admin_oob=SxExpr(await post_admin_header_sx(ctx, slug, oob=True, selected="calendars")),
|
||||
cal_admin_oob=SxExpr(await _calendar_admin_header_sx(ctx, oob=True)),
|
||||
cal_admin_oob=SxExpr(_calendar_admin_header_sx(ctx, oob=True)),
|
||||
slot_oob_wrap=SxExpr(await oob_header_sx("calendar-admin-header-child",
|
||||
"slot-header-child", await _slot_header_html(ctx))),
|
||||
"slot-header-child", _slot_header_html(ctx))),
|
||||
clear_oob=SxExpr(_clear_deeper_oob("post-row", "post-header-child",
|
||||
"post-admin-row", "post-admin-header-child",
|
||||
"calendar-row", "calendar-header-child",
|
||||
@@ -129,9 +129,9 @@ async def _day_admin_full(ctx: dict, **kw: Any) -> str:
|
||||
return await render_to_sx_with_env("events-day-admin-layout-full", {},
|
||||
post_header=SxExpr(await _post_header_sx(ctx)),
|
||||
admin_header=SxExpr(await post_admin_header_sx(ctx, slug, selected="calendars")),
|
||||
calendar_header=SxExpr(await _calendar_header_sx(ctx)),
|
||||
day_header=SxExpr(await _day_header_sx(ctx)),
|
||||
day_admin_header=SxExpr(await _day_admin_header_sx(ctx)),
|
||||
calendar_header=SxExpr(_calendar_header_sx(ctx)),
|
||||
day_header=SxExpr(_day_header_sx(ctx)),
|
||||
day_admin_header=SxExpr(_day_admin_header_sx(ctx)),
|
||||
)
|
||||
|
||||
|
||||
@@ -142,9 +142,9 @@ async def _day_admin_oob(ctx: dict, **kw: Any) -> str:
|
||||
slug = (ctx.get("post") or {}).get("slug", "")
|
||||
return await render_to_sx_with_env("events-day-admin-layout-oob", {},
|
||||
admin_oob=SxExpr(await post_admin_header_sx(ctx, slug, oob=True, selected="calendars")),
|
||||
cal_oob=SxExpr(await _calendar_header_sx(ctx, oob=True)),
|
||||
cal_oob=SxExpr(_calendar_header_sx(ctx, oob=True)),
|
||||
day_admin_oob_wrap=SxExpr(await oob_header_sx("day-header-child",
|
||||
"day-admin-header-child", await _day_admin_header_sx(ctx))),
|
||||
"day-admin-header-child", _day_admin_header_sx(ctx))),
|
||||
clear_oob=SxExpr(_clear_deeper_oob("post-row", "post-header-child",
|
||||
"post-admin-row", "post-admin-header-child",
|
||||
"calendar-row", "calendar-header-child",
|
||||
@@ -160,9 +160,9 @@ async def _entry_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.parser import SxExpr
|
||||
return await render_to_sx_with_env("events-entry-layout-full", {},
|
||||
post_header=SxExpr(await _post_header_sx(ctx)),
|
||||
calendar_header=SxExpr(await _calendar_header_sx(ctx)),
|
||||
day_header=SxExpr(await _day_header_sx(ctx)),
|
||||
entry_header=SxExpr(await _entry_header_html(ctx)),
|
||||
calendar_header=SxExpr(_calendar_header_sx(ctx)),
|
||||
day_header=SxExpr(_day_header_sx(ctx)),
|
||||
entry_header=SxExpr(_entry_header_html(ctx)),
|
||||
)
|
||||
|
||||
|
||||
@@ -170,9 +170,9 @@ async def _entry_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env, oob_header_sx
|
||||
from shared.sx.parser import SxExpr
|
||||
return await render_to_sx_with_env("events-entry-layout-oob", {},
|
||||
day_oob=SxExpr(await _day_header_sx(ctx, oob=True)),
|
||||
day_oob=SxExpr(_day_header_sx(ctx, oob=True)),
|
||||
entry_oob_wrap=SxExpr(await oob_header_sx("day-header-child",
|
||||
"entry-header-child", await _entry_header_html(ctx))),
|
||||
"entry-header-child", _entry_header_html(ctx))),
|
||||
clear_oob=SxExpr(_clear_deeper_oob("post-row", "post-header-child",
|
||||
"calendar-row", "calendar-header-child",
|
||||
"day-row", "day-header-child",
|
||||
@@ -190,10 +190,10 @@ async def _entry_admin_full(ctx: dict, **kw: Any) -> str:
|
||||
return await render_to_sx_with_env("events-entry-admin-layout-full", {},
|
||||
post_header=SxExpr(await _post_header_sx(ctx)),
|
||||
admin_header=SxExpr(await post_admin_header_sx(ctx, slug, selected="calendars")),
|
||||
calendar_header=SxExpr(await _calendar_header_sx(ctx)),
|
||||
day_header=SxExpr(await _day_header_sx(ctx)),
|
||||
entry_header=SxExpr(await _entry_header_html(ctx)),
|
||||
entry_admin_header=SxExpr(await _entry_admin_header_html(ctx)),
|
||||
calendar_header=SxExpr(_calendar_header_sx(ctx)),
|
||||
day_header=SxExpr(_day_header_sx(ctx)),
|
||||
entry_header=SxExpr(_entry_header_html(ctx)),
|
||||
entry_admin_header=SxExpr(_entry_admin_header_html(ctx)),
|
||||
)
|
||||
|
||||
|
||||
@@ -204,9 +204,9 @@ async def _entry_admin_oob(ctx: dict, **kw: Any) -> str:
|
||||
slug = (ctx.get("post") or {}).get("slug", "")
|
||||
return await render_to_sx_with_env("events-entry-admin-layout-oob", {},
|
||||
admin_oob=SxExpr(await post_admin_header_sx(ctx, slug, oob=True, selected="calendars")),
|
||||
entry_oob=SxExpr(await _entry_header_html(ctx, oob=True)),
|
||||
entry_oob=SxExpr(_entry_header_html(ctx, oob=True)),
|
||||
entry_admin_oob_wrap=SxExpr(await oob_header_sx("entry-header-child",
|
||||
"entry-admin-header-child", await _entry_admin_header_html(ctx))),
|
||||
"entry-admin-header-child", _entry_admin_header_html(ctx))),
|
||||
clear_oob=SxExpr(_clear_deeper_oob("post-row", "post-header-child",
|
||||
"post-admin-row", "post-admin-header-child",
|
||||
"calendar-row", "calendar-header-child",
|
||||
@@ -223,11 +223,11 @@ async def _ticket_types_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.parser import SxExpr
|
||||
return await render_to_sx_with_env("events-ticket-types-layout-full", {},
|
||||
post_header=SxExpr(await _post_header_sx(ctx)),
|
||||
calendar_header=SxExpr(await _calendar_header_sx(ctx)),
|
||||
day_header=SxExpr(await _day_header_sx(ctx)),
|
||||
entry_header=SxExpr(await _entry_header_html(ctx)),
|
||||
entry_admin_header=SxExpr(await _entry_admin_header_html(ctx)),
|
||||
ticket_types_header=SxExpr(await _ticket_types_header_html(ctx)),
|
||||
calendar_header=SxExpr(_calendar_header_sx(ctx)),
|
||||
day_header=SxExpr(_day_header_sx(ctx)),
|
||||
entry_header=SxExpr(_entry_header_html(ctx)),
|
||||
entry_admin_header=SxExpr(_entry_admin_header_html(ctx)),
|
||||
ticket_types_header=SxExpr(_ticket_types_header_html(ctx)),
|
||||
)
|
||||
|
||||
|
||||
@@ -235,9 +235,9 @@ async def _ticket_types_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env, oob_header_sx
|
||||
from shared.sx.parser import SxExpr
|
||||
return await render_to_sx_with_env("events-ticket-types-layout-oob", {},
|
||||
entry_admin_oob=SxExpr(await _entry_admin_header_html(ctx, oob=True)),
|
||||
entry_admin_oob=SxExpr(_entry_admin_header_html(ctx, oob=True)),
|
||||
ticket_types_oob_wrap=SxExpr(await oob_header_sx("entry-admin-header-child",
|
||||
"ticket_types-header-child", await _ticket_types_header_html(ctx))),
|
||||
"ticket_types-header-child", _ticket_types_header_html(ctx))),
|
||||
)
|
||||
|
||||
|
||||
@@ -248,12 +248,12 @@ async def _ticket_type_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.parser import SxExpr
|
||||
return await render_to_sx_with_env("events-ticket-type-layout-full", {},
|
||||
post_header=SxExpr(await _post_header_sx(ctx)),
|
||||
calendar_header=SxExpr(await _calendar_header_sx(ctx)),
|
||||
day_header=SxExpr(await _day_header_sx(ctx)),
|
||||
entry_header=SxExpr(await _entry_header_html(ctx)),
|
||||
entry_admin_header=SxExpr(await _entry_admin_header_html(ctx)),
|
||||
ticket_types_header=SxExpr(await _ticket_types_header_html(ctx)),
|
||||
ticket_type_header=SxExpr(await _ticket_type_header_html(ctx)),
|
||||
calendar_header=SxExpr(_calendar_header_sx(ctx)),
|
||||
day_header=SxExpr(_day_header_sx(ctx)),
|
||||
entry_header=SxExpr(_entry_header_html(ctx)),
|
||||
entry_admin_header=SxExpr(_entry_admin_header_html(ctx)),
|
||||
ticket_types_header=SxExpr(_ticket_types_header_html(ctx)),
|
||||
ticket_type_header=SxExpr(_ticket_type_header_html(ctx)),
|
||||
)
|
||||
|
||||
|
||||
@@ -261,9 +261,9 @@ async def _ticket_type_oob(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env, oob_header_sx
|
||||
from shared.sx.parser import SxExpr
|
||||
return await render_to_sx_with_env("events-ticket-type-layout-oob", {},
|
||||
ticket_types_oob=SxExpr(await _ticket_types_header_html(ctx, oob=True)),
|
||||
ticket_types_oob=SxExpr(_ticket_types_header_html(ctx, oob=True)),
|
||||
ticket_type_oob_wrap=SxExpr(await oob_header_sx("ticket_types-header-child",
|
||||
"ticket_type-header-child", await _ticket_type_header_html(ctx))),
|
||||
"ticket_type-header-child", _ticket_type_header_html(ctx))),
|
||||
)
|
||||
|
||||
|
||||
@@ -274,7 +274,7 @@ async def _markets_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.parser import SxExpr
|
||||
return await render_to_sx_with_env("events-markets-layout-full", {},
|
||||
post_header=SxExpr(await _post_header_sx(ctx)),
|
||||
markets_header=SxExpr(await _markets_header_sx(ctx)),
|
||||
markets_header=SxExpr(_markets_header_sx(ctx)),
|
||||
)
|
||||
|
||||
|
||||
@@ -284,5 +284,5 @@ async def _markets_oob(ctx: dict, **kw: Any) -> str:
|
||||
return await render_to_sx_with_env("events-markets-layout-oob", {},
|
||||
post_oob=SxExpr(await _post_header_sx(ctx, oob=True)),
|
||||
markets_oob_wrap=SxExpr(await oob_header_sx("post-header-child",
|
||||
"markets-header-child", await _markets_header_sx(ctx))),
|
||||
"markets-header-child", _markets_header_sx(ctx))),
|
||||
)
|
||||
|
||||
@@ -40,7 +40,7 @@ async def render_all_events_page(ctx: dict, entries, has_more, pending_tickets,
|
||||
ticket_url = url_for("all_events.adjust_ticket")
|
||||
next_url = prefix + url_for("all_events.entries_fragment", page=page + 1) + (f"?view={view}" if view != "list" else "")
|
||||
|
||||
content = await _events_main_panel_html(
|
||||
content = _events_main_panel_html(
|
||||
ctx, entries, has_more, pending_tickets, page_info,
|
||||
page, view, ticket_url, next_url, events_url,
|
||||
)
|
||||
@@ -59,15 +59,15 @@ async def render_all_events_oob(ctx: dict, entries, has_more, pending_tickets,
|
||||
ticket_url = url_for("all_events.adjust_ticket")
|
||||
next_url = prefix + url_for("all_events.entries_fragment", page=page + 1) + (f"?view={view}" if view != "list" else "")
|
||||
|
||||
content = await _events_main_panel_html(
|
||||
content = _events_main_panel_html(
|
||||
ctx, entries, has_more, pending_tickets, page_info,
|
||||
page, view, ticket_url, next_url, events_url,
|
||||
)
|
||||
return await oob_page_sx(content=content)
|
||||
|
||||
|
||||
async def render_all_events_cards(entries, has_more, pending_tickets,
|
||||
page_info, page, view) -> str:
|
||||
def render_all_events_cards(entries, has_more, pending_tickets,
|
||||
page_info, page, view) -> str:
|
||||
"""Pagination fragment: all events cards only."""
|
||||
from quart import url_for
|
||||
from shared.utils import route_prefix
|
||||
@@ -77,7 +77,7 @@ async def render_all_events_cards(entries, has_more, pending_tickets,
|
||||
ticket_url = url_for("all_events.adjust_ticket")
|
||||
next_url = prefix + url_for("all_events.entries_fragment", page=page + 1) + (f"?view={view}" if view != "list" else "")
|
||||
|
||||
return await _entry_cards_html(
|
||||
return _entry_cards_html(
|
||||
entries, page_info, pending_tickets, ticket_url, events_url,
|
||||
view, page, has_more, next_url,
|
||||
)
|
||||
@@ -99,7 +99,7 @@ async def render_page_summary_page(ctx: dict, entries, has_more, pending_tickets
|
||||
ticket_url = url_for("page_summary.adjust_ticket")
|
||||
next_url = prefix + url_for("page_summary.entries_fragment", page=page + 1) + (f"?view={view}" if view != "list" else "")
|
||||
|
||||
content = await _events_main_panel_html(
|
||||
content = _events_main_panel_html(
|
||||
ctx, entries, has_more, pending_tickets, page_info,
|
||||
page, view, ticket_url, next_url, events_url,
|
||||
is_page_scoped=True, post=post,
|
||||
@@ -122,7 +122,7 @@ async def render_page_summary_oob(ctx: dict, entries, has_more, pending_tickets,
|
||||
ticket_url = url_for("page_summary.adjust_ticket")
|
||||
next_url = prefix + url_for("page_summary.entries_fragment", page=page + 1) + (f"?view={view}" if view != "list" else "")
|
||||
|
||||
content = await _events_main_panel_html(
|
||||
content = _events_main_panel_html(
|
||||
ctx, entries, has_more, pending_tickets, page_info,
|
||||
page, view, ticket_url, next_url, events_url,
|
||||
is_page_scoped=True, post=post,
|
||||
@@ -133,8 +133,8 @@ async def render_page_summary_oob(ctx: dict, entries, has_more, pending_tickets,
|
||||
return await oob_page_sx(oobs=oobs, content=content)
|
||||
|
||||
|
||||
async def render_page_summary_cards(entries, has_more, pending_tickets,
|
||||
page_info, page, view, post) -> str:
|
||||
def render_page_summary_cards(entries, has_more, pending_tickets,
|
||||
page_info, page, view, post) -> str:
|
||||
"""Pagination fragment: page-scoped events cards only."""
|
||||
from quart import url_for
|
||||
from shared.utils import route_prefix
|
||||
@@ -144,7 +144,7 @@ async def render_page_summary_cards(entries, has_more, pending_tickets,
|
||||
ticket_url = url_for("page_summary.adjust_ticket")
|
||||
next_url = prefix + url_for("page_summary.entries_fragment", page=page + 1) + (f"?view={view}" if view != "list" else "")
|
||||
|
||||
return await _entry_cards_html(
|
||||
return _entry_cards_html(
|
||||
entries, page_info, pending_tickets, ticket_url, events_url,
|
||||
view, page, has_more, next_url,
|
||||
is_page_scoped=True, post=post,
|
||||
@@ -157,7 +157,7 @@ async def render_page_summary_cards(entries, has_more, pending_tickets,
|
||||
|
||||
async def render_calendars_page(ctx: dict) -> str:
|
||||
"""Full page: calendars listing."""
|
||||
content = await _calendars_main_panel_sx(ctx)
|
||||
content = _calendars_main_panel_sx(ctx)
|
||||
ctx = await _ensure_container_nav(ctx)
|
||||
slug = (ctx.get("post") or {}).get("slug", "")
|
||||
root_hdr = await render_to_sx_with_env("layout-root-full", {})
|
||||
@@ -168,7 +168,7 @@ async def render_calendars_page(ctx: dict) -> str:
|
||||
|
||||
async def render_calendars_oob(ctx: dict) -> str:
|
||||
"""OOB response: calendars listing."""
|
||||
content = await _calendars_main_panel_sx(ctx)
|
||||
content = _calendars_main_panel_sx(ctx)
|
||||
ctx = await _ensure_container_nav(ctx)
|
||||
slug = (ctx.get("post") or {}).get("slug", "")
|
||||
oobs = await post_admin_header_sx(ctx, slug, oob=True, selected="calendars")
|
||||
@@ -183,19 +183,19 @@ async def render_calendars_oob(ctx: dict) -> str:
|
||||
|
||||
async def render_calendar_page(ctx: dict) -> str:
|
||||
"""Full page: calendar month view."""
|
||||
content = await _calendar_main_panel_html(ctx)
|
||||
content = _calendar_main_panel_html(ctx)
|
||||
hdr = await render_to_sx_with_env("layout-root-full", {})
|
||||
child = await _post_header_sx(ctx) + await _calendar_header_sx(ctx)
|
||||
child = await _post_header_sx(ctx) + _calendar_header_sx(ctx)
|
||||
hdr += await header_child_sx(child)
|
||||
return await full_page_sx(ctx, header_rows=hdr, content=content)
|
||||
|
||||
|
||||
async def render_calendar_oob(ctx: dict) -> str:
|
||||
"""OOB response: calendar month view."""
|
||||
content = await _calendar_main_panel_html(ctx)
|
||||
content = _calendar_main_panel_html(ctx)
|
||||
oobs = await _post_header_sx(ctx, oob=True)
|
||||
oobs += await oob_header_sx("post-header-child", "calendar-header-child",
|
||||
await _calendar_header_sx(ctx))
|
||||
_calendar_header_sx(ctx))
|
||||
oobs += _clear_deeper_oob("post-row", "post-header-child",
|
||||
"calendar-row", "calendar-header-child")
|
||||
return await oob_page_sx(oobs=oobs, content=content)
|
||||
@@ -207,20 +207,20 @@ async def render_calendar_oob(ctx: dict) -> str:
|
||||
|
||||
async def render_day_page(ctx: dict) -> str:
|
||||
"""Full page: day detail."""
|
||||
content = await _day_main_panel_html(ctx)
|
||||
content = _day_main_panel_html(ctx)
|
||||
hdr = await render_to_sx_with_env("layout-root-full", {})
|
||||
child = (await _post_header_sx(ctx)
|
||||
+ await _calendar_header_sx(ctx) + await _day_header_sx(ctx))
|
||||
+ _calendar_header_sx(ctx) + _day_header_sx(ctx))
|
||||
hdr += await header_child_sx(child)
|
||||
return await full_page_sx(ctx, header_rows=hdr, content=content)
|
||||
|
||||
|
||||
async def render_day_oob(ctx: dict) -> str:
|
||||
"""OOB response: day detail."""
|
||||
content = await _day_main_panel_html(ctx)
|
||||
oobs = await _calendar_header_sx(ctx, oob=True)
|
||||
content = _day_main_panel_html(ctx)
|
||||
oobs = _calendar_header_sx(ctx, oob=True)
|
||||
oobs += await oob_header_sx("calendar-header-child", "day-header-child",
|
||||
await _day_header_sx(ctx))
|
||||
_day_header_sx(ctx))
|
||||
oobs += _clear_deeper_oob("post-row", "post-header-child",
|
||||
"calendar-row", "calendar-header-child",
|
||||
"day-row", "day-header-child")
|
||||
@@ -231,36 +231,36 @@ async def render_day_oob(ctx: dict) -> str:
|
||||
# Day main panel -- public API
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def render_day_main_panel(ctx: dict) -> str:
|
||||
def render_day_main_panel(ctx: dict) -> str:
|
||||
"""Public wrapper for day main panel rendering."""
|
||||
return await _day_main_panel_html(ctx)
|
||||
return _day_main_panel_html(ctx)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Calendar description display + edit form
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def render_calendar_description(calendar, *, oob: bool = False) -> str:
|
||||
def render_calendar_description(calendar, *, oob: bool = False) -> str:
|
||||
"""Render calendar description display with edit button, optionally with OOB title."""
|
||||
from quart import url_for
|
||||
from shared.sx.helpers import render_to_sx
|
||||
from shared.sx.helpers import sx_call
|
||||
|
||||
cal_slug = getattr(calendar, "slug", "")
|
||||
edit_url = url_for("calendar.admin.calendar_description_edit", calendar_slug=cal_slug)
|
||||
html = await _calendar_description_display_html(calendar, edit_url)
|
||||
html = _calendar_description_display_html(calendar, edit_url)
|
||||
|
||||
if oob:
|
||||
desc = getattr(calendar, "description", "") or ""
|
||||
html += await render_to_sx("events-calendar-description-title-oob",
|
||||
html += sx_call("events-calendar-description-title-oob",
|
||||
description=desc)
|
||||
return html
|
||||
|
||||
|
||||
async def render_calendar_description_edit(calendar) -> str:
|
||||
def render_calendar_description_edit(calendar) -> str:
|
||||
"""Render calendar description edit form."""
|
||||
from quart import url_for
|
||||
from shared.browser.app.csrf import generate_csrf_token
|
||||
from shared.sx.helpers import render_to_sx
|
||||
from shared.sx.helpers import sx_call
|
||||
csrf = generate_csrf_token()
|
||||
cal_slug = getattr(calendar, "slug", "")
|
||||
desc = getattr(calendar, "description", "") or ""
|
||||
@@ -268,7 +268,7 @@ async def render_calendar_description_edit(calendar) -> str:
|
||||
save_url = url_for("calendar.admin.calendar_description_save", calendar_slug=cal_slug)
|
||||
cancel_url = url_for("calendar.admin.calendar_description_view", calendar_slug=cal_slug)
|
||||
|
||||
return await render_to_sx("events-calendar-description-edit-form",
|
||||
return sx_call("events-calendar-description-edit-form",
|
||||
save_url=save_url, cancel_url=cancel_url,
|
||||
csrf=csrf, description=desc)
|
||||
|
||||
@@ -277,11 +277,11 @@ async def render_calendar_description_edit(calendar) -> str:
|
||||
# Calendars / Markets list panels (for POST create / DELETE)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def render_calendars_list_panel(ctx: dict) -> str:
|
||||
def render_calendars_list_panel(ctx: dict) -> str:
|
||||
"""Render the calendars main panel HTML for POST/DELETE response."""
|
||||
return await _calendars_main_panel_sx(ctx)
|
||||
return _calendars_main_panel_sx(ctx)
|
||||
|
||||
|
||||
async def render_markets_list_panel(ctx: dict) -> str:
|
||||
def render_markets_list_panel(ctx: dict) -> str:
|
||||
"""Render the markets main panel HTML for POST/DELETE response."""
|
||||
return await _markets_main_panel_html(ctx)
|
||||
return _markets_main_panel_html(ctx)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
from shared.sx.helpers import render_to_sx
|
||||
from shared.sx.helpers import sx_call
|
||||
from shared.sx.parser import SxExpr
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ _SLOT_PICKER_JS = """\
|
||||
# Slot options (shared by entry edit + add forms)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def _slot_options_html(day_slots, selected_slot_id=None) -> str:
|
||||
def _slot_options_html(day_slots, selected_slot_id=None) -> str:
|
||||
"""Build slot <option> elements."""
|
||||
parts = []
|
||||
for slot in day_slots:
|
||||
@@ -129,7 +129,7 @@ async def _slot_options_html(day_slots, selected_slot_id=None) -> str:
|
||||
label_parts.append("[flexible]")
|
||||
label = " ".join(label_parts)
|
||||
|
||||
parts.append(await render_to_sx("events-slot-option",
|
||||
parts.append(sx_call("events-slot-option",
|
||||
value=str(slot.id),
|
||||
data_start=start, data_end=end,
|
||||
data_flexible="1" if flexible else "0",
|
||||
@@ -143,7 +143,7 @@ async def _slot_options_html(day_slots, selected_slot_id=None) -> str:
|
||||
# Slot header row
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def _slot_header_html(ctx: dict, *, oob: bool = False) -> str:
|
||||
def _slot_header_html(ctx: dict, *, oob: bool = False) -> str:
|
||||
"""Build the slot detail header row."""
|
||||
from quart import url_for
|
||||
|
||||
@@ -156,10 +156,10 @@ async def _slot_header_html(ctx: dict, *, oob: bool = False) -> str:
|
||||
return ""
|
||||
|
||||
desc = getattr(slot, "description", "") or ""
|
||||
label_sx = await render_to_sx("events-slot-label",
|
||||
label_sx = sx_call("events-slot-label",
|
||||
name=slot.name, description=desc)
|
||||
|
||||
return await render_to_sx("menu-row-sx", id="slot-row", level=5,
|
||||
return sx_call("menu-row-sx", id="slot-row", level=5,
|
||||
link_label_content=SxExpr(label_sx),
|
||||
child_id="slot-header-child", oob=oob)
|
||||
|
||||
@@ -168,7 +168,7 @@ async def _slot_header_html(ctx: dict, *, oob: bool = False) -> str:
|
||||
# Slot main panel
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def render_slot_main_panel(slot, calendar, *, oob: bool = False) -> str:
|
||||
def render_slot_main_panel(slot, calendar, *, oob: bool = False) -> str:
|
||||
"""Render slot detail view."""
|
||||
from quart import url_for, g
|
||||
|
||||
@@ -191,15 +191,15 @@ async def render_slot_main_panel(slot, calendar, *, oob: bool = False) -> str:
|
||||
# Days pills
|
||||
if days and days[0] != "\u2014":
|
||||
days_inner = "".join(
|
||||
await render_to_sx("events-slot-day-pill", day=d) for d in days
|
||||
sx_call("events-slot-day-pill", day=d) for d in days
|
||||
)
|
||||
days_html = await render_to_sx("events-slot-days-pills", days_inner=SxExpr(days_inner))
|
||||
days_html = sx_call("events-slot-days-pills", days_inner=SxExpr(days_inner))
|
||||
else:
|
||||
days_html = await render_to_sx("events-slot-no-days")
|
||||
days_html = sx_call("events-slot-no-days")
|
||||
|
||||
sid = str(slot.id)
|
||||
|
||||
result = await render_to_sx("events-slot-panel",
|
||||
result = sx_call("events-slot-panel",
|
||||
slot_id=sid, list_container=list_container,
|
||||
days=SxExpr(days_html),
|
||||
flexible="yes" if flexible else "no",
|
||||
@@ -208,7 +208,7 @@ async def render_slot_main_panel(slot, calendar, *, oob: bool = False) -> str:
|
||||
pre_action=pre_action, edit_url=edit_url)
|
||||
|
||||
if oob:
|
||||
result += await render_to_sx("events-slot-description-oob", description=desc)
|
||||
result += sx_call("events-slot-description-oob", description=desc)
|
||||
|
||||
return result
|
||||
|
||||
@@ -217,7 +217,7 @@ async def render_slot_main_panel(slot, calendar, *, oob: bool = False) -> str:
|
||||
# Slots table
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def render_slots_table(slots, calendar) -> str:
|
||||
def render_slots_table(slots, calendar) -> str:
|
||||
"""Render slots table with rows and add button."""
|
||||
from quart import url_for, g
|
||||
from shared.browser.app.csrf import generate_csrf_token
|
||||
@@ -243,18 +243,18 @@ async def render_slots_table(slots, calendar) -> str:
|
||||
day_list = days_display.split(", ")
|
||||
if day_list and day_list[0] != "\u2014":
|
||||
days_inner = "".join(
|
||||
await render_to_sx("events-slot-day-pill", day=d) for d in day_list
|
||||
sx_call("events-slot-day-pill", day=d) for d in day_list
|
||||
)
|
||||
days_html = await render_to_sx("events-slot-days-pills", days_inner=SxExpr(days_inner))
|
||||
days_html = sx_call("events-slot-days-pills", days_inner=SxExpr(days_inner))
|
||||
else:
|
||||
days_html = await render_to_sx("events-slot-no-days")
|
||||
days_html = sx_call("events-slot-no-days")
|
||||
|
||||
time_start = s.time_start.strftime("%H:%M") if s.time_start else ""
|
||||
time_end = s.time_end.strftime("%H:%M") if s.time_end else ""
|
||||
cost = getattr(s, "cost", None)
|
||||
cost_str = f"{cost:.2f}" if cost is not None else ""
|
||||
|
||||
rows_html += await render_to_sx("events-slots-row",
|
||||
rows_html += sx_call("events-slots-row",
|
||||
tr_cls=tr_cls, slot_href=slot_href,
|
||||
pill_cls=pill_cls, hx_select=hx_select,
|
||||
slot_name=s.name, description=desc,
|
||||
@@ -265,11 +265,11 @@ async def render_slots_table(slots, calendar) -> str:
|
||||
del_url=del_url,
|
||||
csrf_hdr=f'{{"X-CSRFToken": "{csrf}"}}')
|
||||
else:
|
||||
rows_html = await render_to_sx("events-slots-empty-row")
|
||||
rows_html = sx_call("events-slots-empty-row")
|
||||
|
||||
add_url = url_for("calendar.slots.add_form", calendar_slug=cal_slug)
|
||||
|
||||
return await render_to_sx("events-slots-table",
|
||||
return sx_call("events-slots-table",
|
||||
list_container=list_container, rows=SxExpr(rows_html),
|
||||
pre_action=pre_action, add_url=add_url)
|
||||
|
||||
@@ -278,7 +278,7 @@ async def render_slots_table(slots, calendar) -> str:
|
||||
# Slot edit form
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def render_slot_edit_form(slot, calendar) -> str:
|
||||
def render_slot_edit_form(slot, calendar) -> str:
|
||||
"""Render slot edit form (replaces _types/slot/_edit.html)."""
|
||||
from quart import url_for, g
|
||||
from shared.browser.app.csrf import generate_csrf_token
|
||||
@@ -305,18 +305,18 @@ async def render_slot_edit_form(slot, calendar) -> str:
|
||||
("fri", "Fri"), ("sat", "Sat"), ("sun", "Sun")]
|
||||
all_checked = all(getattr(slot, k, False) for k, _ in day_keys)
|
||||
|
||||
days_parts = [await render_to_sx("events-day-all-checkbox",
|
||||
days_parts = [sx_call("events-day-all-checkbox",
|
||||
checked="checked" if all_checked else None)]
|
||||
for key, label in day_keys:
|
||||
checked = getattr(slot, key, False)
|
||||
days_parts.append(await render_to_sx("events-day-checkbox",
|
||||
days_parts.append(sx_call("events-day-checkbox",
|
||||
name=key, label=label,
|
||||
checked="checked" if checked else None))
|
||||
days_html = "".join(days_parts)
|
||||
|
||||
flexible = getattr(slot, "flexible", False)
|
||||
|
||||
return await render_to_sx("events-slot-edit-form",
|
||||
return sx_call("events-slot-edit-form",
|
||||
slot_id=str(sid), list_container=list_container,
|
||||
put_url=put_url, cancel_url=cancel_url, csrf=csrf,
|
||||
name_val=slot.name or "", cost_val=cost_val,
|
||||
@@ -330,7 +330,7 @@ async def render_slot_edit_form(slot, calendar) -> str:
|
||||
# Slot add form / button
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def render_slot_add_form(calendar) -> str:
|
||||
def render_slot_add_form(calendar) -> str:
|
||||
"""Render slot add form (replaces _types/slots/_add.html)."""
|
||||
from quart import url_for, g
|
||||
from shared.browser.app.csrf import generate_csrf_token
|
||||
@@ -348,19 +348,19 @@ async def render_slot_add_form(calendar) -> str:
|
||||
# Days checkboxes (all unchecked for add)
|
||||
day_keys = [("mon", "Mon"), ("tue", "Tue"), ("wed", "Wed"), ("thu", "Thu"),
|
||||
("fri", "Fri"), ("sat", "Sat"), ("sun", "Sun")]
|
||||
days_parts = [await render_to_sx("events-day-all-checkbox", checked=None)]
|
||||
days_parts = [sx_call("events-day-all-checkbox", checked=None)]
|
||||
for key, label in day_keys:
|
||||
days_parts.append(await render_to_sx("events-day-checkbox", name=key, label=label, checked=None))
|
||||
days_parts.append(sx_call("events-day-checkbox", name=key, label=label, checked=None))
|
||||
days_html = "".join(days_parts)
|
||||
|
||||
return await render_to_sx("events-slot-add-form",
|
||||
return sx_call("events-slot-add-form",
|
||||
post_url=post_url, csrf=csrf_hdr,
|
||||
days=SxExpr(days_html),
|
||||
action_btn=action_btn, cancel_btn=cancel_btn,
|
||||
cancel_url=cancel_url)
|
||||
|
||||
|
||||
async def render_slot_add_button(calendar) -> str:
|
||||
def render_slot_add_button(calendar) -> str:
|
||||
"""Render slot add button (replaces _types/slots/_add_button.html)."""
|
||||
from quart import url_for, g
|
||||
|
||||
@@ -369,4 +369,4 @@ async def render_slot_add_button(calendar) -> str:
|
||||
cal_slug = getattr(calendar, "slug", "")
|
||||
add_url = url_for("calendar.slots.add_form", calendar_slug=cal_slug)
|
||||
|
||||
return await render_to_sx("events-slot-add-button", pre_action=pre_action, add_url=add_url)
|
||||
return sx_call("events-slot-add-button", pre_action=pre_action, add_url=add_url)
|
||||
|
||||
@@ -3,7 +3,7 @@ from __future__ import annotations
|
||||
|
||||
from markupsafe import escape
|
||||
|
||||
from shared.sx.helpers import render_to_sx
|
||||
from shared.sx.helpers import sx_call
|
||||
from shared.sx.parser import SxExpr
|
||||
|
||||
from .utils import (
|
||||
@@ -15,7 +15,7 @@ from .utils import (
|
||||
# Ticket widget (inline +/- for entry cards)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def _ticket_widget_html(entry, qty: int, ticket_url: str, *, ctx: dict) -> str:
|
||||
def _ticket_widget_html(entry, qty: int, ticket_url: str, *, ctx: dict) -> str:
|
||||
"""Render the inline +/- ticket widget."""
|
||||
csrf_token_val = ""
|
||||
if ctx:
|
||||
@@ -32,22 +32,22 @@ async def _ticket_widget_html(entry, qty: int, ticket_url: str, *, ctx: dict) ->
|
||||
tp = getattr(entry, "ticket_price", 0) or 0
|
||||
tgt = f"#page-ticket-{eid}"
|
||||
|
||||
async def _tw_form(count_val, btn_html):
|
||||
return await render_to_sx("events-tw-form",
|
||||
def _tw_form(count_val, btn_html):
|
||||
return sx_call("events-tw-form",
|
||||
ticket_url=ticket_url, target=tgt,
|
||||
csrf=csrf_token_val, entry_id=str(eid),
|
||||
count_val=str(count_val), btn=SxExpr(btn_html))
|
||||
|
||||
if qty == 0:
|
||||
inner = await _tw_form(1, await render_to_sx("events-tw-cart-plus"))
|
||||
inner = _tw_form(1, sx_call("events-tw-cart-plus"))
|
||||
else:
|
||||
minus = await _tw_form(qty - 1, await render_to_sx("events-tw-minus"))
|
||||
cart_icon = await render_to_sx("events-tw-cart-icon", qty=str(qty))
|
||||
plus = await _tw_form(qty + 1, await render_to_sx("events-tw-plus"))
|
||||
minus = _tw_form(qty - 1, sx_call("events-tw-minus"))
|
||||
cart_icon = sx_call("events-tw-cart-icon", qty=str(qty))
|
||||
plus = _tw_form(qty + 1, sx_call("events-tw-plus"))
|
||||
inner = minus + cart_icon + plus
|
||||
|
||||
return await render_to_sx("events-tw-widget",
|
||||
entry_id=str(eid), price=f"£{tp:.2f}",
|
||||
return sx_call("events-tw-widget",
|
||||
entry_id=str(eid), price=f"\u00a3{tp:.2f}",
|
||||
inner=SxExpr(inner))
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ async def _ticket_widget_html(entry, qty: int, ticket_url: str, *, ctx: dict) ->
|
||||
# Tickets main panel (my tickets)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def _tickets_main_panel_html(ctx: dict, tickets: list) -> str:
|
||||
def _tickets_main_panel_html(ctx: dict, tickets: list) -> str:
|
||||
"""Render my tickets list."""
|
||||
from quart import url_for
|
||||
|
||||
@@ -75,16 +75,16 @@ async def _tickets_main_panel_html(ctx: dict, tickets: list) -> str:
|
||||
if entry.end_at:
|
||||
time_str += f" \u2013 {entry.end_at.strftime('%H:%M')}"
|
||||
|
||||
ticket_cards.append(await render_to_sx("events-ticket-card",
|
||||
ticket_cards.append(sx_call("events-ticket-card",
|
||||
href=href, entry_name=entry_name,
|
||||
type_name=tt.name if tt else None,
|
||||
time_str=time_str or None,
|
||||
cal_name=cal.name if cal else None,
|
||||
badge=SxExpr(await _ticket_state_badge_html(state)),
|
||||
badge=SxExpr(_ticket_state_badge_html(state)),
|
||||
code_prefix=ticket.code[:8]))
|
||||
|
||||
cards_html = "".join(ticket_cards)
|
||||
return await render_to_sx("events-tickets-panel",
|
||||
return sx_call("events-tickets-panel",
|
||||
list_container=_list_container(ctx),
|
||||
has_tickets=bool(tickets), cards=SxExpr(cards_html))
|
||||
|
||||
@@ -93,7 +93,7 @@ async def _tickets_main_panel_html(ctx: dict, tickets: list) -> str:
|
||||
# Ticket detail panel
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def _ticket_detail_panel_html(ctx: dict, ticket) -> str:
|
||||
def _ticket_detail_panel_html(ctx: dict, ticket) -> str:
|
||||
"""Render a single ticket detail with QR code."""
|
||||
from quart import url_for
|
||||
|
||||
@@ -110,7 +110,7 @@ async def _ticket_detail_panel_html(ctx: dict, ticket) -> str:
|
||||
back_href = url_for("defpage_my_tickets")
|
||||
|
||||
# Badge with larger sizing
|
||||
badge = (await _ticket_state_badge_html(state)).replace('px-2 py-0.5 text-xs', 'px-3 py-1 text-sm')
|
||||
badge = (_ticket_state_badge_html(state)).replace('px-2 py-0.5 text-xs', 'px-3 py-1 text-sm')
|
||||
|
||||
# Time info
|
||||
time_date = entry.start_at.strftime("%A, %B %d, %Y") if entry and entry.start_at else None
|
||||
@@ -129,7 +129,7 @@ async def _ticket_detail_panel_html(ctx: dict, ticket) -> str:
|
||||
"}})()"
|
||||
)
|
||||
|
||||
return await render_to_sx("events-ticket-detail",
|
||||
return sx_call("events-ticket-detail",
|
||||
list_container=_list_container(ctx), back_href=back_href,
|
||||
header_bg=header_bg, entry_name=entry_name,
|
||||
badge=SxExpr(badge), type_name=tt.name if tt else None,
|
||||
@@ -143,7 +143,7 @@ async def _ticket_detail_panel_html(ctx: dict, ticket) -> str:
|
||||
# Ticket admin main panel
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def _ticket_admin_main_panel_html(ctx: dict, tickets: list, stats: dict) -> str:
|
||||
def _ticket_admin_main_panel_html(ctx: dict, tickets: list, stats: dict) -> str:
|
||||
"""Render ticket admin dashboard."""
|
||||
from quart import url_for
|
||||
csrf_token = ctx.get("csrf_token")
|
||||
@@ -160,7 +160,7 @@ async def _ticket_admin_main_panel_html(ctx: dict, tickets: list, stats: dict) -
|
||||
]:
|
||||
val = stats.get(key, 0)
|
||||
lbl_cls = text_cls.replace("700", "600").replace("900", "500") if "stone" not in text_cls else "text-stone-500"
|
||||
stats_html += await render_to_sx("events-ticket-admin-stat",
|
||||
stats_html += sx_call("events-ticket-admin-stat",
|
||||
border=border, bg=bg, text_cls=text_cls,
|
||||
label_cls=lbl_cls, value=str(val), label=label)
|
||||
|
||||
@@ -174,29 +174,29 @@ async def _ticket_admin_main_panel_html(ctx: dict, tickets: list, stats: dict) -
|
||||
|
||||
date_html = ""
|
||||
if entry and entry.start_at:
|
||||
date_html = await render_to_sx("events-ticket-admin-date",
|
||||
date_html = sx_call("events-ticket-admin-date",
|
||||
date_str=entry.start_at.strftime("%d %b %Y, %H:%M"))
|
||||
|
||||
action_html = ""
|
||||
if state in ("confirmed", "reserved"):
|
||||
checkin_url = url_for("ticket_admin.do_checkin", code=code)
|
||||
action_html = await render_to_sx("events-ticket-admin-checkin-form",
|
||||
action_html = sx_call("events-ticket-admin-checkin-form",
|
||||
checkin_url=checkin_url, code=code, csrf=csrf)
|
||||
elif state == "checked_in":
|
||||
checked_in_at = getattr(ticket, "checked_in_at", None)
|
||||
t_str = checked_in_at.strftime("%H:%M") if checked_in_at else ""
|
||||
action_html = await render_to_sx("events-ticket-admin-checked-in",
|
||||
action_html = sx_call("events-ticket-admin-checked-in",
|
||||
time_str=t_str)
|
||||
|
||||
rows_html += await render_to_sx("events-ticket-admin-row",
|
||||
rows_html += sx_call("events-ticket-admin-row",
|
||||
code=code, code_short=code[:12] + "...",
|
||||
entry_name=entry.name if entry else "\u2014",
|
||||
date=SxExpr(date_html),
|
||||
type_name=tt.name if tt else "\u2014",
|
||||
badge=SxExpr(await _ticket_state_badge_html(state)),
|
||||
badge=SxExpr(_ticket_state_badge_html(state)),
|
||||
action=SxExpr(action_html))
|
||||
|
||||
return await render_to_sx("events-ticket-admin-panel",
|
||||
return sx_call("events-ticket-admin-panel",
|
||||
list_container=_list_container(ctx), stats=SxExpr(stats_html),
|
||||
lookup_url=lookup_url, has_tickets=bool(tickets),
|
||||
rows=SxExpr(rows_html))
|
||||
@@ -206,19 +206,19 @@ async def _ticket_admin_main_panel_html(ctx: dict, tickets: list, stats: dict) -
|
||||
# Public render: ticket widget
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def render_ticket_widget(entry, qty: int, ticket_url: str) -> str:
|
||||
def render_ticket_widget(entry, qty: int, ticket_url: str) -> str:
|
||||
"""Render the +/- ticket widget for page_summary / all_events adjust_ticket."""
|
||||
return await _ticket_widget_html(entry, qty, ticket_url, ctx={})
|
||||
return _ticket_widget_html(entry, qty, ticket_url, ctx={})
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Ticket admin: checkin result
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def render_checkin_result(success: bool, error: str | None, ticket) -> str:
|
||||
def render_checkin_result(success: bool, error: str | None, ticket) -> str:
|
||||
"""Render checkin result: table row on success, error div on failure."""
|
||||
if not success:
|
||||
return await render_to_sx("events-checkin-error",
|
||||
return sx_call("events-checkin-error",
|
||||
message=error or "Check-in failed")
|
||||
if not ticket:
|
||||
return ""
|
||||
@@ -230,15 +230,15 @@ async def render_checkin_result(success: bool, error: str | None, ticket) -> str
|
||||
|
||||
date_html = ""
|
||||
if entry and entry.start_at:
|
||||
date_html = await render_to_sx("events-ticket-admin-date",
|
||||
date_html = sx_call("events-ticket-admin-date",
|
||||
date_str=entry.start_at.strftime("%d %b %Y, %H:%M"))
|
||||
|
||||
return await render_to_sx("events-checkin-success-row",
|
||||
return sx_call("events-checkin-success-row",
|
||||
code=code, code_short=code[:12] + "...",
|
||||
entry_name=entry.name if entry else "\u2014",
|
||||
date=SxExpr(date_html),
|
||||
type_name=tt.name if tt else "\u2014",
|
||||
badge=SxExpr(await _ticket_state_badge_html("checked_in")),
|
||||
badge=SxExpr(_ticket_state_badge_html("checked_in")),
|
||||
time_str=time_str)
|
||||
|
||||
|
||||
@@ -246,13 +246,13 @@ async def render_checkin_result(success: bool, error: str | None, ticket) -> str
|
||||
# Ticket admin: lookup result
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def render_lookup_result(ticket, error: str | None) -> str:
|
||||
def render_lookup_result(ticket, error: str | None) -> str:
|
||||
"""Render ticket lookup result: error div or ticket info card."""
|
||||
from quart import url_for
|
||||
from shared.browser.app.csrf import generate_csrf_token
|
||||
|
||||
if error:
|
||||
return await render_to_sx("events-lookup-error", message=error)
|
||||
return sx_call("events-lookup-error", message=error)
|
||||
if not ticket:
|
||||
return ""
|
||||
|
||||
@@ -264,34 +264,34 @@ async def render_lookup_result(ticket, error: str | None) -> str:
|
||||
csrf = generate_csrf_token()
|
||||
|
||||
# Info section
|
||||
info_html = await render_to_sx("events-lookup-info",
|
||||
info_html = sx_call("events-lookup-info",
|
||||
entry_name=entry.name if entry else "Unknown event")
|
||||
if tt:
|
||||
info_html += await render_to_sx("events-lookup-type", type_name=tt.name)
|
||||
info_html += sx_call("events-lookup-type", type_name=tt.name)
|
||||
if entry and entry.start_at:
|
||||
info_html += await render_to_sx("events-lookup-date",
|
||||
info_html += sx_call("events-lookup-date",
|
||||
date_str=entry.start_at.strftime("%A, %B %d, %Y at %H:%M"))
|
||||
cal = getattr(entry, "calendar", None) if entry else None
|
||||
if cal:
|
||||
info_html += await render_to_sx("events-lookup-cal", cal_name=cal.name)
|
||||
info_html += await render_to_sx("events-lookup-status",
|
||||
badge=SxExpr(await _ticket_state_badge_html(state)), code=code)
|
||||
info_html += sx_call("events-lookup-cal", cal_name=cal.name)
|
||||
info_html += sx_call("events-lookup-status",
|
||||
badge=SxExpr(_ticket_state_badge_html(state)), code=code)
|
||||
if checked_in_at:
|
||||
info_html += await render_to_sx("events-lookup-checkin-time",
|
||||
info_html += sx_call("events-lookup-checkin-time",
|
||||
date_str=checked_in_at.strftime("%B %d, %Y at %H:%M"))
|
||||
|
||||
# Action area
|
||||
action_html = ""
|
||||
if state in ("confirmed", "reserved"):
|
||||
checkin_url = url_for("ticket_admin.do_checkin", code=code)
|
||||
action_html = await render_to_sx("events-lookup-checkin-btn",
|
||||
action_html = sx_call("events-lookup-checkin-btn",
|
||||
checkin_url=checkin_url, code=code, csrf=csrf)
|
||||
elif state == "checked_in":
|
||||
action_html = await render_to_sx("events-lookup-checked-in")
|
||||
action_html = sx_call("events-lookup-checked-in")
|
||||
elif state == "cancelled":
|
||||
action_html = await render_to_sx("events-lookup-cancelled")
|
||||
action_html = sx_call("events-lookup-cancelled")
|
||||
|
||||
return await render_to_sx("events-lookup-card",
|
||||
return sx_call("events-lookup-card",
|
||||
info=SxExpr(info_html), code=code, action=SxExpr(action_html))
|
||||
|
||||
|
||||
@@ -299,7 +299,7 @@ async def render_lookup_result(ticket, error: str | None) -> str:
|
||||
# Ticket admin: entry tickets table
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def render_entry_tickets_admin(entry, tickets: list) -> str:
|
||||
def render_entry_tickets_admin(entry, tickets: list) -> str:
|
||||
"""Render admin ticket table for a specific entry."""
|
||||
from quart import url_for
|
||||
from shared.browser.app.csrf import generate_csrf_token
|
||||
@@ -318,26 +318,26 @@ async def render_entry_tickets_admin(entry, tickets: list) -> str:
|
||||
action_html = ""
|
||||
if state in ("confirmed", "reserved"):
|
||||
checkin_url = url_for("ticket_admin.do_checkin", code=code)
|
||||
action_html = await render_to_sx("events-entry-tickets-admin-checkin",
|
||||
action_html = sx_call("events-entry-tickets-admin-checkin",
|
||||
checkin_url=checkin_url, code=code, csrf=csrf)
|
||||
elif state == "checked_in":
|
||||
t_str = checked_in_at.strftime("%H:%M") if checked_in_at else ""
|
||||
action_html = await render_to_sx("events-ticket-admin-checked-in",
|
||||
action_html = sx_call("events-ticket-admin-checked-in",
|
||||
time_str=t_str)
|
||||
|
||||
rows_html += await render_to_sx("events-entry-tickets-admin-row",
|
||||
rows_html += sx_call("events-entry-tickets-admin-row",
|
||||
code=code, code_short=code[:12] + "...",
|
||||
type_name=tt.name if tt else "\u2014",
|
||||
badge=SxExpr(await _ticket_state_badge_html(state)),
|
||||
badge=SxExpr(_ticket_state_badge_html(state)),
|
||||
action=SxExpr(action_html))
|
||||
|
||||
if tickets:
|
||||
body_html = await render_to_sx("events-entry-tickets-admin-table",
|
||||
body_html = sx_call("events-entry-tickets-admin-table",
|
||||
rows=SxExpr(rows_html))
|
||||
else:
|
||||
body_html = await render_to_sx("events-entry-tickets-admin-empty")
|
||||
body_html = sx_call("events-entry-tickets-admin-empty")
|
||||
|
||||
return await render_to_sx("events-entry-tickets-admin-panel",
|
||||
return sx_call("events-entry-tickets-admin-panel",
|
||||
entry_name=entry.name,
|
||||
count_label=f"{count} ticket{suffix}",
|
||||
body=SxExpr(body_html))
|
||||
@@ -347,7 +347,7 @@ async def render_entry_tickets_admin(entry, tickets: list) -> str:
|
||||
# Ticket type main panel
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def render_ticket_type_main_panel(ticket_type, entry, calendar, day, month, year, *, oob: bool = False) -> str:
|
||||
def render_ticket_type_main_panel(ticket_type, entry, calendar, day, month, year, *, oob: bool = False) -> str:
|
||||
"""Render ticket type detail view."""
|
||||
from quart import url_for, g
|
||||
|
||||
@@ -367,14 +367,14 @@ async def render_ticket_type_main_panel(ticket_type, entry, calendar, day, month
|
||||
year=year, month=month, day=day, entry_id=entry.id,
|
||||
)
|
||||
|
||||
async def _col(label, val):
|
||||
return await render_to_sx("events-ticket-type-col", label=label, value=val)
|
||||
def _col(label, val):
|
||||
return sx_call("events-ticket-type-col", label=label, value=val)
|
||||
|
||||
return await render_to_sx("events-ticket-type-panel",
|
||||
return sx_call("events-ticket-type-panel",
|
||||
ticket_id=tid, list_container=list_container,
|
||||
c1=await _col("Name", ticket_type.name),
|
||||
c2=await _col("Cost", cost_str),
|
||||
c3=await _col("Count", str(count)),
|
||||
c1=_col("Name", ticket_type.name),
|
||||
c2=_col("Cost", cost_str),
|
||||
c3=_col("Count", str(count)),
|
||||
pre_action=pre_action, edit_url=edit_url)
|
||||
|
||||
|
||||
@@ -382,7 +382,7 @@ async def render_ticket_type_main_panel(ticket_type, entry, calendar, day, month
|
||||
# Ticket types table
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def render_ticket_types_table(ticket_types, entry, calendar, day, month, year) -> str:
|
||||
def render_ticket_types_table(ticket_types, entry, calendar, day, month, year) -> str:
|
||||
"""Render ticket types table with rows and add button."""
|
||||
from quart import url_for, g
|
||||
from shared.browser.app.csrf import generate_csrf_token
|
||||
@@ -413,7 +413,7 @@ async def render_ticket_types_table(ticket_types, entry, calendar, day, month, y
|
||||
cost = getattr(tt, "cost", None)
|
||||
cost_str = f"\u00a3{cost:.2f}" if cost is not None else "\u00a30.00"
|
||||
|
||||
rows_html += await render_to_sx("events-ticket-types-row",
|
||||
rows_html += sx_call("events-ticket-types-row",
|
||||
tr_cls=tr_cls, tt_href=tt_href,
|
||||
pill_cls=pill_cls, hx_select=hx_select,
|
||||
tt_name=tt.name, cost_str=cost_str,
|
||||
@@ -421,14 +421,14 @@ async def render_ticket_types_table(ticket_types, entry, calendar, day, month, y
|
||||
del_url=del_url,
|
||||
csrf_hdr=f'{{"X-CSRFToken": "{csrf}"}}')
|
||||
else:
|
||||
rows_html = await render_to_sx("events-ticket-types-empty-row")
|
||||
rows_html = sx_call("events-ticket-types-empty-row")
|
||||
|
||||
add_url = url_for(
|
||||
"calendar.day.calendar_entries.calendar_entry.ticket_types.add_form",
|
||||
calendar_slug=cal_slug, entry_id=eid, year=year, month=month, day=day,
|
||||
)
|
||||
|
||||
return await render_to_sx("events-ticket-types-table",
|
||||
return sx_call("events-ticket-types-table",
|
||||
list_container=list_container, rows=SxExpr(rows_html),
|
||||
action_btn=action_btn, add_url=add_url)
|
||||
|
||||
@@ -437,11 +437,11 @@ async def render_ticket_types_table(ticket_types, entry, calendar, day, month, y
|
||||
# Buy result (ticket purchase confirmation)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def render_buy_result(entry, created_tickets, remaining, cart_count) -> str:
|
||||
def render_buy_result(entry, created_tickets, remaining, cart_count) -> str:
|
||||
"""Render buy result card with created tickets + OOB cart icon."""
|
||||
from quart import url_for
|
||||
|
||||
cart_html = await _cart_icon_oob(cart_count)
|
||||
cart_html = _cart_icon_oob(cart_count)
|
||||
|
||||
count = len(created_tickets)
|
||||
suffix = "s" if count != 1 else ""
|
||||
@@ -449,18 +449,18 @@ async def render_buy_result(entry, created_tickets, remaining, cart_count) -> st
|
||||
tickets_html = ""
|
||||
for ticket in created_tickets:
|
||||
href = url_for("defpage_ticket_detail", code=ticket.code)
|
||||
tickets_html += await render_to_sx("events-buy-result-ticket",
|
||||
tickets_html += sx_call("events-buy-result-ticket",
|
||||
href=href, code_short=ticket.code[:12] + "...")
|
||||
|
||||
remaining_html = ""
|
||||
if remaining is not None:
|
||||
r_suffix = "s" if remaining != 1 else ""
|
||||
remaining_html = await render_to_sx("events-buy-result-remaining",
|
||||
remaining_html = sx_call("events-buy-result-remaining",
|
||||
text=f"{remaining} ticket{r_suffix} remaining")
|
||||
|
||||
my_href = url_for("defpage_my_tickets")
|
||||
|
||||
return cart_html + await render_to_sx("events-buy-result",
|
||||
return cart_html + sx_call("events-buy-result",
|
||||
entry_id=str(entry.id),
|
||||
count_label=f"{count} ticket{suffix} reserved",
|
||||
tickets=SxExpr(tickets_html),
|
||||
@@ -472,7 +472,7 @@ async def render_buy_result(entry, created_tickets, remaining, cart_count) -> st
|
||||
# Buy form (ticket +/- controls)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def render_buy_form(entry, ticket_remaining, ticket_sold_count,
|
||||
def render_buy_form(entry, ticket_remaining, ticket_sold_count,
|
||||
user_ticket_count, user_ticket_counts_by_type) -> str:
|
||||
"""Render the ticket buy/adjust form with +/- controls."""
|
||||
from quart import url_for
|
||||
@@ -489,7 +489,7 @@ async def render_buy_form(entry, ticket_remaining, ticket_sold_count,
|
||||
return ""
|
||||
|
||||
if state != "confirmed":
|
||||
return await render_to_sx("events-buy-not-confirmed", entry_id=eid_s)
|
||||
return sx_call("events-buy-not-confirmed", entry_id=eid_s)
|
||||
|
||||
adjust_url = url_for("tickets.adjust_quantity")
|
||||
target = f"#ticket-buy-{eid}"
|
||||
@@ -498,16 +498,16 @@ async def render_buy_form(entry, ticket_remaining, ticket_sold_count,
|
||||
info_html = ""
|
||||
info_items = ""
|
||||
if ticket_sold_count:
|
||||
info_items += await render_to_sx("events-buy-info-sold",
|
||||
info_items += sx_call("events-buy-info-sold",
|
||||
count=str(ticket_sold_count))
|
||||
if ticket_remaining is not None:
|
||||
info_items += await render_to_sx("events-buy-info-remaining",
|
||||
info_items += sx_call("events-buy-info-remaining",
|
||||
count=str(ticket_remaining))
|
||||
if user_ticket_count:
|
||||
info_items += await render_to_sx("events-buy-info-basket",
|
||||
info_items += sx_call("events-buy-info-basket",
|
||||
count=str(user_ticket_count))
|
||||
if info_items:
|
||||
info_html = await render_to_sx("events-buy-info-bar", items=SxExpr(info_items))
|
||||
info_html = sx_call("events-buy-info-bar", items=SxExpr(info_items))
|
||||
|
||||
active_types = [tt for tt in ticket_types if getattr(tt, "deleted_at", None) is None]
|
||||
|
||||
@@ -517,46 +517,46 @@ async def render_buy_form(entry, ticket_remaining, ticket_sold_count,
|
||||
for tt in active_types:
|
||||
type_count = user_ticket_counts_by_type.get(tt.id, 0) if user_ticket_counts_by_type else 0
|
||||
cost_str = f"\u00a3{tt.cost:.2f}" if tt.cost is not None else "\u00a30.00"
|
||||
type_items += await render_to_sx("events-buy-type-item",
|
||||
type_items += sx_call("events-buy-type-item",
|
||||
type_name=tt.name, cost_str=cost_str,
|
||||
adjust_controls=SxExpr(await _ticket_adjust_controls(csrf, adjust_url, target, eid, type_count, ticket_type_id=tt.id)))
|
||||
body_html = await render_to_sx("events-buy-types-wrapper", items=SxExpr(type_items))
|
||||
adjust_controls=SxExpr(_ticket_adjust_controls(csrf, adjust_url, target, eid, type_count, ticket_type_id=tt.id)))
|
||||
body_html = sx_call("events-buy-types-wrapper", items=SxExpr(type_items))
|
||||
else:
|
||||
qty = user_ticket_count or 0
|
||||
body_html = await render_to_sx("events-buy-default",
|
||||
body_html = sx_call("events-buy-default",
|
||||
price_str=f"\u00a3{tp:.2f}",
|
||||
adjust_controls=SxExpr(await _ticket_adjust_controls(csrf, adjust_url, target, eid, qty)))
|
||||
adjust_controls=SxExpr(_ticket_adjust_controls(csrf, adjust_url, target, eid, qty)))
|
||||
|
||||
return await render_to_sx("events-buy-panel",
|
||||
return sx_call("events-buy-panel",
|
||||
entry_id=eid_s, info=SxExpr(info_html), body=SxExpr(body_html))
|
||||
|
||||
|
||||
async def _ticket_adjust_controls(csrf, adjust_url, target, entry_id, count, *, ticket_type_id=None):
|
||||
def _ticket_adjust_controls(csrf, adjust_url, target, entry_id, count, *, ticket_type_id=None):
|
||||
"""Render +/- ticket controls for buy form."""
|
||||
from quart import url_for
|
||||
|
||||
tt_html = await render_to_sx("events-adjust-tt-hidden",
|
||||
tt_html = sx_call("events-adjust-tt-hidden",
|
||||
ticket_type_id=str(ticket_type_id)) if ticket_type_id else ""
|
||||
eid_s = str(entry_id)
|
||||
|
||||
async def _adj_form(count_val, btn_html, *, extra_cls=""):
|
||||
return await render_to_sx("events-adjust-form",
|
||||
def _adj_form(count_val, btn_html, *, extra_cls=""):
|
||||
return sx_call("events-adjust-form",
|
||||
adjust_url=adjust_url, target=target,
|
||||
extra_cls=extra_cls, csrf=csrf,
|
||||
entry_id=eid_s, tt=SxExpr(tt_html) if tt_html else None,
|
||||
count_val=str(count_val), btn=SxExpr(btn_html))
|
||||
|
||||
if count == 0:
|
||||
return await _adj_form(1, await render_to_sx("events-adjust-cart-plus"),
|
||||
return _adj_form(1, sx_call("events-adjust-cart-plus"),
|
||||
extra_cls="flex items-center")
|
||||
|
||||
my_tickets_href = url_for("defpage_my_tickets")
|
||||
minus = await _adj_form(count - 1, await render_to_sx("events-adjust-minus"))
|
||||
cart_icon = await render_to_sx("events-adjust-cart-icon",
|
||||
minus = _adj_form(count - 1, sx_call("events-adjust-minus"))
|
||||
cart_icon = sx_call("events-adjust-cart-icon",
|
||||
href=my_tickets_href, count=str(count))
|
||||
plus = await _adj_form(count + 1, await render_to_sx("events-adjust-plus"))
|
||||
plus = _adj_form(count + 1, sx_call("events-adjust-plus"))
|
||||
|
||||
return await render_to_sx("events-adjust-controls",
|
||||
return sx_call("events-adjust-controls",
|
||||
minus=SxExpr(minus), cart_icon=SxExpr(cart_icon), plus=SxExpr(plus))
|
||||
|
||||
|
||||
@@ -564,12 +564,12 @@ async def _ticket_adjust_controls(csrf, adjust_url, target, entry_id, count, *,
|
||||
# Adjust response (OOB cart icon + buy form)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def render_adjust_response(entry, ticket_remaining, ticket_sold_count,
|
||||
def render_adjust_response(entry, ticket_remaining, ticket_sold_count,
|
||||
user_ticket_count, user_ticket_counts_by_type,
|
||||
cart_count) -> str:
|
||||
"""Render ticket adjust response: OOB cart icon + buy form."""
|
||||
cart_html = await _cart_icon_oob(cart_count)
|
||||
form_html = await render_buy_form(
|
||||
cart_html = _cart_icon_oob(cart_count)
|
||||
form_html = render_buy_form(
|
||||
entry, ticket_remaining, ticket_sold_count,
|
||||
user_ticket_count, user_ticket_counts_by_type,
|
||||
)
|
||||
@@ -580,7 +580,7 @@ async def render_adjust_response(entry, ticket_remaining, ticket_sold_count,
|
||||
# Ticket types header rows
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def _ticket_types_header_html(ctx: dict, *, oob: bool = False) -> str:
|
||||
def _ticket_types_header_html(ctx: dict, *, oob: bool = False) -> str:
|
||||
"""Build the ticket types header row."""
|
||||
from quart import url_for
|
||||
|
||||
@@ -599,15 +599,15 @@ async def _ticket_types_header_html(ctx: dict, *, oob: bool = False) -> str:
|
||||
|
||||
label_html = '<i class="fa fa-ticket"></i><div class="shrink-0">ticket types</div>'
|
||||
|
||||
nav_html = await render_to_sx("events-admin-placeholder-nav")
|
||||
nav_html = sx_call("events-admin-placeholder-nav")
|
||||
|
||||
return await render_to_sx("menu-row-sx", id="ticket_types-row", level=7,
|
||||
return sx_call("menu-row-sx", id="ticket_types-row", level=7,
|
||||
link_href=link_href, link_label_content=SxExpr(label_html),
|
||||
nav=SxExpr(nav_html) if nav_html else None, child_id="ticket_type-header-child", oob=oob)
|
||||
|
||||
|
||||
|
||||
async def _ticket_type_header_html(ctx: dict, *, oob: bool = False) -> str:
|
||||
def _ticket_type_header_html(ctx: dict, *, oob: bool = False) -> str:
|
||||
"""Build the single ticket type header row."""
|
||||
from quart import url_for
|
||||
|
||||
@@ -635,9 +635,9 @@ async def _ticket_type_header_html(ctx: dict, *, oob: bool = False) -> str:
|
||||
f'</div></div>'
|
||||
)
|
||||
|
||||
nav_html = await render_to_sx("events-admin-placeholder-nav")
|
||||
nav_html = sx_call("events-admin-placeholder-nav")
|
||||
|
||||
return await render_to_sx("menu-row-sx", id="ticket_type-row", level=8,
|
||||
return sx_call("menu-row-sx", id="ticket_type-row", level=8,
|
||||
link_href=link_href, link_label_content=SxExpr(label_html),
|
||||
nav=SxExpr(nav_html) if nav_html else None, child_id="ticket_type-header-child-inner", oob=oob)
|
||||
|
||||
@@ -646,7 +646,7 @@ async def _ticket_type_header_html(ctx: dict, *, oob: bool = False) -> str:
|
||||
# Ticket type edit form
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def render_ticket_type_edit_form(ticket_type, entry, calendar, day, month, year) -> str:
|
||||
def render_ticket_type_edit_form(ticket_type, entry, calendar, day, month, year) -> str:
|
||||
"""Render ticket type edit form (replaces _types/ticket_type/_edit.html)."""
|
||||
from quart import url_for, g
|
||||
from shared.browser.app.csrf import generate_csrf_token
|
||||
@@ -670,7 +670,7 @@ async def render_ticket_type_edit_form(ticket_type, entry, calendar, day, month,
|
||||
cost_val = f"{cost:.2f}" if cost is not None else ""
|
||||
count = getattr(ticket_type, "count", 0)
|
||||
|
||||
return await render_to_sx("events-ticket-type-edit-form",
|
||||
return sx_call("events-ticket-type-edit-form",
|
||||
ticket_id=str(tid), list_container=list_container,
|
||||
put_url=put_url, cancel_url=cancel_url, csrf=csrf,
|
||||
name_val=ticket_type.name or "",
|
||||
@@ -682,7 +682,7 @@ async def render_ticket_type_edit_form(ticket_type, entry, calendar, day, month,
|
||||
# Ticket type add form / button
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def render_ticket_type_add_form(entry, calendar, day, month, year) -> str:
|
||||
def render_ticket_type_add_form(entry, calendar, day, month, year) -> str:
|
||||
"""Render ticket type add form (replaces _types/ticket_types/_add.html)."""
|
||||
from quart import url_for, g
|
||||
from shared.browser.app.csrf import generate_csrf_token
|
||||
@@ -701,13 +701,13 @@ async def render_ticket_type_add_form(entry, calendar, day, month, year) -> str:
|
||||
year=year, month=month, day=day)
|
||||
csrf_hdr = f'{{"X-CSRFToken": "{csrf}"}}'
|
||||
|
||||
return await render_to_sx("events-ticket-type-add-form",
|
||||
return sx_call("events-ticket-type-add-form",
|
||||
post_url=post_url, csrf=csrf_hdr,
|
||||
action_btn=action_btn, cancel_btn=cancel_btn,
|
||||
cancel_url=cancel_url)
|
||||
|
||||
|
||||
async def render_ticket_type_add_button(entry, calendar, day, month, year) -> str:
|
||||
def render_ticket_type_add_button(entry, calendar, day, month, year) -> str:
|
||||
"""Render ticket type add button (replaces _types/ticket_types/_add_button.html)."""
|
||||
from quart import url_for, g
|
||||
|
||||
@@ -719,5 +719,5 @@ async def render_ticket_type_add_button(entry, calendar, day, month, year) -> st
|
||||
calendar_slug=cal_slug, entry_id=entry.id,
|
||||
year=year, month=month, day=day)
|
||||
|
||||
return await render_to_sx("events-ticket-type-add-button",
|
||||
return sx_call("events-ticket-type-add-button",
|
||||
action_btn=action_btn, add_url=add_url)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Badge helpers, OOB helpers, formatting utilities, list containers."""
|
||||
from __future__ import annotations
|
||||
|
||||
from shared.sx.helpers import render_to_sx
|
||||
from shared.sx.helpers import sx_call
|
||||
from shared.sx.parser import SxExpr
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ def _list_container(ctx: dict) -> str:
|
||||
return getattr(styles, "list_container", "") if hasattr(styles, "list_container") else styles.get("list_container", "")
|
||||
|
||||
|
||||
async def _entry_state_badge_html(state: str) -> str:
|
||||
def _entry_state_badge_html(state: str) -> str:
|
||||
"""Render an entry state badge."""
|
||||
state_classes = {
|
||||
"confirmed": "bg-emerald-100 text-emerald-800",
|
||||
@@ -85,10 +85,10 @@ async def _entry_state_badge_html(state: str) -> str:
|
||||
}
|
||||
cls = state_classes.get(state, "bg-stone-100 text-stone-700")
|
||||
label = state.replace("_", " ").capitalize()
|
||||
return await render_to_sx("badge", cls=cls, label=label)
|
||||
return sx_call("badge", cls=cls, label=label)
|
||||
|
||||
|
||||
async def _ticket_state_badge_html(state: str) -> str:
|
||||
def _ticket_state_badge_html(state: str) -> str:
|
||||
"""Render a ticket state badge."""
|
||||
cls_map = {
|
||||
"confirmed": "bg-emerald-100 text-emerald-800",
|
||||
@@ -98,7 +98,7 @@ async def _ticket_state_badge_html(state: str) -> str:
|
||||
}
|
||||
cls = cls_map.get(state, "bg-stone-100 text-stone-700")
|
||||
label = (state or "").replace("_", " ").capitalize()
|
||||
return await render_to_sx("badge", cls=cls, label=label)
|
||||
return sx_call("badge", cls=cls, label=label)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -109,21 +109,21 @@ _LIST_SVG = None
|
||||
_TILE_SVG = None
|
||||
|
||||
|
||||
async def _get_list_svg():
|
||||
def _get_list_svg():
|
||||
global _LIST_SVG
|
||||
if _LIST_SVG is None:
|
||||
_LIST_SVG = await render_to_sx("list-svg")
|
||||
_LIST_SVG = sx_call("list-svg")
|
||||
return _LIST_SVG
|
||||
|
||||
|
||||
async def _get_tile_svg():
|
||||
def _get_tile_svg():
|
||||
global _TILE_SVG
|
||||
if _TILE_SVG is None:
|
||||
_TILE_SVG = await render_to_sx("tile-svg")
|
||||
_TILE_SVG = sx_call("tile-svg")
|
||||
return _TILE_SVG
|
||||
|
||||
|
||||
async def _view_toggle_html(ctx: dict, view: str) -> str:
|
||||
def _view_toggle_html(ctx: dict, view: str) -> str:
|
||||
"""Render the list/tile view toggle bar."""
|
||||
from shared.utils import route_prefix
|
||||
prefix = route_prefix()
|
||||
@@ -142,14 +142,14 @@ async def _view_toggle_html(ctx: dict, view: str) -> str:
|
||||
list_active = 'bg-stone-200 text-stone-800' if view != 'tile' else 'text-stone-400 hover:text-stone-600'
|
||||
tile_active = 'bg-stone-200 text-stone-800' if view == 'tile' else 'text-stone-400 hover:text-stone-600'
|
||||
|
||||
return await render_to_sx("view-toggle",
|
||||
return sx_call("view-toggle",
|
||||
list_href=list_href, tile_href=tile_href,
|
||||
hx_select=hx_select, list_cls=list_active,
|
||||
tile_cls=tile_active, storage_key="events_view",
|
||||
list_svg=SxExpr(await _get_list_svg()), tile_svg=SxExpr(await _get_tile_svg()))
|
||||
list_svg=SxExpr(_get_list_svg()), tile_svg=SxExpr(_get_tile_svg()))
|
||||
|
||||
|
||||
async def _cart_icon_oob(count: int) -> str:
|
||||
def _cart_icon_oob(count: int) -> str:
|
||||
"""Render the OOB cart icon/badge swap."""
|
||||
from quart import g
|
||||
|
||||
@@ -163,9 +163,9 @@ async def _cart_icon_oob(count: int) -> str:
|
||||
|
||||
if count == 0:
|
||||
blog_href = blog_url_fn("/") if blog_url_fn else "/"
|
||||
return await render_to_sx("events-cart-icon-logo",
|
||||
return sx_call("events-cart-icon-logo",
|
||||
blog_href=blog_href, logo=logo)
|
||||
|
||||
cart_href = cart_url_fn("/") if cart_url_fn else "/"
|
||||
return await render_to_sx("events-cart-icon-badge",
|
||||
return sx_call("events-cart-icon-badge",
|
||||
cart_href=cart_href, count=str(count))
|
||||
|
||||
@@ -44,11 +44,11 @@ ALLOWED_CLIENTS = {"blog", "market", "cart", "events", "account"}
|
||||
|
||||
async def _render_social_auth_page(component: str, title: str, **kwargs) -> str:
|
||||
"""Render an auth page with social layout — replaces sx_components helpers."""
|
||||
from shared.sx.helpers import render_to_sx
|
||||
from shared.sx.helpers import sx_call
|
||||
from shared.sx.page import get_template_context
|
||||
from sxc.pages.utils import _social_page
|
||||
ctx = await get_template_context()
|
||||
content = await render_to_sx(component, **{k: v for k, v in kwargs.items() if v})
|
||||
content = sx_call(component, **{k: v for k, v in kwargs.items() if v})
|
||||
return await _social_page(ctx, None, content=content, title=title)
|
||||
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ async def _render_choose_username(*, actor=None, error="", username=""):
|
||||
"""Render choose-username page — replaces sx_components helper."""
|
||||
from shared.browser.app.csrf import generate_csrf_token
|
||||
from shared.config import config
|
||||
from shared.sx.helpers import render_to_sx
|
||||
from shared.sx.helpers import sx_call
|
||||
from shared.sx.parser import SxExpr
|
||||
from shared.sx.page import get_template_context
|
||||
from sxc.pages.utils import _social_page
|
||||
@@ -41,8 +41,8 @@ async def _render_choose_username(*, actor=None, error="", username=""):
|
||||
ap_domain = config().get("ap_domain", "rose-ash.com")
|
||||
check_url = url_for("identity.check_username")
|
||||
|
||||
error_sx = await render_to_sx("auth-error-banner", error=error) if error else ""
|
||||
content = await render_to_sx(
|
||||
error_sx = sx_call("auth-error-banner", error=error) if error else ""
|
||||
content = sx_call(
|
||||
"federation-choose-username",
|
||||
domain=str(escape(ap_domain)),
|
||||
error=SxExpr(error_sx) if error_sx else None,
|
||||
|
||||
@@ -7,7 +7,7 @@ from datetime import datetime
|
||||
from quart import Blueprint, request, g, redirect, url_for, abort, Response
|
||||
|
||||
from shared.services.registry import services
|
||||
from shared.sx.helpers import sx_response, render_to_sx
|
||||
from shared.sx.helpers import sx_response, sx_call
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -47,7 +47,7 @@ def register(url_prefix="/social"):
|
||||
items = await services.federation.get_home_timeline(
|
||||
g.s, actor.id, before=before,
|
||||
)
|
||||
sx_src = await _render_timeline_items(items, "home", actor)
|
||||
sx_src = _render_timeline_items(items, "home", actor)
|
||||
return sx_response(sx_src)
|
||||
|
||||
@bp.get("/public/timeline")
|
||||
@@ -61,7 +61,7 @@ def register(url_prefix="/social"):
|
||||
pass
|
||||
items = await services.federation.get_public_timeline(g.s, before=before)
|
||||
actor = getattr(g, "_social_actor", None)
|
||||
sx_src = await _render_timeline_items(items, "public", actor)
|
||||
sx_src = _render_timeline_items(items, "public", actor)
|
||||
return sx_response(sx_src)
|
||||
|
||||
# -- Compose ---------------------------------------------------------------
|
||||
@@ -117,12 +117,12 @@ def register(url_prefix="/social"):
|
||||
actor_data = _serialize_actor(actor)
|
||||
parts = []
|
||||
for ad in actor_dicts:
|
||||
parts.append(await render_to_sx("federation-actor-card-from-data",
|
||||
parts.append(sx_call("federation-actor-card-from-data",
|
||||
a=ad, actor=actor_data,
|
||||
followed_urls=list(followed_urls), list_type="search"))
|
||||
if len(actors_list) >= 20:
|
||||
next_url = url_for("social.search_page", q=query, page=page + 1)
|
||||
parts.append(await render_to_sx("federation-scroll-sentinel", url=next_url))
|
||||
parts.append(sx_call("federation-scroll-sentinel", url=next_url))
|
||||
sx_src = "(<> " + " ".join(parts) + ")" if parts else ""
|
||||
return sx_response(sx_src)
|
||||
|
||||
@@ -166,7 +166,7 @@ def register(url_prefix="/social"):
|
||||
list_type = "followers" if "/followers" in referer else "following"
|
||||
actor_data = _serialize_actor(actor)
|
||||
ad = _serialize_remote_actor(remote_dto)
|
||||
return sx_response(await render_to_sx("federation-actor-card-from-data",
|
||||
return sx_response(sx_call("federation-actor-card-from-data",
|
||||
a=ad, actor=actor_data,
|
||||
followed_urls=list(followed_urls), list_type=list_type))
|
||||
|
||||
@@ -277,19 +277,19 @@ def register(url_prefix="/social"):
|
||||
boost_cls = "hover:text-green-600"
|
||||
|
||||
reply_url = url_for("social.defpage_compose_form", reply_to=object_id) if object_id else ""
|
||||
reply_sx = await render_to_sx("federation-reply-link", url=reply_url) if reply_url else ""
|
||||
reply_sx = sx_call("federation-reply-link", url=reply_url) if reply_url else ""
|
||||
|
||||
like_form = await render_to_sx("federation-like-form",
|
||||
like_form = sx_call("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 = await render_to_sx("federation-boost-form",
|
||||
boost_form = sx_call("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_response(await render_to_sx("federation-interaction-buttons",
|
||||
return sx_response(sx_call("federation-interaction-buttons",
|
||||
like=SxExpr(like_form),
|
||||
boost=SxExpr(boost_form),
|
||||
reply=SxExpr(reply_sx) if reply_sx else None))
|
||||
@@ -309,12 +309,12 @@ def register(url_prefix="/social"):
|
||||
actor_data = _serialize_actor(actor)
|
||||
parts = []
|
||||
for ad in actor_dicts:
|
||||
parts.append(await render_to_sx("federation-actor-card-from-data",
|
||||
parts.append(sx_call("federation-actor-card-from-data",
|
||||
a=ad, actor=actor_data,
|
||||
followed_urls=[], list_type="following"))
|
||||
if len(actors_list) >= 20:
|
||||
next_url = url_for("social.following_list_page", page=page + 1)
|
||||
parts.append(await render_to_sx("federation-scroll-sentinel", url=next_url))
|
||||
parts.append(sx_call("federation-scroll-sentinel", url=next_url))
|
||||
sx_src = "(<> " + " ".join(parts) + ")" if parts else ""
|
||||
return sx_response(sx_src)
|
||||
|
||||
@@ -335,12 +335,12 @@ def register(url_prefix="/social"):
|
||||
actor_data = _serialize_actor(actor)
|
||||
parts = []
|
||||
for ad in actor_dicts:
|
||||
parts.append(await render_to_sx("federation-actor-card-from-data",
|
||||
parts.append(sx_call("federation-actor-card-from-data",
|
||||
a=ad, actor=actor_data,
|
||||
followed_urls=list(followed_urls), list_type="followers"))
|
||||
if len(actors_list) >= 20:
|
||||
next_url = url_for("social.followers_list_page", page=page + 1)
|
||||
parts.append(await render_to_sx("federation-scroll-sentinel", url=next_url))
|
||||
parts.append(sx_call("federation-scroll-sentinel", url=next_url))
|
||||
sx_src = "(<> " + " ".join(parts) + ")" if parts else ""
|
||||
return sx_response(sx_src)
|
||||
|
||||
@@ -357,7 +357,7 @@ def register(url_prefix="/social"):
|
||||
items = await services.federation.get_actor_timeline(
|
||||
g.s, id, before=before,
|
||||
)
|
||||
sx_src = await _render_timeline_items(items, "actor", actor, id)
|
||||
sx_src = _render_timeline_items(items, "actor", actor, id)
|
||||
return sx_response(sx_src)
|
||||
|
||||
# -- Notifications ---------------------------------------------------------
|
||||
@@ -385,7 +385,7 @@ def register(url_prefix="/social"):
|
||||
return bp
|
||||
|
||||
|
||||
async def _render_timeline_items(items, timeline_type, actor, actor_id=None):
|
||||
def _render_timeline_items(items, timeline_type, actor, actor_id=None):
|
||||
"""Render timeline pagination items as SX fragment."""
|
||||
from sxc.pages.utils import _serialize_timeline_item, _serialize_actor
|
||||
|
||||
@@ -401,7 +401,7 @@ async def _render_timeline_items(items, timeline_type, actor, actor_id=None):
|
||||
else:
|
||||
next_url = url_for(f"social.{timeline_type}_timeline_page", before=before)
|
||||
|
||||
return await render_to_sx("federation-timeline-items",
|
||||
return sx_call("federation-timeline-items",
|
||||
items=item_dicts,
|
||||
timeline_type=timeline_type,
|
||||
actor=actor_data,
|
||||
|
||||
@@ -47,7 +47,7 @@ def register() -> Blueprint:
|
||||
markets, has_more, page_info = await _load_markets(page)
|
||||
|
||||
from sxc.pages.renders import render_all_markets_cards
|
||||
sx_src = await render_all_markets_cards(markets, has_more, page_info, page)
|
||||
sx_src = render_all_markets_cards(markets, has_more, page_info, page)
|
||||
return sx_response(sx_src)
|
||||
|
||||
return bp
|
||||
|
||||
@@ -58,7 +58,7 @@ def register():
|
||||
resp = await make_response(html)
|
||||
elif product_info["page"] > 1:
|
||||
tctx.update(product_info)
|
||||
sx_src = await render_browse_cards(tctx)
|
||||
sx_src = render_browse_cards(tctx)
|
||||
resp = sx_response(sx_src)
|
||||
else:
|
||||
sx_src = await render_browse_oob(tctx)
|
||||
@@ -99,7 +99,7 @@ def register():
|
||||
resp = await make_response(html)
|
||||
elif product_info["page"] > 1:
|
||||
tctx.update(product_info)
|
||||
sx_src = await render_browse_cards(tctx)
|
||||
sx_src = render_browse_cards(tctx)
|
||||
resp = sx_response(sx_src)
|
||||
else:
|
||||
sx_src = await render_browse_oob(tctx)
|
||||
@@ -140,7 +140,7 @@ def register():
|
||||
resp = await make_response(html)
|
||||
elif product_info["page"] > 1:
|
||||
tctx.update(product_info)
|
||||
sx_src = await render_browse_cards(tctx)
|
||||
sx_src = render_browse_cards(tctx)
|
||||
resp = sx_response(sx_src)
|
||||
else:
|
||||
sx_src = await render_browse_oob(tctx)
|
||||
|
||||
@@ -32,7 +32,7 @@ def register() -> Blueprint:
|
||||
|
||||
from sxc.pages.renders import render_page_markets_cards
|
||||
post_slug = post.get("slug", "")
|
||||
sx_src = await render_page_markets_cards(markets, has_more, page, post_slug)
|
||||
sx_src = render_page_markets_cards(markets, has_more, page, post_slug)
|
||||
return sx_response(sx_src)
|
||||
|
||||
return bp
|
||||
|
||||
@@ -129,7 +129,7 @@ def register():
|
||||
from sxc.pages.renders import render_like_toggle_button
|
||||
|
||||
if not g.user:
|
||||
return sx_response(await render_like_toggle_button(product_slug, False), status=403)
|
||||
return sx_response(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(await render_like_toggle_button(product_slug, liked))
|
||||
return sx_response(render_like_toggle_button(product_slug, liked))
|
||||
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@ def register():
|
||||
from sxc.pages.renders import render_cart_added_response
|
||||
item_data = getattr(g, "item_data", {})
|
||||
d = item_data.get("d", {})
|
||||
return sx_response(await render_cart_added_response(g.cart, ci_ns, d))
|
||||
return sx_response(render_cart_added_response(g.cart, ci_ns, d))
|
||||
|
||||
# normal POST: go to cart page
|
||||
from shared.infrastructure.urls import cart_url
|
||||
|
||||
@@ -4,7 +4,7 @@ from __future__ import annotations
|
||||
from typing import Any
|
||||
|
||||
from shared.sx.parser import SxExpr
|
||||
from shared.sx.helpers import render_to_sx
|
||||
from shared.sx.helpers import sx_call
|
||||
|
||||
from .utils import _set_prices, _price_str
|
||||
from .filters import _MOBILE_SENTINEL_HS, _DESKTOP_SENTINEL_HS
|
||||
@@ -14,7 +14,7 @@ from .filters import _MOBILE_SENTINEL_HS, _DESKTOP_SENTINEL_HS
|
||||
# Product card (browse grid item)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def _product_card_sx(p: dict, ctx: dict) -> str:
|
||||
def _product_card_sx(p: dict, ctx: dict) -> str:
|
||||
"""Build a single product card for browse grid as sx call."""
|
||||
from quart import url_for
|
||||
from shared.browser.app.csrf import generate_csrf_token
|
||||
@@ -98,10 +98,10 @@ async def _product_card_sx(p: dict, ctx: dict) -> str:
|
||||
kwargs["search_mid"] = search_mid
|
||||
kwargs["search_post"] = search_post
|
||||
|
||||
return await render_to_sx("market-product-card", **kwargs)
|
||||
return sx_call("market-product-card", **kwargs)
|
||||
|
||||
|
||||
async def _product_cards_sx(ctx: dict) -> str:
|
||||
def _product_cards_sx(ctx: dict) -> str:
|
||||
"""S-expression wire format for product cards (client renders)."""
|
||||
from shared.utils import route_prefix
|
||||
|
||||
@@ -114,7 +114,7 @@ async def _product_cards_sx(ctx: dict) -> str:
|
||||
|
||||
parts = []
|
||||
for p in products:
|
||||
parts.append(await _product_card_sx(p, ctx))
|
||||
parts.append(_product_card_sx(p, ctx))
|
||||
|
||||
if page < total_pages:
|
||||
if callable(qs_fn):
|
||||
@@ -122,25 +122,25 @@ async def _product_cards_sx(ctx: dict) -> str:
|
||||
else:
|
||||
next_qs = f"?page={page + 1}"
|
||||
next_url = prefix + current_local_href + next_qs
|
||||
parts.append(await render_to_sx("sentinel-mobile",
|
||||
parts.append(sx_call("sentinel-mobile",
|
||||
id=f"sentinel-{page}-m", next_url=next_url,
|
||||
hyperscript=_MOBILE_SENTINEL_HS))
|
||||
parts.append(await render_to_sx("sentinel-desktop",
|
||||
parts.append(sx_call("sentinel-desktop",
|
||||
id=f"sentinel-{page}-d", next_url=next_url,
|
||||
hyperscript=_DESKTOP_SENTINEL_HS))
|
||||
else:
|
||||
parts.append(await render_to_sx("end-of-results"))
|
||||
parts.append(sx_call("end-of-results"))
|
||||
|
||||
return "(<> " + " ".join(parts) + ")"
|
||||
|
||||
|
||||
async def _like_button_sx(slug: str, liked: bool, csrf: str, ctx: dict) -> str:
|
||||
def _like_button_sx(slug: str, liked: bool, csrf: str, ctx: dict) -> str:
|
||||
"""Build the like/unlike heart button overlay as sx."""
|
||||
from quart import url_for
|
||||
|
||||
action = url_for("market.browse.product.like_toggle", product_slug=slug)
|
||||
icon_cls = "fa-solid fa-heart text-red-500" if liked else "fa-regular fa-heart text-stone-400"
|
||||
return await render_to_sx(
|
||||
return sx_call(
|
||||
"market-like-button",
|
||||
form_id=f"like-{slug}", action=action, slug=slug,
|
||||
csrf=csrf, icon_cls=icon_cls,
|
||||
@@ -151,7 +151,7 @@ async def _like_button_sx(slug: str, liked: bool, csrf: str, ctx: dict) -> str:
|
||||
# Market cards (all markets / page markets)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def _market_card_sx(market: Any, page_info: dict, *, show_page_badge: bool = True,
|
||||
def _market_card_sx(market: Any, page_info: dict, *, show_page_badge: bool = True,
|
||||
post_slug: str = "") -> str:
|
||||
"""Build a single market card as sx."""
|
||||
from shared.infrastructure.urls import market_url
|
||||
@@ -173,20 +173,20 @@ async def _market_card_sx(market: Any, page_info: dict, *, show_page_badge: bool
|
||||
|
||||
title_sx = ""
|
||||
if market_href:
|
||||
title_sx = await render_to_sx("market-market-card-title-link", href=market_href, name=name)
|
||||
title_sx = sx_call("market-market-card-title-link", href=market_href, name=name)
|
||||
else:
|
||||
title_sx = await render_to_sx("market-market-card-title", name=name)
|
||||
title_sx = sx_call("market-market-card-title", name=name)
|
||||
|
||||
desc_sx = ""
|
||||
if description:
|
||||
desc_sx = await render_to_sx("market-market-card-desc", description=description)
|
||||
desc_sx = sx_call("market-market-card-desc", description=description)
|
||||
|
||||
badge_sx = ""
|
||||
if show_page_badge and p_title:
|
||||
badge_href = market_url(f"/{p_slug}/")
|
||||
badge_sx = await render_to_sx("market-market-card-badge", href=badge_href, title=p_title)
|
||||
badge_sx = sx_call("market-market-card-badge", href=badge_href, title=p_title)
|
||||
|
||||
return await render_to_sx(
|
||||
return sx_call(
|
||||
"market-market-card",
|
||||
title_content=SxExpr(title_sx) if title_sx else None,
|
||||
desc_content=SxExpr(desc_sx) if desc_sx else None,
|
||||
@@ -194,30 +194,30 @@ async def _market_card_sx(market: Any, page_info: dict, *, show_page_badge: bool
|
||||
)
|
||||
|
||||
|
||||
async def _market_cards_sx(markets: list, page_info: dict, page: int, has_more: bool,
|
||||
def _market_cards_sx(markets: list, page_info: dict, page: int, has_more: bool,
|
||||
next_url: str, *, show_page_badge: bool = True,
|
||||
post_slug: str = "") -> str:
|
||||
"""Build market cards with infinite scroll sentinel as sx."""
|
||||
parts = []
|
||||
for m in markets:
|
||||
parts.append(await _market_card_sx(m, page_info, show_page_badge=show_page_badge,
|
||||
parts.append(_market_card_sx(m, page_info, show_page_badge=show_page_badge,
|
||||
post_slug=post_slug))
|
||||
if has_more:
|
||||
parts.append(await render_to_sx(
|
||||
parts.append(sx_call(
|
||||
"sentinel-simple",
|
||||
id=f"sentinel-{page}", next_url=next_url,
|
||||
))
|
||||
return "(<> " + " ".join(parts) + ")"
|
||||
|
||||
|
||||
async def _markets_grid(cards_sx: str) -> str:
|
||||
def _markets_grid(cards_sx: str) -> str:
|
||||
"""Wrap market cards in a grid as sx."""
|
||||
return await render_to_sx("market-markets-grid", cards=SxExpr(cards_sx))
|
||||
return sx_call("market-markets-grid", cards=SxExpr(cards_sx))
|
||||
|
||||
|
||||
async def _no_markets_sx(message: str = "No markets available") -> str:
|
||||
def _no_markets_sx(message: str = "No markets available") -> str:
|
||||
"""Empty state for markets as sx."""
|
||||
return await render_to_sx("empty-state", icon="fa fa-store", message=message,
|
||||
return sx_call("empty-state", icon="fa fa-store", message=message,
|
||||
cls="px-3 py-12 text-center text-stone-400")
|
||||
|
||||
|
||||
@@ -225,14 +225,14 @@ async def _no_markets_sx(message: str = "No markets available") -> str:
|
||||
# Market landing page
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def _market_landing_content_sx(post: dict) -> str:
|
||||
def _market_landing_content_sx(post: dict) -> str:
|
||||
"""Build market landing page content as sx."""
|
||||
parts: list[str] = []
|
||||
if post.get("custom_excerpt"):
|
||||
parts.append(await render_to_sx("market-landing-excerpt", text=post["custom_excerpt"]))
|
||||
parts.append(sx_call("market-landing-excerpt", text=post["custom_excerpt"]))
|
||||
if post.get("feature_image"):
|
||||
parts.append(await render_to_sx("market-landing-image", src=post["feature_image"]))
|
||||
parts.append(sx_call("market-landing-image", src=post["feature_image"]))
|
||||
if post.get("html"):
|
||||
parts.append(await render_to_sx("market-landing-html", html=post["html"]))
|
||||
parts.append(sx_call("market-landing-html", html=post["html"]))
|
||||
inner = "(<> " + " ".join(parts) + ")" if parts else "(<>)"
|
||||
return await render_to_sx("market-landing-content", inner=SxExpr(inner))
|
||||
return sx_call("market-landing-content", inner=SxExpr(inner))
|
||||
|
||||
@@ -3,7 +3,7 @@ from __future__ import annotations
|
||||
|
||||
from shared.sx.parser import SxExpr
|
||||
from shared.sx.helpers import (
|
||||
render_to_sx,
|
||||
sx_call,
|
||||
search_mobile_sx, search_desktop_sx,
|
||||
)
|
||||
|
||||
@@ -101,31 +101,31 @@ async def _desktop_filter_sx(ctx: dict) -> str:
|
||||
search_sx = await search_desktop_sx(ctx)
|
||||
|
||||
# Category summary + sort + like + labels + stickers
|
||||
cat_parts = [await render_to_sx("market-filter-category-label", label=category_label)]
|
||||
cat_parts = [sx_call("market-filter-category-label", label=category_label)]
|
||||
|
||||
if sort_options:
|
||||
cat_parts.append(await _sort_stickers_sx(sort_options, sort, ctx))
|
||||
cat_parts.append(_sort_stickers_sx(sort_options, sort, ctx))
|
||||
|
||||
like_label_parts = [await _like_filter_sx(liked, liked_count, ctx)]
|
||||
like_label_parts = [_like_filter_sx(liked, liked_count, ctx)]
|
||||
if labels:
|
||||
like_label_parts.append(await _labels_filter_sx(labels, selected_labels, ctx, prefix="nav-labels"))
|
||||
like_label_parts.append(_labels_filter_sx(labels, selected_labels, ctx, prefix="nav-labels"))
|
||||
like_labels_sx = "(<> " + " ".join(like_label_parts) + ")"
|
||||
cat_parts.append(await render_to_sx("market-filter-like-labels-nav", inner=SxExpr(like_labels_sx)))
|
||||
cat_parts.append(sx_call("market-filter-like-labels-nav", inner=SxExpr(like_labels_sx)))
|
||||
|
||||
if stickers:
|
||||
cat_parts.append(await _stickers_filter_sx(stickers, selected_stickers, ctx))
|
||||
cat_parts.append(_stickers_filter_sx(stickers, selected_stickers, ctx))
|
||||
|
||||
if subs_local and top_local_href:
|
||||
cat_parts.append(await _subcategory_selector_sx(subs_local, top_local_href, sub_slug, ctx))
|
||||
cat_parts.append(_subcategory_selector_sx(subs_local, top_local_href, sub_slug, ctx))
|
||||
|
||||
cat_inner_sx = "(<> " + " ".join(cat_parts) + ")"
|
||||
cat_summary = await render_to_sx("market-desktop-category-summary", inner=SxExpr(cat_inner_sx))
|
||||
cat_summary = sx_call("market-desktop-category-summary", inner=SxExpr(cat_inner_sx))
|
||||
|
||||
# Brand filter
|
||||
brand_inner = ""
|
||||
if brands:
|
||||
brand_inner = await _brand_filter_sx(brands, selected_brands, ctx)
|
||||
brand_summary = await render_to_sx("market-desktop-brand-summary",
|
||||
brand_inner = _brand_filter_sx(brands, selected_brands, ctx)
|
||||
brand_summary = sx_call("market-desktop-brand-summary",
|
||||
inner=SxExpr(brand_inner) if brand_inner else None)
|
||||
|
||||
return "(<> " + " ".join([search_sx, cat_summary, brand_summary]) + ")"
|
||||
@@ -154,14 +154,14 @@ async def _mobile_filter_summary_sx(ctx: dict) -> str:
|
||||
if sort and sort_options:
|
||||
for k, l, i in sort_options:
|
||||
if k == sort and callable(asset_url_fn):
|
||||
chip_parts.append(await render_to_sx("market-mobile-chip-sort", src=asset_url_fn(i), label=l))
|
||||
chip_parts.append(sx_call("market-mobile-chip-sort", src=asset_url_fn(i), label=l))
|
||||
if liked:
|
||||
liked_parts = [await render_to_sx("market-mobile-chip-liked-icon")]
|
||||
liked_parts = [sx_call("market-mobile-chip-liked-icon")]
|
||||
if liked_count is not None:
|
||||
cls = "text-[10px] text-stone-500" if liked_count != 0 else "text-md text-red-500 font-bold"
|
||||
liked_parts.append(await render_to_sx("market-mobile-chip-count", cls=cls, count=str(liked_count)))
|
||||
liked_parts.append(sx_call("market-mobile-chip-count", cls=cls, count=str(liked_count)))
|
||||
liked_inner = "(<> " + " ".join(liked_parts) + ")"
|
||||
chip_parts.append(await render_to_sx("market-mobile-chip-liked", inner=SxExpr(liked_inner)))
|
||||
chip_parts.append(sx_call("market-mobile-chip-liked", inner=SxExpr(liked_inner)))
|
||||
|
||||
# Selected labels
|
||||
if selected_labels:
|
||||
@@ -169,18 +169,18 @@ async def _mobile_filter_summary_sx(ctx: dict) -> str:
|
||||
for sl in selected_labels:
|
||||
for lb in labels:
|
||||
if lb.get("name") == sl and callable(asset_url_fn):
|
||||
li_parts = [await render_to_sx(
|
||||
li_parts = [sx_call(
|
||||
"market-mobile-chip-image",
|
||||
src=asset_url_fn("nav-labels/" + sl + ".svg"), name=sl,
|
||||
)]
|
||||
if lb.get("count") is not None:
|
||||
cls = "text-[10px] text-stone-500" if lb["count"] != 0 else "text-md text-red-500 font-bold"
|
||||
li_parts.append(await render_to_sx("market-mobile-chip-count", cls=cls, count=str(lb["count"])))
|
||||
li_parts.append(sx_call("market-mobile-chip-count", cls=cls, count=str(lb["count"])))
|
||||
li_inner = "(<> " + " ".join(li_parts) + ")"
|
||||
label_item_parts.append(await render_to_sx("market-mobile-chip-item", inner=SxExpr(li_inner)))
|
||||
label_item_parts.append(sx_call("market-mobile-chip-item", inner=SxExpr(li_inner)))
|
||||
if label_item_parts:
|
||||
label_items = "(<> " + " ".join(label_item_parts) + ")"
|
||||
chip_parts.append(await render_to_sx("market-mobile-chip-list", items=SxExpr(label_items)))
|
||||
chip_parts.append(sx_call("market-mobile-chip-list", items=SxExpr(label_items)))
|
||||
|
||||
# Selected stickers
|
||||
if selected_stickers:
|
||||
@@ -188,18 +188,18 @@ async def _mobile_filter_summary_sx(ctx: dict) -> str:
|
||||
for ss in selected_stickers:
|
||||
for st in stickers:
|
||||
if st.get("name") == ss and callable(asset_url_fn):
|
||||
si_parts = [await render_to_sx(
|
||||
si_parts = [sx_call(
|
||||
"market-mobile-chip-image",
|
||||
src=asset_url_fn("stickers/" + ss + ".svg"), name=ss,
|
||||
)]
|
||||
if st.get("count") is not None:
|
||||
cls = "text-[10px] text-stone-500" if st["count"] != 0 else "text-md text-red-500 font-bold"
|
||||
si_parts.append(await render_to_sx("market-mobile-chip-count", cls=cls, count=str(st["count"])))
|
||||
si_parts.append(sx_call("market-mobile-chip-count", cls=cls, count=str(st["count"])))
|
||||
si_inner = "(<> " + " ".join(si_parts) + ")"
|
||||
sticker_item_parts.append(await render_to_sx("market-mobile-chip-item", inner=SxExpr(si_inner)))
|
||||
sticker_item_parts.append(sx_call("market-mobile-chip-item", inner=SxExpr(si_inner)))
|
||||
if sticker_item_parts:
|
||||
sticker_items = "(<> " + " ".join(sticker_item_parts) + ")"
|
||||
chip_parts.append(await render_to_sx("market-mobile-chip-list", items=SxExpr(sticker_items)))
|
||||
chip_parts.append(sx_call("market-mobile-chip-list", items=SxExpr(sticker_items)))
|
||||
|
||||
# Selected brands
|
||||
if selected_brands:
|
||||
@@ -210,21 +210,21 @@ async def _mobile_filter_summary_sx(ctx: dict) -> str:
|
||||
if br.get("name") == b:
|
||||
count = br.get("count", 0)
|
||||
if count:
|
||||
brand_item_parts.append(await render_to_sx("market-mobile-chip-brand", name=b, count=str(count)))
|
||||
brand_item_parts.append(sx_call("market-mobile-chip-brand", name=b, count=str(count)))
|
||||
else:
|
||||
brand_item_parts.append(await render_to_sx("market-mobile-chip-brand-zero", name=b))
|
||||
brand_item_parts.append(sx_call("market-mobile-chip-brand-zero", name=b))
|
||||
brand_items = "(<> " + " ".join(brand_item_parts) + ")"
|
||||
chip_parts.append(await render_to_sx("market-mobile-chip-brand-list", items=SxExpr(brand_items)))
|
||||
chip_parts.append(sx_call("market-mobile-chip-brand-list", items=SxExpr(brand_items)))
|
||||
|
||||
chips_sx = "(<> " + " ".join(chip_parts) + ")" if chip_parts else '(<>)'
|
||||
chips_row = await render_to_sx("market-mobile-chips-row", inner=SxExpr(chips_sx))
|
||||
chips_row = sx_call("market-mobile-chips-row", inner=SxExpr(chips_sx))
|
||||
|
||||
# Full mobile filter details
|
||||
from shared.utils import route_prefix
|
||||
prefix = route_prefix()
|
||||
mobile_filter = await _mobile_filter_content_sx(ctx, prefix)
|
||||
mobile_filter = _mobile_filter_content_sx(ctx, prefix)
|
||||
|
||||
return await render_to_sx(
|
||||
return sx_call(
|
||||
"market-mobile-filter-summary",
|
||||
search_bar=SxExpr(search_bar),
|
||||
chips=SxExpr(chips_row),
|
||||
@@ -232,7 +232,7 @@ async def _mobile_filter_summary_sx(ctx: dict) -> str:
|
||||
)
|
||||
|
||||
|
||||
async def _mobile_filter_content_sx(ctx: dict, prefix: str) -> str:
|
||||
def _mobile_filter_content_sx(ctx: dict, prefix: str) -> str:
|
||||
"""Build the expanded mobile filter panel contents as sx."""
|
||||
selected_labels = ctx.get("selected_labels", [])
|
||||
selected_stickers = ctx.get("selected_stickers", [])
|
||||
@@ -253,33 +253,33 @@ async def _mobile_filter_content_sx(ctx: dict, prefix: str) -> str:
|
||||
|
||||
# Sort options
|
||||
if sort_options:
|
||||
parts.append(await _sort_stickers_sx(sort_options, sort, ctx, mobile=True))
|
||||
parts.append(_sort_stickers_sx(sort_options, sort, ctx, mobile=True))
|
||||
|
||||
# Clear filters button
|
||||
has_filters = search or selected_labels or selected_stickers or selected_brands
|
||||
if has_filters and callable(qs_fn):
|
||||
clear_url = prefix + current_local_href + qs_fn({"clear_filters": True})
|
||||
parts.append(await render_to_sx("market-mobile-clear-filters", href=clear_url, hx_select=hx_select))
|
||||
parts.append(sx_call("market-mobile-clear-filters", href=clear_url, hx_select=hx_select))
|
||||
|
||||
# Like + labels row
|
||||
like_label_parts = [await _like_filter_sx(liked, liked_count, ctx, mobile=True)]
|
||||
like_label_parts = [_like_filter_sx(liked, liked_count, ctx, mobile=True)]
|
||||
if labels:
|
||||
like_label_parts.append(await _labels_filter_sx(labels, selected_labels, ctx, prefix="nav-labels", mobile=True))
|
||||
like_label_parts.append(_labels_filter_sx(labels, selected_labels, ctx, prefix="nav-labels", mobile=True))
|
||||
like_labels_sx = "(<> " + " ".join(like_label_parts) + ")"
|
||||
parts.append(await render_to_sx("market-mobile-like-labels-row", inner=SxExpr(like_labels_sx)))
|
||||
parts.append(sx_call("market-mobile-like-labels-row", inner=SxExpr(like_labels_sx)))
|
||||
|
||||
# Stickers
|
||||
if stickers:
|
||||
parts.append(await _stickers_filter_sx(stickers, selected_stickers, ctx, mobile=True))
|
||||
parts.append(_stickers_filter_sx(stickers, selected_stickers, ctx, mobile=True))
|
||||
|
||||
# Brands
|
||||
if brands:
|
||||
parts.append(await _brand_filter_sx(brands, selected_brands, ctx, mobile=True))
|
||||
parts.append(_brand_filter_sx(brands, selected_brands, ctx, mobile=True))
|
||||
|
||||
return "(<> " + " ".join(parts) + ")" if parts else "(<>)"
|
||||
|
||||
|
||||
async def _sort_stickers_sx(sort_options: list, current_sort: str, ctx: dict, mobile: bool = False) -> str:
|
||||
def _sort_stickers_sx(sort_options: list, current_sort: str, ctx: dict, mobile: bool = False) -> str:
|
||||
"""Build sort option stickers as sx."""
|
||||
asset_url_fn = ctx.get("asset_url")
|
||||
current_local_href = ctx.get("current_local_href", "/")
|
||||
@@ -297,15 +297,15 @@ async def _sort_stickers_sx(sort_options: list, current_sort: str, ctx: dict, mo
|
||||
active = (k == current_sort)
|
||||
ring = " ring-2 ring-emerald-500 rounded" if active else ""
|
||||
src = asset_url_fn(icon) if callable(asset_url_fn) else icon
|
||||
item_parts.append(await render_to_sx(
|
||||
item_parts.append(sx_call(
|
||||
"market-filter-sort-item",
|
||||
href=href, hx_select=hx_select, ring_cls=ring, src=src, label=label,
|
||||
))
|
||||
items_sx = "(<> " + " ".join(item_parts) + ")" if item_parts else "(<>)"
|
||||
return await render_to_sx("market-filter-sort-row", items=SxExpr(items_sx))
|
||||
return sx_call("market-filter-sort-row", items=SxExpr(items_sx))
|
||||
|
||||
|
||||
async def _like_filter_sx(liked: bool, liked_count: int, ctx: dict, mobile: bool = False) -> str:
|
||||
def _like_filter_sx(liked: bool, liked_count: int, ctx: dict, mobile: bool = False) -> str:
|
||||
"""Build the like filter toggle as sx."""
|
||||
current_local_href = ctx.get("current_local_href", "/")
|
||||
hx_select = ctx.get("hx_select_search", "#main-panel")
|
||||
@@ -320,13 +320,13 @@ async def _like_filter_sx(liked: bool, liked_count: int, ctx: dict, mobile: bool
|
||||
|
||||
icon_cls = "fa-solid fa-heart text-red-500" if liked else "fa-regular fa-heart text-stone-400"
|
||||
size = "text-[40px]" if mobile else "text-2xl"
|
||||
return await render_to_sx(
|
||||
return sx_call(
|
||||
"market-filter-like",
|
||||
href=href, hx_select=hx_select, icon_cls=icon_cls, size_cls=size,
|
||||
)
|
||||
|
||||
|
||||
async def _labels_filter_sx(labels: list, selected: list, ctx: dict, *,
|
||||
def _labels_filter_sx(labels: list, selected: list, ctx: dict, *,
|
||||
prefix: str = "nav-labels", mobile: bool = False) -> str:
|
||||
"""Build label filter buttons as sx."""
|
||||
asset_url_fn = ctx.get("asset_url")
|
||||
@@ -347,14 +347,14 @@ async def _labels_filter_sx(labels: list, selected: list, ctx: dict, *,
|
||||
href = "#"
|
||||
ring = " ring-2 ring-emerald-500 rounded" if is_sel else ""
|
||||
src = asset_url_fn(f"{prefix}/{name}.svg") if callable(asset_url_fn) else ""
|
||||
item_parts.append(await render_to_sx(
|
||||
item_parts.append(sx_call(
|
||||
"market-filter-label-item",
|
||||
href=href, hx_select=hx_select, ring_cls=ring, src=src, name=name,
|
||||
))
|
||||
return "(<> " + " ".join(item_parts) + ")" if item_parts else "(<>)"
|
||||
|
||||
|
||||
async def _stickers_filter_sx(stickers: list, selected: list, ctx: dict, mobile: bool = False) -> str:
|
||||
def _stickers_filter_sx(stickers: list, selected: list, ctx: dict, mobile: bool = False) -> str:
|
||||
"""Build sticker filter grid as sx."""
|
||||
asset_url_fn = ctx.get("asset_url")
|
||||
current_local_href = ctx.get("current_local_href", "/")
|
||||
@@ -376,16 +376,16 @@ async def _stickers_filter_sx(stickers: list, selected: list, ctx: dict, mobile:
|
||||
ring = " ring-2 ring-emerald-500 rounded" if is_sel else ""
|
||||
src = asset_url_fn(f"stickers/{name}.svg") if callable(asset_url_fn) else ""
|
||||
cls = "text-[10px] text-stone-500" if count != 0 else "text-md text-red-500 font-bold"
|
||||
item_parts.append(await render_to_sx(
|
||||
item_parts.append(sx_call(
|
||||
"market-filter-sticker-item",
|
||||
href=href, hx_select=hx_select, ring_cls=ring,
|
||||
src=src, name=name, count_cls=cls, count=str(count),
|
||||
))
|
||||
items_sx = "(<> " + " ".join(item_parts) + ")" if item_parts else "(<>)"
|
||||
return await render_to_sx("market-filter-stickers-row", items=SxExpr(items_sx))
|
||||
return sx_call("market-filter-stickers-row", items=SxExpr(items_sx))
|
||||
|
||||
|
||||
async def _brand_filter_sx(brands: list, selected: list, ctx: dict, mobile: bool = False) -> str:
|
||||
def _brand_filter_sx(brands: list, selected: list, ctx: dict, mobile: bool = False) -> str:
|
||||
"""Build brand filter checkboxes as sx."""
|
||||
current_local_href = ctx.get("current_local_href", "/")
|
||||
hx_select = ctx.get("hx_select_search", "#main-panel")
|
||||
@@ -405,16 +405,16 @@ async def _brand_filter_sx(brands: list, selected: list, ctx: dict, mobile: bool
|
||||
href = "#"
|
||||
bg = " bg-yellow-200" if is_sel else ""
|
||||
cls = "text-md" if count else "text-md text-red-500"
|
||||
item_parts.append(await render_to_sx(
|
||||
item_parts.append(sx_call(
|
||||
"market-filter-brand-item",
|
||||
href=href, hx_select=hx_select, bg_cls=bg,
|
||||
name_cls=cls, name=name, count=str(count),
|
||||
))
|
||||
items_sx = "(<> " + " ".join(item_parts) + ")" if item_parts else "(<>)"
|
||||
return await render_to_sx("market-filter-brands-panel", items=SxExpr(items_sx))
|
||||
return sx_call("market-filter-brands-panel", items=SxExpr(items_sx))
|
||||
|
||||
|
||||
async def _subcategory_selector_sx(subs: list, top_href: str, current_sub: str, ctx: dict) -> str:
|
||||
def _subcategory_selector_sx(subs: list, top_href: str, current_sub: str, ctx: dict) -> str:
|
||||
"""Build subcategory vertical nav as sx."""
|
||||
hx_select = ctx.get("hx_select_search", "#main-panel")
|
||||
from shared.utils import route_prefix
|
||||
@@ -422,7 +422,7 @@ async def _subcategory_selector_sx(subs: list, top_href: str, current_sub: str,
|
||||
|
||||
all_cls = " bg-stone-200 font-medium" if not current_sub else ""
|
||||
all_full_href = rp + top_href
|
||||
item_parts = [await render_to_sx(
|
||||
item_parts = [sx_call(
|
||||
"market-filter-subcategory-item",
|
||||
href=all_full_href, hx_select=hx_select, active_cls=all_cls, name="All",
|
||||
)]
|
||||
@@ -433,9 +433,9 @@ async def _subcategory_selector_sx(subs: list, top_href: str, current_sub: str,
|
||||
active = (slug == current_sub)
|
||||
active_cls = " bg-stone-200 font-medium" if active else ""
|
||||
full_href = rp + href
|
||||
item_parts.append(await render_to_sx(
|
||||
item_parts.append(sx_call(
|
||||
"market-filter-subcategory-item",
|
||||
href=full_href, hx_select=hx_select, active_cls=active_cls, name=name,
|
||||
))
|
||||
items_sx = "(<> " + " ".join(item_parts) + ")"
|
||||
return await render_to_sx("market-filter-subcategory-panel", items=SxExpr(items_sx))
|
||||
return sx_call("market-filter-subcategory-panel", items=SxExpr(items_sx))
|
||||
|
||||
@@ -15,7 +15,7 @@ async def _markets_admin_panel_sx(ctx: dict) -> str:
|
||||
"""Render the markets list + create form panel."""
|
||||
from quart import g, url_for
|
||||
from shared.services.registry import services
|
||||
from shared.sx.helpers import render_to_sx
|
||||
from shared.sx.helpers import sx_call
|
||||
from shared.sx.parser import SxExpr
|
||||
|
||||
rights = ctx.get("rights") or {}
|
||||
@@ -32,30 +32,30 @@ async def _markets_admin_panel_sx(ctx: dict) -> str:
|
||||
form_html = ""
|
||||
if can_create:
|
||||
create_url = url_for("page_admin.create_market")
|
||||
form_html = await render_to_sx("crud-create-form",
|
||||
form_html = sx_call("crud-create-form",
|
||||
create_url=create_url, csrf=csrf,
|
||||
errors_id="market-create-errors",
|
||||
list_id="markets-list",
|
||||
placeholder="e.g. Suma, Craft Fair",
|
||||
btn_label="Add market")
|
||||
|
||||
list_html = await _markets_admin_list_sx(ctx, markets)
|
||||
return await render_to_sx("crud-panel",
|
||||
list_html = _markets_admin_list_sx(ctx, markets)
|
||||
return sx_call("crud-panel",
|
||||
form=SxExpr(form_html), list=SxExpr(list_html),
|
||||
list_id="markets-list")
|
||||
|
||||
|
||||
async def _markets_admin_list_sx(ctx: dict, markets: list) -> str:
|
||||
def _markets_admin_list_sx(ctx: dict, markets: list) -> str:
|
||||
"""Render the markets list items."""
|
||||
from quart import url_for
|
||||
from shared.utils import route_prefix
|
||||
from shared.sx.helpers import render_to_sx
|
||||
from shared.sx.helpers import sx_call
|
||||
csrf_token = ctx.get("csrf_token")
|
||||
csrf = csrf_token() if callable(csrf_token) else (csrf_token or "")
|
||||
prefix = route_prefix()
|
||||
|
||||
if not markets:
|
||||
return await render_to_sx("empty-state",
|
||||
return sx_call("empty-state",
|
||||
message="No markets yet. Create one above.",
|
||||
cls="text-gray-500 mt-4")
|
||||
|
||||
@@ -67,7 +67,7 @@ async def _markets_admin_list_sx(ctx: dict, markets: list) -> str:
|
||||
href = prefix + f"/{post_slug}/{m_slug}/"
|
||||
del_url = url_for("page_admin.delete_market", market_slug=m_slug)
|
||||
csrf_hdr = f'{{"X-CSRFToken":"{csrf}"}}'
|
||||
parts.append(await render_to_sx("crud-item",
|
||||
parts.append(sx_call("crud-item",
|
||||
href=href, name=m_name, slug=m_slug,
|
||||
del_url=del_url, csrf_hdr=csrf_hdr,
|
||||
list_id="markets-list",
|
||||
@@ -116,13 +116,13 @@ async def _h_all_markets_content(**kw):
|
||||
page_info[p.id] = {"title": p.title, "slug": p.slug}
|
||||
|
||||
if not markets:
|
||||
return await _no_markets_sx()
|
||||
return _no_markets_sx()
|
||||
|
||||
prefix = route_prefix()
|
||||
next_url = prefix + url_for("all_markets.markets_fragment", page=page + 1)
|
||||
|
||||
cards = await _market_cards_sx(markets, page_info, page, has_more, next_url)
|
||||
return await _markets_grid(cards)
|
||||
cards = _market_cards_sx(markets, page_info, page, has_more, next_url)
|
||||
return _markets_grid(cards)
|
||||
|
||||
|
||||
async def _h_page_markets_content(slug=None, **kw):
|
||||
@@ -138,30 +138,30 @@ async def _h_page_markets_content(slug=None, **kw):
|
||||
post_slug = post.get("slug", "")
|
||||
|
||||
if not markets:
|
||||
return await _no_markets_sx("No markets for this page")
|
||||
return _no_markets_sx("No markets for this page")
|
||||
|
||||
prefix = route_prefix()
|
||||
next_url = prefix + url_for("page_markets.markets_fragment", page=page + 1)
|
||||
|
||||
cards = await _market_cards_sx(markets, {}, page, has_more, next_url,
|
||||
cards = _market_cards_sx(markets, {}, page, has_more, next_url,
|
||||
show_page_badge=False, post_slug=post_slug)
|
||||
return await _markets_grid(cards)
|
||||
return _markets_grid(cards)
|
||||
|
||||
|
||||
async def _h_page_admin_content(slug=None, **kw):
|
||||
from shared.sx.page import get_template_context
|
||||
from shared.sx.helpers import render_to_sx
|
||||
from shared.sx.helpers import sx_call
|
||||
from shared.sx.parser import SxExpr
|
||||
ctx = await get_template_context()
|
||||
content = await _markets_admin_panel_sx(ctx)
|
||||
return await render_to_sx("market-admin-content-wrap", inner=SxExpr(content))
|
||||
return sx_call("market-admin-content-wrap", inner=SxExpr(content))
|
||||
|
||||
|
||||
async def _h_market_home_content(page_slug=None, market_slug=None, **kw):
|
||||
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", {})
|
||||
return await _market_landing_content_sx(post)
|
||||
return _market_landing_content_sx(post)
|
||||
|
||||
|
||||
def _h_market_admin_content(page_slug=None, market_slug=None, **kw):
|
||||
|
||||
@@ -5,7 +5,7 @@ from typing import Any
|
||||
|
||||
from shared.sx.parser import SxExpr
|
||||
from shared.sx.helpers import (
|
||||
render_to_sx,
|
||||
sx_call,
|
||||
post_header_sx as _post_header_sx,
|
||||
post_admin_header_sx,
|
||||
oob_header_sx as _oob_header_sx,
|
||||
@@ -19,7 +19,7 @@ from .utils import _set_prices, _price_str, _clear_deeper_oob
|
||||
# Header helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def _market_header_sx(ctx: dict, *, oob: bool = False) -> str:
|
||||
def _market_header_sx(ctx: dict, *, oob: bool = False) -> str:
|
||||
"""Build the market-level header row as sx call string."""
|
||||
from quart import url_for
|
||||
|
||||
@@ -28,7 +28,7 @@ async def _market_header_sx(ctx: dict, *, oob: bool = False) -> str:
|
||||
sub_slug = ctx.get("sub_slug", "")
|
||||
hx_select_search = ctx.get("hx_select_search", "#main-panel")
|
||||
|
||||
label_sx = await render_to_sx(
|
||||
label_sx = sx_call(
|
||||
"market-shop-label",
|
||||
title=market_title, top_slug=top_slug or "",
|
||||
sub_div=sub_slug or None,
|
||||
@@ -39,9 +39,9 @@ async def _market_header_sx(ctx: dict, *, oob: bool = False) -> str:
|
||||
# Build desktop nav from categories
|
||||
categories = ctx.get("categories", {})
|
||||
qs = ctx.get("qs", "")
|
||||
nav_sx = await _desktop_category_nav_sx(ctx, categories, qs, hx_select_search)
|
||||
nav_sx = _desktop_category_nav_sx(ctx, categories, qs, hx_select_search)
|
||||
|
||||
return await render_to_sx(
|
||||
return sx_call(
|
||||
"menu-row-sx",
|
||||
id="market-row", level=2,
|
||||
link_href=link_href, link_label_content=SxExpr(label_sx),
|
||||
@@ -50,7 +50,7 @@ async def _market_header_sx(ctx: dict, *, oob: bool = False) -> str:
|
||||
)
|
||||
|
||||
|
||||
async def _desktop_category_nav_sx(ctx: dict, categories: dict, qs: str,
|
||||
def _desktop_category_nav_sx(ctx: dict, categories: dict, qs: str,
|
||||
hx_select: str) -> str:
|
||||
"""Build desktop category navigation links as sx."""
|
||||
from quart import url_for
|
||||
@@ -63,7 +63,7 @@ async def _desktop_category_nav_sx(ctx: dict, categories: dict, qs: str,
|
||||
|
||||
all_href = prefix + url_for("market.browse.browse_all") + qs
|
||||
all_active = (category_label == "All Products")
|
||||
link_parts = [await render_to_sx(
|
||||
link_parts = [sx_call(
|
||||
"market-category-link",
|
||||
href=all_href, hx_select=hx_select, active=all_active,
|
||||
select_colours=select_colours, label="All",
|
||||
@@ -72,7 +72,7 @@ async def _desktop_category_nav_sx(ctx: dict, categories: dict, qs: str,
|
||||
for cat, data in categories.items():
|
||||
cat_href = prefix + url_for("market.browse.browse_top", top_slug=data["slug"]) + qs
|
||||
cat_active = (cat == category_label)
|
||||
link_parts.append(await render_to_sx(
|
||||
link_parts.append(sx_call(
|
||||
"market-category-link",
|
||||
href=cat_href, hx_select=hx_select, active=cat_active,
|
||||
select_colours=select_colours, label=cat,
|
||||
@@ -83,14 +83,14 @@ async def _desktop_category_nav_sx(ctx: dict, categories: dict, qs: str,
|
||||
admin_sx = ""
|
||||
if rights and rights.get("admin"):
|
||||
admin_href = prefix + url_for("defpage_market_admin")
|
||||
admin_sx = await render_to_sx("market-admin-link", href=admin_href, hx_select=hx_select)
|
||||
admin_sx = sx_call("market-admin-link", href=admin_href, hx_select=hx_select)
|
||||
|
||||
return await render_to_sx("market-desktop-category-nav",
|
||||
return sx_call("market-desktop-category-nav",
|
||||
links=SxExpr(links_sx),
|
||||
admin=SxExpr(admin_sx) if admin_sx else None)
|
||||
|
||||
|
||||
async def _product_header_sx(ctx: dict, d: dict, *, oob: bool = False) -> str:
|
||||
def _product_header_sx(ctx: dict, d: dict, *, oob: bool = False) -> str:
|
||||
"""Build the product-level header row as sx call string."""
|
||||
from quart import url_for
|
||||
from shared.browser.app.csrf import generate_csrf_token
|
||||
@@ -100,21 +100,21 @@ async def _product_header_sx(ctx: dict, d: dict, *, oob: bool = False) -> str:
|
||||
hx_select_search = ctx.get("hx_select_search", "#main-panel")
|
||||
link_href = url_for("market.browse.product.product_detail", product_slug=slug)
|
||||
|
||||
label_sx = await render_to_sx("market-product-label", title=title)
|
||||
label_sx = sx_call("market-product-label", title=title)
|
||||
|
||||
# Prices in nav area
|
||||
pr = _set_prices(d)
|
||||
cart = ctx.get("cart", [])
|
||||
prices_nav = await _prices_header_sx(d, pr, cart, slug, ctx)
|
||||
prices_nav = _prices_header_sx(d, pr, cart, slug, ctx)
|
||||
|
||||
rights = ctx.get("rights", {})
|
||||
nav_parts = [prices_nav]
|
||||
if rights and rights.get("admin"):
|
||||
admin_href = url_for("market.browse.product.admin", product_slug=slug)
|
||||
nav_parts.append(await render_to_sx("market-admin-link", href=admin_href, hx_select=hx_select_search))
|
||||
nav_parts.append(sx_call("market-admin-link", href=admin_href, hx_select=hx_select_search))
|
||||
nav_sx = "(<> " + " ".join(nav_parts) + ")"
|
||||
|
||||
return await render_to_sx(
|
||||
return sx_call(
|
||||
"menu-row-sx",
|
||||
id="product-row", level=3,
|
||||
link_href=link_href, link_label_content=SxExpr(label_sx),
|
||||
@@ -122,7 +122,7 @@ async def _product_header_sx(ctx: dict, d: dict, *, oob: bool = False) -> str:
|
||||
)
|
||||
|
||||
|
||||
async def _prices_header_sx(d: dict, pr: dict, cart: list, slug: str, ctx: dict) -> str:
|
||||
def _prices_header_sx(d: dict, pr: dict, cart: list, slug: str, ctx: dict) -> str:
|
||||
"""Build prices + add-to-cart for product header row as sx."""
|
||||
from quart import url_for
|
||||
from shared.browser.app.csrf import generate_csrf_token
|
||||
@@ -133,20 +133,20 @@ async def _prices_header_sx(d: dict, pr: dict, cart: list, slug: str, ctx: dict)
|
||||
|
||||
# Add-to-cart button
|
||||
quantity = sum(ci.quantity for ci in cart if ci.product.slug == slug) if cart else 0
|
||||
add_sx = await _cart_add_sx(slug, quantity, cart_action, csrf, cart_url_fn)
|
||||
add_sx = _cart_add_sx(slug, quantity, cart_action, csrf, cart_url_fn)
|
||||
|
||||
parts = [add_sx]
|
||||
sp_val, rp_val = pr.get("sp_val"), pr.get("rp_val")
|
||||
if sp_val:
|
||||
parts.append(await render_to_sx("market-header-price-special-label"))
|
||||
parts.append(await render_to_sx("market-header-price-special",
|
||||
parts.append(sx_call("market-header-price-special-label"))
|
||||
parts.append(sx_call("market-header-price-special",
|
||||
price=_price_str(sp_val, pr["sp_raw"], pr["sp_cur"])))
|
||||
if rp_val:
|
||||
parts.append(await render_to_sx("market-header-price-strike",
|
||||
parts.append(sx_call("market-header-price-strike",
|
||||
price=_price_str(rp_val, pr["rp_raw"], pr["rp_cur"])))
|
||||
elif rp_val:
|
||||
parts.append(await render_to_sx("market-header-price-regular-label"))
|
||||
parts.append(await render_to_sx("market-header-price-regular",
|
||||
parts.append(sx_call("market-header-price-regular-label"))
|
||||
parts.append(sx_call("market-header-price-regular",
|
||||
price=_price_str(rp_val, pr["rp_raw"], pr["rp_cur"])))
|
||||
|
||||
# RRP
|
||||
@@ -155,23 +155,23 @@ async def _prices_header_sx(d: dict, pr: dict, cart: list, slug: str, ctx: dict)
|
||||
case_size = d.get("case_size_count") or 1
|
||||
if rrp_raw and rrp_val:
|
||||
rrp_str = f"{rrp_raw[0]}{rrp_val * case_size:.2f}"
|
||||
parts.append(await render_to_sx("market-header-rrp", rrp=rrp_str))
|
||||
parts.append(sx_call("market-header-rrp", rrp=rrp_str))
|
||||
|
||||
inner_sx = "(<> " + " ".join(parts) + ")"
|
||||
return await render_to_sx("market-prices-row", inner=SxExpr(inner_sx))
|
||||
return sx_call("market-prices-row", inner=SxExpr(inner_sx))
|
||||
|
||||
|
||||
async def _cart_add_sx(slug: str, quantity: int, action: str, csrf: str,
|
||||
def _cart_add_sx(slug: str, quantity: int, action: str, csrf: str,
|
||||
cart_url_fn: Any = None) -> str:
|
||||
"""Build add-to-cart button or quantity controls as sx."""
|
||||
if not quantity:
|
||||
return await render_to_sx(
|
||||
return sx_call(
|
||||
"market-cart-add-empty",
|
||||
cart_id=f"cart-{slug}", action=action, csrf=csrf,
|
||||
)
|
||||
|
||||
cart_href = cart_url_fn("/") if callable(cart_url_fn) else "/"
|
||||
return await render_to_sx(
|
||||
return sx_call(
|
||||
"market-cart-add-quantity",
|
||||
cart_id=f"cart-{slug}", action=action, csrf=csrf,
|
||||
minus_val=str(quantity - 1), plus_val=str(quantity + 1),
|
||||
@@ -183,7 +183,7 @@ async def _cart_add_sx(slug: str, quantity: int, action: str, csrf: str,
|
||||
# Mobile nav panel
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def _mobile_nav_panel_sx(ctx: dict) -> str:
|
||||
def _mobile_nav_panel_sx(ctx: dict) -> str:
|
||||
"""Build mobile nav panel with category accordion as sx."""
|
||||
from quart import url_for
|
||||
from shared.utils import route_prefix
|
||||
@@ -199,7 +199,7 @@ async def _mobile_nav_panel_sx(ctx: dict) -> str:
|
||||
|
||||
all_href = prefix + url_for("market.browse.browse_all") + qs
|
||||
all_active = (category_label == "All Products")
|
||||
item_parts = [await render_to_sx(
|
||||
item_parts = [sx_call(
|
||||
"market-mobile-all-link",
|
||||
href=all_href, hx_select=hx_select, active=all_active,
|
||||
select_colours=select_colours,
|
||||
@@ -211,10 +211,10 @@ async def _mobile_nav_panel_sx(ctx: dict) -> str:
|
||||
cat_href = prefix + url_for("market.browse.browse_top", top_slug=cat_slug) + qs
|
||||
bg_cls = " bg-stone-900 text-white hover:bg-stone-900" if cat_active else ""
|
||||
|
||||
chevron_sx = await render_to_sx("market-mobile-chevron")
|
||||
chevron_sx = sx_call("market-mobile-chevron")
|
||||
|
||||
cat_count = data.get("count", 0)
|
||||
summary_sx = await render_to_sx(
|
||||
summary_sx = sx_call(
|
||||
"market-mobile-cat-summary",
|
||||
bg_cls=bg_cls, href=cat_href, hx_select=hx_select,
|
||||
select_colours=select_colours, cat_name=cat,
|
||||
@@ -231,19 +231,19 @@ async def _mobile_nav_panel_sx(ctx: dict) -> str:
|
||||
sub_active = (cat_active and sub_slug == sub.get("slug"))
|
||||
sub_label = sub.get("html_label") or sub.get("name", "")
|
||||
sub_count = sub.get("count", 0)
|
||||
sub_link_parts.append(await render_to_sx(
|
||||
sub_link_parts.append(sx_call(
|
||||
"market-mobile-sub-link",
|
||||
select_colours=select_colours, active=sub_active,
|
||||
href=sub_href, hx_select=hx_select, label=sub_label,
|
||||
count_label=f"{sub_count} products", count_str=str(sub_count),
|
||||
))
|
||||
sub_links_sx = "(<> " + " ".join(sub_link_parts) + ")"
|
||||
subs_sx = await render_to_sx("market-mobile-subs-panel", links=SxExpr(sub_links_sx))
|
||||
subs_sx = sx_call("market-mobile-subs-panel", links=SxExpr(sub_links_sx))
|
||||
else:
|
||||
view_href = prefix + url_for("market.browse.browse_top", top_slug=cat_slug) + qs
|
||||
subs_sx = await render_to_sx("market-mobile-view-all", href=view_href, hx_select=hx_select)
|
||||
subs_sx = sx_call("market-mobile-view-all", href=view_href, hx_select=hx_select)
|
||||
|
||||
item_parts.append(await render_to_sx(
|
||||
item_parts.append(sx_call(
|
||||
"market-mobile-cat-details",
|
||||
open=cat_active or None,
|
||||
summary=SxExpr(summary_sx),
|
||||
@@ -251,20 +251,20 @@ async def _mobile_nav_panel_sx(ctx: dict) -> str:
|
||||
))
|
||||
|
||||
items_sx = "(<> " + " ".join(item_parts) + ")"
|
||||
return await render_to_sx("market-mobile-nav-wrapper", items=SxExpr(items_sx))
|
||||
return sx_call("market-mobile-nav-wrapper", items=SxExpr(items_sx))
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Product admin header
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def _product_admin_header_sx(ctx: dict, d: dict, *, oob: bool = False) -> str:
|
||||
def _product_admin_header_sx(ctx: dict, d: dict, *, oob: bool = False) -> str:
|
||||
"""Build product admin header row as sx."""
|
||||
from quart import url_for
|
||||
|
||||
slug = d.get("slug", "")
|
||||
link_href = url_for("market.browse.product.admin", product_slug=slug)
|
||||
return await render_to_sx(
|
||||
return sx_call(
|
||||
"menu-row-sx",
|
||||
id="product-admin-row", level=4,
|
||||
link_href=link_href, link_label="admin!!", icon="fa fa-cog",
|
||||
@@ -296,21 +296,21 @@ async def _market_full(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env
|
||||
return await render_to_sx_with_env("market-browse-layout-full", {},
|
||||
post_header=SxExpr(await _post_header_sx(ctx)),
|
||||
market_header=SxExpr(await _market_header_sx(ctx)))
|
||||
market_header=SxExpr(_market_header_sx(ctx)))
|
||||
|
||||
|
||||
async def _market_oob(ctx: dict, **kw: Any) -> str:
|
||||
oob_hdr = await _oob_header_sx("post-header-child", "market-header-child",
|
||||
await _market_header_sx(ctx))
|
||||
return await render_to_sx("market-browse-layout-oob",
|
||||
_market_header_sx(ctx))
|
||||
return sx_call("market-browse-layout-oob",
|
||||
oob_header=SxExpr(oob_hdr),
|
||||
post_header_oob=SxExpr(await _post_header_sx(ctx, oob=True)),
|
||||
clear_oob=SxExpr(_clear_deeper_oob("post-row", "post-header-child",
|
||||
"market-row", "market-header-child")))
|
||||
|
||||
|
||||
async def _market_mobile(ctx: dict, **kw: Any) -> str:
|
||||
return await _mobile_nav_panel_sx(ctx)
|
||||
def _market_mobile(ctx: dict, **kw: Any) -> str:
|
||||
return _mobile_nav_panel_sx(ctx)
|
||||
|
||||
|
||||
async def _market_admin_full(ctx: dict, **kw: Any) -> str:
|
||||
@@ -318,14 +318,14 @@ async def _market_admin_full(ctx: dict, **kw: Any) -> str:
|
||||
selected = kw.get("selected", "")
|
||||
return await render_to_sx_with_env("market-admin-layout-full", {},
|
||||
post_header=SxExpr(await _post_header_sx(ctx)),
|
||||
market_header=SxExpr(await _market_header_sx(ctx)),
|
||||
market_header=SxExpr(_market_header_sx(ctx)),
|
||||
admin_header=SxExpr(await _market_admin_header_sx(ctx, selected=selected)))
|
||||
|
||||
|
||||
async def _market_admin_oob(ctx: dict, **kw: Any) -> str:
|
||||
selected = kw.get("selected", "")
|
||||
return await render_to_sx("market-admin-layout-oob",
|
||||
market_header_oob=SxExpr(await _market_header_sx(ctx, oob=True)),
|
||||
return sx_call("market-admin-layout-oob",
|
||||
market_header_oob=SxExpr(_market_header_sx(ctx, oob=True)),
|
||||
admin_oob_header=SxExpr(await _oob_header_sx("market-header-child", "market-admin-header-child",
|
||||
await _market_admin_header_sx(ctx, selected=selected))),
|
||||
clear_oob=SxExpr(_clear_deeper_oob("post-row", "post-header-child",
|
||||
|
||||
@@ -5,7 +5,7 @@ from typing import Any
|
||||
|
||||
from shared.sx.parser import SxExpr
|
||||
from shared.sx.helpers import (
|
||||
render_to_sx,
|
||||
sx_call,
|
||||
post_header_sx as _post_header_sx,
|
||||
oob_header_sx as _oob_header_sx,
|
||||
full_page_sx, oob_page_sx,
|
||||
@@ -25,21 +25,21 @@ from .helpers import _markets_admin_panel_sx
|
||||
# Browse page
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def _product_grid(cards_sx: str) -> str:
|
||||
def _product_grid(cards_sx: str) -> str:
|
||||
"""Wrap product cards in a grid as sx."""
|
||||
return await render_to_sx("market-product-grid", cards=SxExpr(cards_sx))
|
||||
return sx_call("market-product-grid", cards=SxExpr(cards_sx))
|
||||
|
||||
|
||||
async def render_browse_page(ctx: dict) -> str:
|
||||
"""Full page: product browse with filters."""
|
||||
cards = await _product_cards_sx(ctx)
|
||||
content = await _product_grid(cards)
|
||||
cards = _product_cards_sx(ctx)
|
||||
content = _product_grid(cards)
|
||||
|
||||
from shared.sx.helpers import render_to_sx_with_env
|
||||
hdr = await render_to_sx_with_env("market-browse-layout-full", {},
|
||||
post_header=SxExpr(await _post_header_sx(ctx)),
|
||||
market_header=SxExpr(await _market_header_sx(ctx)))
|
||||
menu = await _mobile_nav_panel_sx(ctx)
|
||||
market_header=SxExpr(_market_header_sx(ctx)))
|
||||
menu = _mobile_nav_panel_sx(ctx)
|
||||
filter_sx = await _mobile_filter_summary_sx(ctx)
|
||||
aside_sx = await _desktop_filter_sx(ctx)
|
||||
|
||||
@@ -49,17 +49,17 @@ async def render_browse_page(ctx: dict) -> str:
|
||||
|
||||
async def render_browse_oob(ctx: dict) -> str:
|
||||
"""OOB response: product browse."""
|
||||
cards = await _product_cards_sx(ctx)
|
||||
content = await _product_grid(cards)
|
||||
cards = _product_cards_sx(ctx)
|
||||
content = _product_grid(cards)
|
||||
|
||||
oob_hdr = await _oob_header_sx("post-header-child", "market-header-child",
|
||||
await _market_header_sx(ctx))
|
||||
oobs = await render_to_sx("market-browse-layout-oob",
|
||||
_market_header_sx(ctx))
|
||||
oobs = sx_call("market-browse-layout-oob",
|
||||
oob_header=SxExpr(oob_hdr),
|
||||
post_header_oob=SxExpr(await _post_header_sx(ctx, oob=True)),
|
||||
clear_oob=SxExpr(_clear_deeper_oob("post-row", "post-header-child",
|
||||
"market-row", "market-header-child")))
|
||||
menu = await _mobile_nav_panel_sx(ctx)
|
||||
menu = _mobile_nav_panel_sx(ctx)
|
||||
filter_sx = await _mobile_filter_summary_sx(ctx)
|
||||
aside_sx = await _desktop_filter_sx(ctx)
|
||||
|
||||
@@ -67,9 +67,9 @@ async def render_browse_oob(ctx: dict) -> str:
|
||||
menu=menu, filter=filter_sx, aside=aside_sx)
|
||||
|
||||
|
||||
async def render_browse_cards(ctx: dict) -> str:
|
||||
def render_browse_cards(ctx: dict) -> str:
|
||||
"""Pagination fragment: product cards -- sx wire format."""
|
||||
return await _product_cards_sx(ctx)
|
||||
return _product_cards_sx(ctx)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -78,29 +78,29 @@ async def render_browse_cards(ctx: dict) -> str:
|
||||
|
||||
async def render_product_page(ctx: dict, d: dict) -> str:
|
||||
"""Full page: product detail."""
|
||||
content = await _product_detail_sx(d, ctx)
|
||||
meta = await _product_meta_sx(d, ctx)
|
||||
content = _product_detail_sx(d, ctx)
|
||||
meta = _product_meta_sx(d, ctx)
|
||||
|
||||
from shared.sx.helpers import render_to_sx_with_env
|
||||
hdr = await render_to_sx_with_env("market-product-layout-full", {},
|
||||
post_header=SxExpr(await _post_header_sx(ctx)),
|
||||
market_header=SxExpr(await _market_header_sx(ctx)),
|
||||
product_header=SxExpr(await _product_header_sx(ctx, d)))
|
||||
market_header=SxExpr(_market_header_sx(ctx)),
|
||||
product_header=SxExpr(_product_header_sx(ctx, d)))
|
||||
return await full_page_sx(ctx, header_rows=hdr, content=content, meta=meta)
|
||||
|
||||
|
||||
async def render_product_oob(ctx: dict, d: dict) -> str:
|
||||
"""OOB response: product detail."""
|
||||
content = await _product_detail_sx(d, ctx)
|
||||
content = _product_detail_sx(d, ctx)
|
||||
|
||||
oobs = await render_to_sx("market-oob-wrap",
|
||||
parts=SxExpr("(<> " + await _market_header_sx(ctx, oob=True) + " "
|
||||
oobs = sx_call("market-oob-wrap",
|
||||
parts=SxExpr("(<> " + _market_header_sx(ctx, oob=True) + " "
|
||||
+ await _oob_header_sx("market-header-child", "product-header-child",
|
||||
await _product_header_sx(ctx, d)) + " "
|
||||
_product_header_sx(ctx, d)) + " "
|
||||
+ _clear_deeper_oob("post-row", "post-header-child",
|
||||
"market-row", "market-header-child",
|
||||
"product-row", "product-header-child") + ")"))
|
||||
menu = await _mobile_nav_panel_sx(ctx)
|
||||
menu = _mobile_nav_panel_sx(ctx)
|
||||
return await oob_page_sx(oobs=oobs, content=content, menu=menu)
|
||||
|
||||
|
||||
@@ -110,25 +110,25 @@ async def render_product_oob(ctx: dict, d: dict) -> str:
|
||||
|
||||
async def render_product_admin_page(ctx: dict, d: dict) -> str:
|
||||
"""Full page: product admin."""
|
||||
content = await _product_detail_sx(d, ctx)
|
||||
content = _product_detail_sx(d, ctx)
|
||||
|
||||
from shared.sx.helpers import render_to_sx_with_env
|
||||
hdr = await render_to_sx_with_env("market-product-admin-layout-full", {},
|
||||
post_header=SxExpr(await _post_header_sx(ctx)),
|
||||
market_header=SxExpr(await _market_header_sx(ctx)),
|
||||
product_header=SxExpr(await _product_header_sx(ctx, d)),
|
||||
admin_header=SxExpr(await _product_admin_header_sx(ctx, d)))
|
||||
market_header=SxExpr(_market_header_sx(ctx)),
|
||||
product_header=SxExpr(_product_header_sx(ctx, d)),
|
||||
admin_header=SxExpr(_product_admin_header_sx(ctx, d)))
|
||||
return await full_page_sx(ctx, header_rows=hdr, content=content)
|
||||
|
||||
|
||||
async def render_product_admin_oob(ctx: dict, d: dict) -> str:
|
||||
"""OOB response: product admin."""
|
||||
content = await _product_detail_sx(d, ctx)
|
||||
content = _product_detail_sx(d, ctx)
|
||||
|
||||
oobs = await render_to_sx("market-oob-wrap",
|
||||
parts=SxExpr("(<> " + await _product_header_sx(ctx, d, oob=True) + " "
|
||||
oobs = sx_call("market-oob-wrap",
|
||||
parts=SxExpr("(<> " + _product_header_sx(ctx, d, oob=True) + " "
|
||||
+ await _oob_header_sx("product-header-child", "product-admin-header-child",
|
||||
await _product_admin_header_sx(ctx, d)) + " "
|
||||
_product_admin_header_sx(ctx, d)) + " "
|
||||
+ _clear_deeper_oob("post-row", "post-header-child",
|
||||
"market-row", "market-header-child",
|
||||
"product-row", "product-header-child",
|
||||
@@ -149,7 +149,7 @@ async def render_markets_admin_list_panel(ctx: dict) -> str:
|
||||
# Public API: POST handler fragment renderers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def render_all_markets_cards(markets: list, has_more: bool,
|
||||
def render_all_markets_cards(markets: list, has_more: bool,
|
||||
page_info: dict, page: int) -> str:
|
||||
"""Pagination fragment: all markets cards."""
|
||||
from quart import url_for
|
||||
@@ -157,10 +157,10 @@ async def render_all_markets_cards(markets: list, has_more: bool,
|
||||
|
||||
prefix = route_prefix()
|
||||
next_url = prefix + url_for("all_markets.markets_fragment", page=page + 1)
|
||||
return await _market_cards_sx(markets, page_info, page, has_more, next_url)
|
||||
return _market_cards_sx(markets, page_info, page, has_more, next_url)
|
||||
|
||||
|
||||
async def render_page_markets_cards(markets: list, has_more: bool,
|
||||
def render_page_markets_cards(markets: list, has_more: bool,
|
||||
page: int, post_slug: str) -> str:
|
||||
"""Pagination fragment: page-scoped markets cards."""
|
||||
from quart import url_for
|
||||
@@ -168,11 +168,11 @@ async def render_page_markets_cards(markets: list, has_more: bool,
|
||||
|
||||
prefix = route_prefix()
|
||||
next_url = prefix + url_for("page_markets.markets_fragment", page=page + 1)
|
||||
return await _market_cards_sx(markets, {}, page, has_more, next_url,
|
||||
return _market_cards_sx(markets, {}, page, has_more, next_url,
|
||||
show_page_badge=False, post_slug=post_slug)
|
||||
|
||||
|
||||
async def render_like_toggle_button(slug: str, liked: bool, *,
|
||||
def render_like_toggle_button(slug: str, liked: bool, *,
|
||||
like_url: str | None = None,
|
||||
item_type: str = "product") -> str:
|
||||
"""Render a standalone like toggle button for HTMX POST response."""
|
||||
@@ -193,7 +193,7 @@ async def render_like_toggle_button(slug: str, liked: bool, *,
|
||||
icon = "fa-regular fa-heart"
|
||||
label = f"Like this {item_type}"
|
||||
|
||||
return await render_to_sx(
|
||||
return sx_call(
|
||||
"market-like-toggle-button",
|
||||
colour=colour, action=like_url,
|
||||
hx_headers=f'{{"X-CSRFToken": "{csrf}"}}',
|
||||
@@ -201,7 +201,7 @@ async def render_like_toggle_button(slug: str, liked: bool, *,
|
||||
)
|
||||
|
||||
|
||||
async def render_cart_added_response(cart: list, item: Any, d: dict) -> str:
|
||||
def render_cart_added_response(cart: list, item: Any, d: dict) -> str:
|
||||
"""Render the HTMX response after add-to-cart.
|
||||
|
||||
Returns OOB fragments: cart-mini icon + product add/remove buttons + cart item row.
|
||||
@@ -217,30 +217,30 @@ async def render_cart_added_response(cart: list, item: Any, d: dict) -> str:
|
||||
# 1. Cart mini icon OOB
|
||||
if count > 0:
|
||||
cart_href = _cart_url("/")
|
||||
cart_mini = await render_to_sx("market-cart-mini-count", href=cart_href, count=str(count))
|
||||
cart_mini = sx_call("market-cart-mini-count", href=cart_href, count=str(count))
|
||||
else:
|
||||
from shared.config import config
|
||||
blog_href = config().get("blog_url", "/")
|
||||
logo = config().get("logo", "")
|
||||
cart_mini = await render_to_sx("market-cart-mini-empty", href=blog_href, logo=logo)
|
||||
cart_mini = sx_call("market-cart-mini-empty", href=blog_href, logo=logo)
|
||||
|
||||
# 2. Add/remove buttons OOB
|
||||
action = url_for("market.browse.product.cart", product_slug=slug)
|
||||
quantity = getattr(item, "quantity", 0) if item else 0
|
||||
if not quantity:
|
||||
cart_add = await render_to_sx(
|
||||
cart_add = sx_call(
|
||||
"market-cart-add-empty",
|
||||
cart_id=f"cart-{slug}", action=action, csrf=csrf,
|
||||
)
|
||||
else:
|
||||
cart_href = _cart_url("/") if callable(_cart_url) else "/"
|
||||
cart_add = await render_to_sx(
|
||||
cart_add = sx_call(
|
||||
"market-cart-add-quantity",
|
||||
cart_id=f"cart-{slug}", action=action, csrf=csrf,
|
||||
minus_val=str(quantity - 1), plus_val=str(quantity + 1),
|
||||
quantity=str(quantity), cart_href=cart_href,
|
||||
)
|
||||
add_sx = await render_to_sx(
|
||||
add_sx = sx_call(
|
||||
"market-cart-add-oob",
|
||||
id=f"cart-add-{slug}",
|
||||
inner=SxExpr(cart_add),
|
||||
|
||||
@@ -3,8 +3,8 @@ from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from shared.sx.parser import serialize, SxExpr
|
||||
from shared.sx.helpers import render_to_sx
|
||||
from shared.sx.parser import SxExpr
|
||||
from shared.sx.helpers import sx_call
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -22,7 +22,6 @@ _MARKET_DEEP_IDS = [
|
||||
|
||||
def _clear_deeper_oob(*keep_ids: str) -> str:
|
||||
"""Clear all market header rows/children NOT in keep_ids."""
|
||||
from shared.sx.helpers import sx_call
|
||||
to_clear = [i for i in _MARKET_DEEP_IDS if i not in keep_ids]
|
||||
return " ".join(sx_call("clear-oob-div", id=i) for i in to_clear)
|
||||
|
||||
@@ -55,27 +54,27 @@ def _set_prices(item: dict) -> dict:
|
||||
rp_val=rp_val, rp_raw=rp_raw, rp_cur=rp_cur)
|
||||
|
||||
|
||||
async def _card_price_sx(p: dict) -> str:
|
||||
def _card_price_sx(p: dict) -> str:
|
||||
"""Build price line for product card as sx call."""
|
||||
pr = _set_prices(p)
|
||||
sp_str = _price_str(pr["sp_val"], pr["sp_raw"], pr["sp_cur"])
|
||||
rp_str = _price_str(pr["rp_val"], pr["rp_raw"], pr["rp_cur"])
|
||||
parts: list[str] = []
|
||||
if pr["sp_val"]:
|
||||
parts.append(await render_to_sx("market-price-special", price=sp_str))
|
||||
parts.append(sx_call("market-price-special", price=sp_str))
|
||||
if pr["rp_val"]:
|
||||
parts.append(await render_to_sx("market-price-regular-strike", price=rp_str))
|
||||
parts.append(sx_call("market-price-regular-strike", price=rp_str))
|
||||
elif pr["rp_val"]:
|
||||
parts.append(await render_to_sx("market-price-regular", price=rp_str))
|
||||
parts.append(sx_call("market-price-regular", price=rp_str))
|
||||
inner = "(<> " + " ".join(parts) + ")" if parts else None
|
||||
return await render_to_sx("market-price-line", inner=SxExpr(inner) if inner else None)
|
||||
return sx_call("market-price-line", inner=SxExpr(inner) if inner else None)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Product detail page content
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def _product_detail_sx(d: dict, ctx: dict) -> str:
|
||||
def _product_detail_sx(d: dict, ctx: dict) -> str:
|
||||
"""Build product detail main panel content as sx."""
|
||||
from quart import url_for
|
||||
from shared.browser.app.csrf import generate_csrf_token
|
||||
@@ -97,19 +96,19 @@ async def _product_detail_sx(d: dict, ctx: dict) -> str:
|
||||
# Like button
|
||||
like_sx = ""
|
||||
if user:
|
||||
like_sx = await _like_button_sx(slug, liked_by_current_user, csrf, ctx)
|
||||
like_sx = _like_button_sx(slug, liked_by_current_user, csrf, ctx)
|
||||
|
||||
# Main image + labels
|
||||
label_parts: list[str] = []
|
||||
if callable(asset_url_fn):
|
||||
for l in labels:
|
||||
label_parts.append(await render_to_sx(
|
||||
label_parts.append(sx_call(
|
||||
"market-label-overlay",
|
||||
src=asset_url_fn("labels/" + l + ".svg"),
|
||||
))
|
||||
labels_sx = "(<> " + " ".join(label_parts) + ")" if label_parts else None
|
||||
|
||||
gallery_inner = await render_to_sx(
|
||||
gallery_inner = sx_call(
|
||||
"market-detail-gallery-inner",
|
||||
like=SxExpr(like_sx) if like_sx else None,
|
||||
image=images[0], alt=d.get("title", ""),
|
||||
@@ -120,9 +119,9 @@ async def _product_detail_sx(d: dict, ctx: dict) -> str:
|
||||
# Prev/next buttons
|
||||
nav_buttons = ""
|
||||
if len(images) > 1:
|
||||
nav_buttons = await render_to_sx("market-detail-nav-buttons")
|
||||
nav_buttons = sx_call("market-detail-nav-buttons")
|
||||
|
||||
gallery_sx = await render_to_sx(
|
||||
gallery_sx = sx_call(
|
||||
"market-detail-gallery",
|
||||
inner=SxExpr(gallery_inner),
|
||||
nav=SxExpr(nav_buttons) if nav_buttons else None,
|
||||
@@ -133,18 +132,18 @@ async def _product_detail_sx(d: dict, ctx: dict) -> str:
|
||||
if len(images) > 1:
|
||||
thumb_parts = []
|
||||
for i, u in enumerate(images):
|
||||
thumb_parts.append(await render_to_sx(
|
||||
thumb_parts.append(sx_call(
|
||||
"market-detail-thumb",
|
||||
title=f"Image {i+1}", src=u, alt=f"thumb {i+1}",
|
||||
))
|
||||
thumbs_sx = "(<> " + " ".join(thumb_parts) + ")"
|
||||
gallery_parts.append(await render_to_sx("market-detail-thumbs", thumbs=SxExpr(thumbs_sx)))
|
||||
gallery_parts.append(sx_call("market-detail-thumbs", thumbs=SxExpr(thumbs_sx)))
|
||||
gallery_final = "(<> " + " ".join(gallery_parts) + ")"
|
||||
else:
|
||||
like_sx = ""
|
||||
if user:
|
||||
like_sx = await _like_button_sx(slug, liked_by_current_user, csrf, ctx)
|
||||
gallery_final = await render_to_sx("market-detail-no-image",
|
||||
like_sx = _like_button_sx(slug, liked_by_current_user, csrf, ctx)
|
||||
gallery_final = sx_call("market-detail-no-image",
|
||||
like=SxExpr(like_sx) if like_sx else None)
|
||||
|
||||
# Stickers below gallery
|
||||
@@ -152,12 +151,12 @@ async def _product_detail_sx(d: dict, ctx: dict) -> str:
|
||||
if stickers and callable(asset_url_fn):
|
||||
sticker_parts = []
|
||||
for s in stickers:
|
||||
sticker_parts.append(await render_to_sx(
|
||||
sticker_parts.append(sx_call(
|
||||
"market-detail-sticker",
|
||||
src=asset_url_fn("stickers/" + s + ".svg"), name=s,
|
||||
))
|
||||
sticker_items_sx = "(<> " + " ".join(sticker_parts) + ")"
|
||||
stickers_sx = await render_to_sx("market-detail-stickers", items=SxExpr(sticker_items_sx))
|
||||
stickers_sx = sx_call("market-detail-stickers", items=SxExpr(sticker_items_sx))
|
||||
|
||||
# Right column: prices, description, sections
|
||||
pr = _set_prices(d)
|
||||
@@ -167,15 +166,15 @@ async def _product_detail_sx(d: dict, ctx: dict) -> str:
|
||||
extra_parts: list[str] = []
|
||||
ppu = d.get("price_per_unit") or d.get("price_per_unit_raw")
|
||||
if ppu:
|
||||
extra_parts.append(await render_to_sx(
|
||||
extra_parts.append(sx_call(
|
||||
"market-detail-unit-price",
|
||||
price=_price_str(d.get("price_per_unit"), d.get("price_per_unit_raw"), d.get("price_per_unit_currency")),
|
||||
))
|
||||
if d.get("case_size_raw"):
|
||||
extra_parts.append(await render_to_sx("market-detail-case-size", size=d["case_size_raw"]))
|
||||
extra_parts.append(sx_call("market-detail-case-size", size=d["case_size_raw"]))
|
||||
if extra_parts:
|
||||
extras_sx = "(<> " + " ".join(extra_parts) + ")"
|
||||
detail_parts.append(await render_to_sx("market-detail-extras", inner=SxExpr(extras_sx)))
|
||||
detail_parts.append(sx_call("market-detail-extras", inner=SxExpr(extras_sx)))
|
||||
|
||||
# Description
|
||||
desc_short = d.get("description_short")
|
||||
@@ -183,28 +182,28 @@ async def _product_detail_sx(d: dict, ctx: dict) -> str:
|
||||
if desc_short or desc_html_val:
|
||||
desc_parts: list[str] = []
|
||||
if desc_short:
|
||||
desc_parts.append(await render_to_sx("market-detail-desc-short", text=desc_short))
|
||||
desc_parts.append(sx_call("market-detail-desc-short", text=desc_short))
|
||||
if desc_html_val:
|
||||
desc_parts.append(await render_to_sx("market-detail-desc-html", html=desc_html_val))
|
||||
desc_parts.append(sx_call("market-detail-desc-html", html=desc_html_val))
|
||||
desc_inner = "(<> " + " ".join(desc_parts) + ")"
|
||||
detail_parts.append(await render_to_sx("market-detail-desc-wrapper", inner=SxExpr(desc_inner)))
|
||||
detail_parts.append(sx_call("market-detail-desc-wrapper", inner=SxExpr(desc_inner)))
|
||||
|
||||
# Sections (expandable)
|
||||
sections = d.get("sections", [])
|
||||
if sections:
|
||||
sec_parts = []
|
||||
for sec in sections:
|
||||
sec_parts.append(await render_to_sx(
|
||||
sec_parts.append(sx_call(
|
||||
"market-detail-section",
|
||||
title=sec.get("title", ""), html=sec.get("html", ""),
|
||||
))
|
||||
sec_items_sx = "(<> " + " ".join(sec_parts) + ")"
|
||||
detail_parts.append(await render_to_sx("market-detail-sections", items=SxExpr(sec_items_sx)))
|
||||
detail_parts.append(sx_call("market-detail-sections", items=SxExpr(sec_items_sx)))
|
||||
|
||||
details_inner_sx = "(<> " + " ".join(detail_parts) + ")" if detail_parts else "(<>)"
|
||||
details_sx = await render_to_sx("market-detail-right-col", inner=SxExpr(details_inner_sx))
|
||||
details_sx = sx_call("market-detail-right-col", inner=SxExpr(details_inner_sx))
|
||||
|
||||
return await render_to_sx(
|
||||
return sx_call(
|
||||
"market-detail-layout",
|
||||
gallery=SxExpr(gallery_final),
|
||||
stickers=SxExpr(stickers_sx) if stickers_sx else None,
|
||||
@@ -216,7 +215,7 @@ async def _product_detail_sx(d: dict, ctx: dict) -> str:
|
||||
# Product meta (OpenGraph, JSON-LD)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def _product_meta_sx(d: dict, ctx: dict) -> str:
|
||||
def _product_meta_sx(d: dict, ctx: dict) -> str:
|
||||
"""Build product meta tags as sx (auto-hoisted to <head> by sx.js)."""
|
||||
import json
|
||||
from quart import request
|
||||
@@ -234,34 +233,34 @@ async def _product_meta_sx(d: dict, ctx: dict) -> str:
|
||||
price = d.get("special_price") or d.get("regular_price") or d.get("rrp")
|
||||
price_currency = d.get("special_price_currency") or d.get("regular_price_currency") or d.get("rrp_currency")
|
||||
|
||||
parts = [await render_to_sx("market-meta-title", title=title)]
|
||||
parts.append(await render_to_sx("market-meta-description", description=description))
|
||||
parts = [sx_call("market-meta-title", title=title)]
|
||||
parts.append(sx_call("market-meta-description", description=description))
|
||||
if canonical:
|
||||
parts.append(await render_to_sx("market-meta-canonical", href=canonical))
|
||||
parts.append(sx_call("market-meta-canonical", href=canonical))
|
||||
|
||||
# OpenGraph
|
||||
site_title = ctx.get("base_title", "")
|
||||
parts.append(await render_to_sx("market-meta-og", property="og:site_name", content=site_title))
|
||||
parts.append(await render_to_sx("market-meta-og", property="og:type", content="product"))
|
||||
parts.append(await render_to_sx("market-meta-og", property="og:title", content=title))
|
||||
parts.append(await render_to_sx("market-meta-og", property="og:description", content=description))
|
||||
parts.append(sx_call("market-meta-og", property="og:site_name", content=site_title))
|
||||
parts.append(sx_call("market-meta-og", property="og:type", content="product"))
|
||||
parts.append(sx_call("market-meta-og", property="og:title", content=title))
|
||||
parts.append(sx_call("market-meta-og", property="og:description", content=description))
|
||||
if canonical:
|
||||
parts.append(await render_to_sx("market-meta-og", property="og:url", content=canonical))
|
||||
parts.append(sx_call("market-meta-og", property="og:url", content=canonical))
|
||||
if image_url:
|
||||
parts.append(await render_to_sx("market-meta-og", property="og:image", content=image_url))
|
||||
parts.append(sx_call("market-meta-og", property="og:image", content=image_url))
|
||||
if price and price_currency:
|
||||
parts.append(await render_to_sx("market-meta-og", property="product:price:amount", content=f"{price:.2f}"))
|
||||
parts.append(await render_to_sx("market-meta-og", property="product:price:currency", content=price_currency))
|
||||
parts.append(sx_call("market-meta-og", property="product:price:amount", content=f"{price:.2f}"))
|
||||
parts.append(sx_call("market-meta-og", property="product:price:currency", content=price_currency))
|
||||
if brand:
|
||||
parts.append(await render_to_sx("market-meta-og", property="product:brand", content=brand))
|
||||
parts.append(sx_call("market-meta-og", property="product:brand", content=brand))
|
||||
|
||||
# Twitter
|
||||
card_type = "summary_large_image" if image_url else "summary"
|
||||
parts.append(await render_to_sx("market-meta-twitter", name="twitter:card", content=card_type))
|
||||
parts.append(await render_to_sx("market-meta-twitter", name="twitter:title", content=title))
|
||||
parts.append(await render_to_sx("market-meta-twitter", name="twitter:description", content=description))
|
||||
parts.append(sx_call("market-meta-twitter", name="twitter:card", content=card_type))
|
||||
parts.append(sx_call("market-meta-twitter", name="twitter:title", content=title))
|
||||
parts.append(sx_call("market-meta-twitter", name="twitter:description", content=description))
|
||||
if image_url:
|
||||
parts.append(await render_to_sx("market-meta-twitter", name="twitter:image", content=image_url))
|
||||
parts.append(sx_call("market-meta-twitter", name="twitter:image", content=image_url))
|
||||
|
||||
# JSON-LD
|
||||
jsonld = {
|
||||
@@ -283,6 +282,6 @@ async def _product_meta_sx(d: dict, ctx: dict) -> str:
|
||||
"url": canonical,
|
||||
"availability": "https://schema.org/InStock",
|
||||
}
|
||||
parts.append(await render_to_sx("market-meta-jsonld", json=json.dumps(jsonld)))
|
||||
parts.append(sx_call("market-meta-jsonld", json=json.dumps(jsonld)))
|
||||
|
||||
return "(<> " + " ".join(parts) + ")"
|
||||
|
||||
@@ -15,17 +15,17 @@ async def _render_checkout_return(ctx: dict, order=None, status: str = "",
|
||||
calendar_entries=None, order_tickets=None) -> str:
|
||||
"""Render checkout return page — replaces sx_components helper."""
|
||||
from shared.sx.helpers import (
|
||||
render_to_sx, root_header_sx, header_child_sx, full_page_sx, call_url,
|
||||
sx_call, root_header_sx, header_child_sx, full_page_sx, call_url,
|
||||
)
|
||||
from shared.sx.parser import SxExpr
|
||||
from shared.infrastructure.urls import market_product_url
|
||||
|
||||
filt = await render_to_sx("checkout-return-header", status=status)
|
||||
filt = sx_call("checkout-return-header", status=status)
|
||||
|
||||
if not order:
|
||||
content = await render_to_sx("checkout-return-missing")
|
||||
content = sx_call("checkout-return-missing")
|
||||
else:
|
||||
summary = await render_to_sx("order-summary-card",
|
||||
summary = sx_call("order-summary-card",
|
||||
order_id=order.id,
|
||||
created_at=order.created_at.strftime("%-d %b %Y, %H:%M") if order.created_at else None,
|
||||
description=order.description, status=order.status,
|
||||
@@ -39,19 +39,19 @@ async def _render_checkout_return(ctx: dict, order=None, status: str = "",
|
||||
for item in order.items:
|
||||
product_url = market_product_url(item.product_slug)
|
||||
if item.product_image:
|
||||
img = await render_to_sx("order-item-image",
|
||||
img = sx_call("order-item-image",
|
||||
src=item.product_image,
|
||||
alt=item.product_title or "Product image")
|
||||
else:
|
||||
img = await render_to_sx("order-item-no-image")
|
||||
item_parts.append(await render_to_sx("order-item-row",
|
||||
img = sx_call("order-item-no-image")
|
||||
item_parts.append(sx_call("order-item-row",
|
||||
href=product_url, img=SxExpr(img),
|
||||
title=item.product_title or "Unknown product",
|
||||
pid=f"Product ID: {item.product_id}",
|
||||
qty=f"Qty: {item.quantity}",
|
||||
price=f"{item.currency or order.currency or 'GBP'} {item.unit_price or 0:.2f}",
|
||||
))
|
||||
items = await render_to_sx("order-items-panel",
|
||||
items = sx_call("order-items-panel",
|
||||
items=SxExpr("(<> " + " ".join(item_parts) + ")"))
|
||||
|
||||
calendar = ""
|
||||
@@ -68,13 +68,13 @@ async def _render_checkout_return(ctx: dict, order=None, status: str = "",
|
||||
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(await render_to_sx("order-calendar-entry",
|
||||
cal_parts.append(sx_call("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 = await render_to_sx("order-calendar-section",
|
||||
calendar = sx_call("order-calendar-section",
|
||||
items=SxExpr("(<> " + " ".join(cal_parts) + ")"))
|
||||
|
||||
tickets = ""
|
||||
@@ -92,23 +92,23 @@ async def _render_checkout_return(ctx: dict, order=None, status: str = "",
|
||||
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(await render_to_sx("checkout-return-ticket",
|
||||
tk_parts.append(sx_call("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 = await render_to_sx("checkout-return-tickets",
|
||||
tickets = sx_call("checkout-return-tickets",
|
||||
items=SxExpr("(<> " + " ".join(tk_parts) + ")"))
|
||||
|
||||
status_msg = ""
|
||||
if order.status == "failed":
|
||||
status_msg = await render_to_sx("checkout-return-failed", order_id=order.id)
|
||||
status_msg = sx_call("checkout-return-failed", order_id=order.id)
|
||||
elif order.status == "paid":
|
||||
status_msg = await render_to_sx("checkout-return-paid")
|
||||
status_msg = sx_call("checkout-return-paid")
|
||||
|
||||
content = await render_to_sx("checkout-return-content",
|
||||
content = sx_call("checkout-return-content",
|
||||
summary=SxExpr(summary),
|
||||
items=SxExpr(items) if items else None,
|
||||
calendar=SxExpr(calendar) if calendar else None,
|
||||
@@ -117,7 +117,7 @@ async def _render_checkout_return(ctx: dict, order=None, status: str = "",
|
||||
)
|
||||
|
||||
account_url = call_url(ctx, "account_url", "")
|
||||
auth_hdr = await render_to_sx("auth-header-row", account_url=account_url)
|
||||
auth_hdr = sx_call("auth-header-row", account_url=account_url)
|
||||
hdr = "(<> " + await root_header_sx(ctx) + " " + await header_child_sx(auth_hdr) + ")"
|
||||
|
||||
return await full_page_sx(ctx, header_rows=hdr, filter=filt, content=content)
|
||||
|
||||
@@ -70,16 +70,16 @@ def register() -> Blueprint:
|
||||
|
||||
if not hosted_url:
|
||||
from shared.sx.page import get_template_context
|
||||
from shared.sx.helpers import render_to_sx, root_header_sx, header_child_sx, full_page_sx, call_url
|
||||
from shared.sx.helpers import sx_call, root_header_sx, header_child_sx, full_page_sx, call_url
|
||||
from shared.sx.parser import SxExpr
|
||||
from shared.infrastructure.urls import cart_url
|
||||
tctx = await get_template_context()
|
||||
account_url = call_url(tctx, "account_url", "")
|
||||
auth_hdr = await render_to_sx("auth-header-row", account_url=account_url)
|
||||
auth_hdr = sx_call("auth-header-row", account_url=account_url)
|
||||
hdr = "(<> " + await root_header_sx(tctx) + " " + await header_child_sx(auth_hdr) + ")"
|
||||
filt = await render_to_sx("checkout-error-header")
|
||||
order_sx = await render_to_sx("checkout-error-order-id", oid=f"#{order.id}")
|
||||
content = await render_to_sx(
|
||||
filt = sx_call("checkout-error-header")
|
||||
order_sx = sx_call("checkout-error-order-id", oid=f"#{order.id}")
|
||||
content = sx_call(
|
||||
"checkout-error-content",
|
||||
msg="No hosted checkout URL returned from SumUp when trying to reopen payment.",
|
||||
order=SxExpr(order_sx),
|
||||
|
||||
@@ -73,7 +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, render_to_sx
|
||||
from shared.sx.helpers import sx_response, sx_call
|
||||
from shared.utils import route_prefix
|
||||
|
||||
pfx = route_prefix()
|
||||
@@ -95,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(await render_to_sx("order-row-pair",
|
||||
parts.append(sx_call("order-row-pair",
|
||||
order=od,
|
||||
detail_url_prefix=detail_prefix))
|
||||
if page < total_pages:
|
||||
parts.append(await render_to_sx("infinite-scroll",
|
||||
parts.append(sx_call("infinite-scroll",
|
||||
url=rows_url + qs_fn(page=page + 1),
|
||||
page=page, total_pages=total_pages,
|
||||
id_prefix="orders", colspan=5))
|
||||
else:
|
||||
parts.append(await render_to_sx("order-end-row"))
|
||||
parts.append(sx_call("order-end-row"))
|
||||
sx_src = "(<> " + " ".join(parts) + ")"
|
||||
|
||||
resp = sx_response(sx_src)
|
||||
|
||||
@@ -149,7 +149,7 @@ 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, render_to_sx,
|
||||
header_child_sx, full_page_sx, sx_call,
|
||||
)
|
||||
hdr = await root_header_sx(ctx)
|
||||
|
||||
@@ -162,7 +162,7 @@ async def _rich_error_page(errnum: str, message: str, image: str | None = None)
|
||||
hdr = "(<> " + hdr + " " + await header_child_sx(post_row) + ")"
|
||||
|
||||
# Error content
|
||||
error_content = await render_to_sx("error-content", errnum=errnum, message=message, image=image)
|
||||
error_content = sx_call("error-content", errnum=errnum, message=message, image=image)
|
||||
|
||||
return await full_page_sx(ctx, header_rows=hdr, content=error_content)
|
||||
except Exception:
|
||||
|
||||
@@ -60,7 +60,7 @@ async def root_header_sx(ctx: dict, *, oob: bool = False) -> str:
|
||||
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 await render_to_sx("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", ""),
|
||||
@@ -86,7 +86,7 @@ async def mobile_root_nav_sx(ctx: dict) -> str:
|
||||
auth_menu = ctx.get("auth_menu") or ""
|
||||
if not nav_tree and not auth_menu:
|
||||
return ""
|
||||
return await render_to_sx("mobile-root-nav",
|
||||
return await _render_to_sx("mobile-root-nav",
|
||||
nav_tree=_as_sx(nav_tree),
|
||||
auth_menu=_as_sx(auth_menu),
|
||||
)
|
||||
@@ -107,13 +107,13 @@ async 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(await render_to_sx("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()
|
||||
# Skip empty fragment wrappers like "(<> )"
|
||||
if container_nav and container_nav.replace("(<>", "").replace(")", "").strip():
|
||||
parts.append(await render_to_sx("container-nav-wrapper",
|
||||
parts.append(await _render_to_sx("container-nav-wrapper",
|
||||
content=SxExpr(container_nav)))
|
||||
|
||||
# Admin cog
|
||||
@@ -125,7 +125,7 @@ async def _post_nav_items_sx(ctx: dict) -> str:
|
||||
from quart import request
|
||||
admin_href = call_url(ctx, "blog_url", f"/{slug}/admin/")
|
||||
is_admin_page = ctx.get("is_admin_section") or "/admin" in request.path
|
||||
admin_nav = await render_to_sx("admin-cog-button",
|
||||
admin_nav = await _render_to_sx("admin-cog-button",
|
||||
href=admin_href,
|
||||
is_admin_page=is_admin_page or None)
|
||||
if admin_nav:
|
||||
@@ -155,7 +155,7 @@ async def _post_admin_nav_items_sx(ctx: dict, slug: str,
|
||||
continue
|
||||
href = url_fn(path)
|
||||
is_sel = label == selected
|
||||
parts.append(await render_to_sx("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 ""
|
||||
@@ -173,7 +173,7 @@ async def post_mobile_nav_sx(ctx: dict) -> str:
|
||||
post = ctx.get("post") or {}
|
||||
slug = post.get("slug", "")
|
||||
title = (post.get("title") or slug)[:40]
|
||||
return await render_to_sx("mobile-menu-section",
|
||||
return await _render_to_sx("mobile-menu-section",
|
||||
label=title,
|
||||
href=call_url(ctx, "blog_url", f"/{slug}/"),
|
||||
level=1,
|
||||
@@ -181,22 +181,10 @@ async def post_mobile_nav_sx(ctx: dict) -> str:
|
||||
)
|
||||
|
||||
|
||||
async def post_admin_mobile_nav_sx(ctx: dict, slug: str,
|
||||
selected: str = "") -> str:
|
||||
"""Post-admin mobile menu section."""
|
||||
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 await render_to_sx("mobile-menu-section",
|
||||
label="admin", href=admin_href, level=2,
|
||||
items=SxExpr(nav),
|
||||
)
|
||||
|
||||
|
||||
async def search_mobile_sx(ctx: dict) -> str:
|
||||
"""Build mobile search input as sx wire format."""
|
||||
return await render_to_sx("search-mobile",
|
||||
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", ""),
|
||||
@@ -207,7 +195,7 @@ async def search_mobile_sx(ctx: dict) -> str:
|
||||
|
||||
async def search_desktop_sx(ctx: dict) -> str:
|
||||
"""Build desktop search input as sx wire format."""
|
||||
return await render_to_sx("search-desktop",
|
||||
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", ""),
|
||||
@@ -225,11 +213,11 @@ async def post_header_sx(ctx: dict, *, oob: bool = False, child: str = "") -> st
|
||||
title = (post.get("title") or "")[:160]
|
||||
feature_image = post.get("feature_image")
|
||||
|
||||
label_sx = await render_to_sx("post-label", feature_image=feature_image, title=title)
|
||||
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 await render_to_sx("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),
|
||||
@@ -244,7 +232,7 @@ 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 wire format."""
|
||||
# Label
|
||||
label_sx = await render_to_sx("post-admin-label",
|
||||
label_sx = await _render_to_sx("post-admin-label",
|
||||
selected=str(escape(selected)) if selected else None)
|
||||
|
||||
nav_sx = await _post_admin_nav_items_sx(ctx, slug, selected) or None
|
||||
@@ -253,7 +241,7 @@ async def post_admin_header_sx(ctx: dict, slug: str, *, oob: bool = False,
|
||||
blog_fn = ctx.get("blog_url")
|
||||
admin_href = blog_fn(f"/{slug}/admin/") if callable(blog_fn) else f"/{slug}/admin/"
|
||||
|
||||
return await render_to_sx("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),
|
||||
@@ -268,7 +256,7 @@ async def oob_header_sx(parent_id: str, child_id: str, row_sx: str) -> str:
|
||||
child_id is accepted for call-site compatibility but no longer used —
|
||||
the child placeholder is created by ~menu-row-sx itself.
|
||||
"""
|
||||
return await render_to_sx("oob-header-sx",
|
||||
return await _render_to_sx("oob-header-sx",
|
||||
parent_id=parent_id,
|
||||
row=SxExpr(row_sx),
|
||||
)
|
||||
@@ -276,7 +264,7 @@ async def oob_header_sx(parent_id: str, child_id: str, row_sx: str) -> str:
|
||||
|
||||
async def header_child_sx(inner_sx: str, *, id: str = "root-header-child") -> str:
|
||||
"""Wrap inner sx in a header-child div."""
|
||||
return await render_to_sx("header-child-sx",
|
||||
return await _render_to_sx("header-child-sx",
|
||||
id=id, inner=SxExpr(f"(<> {inner_sx})"),
|
||||
)
|
||||
|
||||
@@ -284,7 +272,7 @@ async def header_child_sx(inner_sx: str, *, id: str = "root-header-child") -> st
|
||||
async def oob_page_sx(*, oobs: str = "", filter: str = "", aside: str = "",
|
||||
content: str = "", menu: str = "") -> str:
|
||||
"""Build OOB response as sx wire format."""
|
||||
return await render_to_sx("oob-sx",
|
||||
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,
|
||||
@@ -305,7 +293,7 @@ async def full_page_sx(ctx: dict, *, header_rows: str,
|
||||
# Auto-generate mobile nav from context when no menu provided
|
||||
if not menu:
|
||||
menu = await mobile_root_nav_sx(ctx)
|
||||
body_sx = await render_to_sx("app-body",
|
||||
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,
|
||||
@@ -345,8 +333,8 @@ def _build_component_ast(__name: str, **kwargs: Any) -> list:
|
||||
return ast
|
||||
|
||||
|
||||
async def render_to_sx_with_env(__name: str, extra_env: dict, **kwargs: Any) -> str:
|
||||
"""Like ``render_to_sx`` but merges *extra_env* into the evaluation
|
||||
async def _render_to_sx_with_env(__name: str, extra_env: dict, **kwargs: Any) -> str:
|
||||
"""Like ``_render_to_sx`` but merges *extra_env* into the evaluation
|
||||
environment before eval. Used by ``register_sx_layout`` so .sx
|
||||
defcomps can read ctx values as free variables.
|
||||
|
||||
@@ -354,6 +342,8 @@ async def render_to_sx_with_env(__name: str, extra_env: dict, **kwargs: Any) ->
|
||||
top-level component body is expanded server-side — free variables
|
||||
from *extra_env* are resolved during expansion rather than being
|
||||
serialized as unresolved symbols for the client.
|
||||
|
||||
**Private** — service code should use ``sx_call()`` or defmacros instead.
|
||||
"""
|
||||
from .jinja_bridge import get_component_env, _get_request_context
|
||||
from .async_eval import async_eval_slot_to_sx
|
||||
@@ -365,16 +355,15 @@ async def render_to_sx_with_env(__name: str, extra_env: dict, **kwargs: Any) ->
|
||||
return await async_eval_slot_to_sx(ast, env, ctx)
|
||||
|
||||
|
||||
async def render_to_sx(__name: str, **kwargs: Any) -> str:
|
||||
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
|
||||
**Private** — service code should use ``sx_call()`` or defmacros instead.
|
||||
Only infrastructure code (helpers.py, layouts.py) should call this.
|
||||
"""
|
||||
from .jinja_bridge import get_component_env, _get_request_context
|
||||
from .async_eval import async_eval_to_sx
|
||||
@@ -385,6 +374,11 @@ async def render_to_sx(__name: str, **kwargs: Any) -> str:
|
||||
return await async_eval_to_sx(ast, env, ctx)
|
||||
|
||||
|
||||
# Backwards-compat alias — layout infrastructure still imports this.
|
||||
# Will be removed once all layouts use register_sx_layout().
|
||||
render_to_sx_with_env = _render_to_sx_with_env
|
||||
|
||||
|
||||
async def render_to_html(__name: str, **kwargs: Any) -> str:
|
||||
"""Call a defcomp and get HTML back. No SX string literals.
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
Named layout presets for defpage.
|
||||
|
||||
Each layout generates header rows for full-page and OOB rendering.
|
||||
Layouts wrap existing helper functions from ``shared.sx.helpers`` so
|
||||
defpage can reference them by name (e.g. ``:layout :root``).
|
||||
Built-in layouts delegate to .sx defcomps via ``register_sx_layout``.
|
||||
Services register custom layouts via ``register_custom_layout``.
|
||||
|
||||
Layouts are registered in ``_LAYOUT_REGISTRY`` and looked up by
|
||||
``get_layout()`` at request time.
|
||||
@@ -13,12 +13,6 @@ from __future__ import annotations
|
||||
|
||||
from typing import Any, Callable, Awaitable
|
||||
|
||||
from .helpers import (
|
||||
root_header_sx, post_header_sx, post_admin_header_sx,
|
||||
oob_header_sx,
|
||||
mobile_menu_sx, mobile_root_nav_sx,
|
||||
post_mobile_nav_sx, post_admin_mobile_nav_sx,
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -83,57 +77,8 @@ def get_layout(name: str) -> Layout | None:
|
||||
return _LAYOUT_REGISTRY.get(name)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Built-in layouts
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
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 + ")"
|
||||
|
||||
|
||||
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 = await oob_header_sx("post-header-child", "", "")
|
||||
return "(<> " + post_hdr + " " + child_oob + ")"
|
||||
|
||||
|
||||
async def _post_admin_full(ctx: dict, **kw: Any) -> str:
|
||||
slug = ctx.get("post", {}).get("slug", "")
|
||||
selected = kw.get("selected", "")
|
||||
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 + ")"
|
||||
|
||||
|
||||
async def _post_admin_oob(ctx: dict, **kw: Any) -> str:
|
||||
slug = ctx.get("post", {}).get("slug", "")
|
||||
selected = kw.get("selected", "")
|
||||
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 + ")"
|
||||
|
||||
|
||||
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))
|
||||
|
||||
|
||||
async def _post_admin_mobile(ctx: dict, **kw: Any) -> str:
|
||||
slug = ctx.get("post", {}).get("slug", "")
|
||||
selected = kw.get("selected", "")
|
||||
return mobile_menu_sx(
|
||||
await post_admin_mobile_nav_sx(ctx, slug, selected),
|
||||
await post_mobile_nav_sx(ctx),
|
||||
await mobile_root_nav_sx(ctx),
|
||||
)
|
||||
|
||||
|
||||
register_layout(Layout("post", _post_full, _post_oob, _post_mobile))
|
||||
register_layout(Layout("post-admin", _post_admin_full, _post_admin_oob, _post_admin_mobile))
|
||||
# Built-in post/post-admin layouts are registered below via register_sx_layout,
|
||||
# after that function is defined.
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -153,27 +98,29 @@ def register_sx_layout(name: str, full_defcomp: str, oob_defcomp: str,
|
||||
register_sx_layout("account", "account-layout-full",
|
||||
"account-layout-oob", "account-layout-mobile")
|
||||
"""
|
||||
from .helpers import render_to_sx_with_env
|
||||
from .helpers import _render_to_sx_with_env
|
||||
|
||||
async def full_fn(ctx: dict, **kw: Any) -> str:
|
||||
env = {k.replace("_", "-"): v for k, v in kw.items()}
|
||||
return await render_to_sx_with_env(full_defcomp, env)
|
||||
return await _render_to_sx_with_env(full_defcomp, env)
|
||||
|
||||
async def oob_fn(ctx: dict, **kw: Any) -> str:
|
||||
env = {k.replace("_", "-"): v for k, v in kw.items()}
|
||||
return await render_to_sx_with_env(oob_defcomp, env)
|
||||
return await _render_to_sx_with_env(oob_defcomp, env)
|
||||
|
||||
mobile_fn = None
|
||||
if mobile_defcomp:
|
||||
async def mobile_fn(ctx: dict, **kw: Any) -> str:
|
||||
env = {k.replace("_", "-"): v for k, v in kw.items()}
|
||||
return await render_to_sx_with_env(mobile_defcomp, env)
|
||||
return await _render_to_sx_with_env(mobile_defcomp, env)
|
||||
|
||||
register_layout(Layout(name, full_fn, oob_fn, mobile_fn))
|
||||
|
||||
|
||||
# Register built-in "root" layout via .sx defcomps
|
||||
# Register built-in layouts via .sx defcomps
|
||||
register_sx_layout("root", "layout-root-full", "layout-root-oob", "layout-root-mobile")
|
||||
register_sx_layout("post", "layout-post-full", "layout-post-oob", "layout-post-mobile")
|
||||
register_sx_layout("post-admin", "layout-post-admin-full", "layout-post-admin-oob", "layout-post-admin-mobile")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -46,6 +46,7 @@ IO_PRIMITIVES: frozenset[str] = frozenset({
|
||||
"url-for",
|
||||
"route-prefix",
|
||||
"root-header-ctx",
|
||||
"post-header-ctx",
|
||||
"select-colours",
|
||||
"account-nav-ctx",
|
||||
"app-rights",
|
||||
@@ -482,6 +483,80 @@ async def _io_app_rights(
|
||||
return getattr(g, "rights", None) or {}
|
||||
|
||||
|
||||
async def _io_post_header_ctx(
|
||||
args: list[Any], kwargs: dict[str, Any], ctx: RequestContext
|
||||
) -> dict[str, Any]:
|
||||
"""``(post-header-ctx)`` → dict with post-level header values.
|
||||
|
||||
Reads post data from ``g._defpage_ctx`` (set by per-service page
|
||||
helpers), fetches container-nav and page cart count. Result is
|
||||
cached on ``g`` per request.
|
||||
|
||||
Returns dict with keys: slug, title, feature-image, link-href,
|
||||
container-nav, page-cart-count, cart-href, admin-href, is-admin,
|
||||
is-admin-page, select-colours.
|
||||
"""
|
||||
from quart import g, request
|
||||
cached = getattr(g, "_post_header_ctx", None)
|
||||
if cached is not None:
|
||||
return cached
|
||||
|
||||
from shared.infrastructure.urls import app_url
|
||||
from .types import NIL
|
||||
from .parser import SxExpr
|
||||
|
||||
dctx = getattr(g, "_defpage_ctx", None) or {}
|
||||
post = dctx.get("post") or {}
|
||||
slug = post.get("slug", "")
|
||||
if not slug:
|
||||
result: dict[str, Any] = {"slug": ""}
|
||||
g._post_header_ctx = result
|
||||
return result
|
||||
|
||||
title = (post.get("title") or "")[:160]
|
||||
feature_image = post.get("feature_image") or NIL
|
||||
|
||||
# Container nav (pre-fetched by page helper into defpage ctx)
|
||||
raw_nav = dctx.get("container_nav") or ""
|
||||
container_nav: Any = NIL
|
||||
nav_str = str(raw_nav).strip()
|
||||
if nav_str and nav_str.replace("(<>", "").replace(")", "").strip():
|
||||
if isinstance(raw_nav, SxExpr):
|
||||
container_nav = raw_nav
|
||||
else:
|
||||
container_nav = SxExpr(nav_str)
|
||||
|
||||
page_cart_count = dctx.get("page_cart_count", 0) or 0
|
||||
|
||||
rights = getattr(g, "rights", None) or {}
|
||||
is_admin = (
|
||||
rights.get("admin", False)
|
||||
if isinstance(rights, dict)
|
||||
else getattr(rights, "admin", False)
|
||||
)
|
||||
|
||||
is_admin_page = dctx.get("is_admin_section") or "/admin" in request.path
|
||||
|
||||
from quart import current_app
|
||||
select_colours = current_app.jinja_env.globals.get("select_colours", "")
|
||||
|
||||
result = {
|
||||
"slug": slug,
|
||||
"title": title,
|
||||
"feature-image": feature_image,
|
||||
"link-href": app_url("blog", f"/{slug}/"),
|
||||
"container-nav": container_nav,
|
||||
"page-cart-count": page_cart_count,
|
||||
"cart-href": app_url("cart", f"/{slug}/") if page_cart_count else "",
|
||||
"admin-href": app_url("blog", f"/{slug}/admin/"),
|
||||
"is-admin": is_admin,
|
||||
"is-admin-page": is_admin_page or NIL,
|
||||
"select-colours": select_colours,
|
||||
}
|
||||
g._post_header_ctx = result
|
||||
return result
|
||||
|
||||
|
||||
_IO_HANDLERS: dict[str, Any] = {
|
||||
"frag": _io_frag,
|
||||
"query": _io_query,
|
||||
@@ -499,6 +574,7 @@ _IO_HANDLERS: dict[str, Any] = {
|
||||
"url-for": _io_url_for,
|
||||
"route-prefix": _io_route_prefix,
|
||||
"root-header-ctx": _io_root_header_ctx,
|
||||
"post-header-ctx": _io_post_header_ctx,
|
||||
"select-colours": _io_select_colours,
|
||||
"account-nav-ctx": _io_account_nav_ctx,
|
||||
"app-rights": _io_app_rights,
|
||||
|
||||
@@ -203,6 +203,56 @@
|
||||
(defcomp ~layout-root-mobile ()
|
||||
(~root-mobile-auto))
|
||||
|
||||
;; Post layout — root + post header
|
||||
(defcomp ~layout-post-full ()
|
||||
(<> (~root-header-auto)
|
||||
(~header-child-sx :inner (~post-header-auto))))
|
||||
|
||||
(defcomp ~layout-post-oob ()
|
||||
(<> (~post-header-auto true)
|
||||
(~oob-header-sx :parent-id "post-header-child" :row "")))
|
||||
|
||||
(defcomp ~layout-post-mobile ()
|
||||
(let ((__phctx (post-header-ctx))
|
||||
(__rhctx (root-header-ctx)))
|
||||
(<>
|
||||
(when (get __phctx "slug")
|
||||
(~mobile-menu-section
|
||||
:label (slice (get __phctx "title") 0 40)
|
||||
:href (get __phctx "link-href")
|
||||
:level 1
|
||||
:items (~post-nav-auto)))
|
||||
(~root-mobile-auto))))
|
||||
|
||||
;; Post-admin layout — root + post header with nested admin row
|
||||
(defcomp ~layout-post-admin-full (&key selected)
|
||||
(let ((__admin-hdr (~post-admin-header-auto nil selected)))
|
||||
(<> (~root-header-auto)
|
||||
(~header-child-sx
|
||||
:inner (~post-header-auto nil)))))
|
||||
|
||||
(defcomp ~layout-post-admin-oob (&key selected)
|
||||
(<> (~post-header-auto true)
|
||||
(~oob-header-sx :parent-id "post-header-child"
|
||||
:row (~post-admin-header-auto nil selected))))
|
||||
|
||||
(defcomp ~layout-post-admin-mobile (&key selected)
|
||||
(let ((__phctx (post-header-ctx)))
|
||||
(<>
|
||||
(when (get __phctx "slug")
|
||||
(~mobile-menu-section
|
||||
:label "admin"
|
||||
:href (get __phctx "admin-href")
|
||||
:level 2
|
||||
:items (~post-admin-nav-auto selected)))
|
||||
(when (get __phctx "slug")
|
||||
(~mobile-menu-section
|
||||
:label (slice (get __phctx "title") 0 40)
|
||||
:href (get __phctx "link-href")
|
||||
:level 1
|
||||
:items (~post-nav-auto)))
|
||||
(~root-mobile-auto))))
|
||||
|
||||
(defcomp ~error-content (&key errnum message image)
|
||||
(div :class "text-center p-8 max-w-lg mx-auto"
|
||||
(div :class "font-bold text-2xl md:text-4xl text-red-500 mb-4" errnum)
|
||||
@@ -214,6 +264,85 @@
|
||||
(defcomp ~clear-oob-div (&key id)
|
||||
(div :id id :sx-swap-oob "outerHTML"))
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Post-level auto-fetching macros — use (post-header-ctx) IO primitive
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defmacro ~post-nav-auto ()
|
||||
"Post-level nav items: page cart badge + container nav + admin cog."
|
||||
(quasiquote
|
||||
(let ((__phctx (post-header-ctx)))
|
||||
(when (get __phctx "slug")
|
||||
(<>
|
||||
(when (> (get __phctx "page-cart-count") 0)
|
||||
(~page-cart-badge :href (get __phctx "cart-href")
|
||||
:count (str (get __phctx "page-cart-count"))))
|
||||
(when (get __phctx "container-nav")
|
||||
(~container-nav-wrapper :content (get __phctx "container-nav")))
|
||||
(when (get __phctx "is-admin")
|
||||
(~admin-cog-button :href (get __phctx "admin-href")
|
||||
:is-admin-page (get __phctx "is-admin-page"))))))))
|
||||
|
||||
(defmacro ~post-header-auto (oob)
|
||||
"Post-level header row. Reads post data via (post-header-ctx)."
|
||||
(quasiquote
|
||||
(let ((__phctx (post-header-ctx)))
|
||||
(when (get __phctx "slug")
|
||||
(~menu-row-sx :id "post-row" :level 1
|
||||
:link-href (get __phctx "link-href")
|
||||
:link-label-content (~post-label
|
||||
:feature-image (get __phctx "feature-image")
|
||||
:title (get __phctx "title"))
|
||||
:nav (~post-nav-auto)
|
||||
:child-id "post-header-child"
|
||||
:oob (unquote oob) :external true)))))
|
||||
|
||||
(defmacro ~post-admin-nav-auto (selected)
|
||||
"Post-admin nav items: calendars, markets, etc."
|
||||
(quasiquote
|
||||
(let ((__phctx (post-header-ctx)))
|
||||
(when (get __phctx "slug")
|
||||
(let ((__slug (get __phctx "slug"))
|
||||
(__sc (get __phctx "select-colours")))
|
||||
(<>
|
||||
(~nav-link :href (app-url "events" (str "/" __slug "/admin/"))
|
||||
:label "calendars" :select-colours __sc
|
||||
:is-selected (when (= (unquote selected) "calendars") "true"))
|
||||
(~nav-link :href (app-url "market" (str "/" __slug "/admin/"))
|
||||
:label "markets" :select-colours __sc
|
||||
:is-selected (when (= (unquote selected) "markets") "true"))
|
||||
(~nav-link :href (app-url "cart" (str "/" __slug "/admin/payments/"))
|
||||
:label "payments" :select-colours __sc
|
||||
:is-selected (when (= (unquote selected) "payments") "true"))
|
||||
(~nav-link :href (app-url "blog" (str "/" __slug "/admin/entries/"))
|
||||
:label "entries" :select-colours __sc
|
||||
:is-selected (when (= (unquote selected) "entries") "true"))
|
||||
(~nav-link :href (app-url "blog" (str "/" __slug "/admin/data/"))
|
||||
:label "data" :select-colours __sc
|
||||
:is-selected (when (= (unquote selected) "data") "true"))
|
||||
(~nav-link :href (app-url "blog" (str "/" __slug "/admin/preview/"))
|
||||
:label "preview" :select-colours __sc
|
||||
:is-selected (when (= (unquote selected) "preview") "true"))
|
||||
(~nav-link :href (app-url "blog" (str "/" __slug "/admin/edit/"))
|
||||
:label "edit" :select-colours __sc
|
||||
:is-selected (when (= (unquote selected) "edit") "true"))
|
||||
(~nav-link :href (app-url "blog" (str "/" __slug "/admin/settings/"))
|
||||
:label "settings" :select-colours __sc
|
||||
:is-selected (when (= (unquote selected) "settings") "true"))))))))
|
||||
|
||||
(defmacro ~post-admin-header-auto (oob selected)
|
||||
"Post-admin header row. Uses (post-header-ctx) for slug + URLs."
|
||||
(quasiquote
|
||||
(let ((__phctx (post-header-ctx)))
|
||||
(when (get __phctx "slug")
|
||||
(~menu-row-sx :id "post-admin-row" :level 2
|
||||
:link-href (get __phctx "admin-href")
|
||||
:link-label-content (~post-admin-label
|
||||
:selected (unquote selected))
|
||||
:nav (~post-admin-nav-auto (unquote selected))
|
||||
:child-id "post-admin-header-child"
|
||||
:oob (unquote oob))))))
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Shared nav helpers — used by post_header_sx / post_admin_header_sx
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
@@ -222,8 +222,8 @@ def _docs_evaluator_sx() -> str:
|
||||
)
|
||||
|
||||
|
||||
async def _docs_primitives_sx() -> str:
|
||||
prims = await _primitives_section_sx()
|
||||
def _docs_primitives_sx() -> str:
|
||||
prims = _primitives_section_sx()
|
||||
return (
|
||||
f'(~doc-page :title "Primitives"'
|
||||
f' (~doc-section :title "Built-in functions" :id "builtins"'
|
||||
@@ -402,11 +402,11 @@ def _reference_attr_detail_sx(slug: str) -> str:
|
||||
)
|
||||
|
||||
|
||||
async def _reference_attrs_sx() -> str:
|
||||
def _reference_attrs_sx() -> str:
|
||||
from content.pages import REQUEST_ATTRS, BEHAVIOR_ATTRS, SX_UNIQUE_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)
|
||||
req = _attr_table_sx("Request Attributes", REQUEST_ATTRS)
|
||||
beh = _attr_table_sx("Behavior Attributes", BEHAVIOR_ATTRS)
|
||||
uniq = _attr_table_sx("Unique to sx", SX_UNIQUE_ATTRS)
|
||||
return (
|
||||
f'(~doc-page :title "Attribute Reference"'
|
||||
f' (p :class "text-stone-600 mb-6"'
|
||||
@@ -419,10 +419,10 @@ async def _reference_attrs_sx() -> str:
|
||||
)
|
||||
|
||||
|
||||
async def _reference_headers_sx() -> str:
|
||||
def _reference_headers_sx() -> str:
|
||||
from content.pages import REQUEST_HEADERS, RESPONSE_HEADERS
|
||||
req_table = await _headers_table_sx("Request Headers", REQUEST_HEADERS)
|
||||
resp_table = await _headers_table_sx("Response Headers", RESPONSE_HEADERS)
|
||||
req_table = _headers_table_sx("Request Headers", REQUEST_HEADERS)
|
||||
resp_table = _headers_table_sx("Response Headers", RESPONSE_HEADERS)
|
||||
return (
|
||||
f'(~doc-page :title "Headers"'
|
||||
f' (p :class "text-stone-600 mb-6"'
|
||||
@@ -433,29 +433,29 @@ async def _reference_headers_sx() -> str:
|
||||
)
|
||||
|
||||
|
||||
async def _reference_events_sx() -> str:
|
||||
from shared.sx.helpers import render_to_sx
|
||||
def _reference_events_sx() -> str:
|
||||
from shared.sx.helpers import sx_call
|
||||
from shared.sx.parser import SxExpr
|
||||
from content.pages import EVENTS
|
||||
rows = []
|
||||
for name, desc in EVENTS:
|
||||
rows.append(await render_to_sx("doc-two-col-row", name=name, description=desc))
|
||||
rows.append(sx_call("doc-two-col-row", name=name, description=desc))
|
||||
rows_sx = "(<> " + " ".join(rows) + ")"
|
||||
table = await render_to_sx("doc-two-col-table",
|
||||
table = sx_call("doc-two-col-table",
|
||||
intro="sx fires custom DOM events at various points in the request lifecycle.",
|
||||
col1="Event", col2="Description", rows=SxExpr(rows_sx))
|
||||
return f'(~doc-page :title "Events" {table})'
|
||||
|
||||
|
||||
async def _reference_js_api_sx() -> str:
|
||||
from shared.sx.helpers import render_to_sx
|
||||
def _reference_js_api_sx() -> str:
|
||||
from shared.sx.helpers import sx_call
|
||||
from shared.sx.parser import SxExpr
|
||||
from content.pages import JS_API
|
||||
rows = []
|
||||
for name, desc in JS_API:
|
||||
rows.append(await render_to_sx("doc-two-col-row", name=name, description=desc))
|
||||
rows.append(sx_call("doc-two-col-row", name=name, description=desc))
|
||||
rows_sx = "(<> " + " ".join(rows) + ")"
|
||||
table = await render_to_sx("doc-two-col-table",
|
||||
table = sx_call("doc-two-col-table",
|
||||
intro="The client-side sx.js library exposes a public API for programmatic use.",
|
||||
col1="Method", col2="Description", rows=SxExpr(rows_sx))
|
||||
return f'(~doc-page :title "JavaScript API" {table})'
|
||||
|
||||
@@ -18,8 +18,8 @@ async def _sx_full_headers(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env
|
||||
from shared.sx.parser import SxExpr
|
||||
|
||||
main_nav = await _main_nav_sx(kw.get("section"))
|
||||
sx_row = await _sx_header_sx(main_nav)
|
||||
main_nav = _main_nav_sx(kw.get("section"))
|
||||
sx_row = _sx_header_sx(main_nav)
|
||||
return await render_to_sx_with_env("sx-layout-full", {},
|
||||
sx_row=SxExpr(sx_row))
|
||||
|
||||
@@ -29,8 +29,8 @@ async def _sx_oob_headers(ctx: dict, **kw: Any) -> str:
|
||||
from shared.sx.helpers import render_to_sx_with_env, oob_header_sx
|
||||
from shared.sx.parser import SxExpr
|
||||
|
||||
main_nav = await _main_nav_sx(kw.get("section"))
|
||||
sx_row = await _sx_header_sx(main_nav)
|
||||
main_nav = _main_nav_sx(kw.get("section"))
|
||||
sx_row = _sx_header_sx(main_nav)
|
||||
rows = await render_to_sx_with_env("sx-layout-full", {},
|
||||
sx_row=SxExpr(sx_row))
|
||||
return await oob_header_sx("root-header-child", "sx-header-child", rows)
|
||||
@@ -47,9 +47,9 @@ async def _sx_section_full_headers(ctx: dict, **kw: Any) -> str:
|
||||
sub_nav = kw.get("sub_nav", "")
|
||||
selected = kw.get("selected", "")
|
||||
|
||||
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)
|
||||
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)
|
||||
return await render_to_sx_with_env("sx-section-layout-full", {},
|
||||
sx_row=SxExpr(sx_row))
|
||||
|
||||
@@ -65,9 +65,9 @@ async def _sx_section_oob_headers(ctx: dict, **kw: Any) -> str:
|
||||
sub_nav = kw.get("sub_nav", "")
|
||||
selected = kw.get("selected", "")
|
||||
|
||||
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)
|
||||
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)
|
||||
rows = await render_to_sx_with_env("sx-section-layout-full", {},
|
||||
sx_row=SxExpr(sx_row))
|
||||
return await oob_header_sx("root-header-child", "sx-header-child", rows)
|
||||
@@ -76,12 +76,12 @@ async def _sx_section_oob_headers(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, render_to_sx, SxExpr,
|
||||
mobile_menu_sx, mobile_root_nav_sx, sx_call, SxExpr,
|
||||
)
|
||||
|
||||
main_nav = await _main_nav_sx(kw.get("section"))
|
||||
main_nav = _main_nav_sx(kw.get("section"))
|
||||
return mobile_menu_sx(
|
||||
await render_to_sx("mobile-menu-section",
|
||||
sx_call("mobile-menu-section",
|
||||
label="sx", href="/", level=1, colour="violet",
|
||||
items=SxExpr(main_nav)),
|
||||
await mobile_root_nav_sx(ctx),
|
||||
@@ -91,21 +91,21 @@ async def _sx_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, render_to_sx, SxExpr,
|
||||
mobile_menu_sx, mobile_root_nav_sx, sx_call, SxExpr,
|
||||
)
|
||||
|
||||
section = kw.get("section", "")
|
||||
sub_label = kw.get("sub_label", section)
|
||||
sub_href = kw.get("sub_href", "/")
|
||||
sub_nav = kw.get("sub_nav", "")
|
||||
main_nav = await _main_nav_sx(section)
|
||||
main_nav = _main_nav_sx(section)
|
||||
|
||||
parts = []
|
||||
if sub_nav:
|
||||
parts.append(await render_to_sx("mobile-menu-section",
|
||||
parts.append(sx_call("mobile-menu-section",
|
||||
label=sub_label, href=sub_href, level=2, colour="violet",
|
||||
items=SxExpr(sub_nav)))
|
||||
parts.append(await render_to_sx("mobile-menu-section",
|
||||
parts.append(sx_call("mobile-menu-section",
|
||||
label="sx", href="/", level=1, colour="violet",
|
||||
items=SxExpr(main_nav)))
|
||||
parts.append(await mobile_root_nav_sx(ctx))
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from shared.sx.helpers import (
|
||||
render_to_sx, SxExpr,
|
||||
sx_call, SxExpr,
|
||||
)
|
||||
|
||||
|
||||
async def _nav_items_sx(items: list[tuple[str, str]], current: str | None = None) -> str:
|
||||
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(await render_to_sx("nav-link",
|
||||
parts.append(sx_call("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",
|
||||
@@ -18,54 +18,54 @@ async def _nav_items_sx(items: list[tuple[str, str]], current: str | None = None
|
||||
return "(<> " + " ".join(parts) + ")"
|
||||
|
||||
|
||||
async def _doc_nav_sx(items: list[tuple[str, str]], current: str) -> str:
|
||||
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 await render_to_sx("doc-nav", items=SxExpr(f"(list {items_sx})"), current=current)
|
||||
return sx_call("doc-nav", items=SxExpr(f"(list {items_sx})"), current=current)
|
||||
|
||||
|
||||
async def _attr_table_sx(title: str, attrs: list[tuple[str, str, bool]]) -> str:
|
||||
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(await render_to_sx("doc-attr-row", attr=attr, description=desc,
|
||||
rows.append(sx_call("doc-attr-row", attr=attr, description=desc,
|
||||
exists="true" if exists else None,
|
||||
href=href))
|
||||
rows_sx = "(<> " + " ".join(rows) + ")"
|
||||
return await render_to_sx("doc-attr-table", title=title, rows=SxExpr(rows_sx))
|
||||
return sx_call("doc-attr-table", title=title, rows=SxExpr(rows_sx))
|
||||
|
||||
|
||||
async def _headers_table_sx(title: str, headers: list[tuple[str, str, str]]) -> str:
|
||||
def _headers_table_sx(title: str, headers: list[tuple[str, str, str]]) -> str:
|
||||
"""Build a headers reference table."""
|
||||
rows = []
|
||||
for name, value, desc in headers:
|
||||
rows.append(await render_to_sx("doc-headers-row",
|
||||
rows.append(sx_call("doc-headers-row",
|
||||
name=name, value=value, description=desc))
|
||||
rows_sx = "(<> " + " ".join(rows) + ")"
|
||||
return await render_to_sx("doc-headers-table", title=title, rows=SxExpr(rows_sx))
|
||||
return sx_call("doc-headers-table", title=title, rows=SxExpr(rows_sx))
|
||||
|
||||
|
||||
async def _primitives_section_sx() -> str:
|
||||
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(await render_to_sx("doc-primitives-table",
|
||||
parts.append(sx_call("doc-primitives-table",
|
||||
category=category,
|
||||
primitives=SxExpr(f"(list {prims_sx})")))
|
||||
return " ".join(parts)
|
||||
|
||||
|
||||
async def _sx_header_sx(nav: str | None = None, *, child: str | None = None) -> str:
|
||||
def _sx_header_sx(nav: str | None = None, *, child: str | None = None) -> str:
|
||||
"""Build the sx docs menu-row."""
|
||||
label_sx = await render_to_sx("sx-docs-label")
|
||||
return await render_to_sx("menu-row-sx",
|
||||
label_sx = sx_call("sx-docs-label")
|
||||
return sx_call("menu-row-sx",
|
||||
id="sx-row", level=1, colour="violet",
|
||||
link_href="/", link_label="sx",
|
||||
link_label_content=SxExpr(label_sx),
|
||||
@@ -75,40 +75,40 @@ async def _sx_header_sx(nav: str | None = None, *, child: str | None = None) ->
|
||||
)
|
||||
|
||||
|
||||
async def _docs_nav_sx(current: str | None = None) -> str:
|
||||
def _docs_nav_sx(current: str | None = None) -> str:
|
||||
from content.pages import DOCS_NAV
|
||||
return await _nav_items_sx(DOCS_NAV, current)
|
||||
return _nav_items_sx(DOCS_NAV, current)
|
||||
|
||||
|
||||
async def _reference_nav_sx(current: str | None = None) -> str:
|
||||
def _reference_nav_sx(current: str | None = None) -> str:
|
||||
from content.pages import REFERENCE_NAV
|
||||
return await _nav_items_sx(REFERENCE_NAV, current)
|
||||
return _nav_items_sx(REFERENCE_NAV, current)
|
||||
|
||||
|
||||
async def _protocols_nav_sx(current: str | None = None) -> str:
|
||||
def _protocols_nav_sx(current: str | None = None) -> str:
|
||||
from content.pages import PROTOCOLS_NAV
|
||||
return await _nav_items_sx(PROTOCOLS_NAV, current)
|
||||
return _nav_items_sx(PROTOCOLS_NAV, current)
|
||||
|
||||
|
||||
async def _examples_nav_sx(current: str | None = None) -> str:
|
||||
def _examples_nav_sx(current: str | None = None) -> str:
|
||||
from content.pages import EXAMPLES_NAV
|
||||
return await _nav_items_sx(EXAMPLES_NAV, current)
|
||||
return _nav_items_sx(EXAMPLES_NAV, current)
|
||||
|
||||
|
||||
async def _essays_nav_sx(current: str | None = None) -> str:
|
||||
def _essays_nav_sx(current: str | None = None) -> str:
|
||||
from content.pages import ESSAYS_NAV
|
||||
return await _nav_items_sx(ESSAYS_NAV, current)
|
||||
return _nav_items_sx(ESSAYS_NAV, current)
|
||||
|
||||
|
||||
async def _main_nav_sx(current_section: str | None = None) -> str:
|
||||
def _main_nav_sx(current_section: str | None = None) -> str:
|
||||
from content.pages import MAIN_NAV
|
||||
return await _nav_items_sx(MAIN_NAV, current_section)
|
||||
return _nav_items_sx(MAIN_NAV, current_section)
|
||||
|
||||
|
||||
async def _sub_row_sx(sub_label: str, sub_href: str, sub_nav: str,
|
||||
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 await render_to_sx("menu-row-sx",
|
||||
return sx_call("menu-row-sx",
|
||||
id="sx-sub-row", level=2, colour="violet",
|
||||
link_href=sub_href, link_label=sub_label,
|
||||
selected=selected or None,
|
||||
|
||||
@@ -5,7 +5,7 @@ import os
|
||||
from datetime import datetime
|
||||
|
||||
from shared.sx.jinja_bridge import load_service_components
|
||||
from shared.sx.helpers import render_to_sx, SxExpr, render_to_sx_with_env, full_page_sx
|
||||
from shared.sx.helpers import sx_call, SxExpr, render_to_sx_with_env, full_page_sx
|
||||
|
||||
# Load test-specific .sx components at import time
|
||||
load_service_components(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
|
||||
@@ -75,7 +75,7 @@ def _build_summary_data(result: dict | None, running: bool, csrf: str,
|
||||
|
||||
async def test_detail_sx(test: dict) -> str:
|
||||
"""Return s-expression wire format for a test detail view."""
|
||||
return await render_to_sx("test-detail-section", test=test)
|
||||
return sx_call("test-detail-section", test=test)
|
||||
|
||||
|
||||
async def render_dashboard_page_sx(ctx: dict, result: dict | None,
|
||||
@@ -96,9 +96,9 @@ async def render_dashboard_page_sx(ctx: dict, result: dict | None,
|
||||
else:
|
||||
summary_data["state"] = "empty-filtered"
|
||||
|
||||
inner = await render_to_sx("test-results-partial",
|
||||
inner = sx_call("test-results-partial",
|
||||
summary_data=summary_data, sections=sections, has_failures=has_failures)
|
||||
content = await render_to_sx("test-results-wrap", running=running, inner=SxExpr(inner))
|
||||
content = sx_call("test-results-wrap", running=running, inner=SxExpr(inner))
|
||||
hdr = await render_to_sx_with_env("test-layout-full", {},
|
||||
services=_service_list(),
|
||||
active_service=active_service,
|
||||
@@ -124,9 +124,9 @@ async def render_results_partial_sx(result: dict | None, running: bool,
|
||||
else:
|
||||
summary_data["state"] = "empty-filtered"
|
||||
|
||||
inner = await render_to_sx("test-results-partial",
|
||||
inner = sx_call("test-results-partial",
|
||||
summary_data=summary_data, sections=sections, has_failures=has_failures)
|
||||
return await render_to_sx("test-results-wrap", running=running, inner=SxExpr(inner))
|
||||
return sx_call("test-results-wrap", running=running, inner=SxExpr(inner))
|
||||
|
||||
|
||||
async def render_test_detail_page_sx(ctx: dict, test: dict) -> str:
|
||||
@@ -136,7 +136,7 @@ async def render_test_detail_page_sx(ctx: dict, test: dict) -> str:
|
||||
test_nodeid=test["nodeid"],
|
||||
test_label=test["nodeid"].rsplit("::", 1)[-1],
|
||||
)
|
||||
content = await render_to_sx("test-detail",
|
||||
content = sx_call("test-detail",
|
||||
nodeid=test["nodeid"],
|
||||
outcome=test["outcome"],
|
||||
duration=str(test["duration"]),
|
||||
|
||||
Reference in New Issue
Block a user