Refactor SX templates: shared components, Python migration, cleanup
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 6m0s

- Extract shared components (empty-state, delete-btn, sentinel, crud-*,
  view-toggle, img-or-placeholder, avatar, sumup-settings-form, auth
  forms, order tables/detail/checkout)
- Migrate all Python sx_call() callers to use shared components directly
- Remove 55+ thin wrapper defcomps from domain .sx files
- Remove trivial passthrough wrappers (blog-header-label, market-card-text, etc)
- Unify duplicate auth flows (account + federation) into shared/sx/templates/auth.sx
- Unify duplicate order views (cart + orders) into shared/sx/templates/orders.sx
- Disable static file caching in dev (SEND_FILE_MAX_AGE_DEFAULT=0)
- Add SX response validation and debug headers

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-01 20:34:34 +00:00
parent 755313bd29
commit c0d369eb8e
58 changed files with 3473 additions and 1210 deletions

View File

@@ -10,6 +10,7 @@ from __future__ import annotations
import os
from typing import Any
from shared.sx.jinja_bridge import load_service_components
from shared.sx.parser import serialize
from shared.sx.helpers import (
call_url, get_asset_url, sx_call, SxExpr,
root_header_sx,
@@ -102,7 +103,7 @@ def _market_header_sx(ctx: dict, *, oob: bool = False) -> str:
sub_slug = ctx.get("sub_slug", "")
hx_select_search = ctx.get("hx_select_search", "#main-panel")
sub_div = sx_call("market-sub-slug", sub=sub_slug) if sub_slug else ""
sub_div = f'(div {serialize(sub_slug)})' if sub_slug else ""
label_sx = sx_call(
"market-shop-label",
title=market_title, top_slug=top_slug or "",
@@ -439,14 +440,14 @@ def _product_cards_sx(ctx: dict) -> str:
else:
next_qs = f"?page={page + 1}"
next_url = prefix + current_local_href + next_qs
parts.append(sx_call("market-sentinel-mobile",
parts.append(sx_call("sentinel-mobile",
id=f"sentinel-{page}-m", next_url=next_url,
hyperscript=_MOBILE_SENTINEL_HS))
parts.append(sx_call("market-sentinel-desktop",
parts.append(sx_call("sentinel-desktop",
id=f"sentinel-{page}-d", next_url=next_url,
hyperscript=_DESKTOP_SENTINEL_HS))
else:
parts.append(sx_call("market-sentinel-end"))
parts.append(sx_call("end-of-results"))
return "(<> " + " ".join(parts) + ")"
@@ -1170,7 +1171,7 @@ def _market_cards_sx(markets: list, page_info: dict, page: int, has_more: bool,
post_slug=post_slug) for m in markets]
if has_more:
parts.append(sx_call(
"market-market-sentinel",
"sentinel-simple",
id=f"sentinel-{page}", next_url=next_url,
))
return "(<> " + " ".join(parts) + ")"
@@ -1197,7 +1198,8 @@ def _markets_grid(cards_sx: str) -> str:
def _no_markets_sx(message: str = "No markets available") -> str:
"""Empty state for markets as sx."""
return sx_call("market-no-markets", message=message)
return sx_call("empty-state", icon="fa fa-store", message=message,
cls="px-3 py-12 text-center text-stone-400")
async def render_all_markets_page(ctx: dict, markets: list, has_more: bool,
@@ -1214,7 +1216,7 @@ async def render_all_markets_page(ctx: dict, markets: list, has_more: bool,
content = _markets_grid(cards)
else:
content = _no_markets_sx()
content = "(<> " + content + " " + sx_call("market-bottom-spacer") + ")"
content = "(<> " + content + " " + '(div :class "pb-8")' + ")"
hdr = root_header_sx(ctx)
return full_page_sx(ctx, header_rows=hdr, content=content)
@@ -1234,7 +1236,7 @@ async def render_all_markets_oob(ctx: dict, markets: list, has_more: bool,
content = _markets_grid(cards)
else:
content = _no_markets_sx()
content = "(<> " + content + " " + sx_call("market-bottom-spacer") + ")"
content = "(<> " + content + " " + '(div :class "pb-8")' + ")"
oobs = root_header_sx(ctx, oob=True)
return oob_page_sx(oobs=oobs, content=content)
@@ -1272,7 +1274,7 @@ async def render_page_markets_page(ctx: dict, markets: list, has_more: bool,
content = _markets_grid(cards)
else:
content = _no_markets_sx("No markets for this page")
content = "(<> " + content + " " + sx_call("market-bottom-spacer") + ")"
content = "(<> " + content + " " + '(div :class "pb-8")' + ")"
hdr = root_header_sx(ctx)
hdr = "(<> " + hdr + " " + header_child_sx(_post_header_sx(ctx)) + ")"
@@ -1296,7 +1298,7 @@ async def render_page_markets_oob(ctx: dict, markets: list, has_more: bool,
content = _markets_grid(cards)
else:
content = _no_markets_sx("No markets for this page")
content = "(<> " + content + " " + sx_call("market-bottom-spacer") + ")"
content = "(<> " + content + " " + '(div :class "pb-8")' + ")"
oobs = _oob_header_sx("post-header-child", "market-header-child", "")
oobs = "(<> " + oobs + " " + _post_header_sx(ctx, oob=True) + ")"
@@ -1535,12 +1537,17 @@ async def _markets_admin_panel_sx(ctx: dict) -> str:
form_html = ""
if can_create:
create_url = url_for("page_admin.create_market")
form_html = sx_call("market-admin-create-form",
create_url=create_url, csrf=csrf)
form_html = sx_call("crud-create-form",
create_url=create_url, csrf=csrf,
errors_id="market-create-errors",
list_id="markets-list",
placeholder="e.g. Suma, Craft Fair",
btn_label="Add market")
list_html = _markets_admin_list_sx(ctx, markets)
return sx_call("market-admin-panel",
form=SxExpr(form_html), list=SxExpr(list_html))
return sx_call("crud-panel",
form=SxExpr(form_html), list=SxExpr(list_html),
list_id="markets-list")
def _markets_admin_list_sx(ctx: dict, markets: list) -> str:
@@ -1552,7 +1559,9 @@ def _markets_admin_list_sx(ctx: dict, markets: list) -> str:
prefix = route_prefix()
if not markets:
return sx_call("market-admin-empty")
return sx_call("empty-state",
message="No markets yet. Create one above.",
cls="text-gray-500 mt-4")
parts = []
for m in markets:
@@ -1562,9 +1571,12 @@ def _markets_admin_list_sx(ctx: dict, markets: list) -> str:
href = prefix + f"/{post_slug}/{m_slug}/"
del_url = url_for("page_admin.delete_market", market_slug=m_slug)
csrf_hdr = f'{{"X-CSRFToken":"{csrf}"}}'
parts.append(sx_call("market-admin-item",
parts.append(sx_call("crud-item",
href=href, name=m_name, slug=m_slug,
del_url=del_url, csrf_hdr=csrf_hdr))
del_url=del_url, csrf_hdr=csrf_hdr,
list_id="markets-list",
confirm_title="Delete market?",
confirm_text="Products will be hidden (soft delete)"))
return "".join(parts)