"""Public render functions called from bp routes.""" from __future__ import annotations from typing import Any from shared.sx.parser import SxExpr from shared.sx.helpers import ( sx_call, post_header_sx as _post_header_sx, oob_header_sx as _oob_header_sx, full_page_sx, oob_page_sx, ) from .utils import _product_detail_sx, _product_meta_sx from .cards import _product_cards_sx, _market_cards_sx from .filters import _desktop_filter_sx, _mobile_filter_summary_sx from .layouts import ( _market_header_sx, _product_header_sx, _product_admin_header_sx, _mobile_nav_panel_sx, ) from .helpers import _markets_admin_panel_sx # --------------------------------------------------------------------------- # Browse page # --------------------------------------------------------------------------- async def render_browse_page(ctx: dict) -> str: """Full page: product browse with filters.""" content = sx_call("market-product-grid", cards=SxExpr(_product_cards_sx(ctx))) from shared.sx.helpers import render_to_sx_with_env hdr = await render_to_sx_with_env("layouts/browse-layout-full", {}) menu = _mobile_nav_panel_sx(ctx) filter_sx = await _mobile_filter_summary_sx(ctx) aside_sx = await _desktop_filter_sx(ctx) return await full_page_sx(ctx, header_rows=hdr, content=content, menu=menu, filter=filter_sx, aside=aside_sx) async def render_browse_oob(ctx: dict) -> str: """OOB response: product browse.""" content = sx_call("market-product-grid", cards=SxExpr(_product_cards_sx(ctx))) # Layout handles all OOB headers via auto-fetch macros oobs = sx_call("market-browse-layout-oob") menu = _mobile_nav_panel_sx(ctx) filter_sx = await _mobile_filter_summary_sx(ctx) aside_sx = await _desktop_filter_sx(ctx) return await oob_page_sx(oobs=oobs, content=content, menu=menu, filter=filter_sx, aside=aside_sx) def render_browse_cards(ctx: dict) -> str: """Pagination fragment: product cards -- sx wire format.""" return _product_cards_sx(ctx) # --------------------------------------------------------------------------- # Product detail # --------------------------------------------------------------------------- async def render_product_page(ctx: dict, d: dict) -> str: """Full page: product detail.""" content = _product_detail_sx(d, ctx) meta = _product_meta_sx(d, ctx) from shared.sx.helpers import render_to_sx_with_env hdr = await render_to_sx_with_env("layouts/product-layout-full", {}, post_header=await _post_header_sx(ctx), market_header=_market_header_sx(ctx), product_header=_product_header_sx(ctx, d)) return await full_page_sx(ctx, header_rows=hdr, content=content, meta=meta) async def render_product_oob(ctx: dict, d: dict) -> str: """OOB response: product detail.""" content = _product_detail_sx(d, ctx) oobs = sx_call("market-product-oob", market_header=SxExpr(_market_header_sx(ctx, oob=True)), oob_header=SxExpr(await _oob_header_sx("market-header-child", "product-header-child", _product_header_sx(ctx, d)))) menu = _mobile_nav_panel_sx(ctx) return await oob_page_sx(oobs=oobs, content=content, menu=menu) # --------------------------------------------------------------------------- # Product admin # --------------------------------------------------------------------------- async def render_product_admin_page(ctx: dict, d: dict) -> str: """Full page: product admin.""" content = _product_detail_sx(d, ctx) from shared.sx.helpers import render_to_sx_with_env hdr = await render_to_sx_with_env("layouts/product-admin-layout-full", {}, post_header=await _post_header_sx(ctx), market_header=_market_header_sx(ctx), product_header=_product_header_sx(ctx, d), admin_header=_product_admin_header_sx(ctx, d)) return await full_page_sx(ctx, header_rows=hdr, content=content) async def render_product_admin_oob(ctx: dict, d: dict) -> str: """OOB response: product admin.""" content = _product_detail_sx(d, ctx) oobs = sx_call("market-product-admin-oob", product_header=SxExpr(_product_header_sx(ctx, d, oob=True)), oob_header=SxExpr(await _oob_header_sx("product-header-child", "product-admin-header-child", _product_admin_header_sx(ctx, d)))) return await oob_page_sx(oobs=oobs, content=content) # --------------------------------------------------------------------------- # Market admin list panel # --------------------------------------------------------------------------- async def render_markets_admin_list_panel(ctx: dict) -> str: """Render the markets admin panel HTML for POST/DELETE response.""" return await _markets_admin_panel_sx(ctx) # --------------------------------------------------------------------------- # Public API: POST handler fragment renderers # --------------------------------------------------------------------------- def render_all_markets_cards(markets: list, has_more: bool, page_info: dict, page: int) -> str: """Pagination fragment: all markets cards.""" from quart import url_for from shared.utils import route_prefix prefix = route_prefix() next_url = prefix + url_for("all_markets.markets_fragment", page=page + 1) return _market_cards_sx(markets, page_info, page, has_more, next_url) def render_page_markets_cards(markets: list, has_more: bool, page: int, post_slug: str) -> str: """Pagination fragment: page-scoped markets cards.""" from quart import url_for from shared.utils import route_prefix prefix = route_prefix() next_url = prefix + url_for("page_markets.markets_fragment", page=page + 1) return _market_cards_sx(markets, {}, page, has_more, next_url, show_page_badge=False, post_slug=post_slug) def render_like_toggle_button(slug: str, liked: bool, *, like_url: str | None = None, item_type: str = "product") -> str: """Render a standalone like toggle button for HTMX POST response.""" from shared.browser.app.csrf import generate_csrf_token from quart import url_for from shared.utils import host_url csrf = generate_csrf_token() if not like_url: like_url = host_url(url_for("market.browse.product.like_toggle", product_slug=slug)) if liked: colour = "text-red-600" icon = "fa-solid fa-heart" label = f"Unlike this {item_type}" else: colour = "text-stone-300" icon = "fa-regular fa-heart" label = f"Like this {item_type}" return sx_call( "market-like-toggle-button", colour=colour, action=like_url, hx_headers={"X-CSRFToken": csrf}, label=label, icon_cls=icon, ) def render_cart_added_response(cart: list, item: Any, d: dict) -> str: """Render the HTMX response after add-to-cart via ~market-cart-added-response.""" from shared.browser.app.csrf import generate_csrf_token from quart import url_for from shared.infrastructure.urls import cart_url as _cart_url csrf = generate_csrf_token() slug = d.get("slug", "") count = sum(getattr(ci, "quantity", 0) for ci in cart) quantity = getattr(item, "quantity", 0) if item else 0 action = url_for("market.browse.product.cart", product_slug=slug) if count > 0: cart_href = _cart_url("/") blog_href = "" logo = "" else: from shared.config import config cart_href = "" blog_href = config().get("blog_url", "/") logo = config().get("logo", "") return sx_call("market-cart-added-response", has_count=str(count) if count > 0 else None, cart_href=cart_href, blog_href=blog_href, logo=logo, slug=slug, action=action, csrf=csrf, quantity=str(quantity), minus_val=str(quantity - 1) if quantity > 0 else "0", plus_val=str(quantity + 1), )