diff --git a/market/sxc/pages/helpers.py b/market/sxc/pages/helpers.py index ab41497..24c1f3e 100644 --- a/market/sxc/pages/helpers.py +++ b/market/sxc/pages/helpers.py @@ -1,98 +1,33 @@ -"""Page helpers for market defpage system.""" +"""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_cards_sx, _markets_grid, _no_markets_sx, - _market_landing_content_sx, -) +from .cards import _market_card_data # --------------------------------------------------------------------------- -# Page admin panel (used by _h_page_admin_content) +# Registration # --------------------------------------------------------------------------- -async def _markets_admin_panel_sx(ctx: dict) -> str: - """Render the markets list + create form panel.""" - from quart import g, url_for - from shared.services.registry import services - from shared.sx.helpers import sx_call - from shared.sx.parser import SxExpr - - 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_token = ctx.get("csrf_token") - csrf = csrf_token() if callable(csrf_token) else (csrf_token or "") - - post = ctx.get("post") or {} - post_id = post.get("id") - markets = await services.market.marketplaces_for_container(g.s, "page", post_id) if post_id else [] - - form_html = "" - if can_create: - create_url = url_for("page_admin.create_market") - 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("crud-panel", - form=SxExpr(form_html), list=SxExpr(list_html), - list_id="markets-list") - - -def _markets_admin_list_sx(ctx: dict, markets: list) -> str: - """Render the markets list items.""" - from quart import url_for - from shared.utils import route_prefix - from shared.sx.helpers import sx_call - csrf_token = ctx.get("csrf_token") - csrf = csrf_token() if callable(csrf_token) else (csrf_token or "") - prefix = route_prefix() - - if not markets: - return sx_call("empty-state", - message="No markets yet. Create one above.", - cls="text-gray-500 mt-4") - - parts = [] - for m in markets: - 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 "") - post_slug = (ctx.get("post") or {}).get("slug", "") - 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("crud-item", - href=href, name=m_name, slug=m_slug, - 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) - - -# =========================================================================== -# Page helpers -# =========================================================================== - def _register_market_helpers() -> None: from shared.sx.pages import register_page_helpers register_page_helpers("market", { - "all-markets-content": _h_all_markets_content, - "page-markets-content": _h_page_markets_content, - "page-admin-content": _h_page_admin_content, - "market-home-content": _h_market_home_content, - "market-admin-content": _h_market_admin_content, + "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, }) -async def _h_all_markets_content(**kw): +# --------------------------------------------------------------------------- +# 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 @@ -116,16 +51,26 @@ async def _h_all_markets_content(**kw): page_info[p.id] = {"title": p.title, "slug": p.slug} if not markets: - return _no_markets_sx() + 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] - cards = _market_cards_sx(markets, page_info, page, has_more, next_url) - return _markets_grid(cards) + return { + "no-markets": False, + "market-data": market_data, + "market-page": page, + "has-more": has_more, + "next-url": next_url, + } -async def _h_page_markets_content(slug=None, **kw): +# --------------------------------------------------------------------------- +# 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 @@ -138,30 +83,76 @@ async def _h_page_markets_content(slug=None, **kw): post_slug = post.get("slug", "") if not markets: - return _no_markets_sx("No markets for this page") + 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] - cards = _market_cards_sx(markets, {}, page, has_more, next_url, - show_page_badge=False, post_slug=post_slug) - return _markets_grid(cards) + return { + "no-markets": False, + "market-data": market_data, + "market-page": page, + "has-more": has_more, + "next-url": next_url, + } -async def _h_page_admin_content(slug=None, **kw): +# --------------------------------------------------------------------------- +# 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.sx.helpers import sx_call + 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() - content = await _markets_admin_panel_sx(ctx) - return sx_call("market-admin-content-wrap", inner=content) + 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 = f'{{"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, + } -def _h_market_home_content(page_slug=None, market_slug=None, **kw): +# --------------------------------------------------------------------------- +# 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 _market_landing_content_sx(post) - - -def _h_market_admin_content(page_slug=None, market_slug=None, **kw): - return '"market admin"' + return { + "excerpt": post.get("custom_excerpt") or None, + "feature-image": post.get("feature_image") or None, + "html": post.get("html") or None, + } diff --git a/market/sxc/pages/market.sx b/market/sxc/pages/market.sx index 65070fd..220b315 100644 --- a/market/sxc/pages/market.sx +++ b/market/sxc/pages/market.sx @@ -1,4 +1,5 @@ ;; Market app defpage declarations. +;; All helpers return data dicts — markup composition in SX. ;; ;; all-markets-index: / — global view across all pages ;; page-markets-index: // — markets for a single page @@ -10,28 +11,64 @@ :path "/" :auth :public :layout :root - :content (all-markets-content)) + :data (all-markets-data) + :content (if no-markets + (~empty-state :icon "fa fa-store" :message "No markets available" + :cls "px-3 py-12 text-center text-stone-400") + (~market-markets-grid + :cards (~market-cards-content + :markets market-data :page market-page + :has-more has-more :next-url next-url)))) (defpage page-markets-index :path "//" :auth :public :layout :post - :content (page-markets-content)) + :data (page-markets-data) + :content (if no-markets + (~empty-state :message "No markets for this page" + :cls "px-3 py-12 text-center text-stone-400") + (~market-markets-grid + :cards (~market-cards-content + :markets market-data :page market-page + :has-more has-more :next-url next-url)))) (defpage page-admin :path "//admin/" :auth :admin :layout (:post-admin :selected "markets") - :content (page-admin-content)) + :data (page-admin-data) + :content (~market-admin-content-wrap + :inner (~crud-panel + :list-id "markets-list" + :form (when can-create + (~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 (if admin-markets + (<> (map (fn (m) + (~crud-item + :href (get m "href") :name (get m "name") :slug (get m "slug") + :del-url (get m "del-url") :csrf-hdr (get m "csrf-hdr") + :list-id "markets-list" + :confirm-title "Delete market?" + :confirm-text "Products will be hidden (soft delete)")) + admin-markets)) + (~empty-state + :message "No markets yet. Create one above." + :cls "text-gray-500 mt-4"))))) (defpage market-home :path "///" :auth :public :layout :market - :content (market-home-content)) + :data (market-home-data) + :content (~market-landing-from-data + :excerpt excerpt :feature-image feature-image :html html)) (defpage market-admin :path "///admin/" :auth :admin :layout (:market-admin :selected "markets") - :content (market-admin-content)) + :content "market admin")