Remove render_to_sx from public API: enforce sx_call for all service code

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:
2026-03-04 19:30:45 +00:00
parent 57e0d0c341
commit 959e63d440
61 changed files with 1352 additions and 1208 deletions

View File

@@ -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,

View File

@@ -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(" &middot; ", "")
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>&middot; {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>&middot; &pound;{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))

View File

@@ -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)

View File

@@ -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))),
)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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))