All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 2m6s
sx-headers attributes now use native SX dict format {:key val} instead of
JSON strings. Eliminates manual JSON string construction in both .sx files
and Python callers.
- sx.js: parse sx-headers/sx-vals as SX dict ({: prefix) with JSON fallback,
add _serializeDict for dict→attribute serialization, fix verbInfo scope in
_doFetch error handler
- html.py: serialize dict attribute values via SX serialize() not str()
- All .sx files: {:X-CSRFToken csrf} replaces (str "{\"X-CSRFToken\": ...}")
- All Python callers: {"X-CSRFToken": csrf} dict replaces f-string JSON
- Blog like: extract ~blog-like-toggle, fix POST returning wrong component,
fix emoji escapes in .sx (parser has no \U support), fix card :hx-headers
keyword mismatch, wrap sx_content in SxExpr for evaluation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
159 lines
5.7 KiB
Python
159 lines
5.7 KiB
Python
"""Market page helpers — data-only.
|
|
|
|
All helpers return data values (dicts, lists) — no sx_call().
|
|
Markup composition lives entirely in .sx defpage and .sx defcomp files.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from .cards import _market_card_data
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Registration
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def _register_market_helpers() -> None:
|
|
from shared.sx.pages import register_page_helpers
|
|
|
|
register_page_helpers("market", {
|
|
"all-markets-data": _h_all_markets_data,
|
|
"page-markets-data": _h_page_markets_data,
|
|
"page-admin-data": _h_page_admin_data,
|
|
"market-home-data": _h_market_home_data,
|
|
})
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# All markets (global view)
|
|
# ---------------------------------------------------------------------------
|
|
|
|
async def _h_all_markets_data(**kw) -> dict:
|
|
from quart import g, url_for, request
|
|
from shared.utils import route_prefix
|
|
from shared.services.registry import services
|
|
from shared.infrastructure.data_client import fetch_data
|
|
from shared.contracts.dtos import PostDTO, dto_from_dict
|
|
|
|
page = int(request.args.get("page", 1))
|
|
markets, has_more = await services.market.list_marketplaces(
|
|
g.s, page=page, per_page=20,
|
|
)
|
|
|
|
page_info = {}
|
|
if markets:
|
|
post_ids = list({m.container_id for m in markets if m.container_type == "page"})
|
|
if post_ids:
|
|
raw_posts = await fetch_data("blog", "posts-by-ids",
|
|
params={"ids": ",".join(str(i) for i in post_ids)},
|
|
required=False) or []
|
|
for raw_p in raw_posts:
|
|
p = dto_from_dict(PostDTO, raw_p)
|
|
page_info[p.id] = {"title": p.title, "slug": p.slug}
|
|
|
|
if not markets:
|
|
return {"no-markets": True}
|
|
|
|
prefix = route_prefix()
|
|
next_url = prefix + url_for("all_markets.markets_fragment", page=page + 1)
|
|
market_data = [_market_card_data(m, page_info) for m in markets]
|
|
|
|
return {
|
|
"no-markets": False,
|
|
"market-data": market_data,
|
|
"market-page": page,
|
|
"has-more": has_more,
|
|
"next-url": next_url,
|
|
}
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Page markets (markets for a single page)
|
|
# ---------------------------------------------------------------------------
|
|
|
|
async def _h_page_markets_data(slug=None, **kw) -> dict:
|
|
from quart import g, url_for, request
|
|
from shared.utils import route_prefix
|
|
from shared.services.registry import services
|
|
|
|
post = g.post_data["post"]
|
|
page = int(request.args.get("page", 1))
|
|
markets, has_more = await services.market.list_marketplaces(
|
|
g.s, "page", post["id"], page=page, per_page=20,
|
|
)
|
|
post_slug = post.get("slug", "")
|
|
|
|
if not markets:
|
|
return {"no-markets": True}
|
|
|
|
prefix = route_prefix()
|
|
next_url = prefix + url_for("page_markets.markets_fragment", page=page + 1)
|
|
market_data = [_market_card_data(m, {}, show_page_badge=False,
|
|
post_slug=post_slug) for m in markets]
|
|
|
|
return {
|
|
"no-markets": False,
|
|
"market-data": market_data,
|
|
"market-page": page,
|
|
"has-more": has_more,
|
|
"next-url": next_url,
|
|
}
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Page admin (CRUD panel for markets under a page)
|
|
# ---------------------------------------------------------------------------
|
|
|
|
async def _h_page_admin_data(slug=None, **kw) -> dict:
|
|
from quart import g, url_for
|
|
from shared.sx.page import get_template_context
|
|
from shared.services.registry import services
|
|
from shared.browser.app.csrf import generate_csrf_token
|
|
from shared.utils import route_prefix
|
|
|
|
ctx = await get_template_context()
|
|
rights = ctx.get("rights") or {}
|
|
is_admin = getattr(rights, "admin", False) if hasattr(rights, "admin") else rights.get("admin", False)
|
|
has_access = ctx.get("has_access")
|
|
can_create = has_access("page_admin.create_market") if callable(has_access) else is_admin
|
|
csrf = generate_csrf_token()
|
|
|
|
post = ctx.get("post") or {}
|
|
post_id = post.get("id")
|
|
post_slug = post.get("slug", "")
|
|
markets_raw = await services.market.marketplaces_for_container(g.s, "page", post_id) if post_id else []
|
|
|
|
prefix = route_prefix()
|
|
markets = []
|
|
for m in markets_raw:
|
|
m_slug = getattr(m, "slug", "") or (m.get("slug", "") if isinstance(m, dict) else "")
|
|
m_name = getattr(m, "name", "") or (m.get("name", "") if isinstance(m, dict) else "")
|
|
href = prefix + f"/{post_slug}/{m_slug}/"
|
|
del_url = url_for("page_admin.delete_market", market_slug=m_slug)
|
|
csrf_hdr = {"X-CSRFToken": csrf}
|
|
markets.append({
|
|
"href": href, "name": m_name, "slug": m_slug,
|
|
"del-url": del_url, "csrf-hdr": csrf_hdr,
|
|
})
|
|
|
|
return {
|
|
"can-create": can_create,
|
|
"create-url": url_for("page_admin.create_market") if can_create else None,
|
|
"csrf": csrf,
|
|
"admin-markets": markets,
|
|
}
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Market landing page
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def _h_market_home_data(page_slug=None, market_slug=None, **kw) -> dict:
|
|
from quart import g
|
|
post_data = getattr(g, "post_data", {})
|
|
post = post_data.get("post", {})
|
|
return {
|
|
"excerpt": post.get("custom_excerpt") or None,
|
|
"feature-image": post.get("feature_image") or None,
|
|
"html": post.get("html") or None,
|
|
}
|