""" Market service s-expression page components. Renders market landing, browse (category/subcategory), product detail, product admin, market admin, page markets, and all markets pages. Called from route handlers in place of ``render_template()``. """ from __future__ import annotations from typing import Any from markupsafe import escape from shared.sexp.jinja_bridge import sexp from shared.sexp.helpers import ( call_url, get_asset_url, root_header_html, search_mobile_html, search_desktop_html, full_page, oob_page, ) # --------------------------------------------------------------------------- # Price helpers # --------------------------------------------------------------------------- _SYM = {"GBP": "£", "EUR": "€", "USD": "$"} def _price_str(val, raw, cur) -> str: if raw: return str(raw) if isinstance(val, (int, float)): return f"{_SYM.get(cur, '')}{val:.2f}" return str(val or "") def _set_prices(item: dict) -> dict: """Extract price values from product dict (mirrors prices.html set_prices macro).""" oe = item.get("oe_list_price") or {} sp_val = item.get("special_price") or (oe.get("special") if oe else None) sp_raw = item.get("special_price_raw") or (oe.get("special_raw") if oe else None) sp_cur = item.get("special_price_currency") or (oe.get("special_currency") if oe else None) rp_val = item.get("regular_price") or item.get("rrp") or (oe.get("rrp") if oe else None) rp_raw = item.get("regular_price_raw") or item.get("rrp_raw") or (oe.get("rrp_raw") if oe else None) rp_cur = item.get("regular_price_currency") or item.get("rrp_currency") or (oe.get("rrp_currency") if oe else None) return dict(sp_val=sp_val, sp_raw=sp_raw, sp_cur=sp_cur, rp_val=rp_val, rp_raw=rp_raw, rp_cur=rp_cur) def _card_price_html(p: dict) -> str: """Render price line for product card (mirrors prices.html card_price macro).""" pr = _set_prices(p) sp_str = _price_str(pr["sp_val"], pr["sp_raw"], pr["sp_cur"]) rp_str = _price_str(pr["rp_val"], pr["rp_raw"], pr["rp_cur"]) parts = ['
'] if pr["sp_val"]: parts.append(f'
{sp_str}
') if pr["rp_val"]: parts.append(f'
{rp_str}
') elif pr["rp_val"]: parts.append(f'
{rp_str}
') parts.append("
") return "".join(parts) # --------------------------------------------------------------------------- # Header helpers # --------------------------------------------------------------------------- def _post_header_html(ctx: dict, *, oob: bool = False) -> str: """Build the post-level header row (feature image + title + page cart count).""" post = ctx.get("post") or {} slug = post.get("slug", "") title = (post.get("title") or "")[:160] feature_image = post.get("feature_image") label_parts = [] if feature_image: label_parts.append( f'' ) label_parts.append(f"{escape(title)}") label_html = "".join(label_parts) nav_parts = [] page_cart_count = ctx.get("page_cart_count", 0) if page_cart_count and page_cart_count > 0: cart_href = call_url(ctx, "cart_url", f"/{slug}/") nav_parts.append( f'' f'' f'{page_cart_count}' ) # Container nav container_nav = ctx.get("container_nav_html", "") if container_nav: nav_parts.append(container_nav) nav_html = "".join(nav_parts) link_href = call_url(ctx, "blog_url", f"/{slug}/") return sexp( '(~menu-row :id "post-row" :level 1' ' :link-href lh :link-label-html llh' ' :nav-html nh :child-id "post-header-child" :oob oob)', lh=link_href, llh=label_html, nh=nav_html, oob=oob, ) def _market_header_html(ctx: dict, *, oob: bool = False) -> str: """Build the market-level header row (shop icon + market title + category slugs + nav).""" from quart import url_for market_title = ctx.get("market_title", "") top_slug = ctx.get("top_slug", "") sub_slug = ctx.get("sub_slug", "") hx_select_search = ctx.get("hx_select_search", "#main-panel") label_parts = [ '
', f'
{escape(market_title)}
', '
', f"
{escape(top_slug or '')}
", ] if sub_slug: label_parts.append(f"
{escape(sub_slug)}
") label_parts.append("
") label_html = "".join(label_parts) link_href = url_for("market.browse.home") # Build desktop nav from categories categories = ctx.get("categories", {}) qs = ctx.get("qs", "") nav_html = _desktop_category_nav_html(ctx, categories, qs, hx_select_search) return sexp( '(~menu-row :id "market-row" :level 2' ' :link-href lh :link-label-html llh' ' :nav-html nh :child-id "market-header-child" :oob oob)', lh=link_href, llh=label_html, nh=nav_html, oob=oob, ) def _desktop_category_nav_html(ctx: dict, categories: dict, qs: str, hx_select: str) -> str: """Build desktop category navigation links.""" from quart import url_for from shared.utils import route_prefix prefix = route_prefix() category_label = ctx.get("category_label", "") select_colours = ctx.get("select_colours", "") rights = ctx.get("rights", {}) parts = ['") return "".join(parts) def _product_header_html(ctx: dict, d: dict, *, oob: bool = False) -> str: """Build the product-level header row (bag icon + title + prices + admin).""" from quart import url_for from shared.browser.app.csrf import generate_csrf_token slug = d.get("slug", "") title = d.get("title", "") hx_select_search = ctx.get("hx_select_search", "#main-panel") link_href = url_for("market.browse.product.product_detail", product_slug=slug) label_html = f'
{escape(title)}
' # Prices in nav area pr = _set_prices(d) cart = ctx.get("cart", []) prices_nav = _prices_header_html(d, pr, cart, slug, ctx) rights = ctx.get("rights", {}) admin_html = "" if rights and rights.get("admin"): admin_href = url_for("market.browse.product.admin", product_slug=slug) admin_html = ( f'' f'' ) nav_html = prices_nav + admin_html return sexp( '(~menu-row :id "product-row" :level 3' ' :link-href lh :link-label-html llh' ' :nav-html nh :child-id "product-header-child" :oob oob)', lh=link_href, llh=label_html, nh=nav_html, oob=oob, ) def _prices_header_html(d: dict, pr: dict, cart: list, slug: str, ctx: dict) -> str: """Build prices + add-to-cart for product header row.""" from quart import url_for from shared.browser.app.csrf import generate_csrf_token csrf = generate_csrf_token() cart_action = url_for("market.browse.product.cart", product_slug=slug) cart_url_fn = ctx.get("cart_url") # Add-to-cart button quantity = sum(ci.quantity for ci in cart if ci.product.slug == slug) if cart else 0 add_html = _cart_add_html(slug, quantity, cart_action, csrf, cart_url_fn) parts = ['
'] parts.append(add_html) sp_val, rp_val = pr.get("sp_val"), pr.get("rp_val") if sp_val: parts.append(f'
Special price
') parts.append(f'
{_price_str(sp_val, pr["sp_raw"], pr["sp_cur"])}
') if rp_val: parts.append(f'
{_price_str(rp_val, pr["rp_raw"], pr["rp_cur"])}
') elif rp_val: parts.append(f'') parts.append(f'
{_price_str(rp_val, pr["rp_raw"], pr["rp_cur"])}
') # RRP rrp_raw = d.get("rrp_raw") rrp_val = d.get("rrp") case_size = d.get("case_size_count") or 1 if rrp_raw and rrp_val: rrp_str = f"{rrp_raw[0]}{rrp_val * case_size:.2f}" parts.append(f'
rrp: {rrp_str}
') parts.append("
") return "".join(parts) def _cart_add_html(slug: str, quantity: int, action: str, csrf: str, cart_url_fn: Any = None) -> str: """Render add-to-cart button or quantity controls.""" if not quantity: return ( f'
' f'
' f'' f'' f'
' ) cart_href = cart_url_fn("/") if callable(cart_url_fn) else "/" return ( f'
' f'
' f'' f'' f'
' f'' f'' f'' f'{quantity}' f'
' f'' f'' f'
' f'
' ) # --------------------------------------------------------------------------- # Mobile nav panel # --------------------------------------------------------------------------- def _mobile_nav_panel_html(ctx: dict) -> str: """Build mobile nav panel with category accordion.""" from quart import url_for from shared.utils import route_prefix prefix = route_prefix() categories = ctx.get("categories", {}) qs = ctx.get("qs", "") category_label = ctx.get("category_label", "") top_slug = ctx.get("top_slug", "") sub_slug = ctx.get("sub_slug", "") hx_select = ctx.get("hx_select_search", "#main-panel") select_colours = ctx.get("select_colours", "") parts = ['
'] all_href = prefix + url_for("market.browse.browse_all") + qs all_active = (category_label == "All Products") parts.append( f'' f'
All
' ) for cat, data in categories.items(): cat_slug = data.get("slug", "") cat_active = (top_slug == cat_slug.lower() if top_slug else False) open_attr = " open" if cat_active else "" cat_href = prefix + url_for("market.browse.browse_top", top_slug=cat_slug) + qs bg_cls = " bg-stone-900 text-white hover:bg-stone-900" if cat_active else "" parts.append(f'
') parts.append( f'' f'' f'
{escape(cat)}
' f'
{data.get("count", 0)}
' f'' f'
' ) subs = data.get("subs", []) if subs: parts.append('
') parts.append('
') for sub in subs: sub_href = prefix + url_for("market.browse.browse_sub", top_slug=cat_slug, sub_slug=sub["slug"]) + qs sub_active = (cat_active and sub_slug == sub.get("slug")) parts.append( f'' f'
{escape(sub.get("html_label") or sub.get("name", ""))}
' f'
{sub.get("count", 0)}
' ) parts.append("
") else: view_href = prefix + url_for("market.browse.browse_top", top_slug=cat_slug) + qs parts.append( f'' ) parts.append("
") parts.append("
") return "".join(parts) # --------------------------------------------------------------------------- # Product card (browse grid item) # --------------------------------------------------------------------------- def _product_card_html(p: dict, ctx: dict) -> str: """Render a single product card for browse grid.""" from quart import url_for from shared.browser.app.csrf import generate_csrf_token from shared.utils import route_prefix prefix = route_prefix() slug = p.get("slug", "") item_href = prefix + url_for("market.browse.product.product_detail", product_slug=slug) hx_select = ctx.get("hx_select_search", "#main-panel") asset_url_fn = ctx.get("asset_url") cart = ctx.get("cart", []) selected_brands = ctx.get("selected_brands", []) selected_stickers = ctx.get("selected_stickers", []) search = ctx.get("search", "") user = ctx.get("user") csrf = generate_csrf_token() cart_action = url_for("market.browse.product.cart", product_slug=slug) # Like button overlay like_html = "" if user: liked = p.get("is_liked", False) like_html = _like_button_html(slug, liked, csrf, ctx) # Image image = p.get("image") labels = p.get("labels", []) brand = p.get("brand", "") brand_highlight = " bg-yellow-200" if brand in selected_brands else "" if image: labels_html = "".join( f'' for l in labels ) if callable(asset_url_fn) else "" img_html = ( f'
' f'
' f'no image' f'{labels_html}
' f'
{escape(brand)}
' f'
' ) else: labels_list = "".join(f"
  • {l}
  • " for l in labels) img_html = ( f'
    ' f'
    ' f'
    No image
    ' f'' f'
    {escape(brand)}
    ' f'
    ' ) price_html = _card_price_html(p) # Cart button quantity = sum(ci.quantity for ci in cart if ci.product.slug == slug) if cart else 0 cart_url_fn = ctx.get("cart_url") add_html = _cart_add_html(slug, quantity, cart_action, csrf, cart_url_fn) # Stickers stickers = p.get("stickers", []) stickers_html = "" if stickers and callable(asset_url_fn): sticker_parts = [] for s in stickers: found = s in selected_stickers src = asset_url_fn(f"stickers/{s}.svg") sticker_parts.append( f'{escape(s)}' ) stickers_html = '
    ' + "".join(sticker_parts) + "
    " # Title with search highlight title = p.get("title", "") if search and search.lower() in title.lower(): idx = title.lower().index(search.lower()) highlighted = f"{escape(title[:idx])}{escape(title[idx:idx+len(search)])}{escape(title[idx+len(search):])}" else: highlighted = escape(title) return ( f'
    ' f'{like_html}' f'' f'{img_html}{price_html}' f'
    {add_html}
    ' f'' f'{stickers_html}' f'
    {highlighted}
    ' f'
    ' ) def _like_button_html(slug: str, liked: bool, csrf: str, ctx: dict) -> str: """Render the like/unlike heart button overlay.""" from quart import url_for action = url_for("market.browse.product.like_toggle", product_slug=slug) icon_cls = "fa-solid fa-heart text-red-500" if liked else "fa-regular fa-heart text-stone-400" return ( f'
    ' f'
    ' f'' f'
    ' ) # --------------------------------------------------------------------------- # Product cards (pagination fragment) # --------------------------------------------------------------------------- def _product_cards_html(ctx: dict) -> str: """Render product cards with infinite scroll sentinels.""" from shared.utils import route_prefix prefix = route_prefix() products = ctx.get("products", []) page = ctx.get("page", 1) total_pages = ctx.get("total_pages", 1) current_local_href = ctx.get("current_local_href", "/") qs_fn = ctx.get("qs_filter") parts = [_product_card_html(p, ctx) for p in products] if page < total_pages: # Build next page URL if callable(qs_fn): next_qs = qs_fn({"page": page + 1}) else: next_qs = f"?page={page + 1}" next_url = prefix + current_local_href + next_qs # Mobile sentinel parts.append( f'' ) # Desktop sentinel parts.append( f'' ) else: parts.append('
    End of results
    ') return "".join(parts) # --------------------------------------------------------------------------- # Browse filter panels (mobile + desktop) # --------------------------------------------------------------------------- def _desktop_filter_html(ctx: dict) -> str: """Build the desktop aside filter panel (search, category, sort, like, labels, stickers, brands).""" from quart import url_for from shared.utils import route_prefix prefix = route_prefix() category_label = ctx.get("category_label", "") search = ctx.get("search", "") search_count = ctx.get("search_count", "") current_local_href = ctx.get("current_local_href", "/") hx_select = ctx.get("hx_select", "#main-panel") sort_options = ctx.get("sort_options", []) sort = ctx.get("sort", "") labels = ctx.get("labels", []) selected_labels = ctx.get("selected_labels", []) stickers = ctx.get("stickers", []) selected_stickers = ctx.get("selected_stickers", []) brands = ctx.get("brands", []) selected_brands = ctx.get("selected_brands", []) liked = ctx.get("liked", False) liked_count = ctx.get("liked_count", 0) subs_local = ctx.get("subs_local", []) top_local_href = ctx.get("top_local_href", "") sub_slug = ctx.get("sub_slug", "") asset_url_fn = ctx.get("asset_url") # Search search_html = search_desktop_html(ctx) # Category summary + sort + like + labels + stickers parts = [search_html] parts.append(f'
    ') parts.append(f'
    {escape(category_label)}
    ') # Sort stickers if sort_options: parts.append(_sort_stickers_html(sort_options, sort, ctx)) # Like + labels row parts.append('") # Stickers if stickers: parts.append(_stickers_filter_html(stickers, selected_stickers, ctx)) # Subcategory selector if subs_local and top_local_href: parts.append(_subcategory_selector_html(subs_local, top_local_href, sub_slug, ctx)) parts.append("
    ") # Brand filter parts.append(f'
    ') if brands: parts.append(_brand_filter_html(brands, selected_brands, ctx)) parts.append("
    ") return "".join(parts) def _mobile_filter_summary_html(ctx: dict) -> str: """Build mobile filter summary (collapsible bar showing active filters).""" # Simplified version — just the filter details/summary wrapper asset_url_fn = ctx.get("asset_url") search = ctx.get("search", "") search_count = ctx.get("search_count", "") current_local_href = ctx.get("current_local_href", "/") hx_select = ctx.get("hx_select", "#main-panel") sort = ctx.get("sort", "") sort_options = ctx.get("sort_options", []) liked = ctx.get("liked", False) liked_count = ctx.get("liked_count", 0) selected_labels = ctx.get("selected_labels", []) selected_stickers = ctx.get("selected_stickers", []) selected_brands = ctx.get("selected_brands", []) labels = ctx.get("labels", []) stickers = ctx.get("stickers", []) brands = ctx.get("brands", []) # Search bar search_bar = search_mobile_html(ctx) # Summary chips showing active filters chip_parts = ['
    '] if sort and sort_options: for k, l, i in sort_options: if k == sort and callable(asset_url_fn): chip_parts.append(f'') if liked: chip_parts.append('
    ' f'') if liked_count is not None: cls = "text-[10px] text-stone-500" if liked_count != 0 else "text-md text-red-500 font-bold" chip_parts.append(f'
    {liked_count}
    ') chip_parts.append("
    ") # Selected labels if selected_labels: chip_parts.append('") # Selected stickers if selected_stickers: chip_parts.append('") # Selected brands if selected_brands: chip_parts.append('") chip_parts.append("
    ") chips_html = "".join(chip_parts) # Full mobile filter details from shared.utils import route_prefix prefix = route_prefix() mobile_filter = _mobile_filter_content_html(ctx, prefix) return ( f'
    ' f'' f'{search_bar}' f'
    ' f'{chips_html}' f'
    ' f'
    ' f'{mobile_filter}' f'
    ' ) def _mobile_filter_content_html(ctx: dict, prefix: str) -> str: """Build the expanded mobile filter panel contents.""" from shared.utils import route_prefix search = ctx.get("search", "") selected_labels = ctx.get("selected_labels", []) selected_stickers = ctx.get("selected_stickers", []) selected_brands = ctx.get("selected_brands", []) current_local_href = ctx.get("current_local_href", "/") hx_select = ctx.get("hx_select_search", "#main-panel") sort_options = ctx.get("sort_options", []) sort = ctx.get("sort", "") liked = ctx.get("liked", False) liked_count = ctx.get("liked_count", 0) labels = ctx.get("labels", []) stickers = ctx.get("stickers", []) brands = ctx.get("brands", []) asset_url_fn = ctx.get("asset_url") qs_fn = ctx.get("qs_filter") parts = [] # Sort options if sort_options: parts.append(_sort_stickers_html(sort_options, sort, ctx, mobile=True)) # Clear filters button has_filters = search or selected_labels or selected_stickers or selected_brands if has_filters and callable(qs_fn): clear_url = prefix + current_local_href + qs_fn({"clear_filters": True}) parts.append( f'
    ' f'' f'clear filters
    ' ) # Like + labels row parts.append('
    ') parts.append(_like_filter_html(liked, liked_count, ctx, mobile=True)) if labels: parts.append(_labels_filter_html(labels, selected_labels, ctx, prefix="nav-labels", mobile=True)) parts.append("
    ") # Stickers if stickers: parts.append(_stickers_filter_html(stickers, selected_stickers, ctx, mobile=True)) # Brands if brands: parts.append(_brand_filter_html(brands, selected_brands, ctx, mobile=True)) return "".join(parts) def _sort_stickers_html(sort_options: list, current_sort: str, ctx: dict, mobile: bool = False) -> str: """Render sort option stickers.""" asset_url_fn = ctx.get("asset_url") current_local_href = ctx.get("current_local_href", "/") hx_select = ctx.get("hx_select_search", "#main-panel") qs_fn = ctx.get("qs_filter") from shared.utils import route_prefix prefix = route_prefix() parts = ['
    '] for k, label, icon in sort_options: if callable(qs_fn): href = prefix + current_local_href + qs_fn({"sort": k}) else: href = "#" active = (k == current_sort) ring = " ring-2 ring-emerald-500 rounded" if active else "" src = asset_url_fn(icon) if callable(asset_url_fn) else icon parts.append( f'' f'{escape(label)}' f'{escape(label)}' ) parts.append("
    ") return "".join(parts) def _like_filter_html(liked: bool, liked_count: int, ctx: dict, mobile: bool = False) -> str: """Render the like filter toggle.""" current_local_href = ctx.get("current_local_href", "/") hx_select = ctx.get("hx_select_search", "#main-panel") qs_fn = ctx.get("qs_filter") from shared.utils import route_prefix prefix = route_prefix() if callable(qs_fn): href = prefix + current_local_href + qs_fn({"liked": not liked}) else: href = "#" icon_cls = "fa-solid fa-heart text-red-500" if liked else "fa-regular fa-heart text-stone-400" size = "text-[40px]" if mobile else "text-2xl" return ( f'' f'' ) def _labels_filter_html(labels: list, selected: list, ctx: dict, *, prefix: str = "nav-labels", mobile: bool = False) -> str: """Render label filter buttons.""" asset_url_fn = ctx.get("asset_url") current_local_href = ctx.get("current_local_href", "/") hx_select = ctx.get("hx_select_search", "#main-panel") qs_fn = ctx.get("qs_filter") from shared.utils import route_prefix rp = route_prefix() parts = [] for lb in labels: name = lb.get("name", "") is_sel = name in selected if callable(qs_fn): new_sel = [s for s in selected if s != name] if is_sel else selected + [name] href = rp + current_local_href + qs_fn({"labels": new_sel}) else: href = "#" ring = " ring-2 ring-emerald-500 rounded" if is_sel else "" src = asset_url_fn(f"{prefix}/{name}.svg") if callable(asset_url_fn) else "" parts.append( f'' f'{escape(name)}' ) return "".join(parts) def _stickers_filter_html(stickers: list, selected: list, ctx: dict, mobile: bool = False) -> str: """Render sticker filter grid.""" asset_url_fn = ctx.get("asset_url") current_local_href = ctx.get("current_local_href", "/") hx_select = ctx.get("hx_select_search", "#main-panel") qs_fn = ctx.get("qs_filter") from shared.utils import route_prefix rp = route_prefix() parts = ['
    '] for st in stickers: name = st.get("name", "") count = st.get("count", 0) is_sel = name in selected if callable(qs_fn): new_sel = [s for s in selected if s != name] if is_sel else selected + [name] href = rp + current_local_href + qs_fn({"stickers": new_sel}) else: href = "#" ring = " ring-2 ring-emerald-500 rounded" if is_sel else "" src = asset_url_fn(f"stickers/{name}.svg") if callable(asset_url_fn) else "" cls = "text-[10px] text-stone-500" if count != 0 else "text-md text-red-500 font-bold" parts.append( f'' f'{escape(name)}' f'{count}' ) parts.append("
    ") return "".join(parts) def _brand_filter_html(brands: list, selected: list, ctx: dict, mobile: bool = False) -> str: """Render brand filter checkboxes.""" current_local_href = ctx.get("current_local_href", "/") hx_select = ctx.get("hx_select_search", "#main-panel") qs_fn = ctx.get("qs_filter") from shared.utils import route_prefix rp = route_prefix() parts = ['
    '] for br in brands: name = br.get("name", "") count = br.get("count", 0) is_sel = name in selected if callable(qs_fn): new_sel = [s for s in selected if s != name] if is_sel else selected + [name] href = rp + current_local_href + qs_fn({"brands": new_sel}) else: href = "#" bg = " bg-yellow-200" if is_sel else "" cls = "text-md" if count else "text-md text-red-500" parts.append( f'' f'
    {escape(name)}
    ' f'
    {count}
    ' ) parts.append("
    ") return "".join(parts) def _subcategory_selector_html(subs: list, top_href: str, current_sub: str, ctx: dict) -> str: """Render subcategory vertical nav.""" hx_select = ctx.get("hx_select_search", "#main-panel") from shared.utils import route_prefix rp = route_prefix() parts = ['
    '] # "All" link parts.append( f'All' ) for sub in subs: slug = sub.get("slug", "") name = sub.get("name", "") href = sub.get("href", "") active = (slug == current_sub) parts.append( f'{escape(name)}' ) parts.append("
    ") return "".join(parts) # --------------------------------------------------------------------------- # Product detail page content # --------------------------------------------------------------------------- def _product_detail_html(d: dict, ctx: dict) -> str: """Build product detail main panel content.""" from quart import url_for from shared.browser.app.csrf import generate_csrf_token asset_url_fn = ctx.get("asset_url") user = ctx.get("user") liked_by_current_user = ctx.get("liked_by_current_user", False) csrf = generate_csrf_token() images = d.get("images", []) labels = d.get("labels", []) stickers = d.get("stickers", []) brand = d.get("brand", "") slug = d.get("slug", "") # Gallery if images: # Like button like_html = "" if user: like_html = _like_button_html(slug, liked_by_current_user, csrf, ctx) # Main image + labels labels_overlay = "".join( f'' for l in labels ) if callable(asset_url_fn) else "" gallery_html = ( f'
    ' f'{like_html}' f'
    ' f'{escape(d.get(' f'{labels_overlay}
    ' f'
    {escape(brand)}
    ' ) # Prev/next buttons if len(images) > 1: gallery_html += ( '' '' ) gallery_html += "
    " # Thumbnails if len(images) > 1: thumbs = "".join( f'' f'' for i, u in enumerate(images) ) gallery_html += f'
    {thumbs}
    ' else: like_html = "" if user: like_html = _like_button_html(slug, liked_by_current_user, csrf, ctx) gallery_html = ( f'
    ' f'{like_html}No image
    ' ) # Stickers below gallery stickers_html = "" if stickers and callable(asset_url_fn): sticker_parts = "".join( f'{escape(s)}' for s in stickers ) stickers_html = f'
    {sticker_parts}
    ' # Right column: prices, description, sections pr = _set_prices(d) details_parts = ['
    '] # Unit price / case size extras extras = [] ppu = d.get("price_per_unit") or d.get("price_per_unit_raw") if ppu: extras.append(f'
    Unit price: {_price_str(d.get("price_per_unit"), d.get("price_per_unit_raw"), d.get("price_per_unit_currency"))}
    ') if d.get("case_size_raw"): extras.append(f'
    Case size: {d["case_size_raw"]}
    ') if extras: details_parts.append('
    ' + "".join(extras) + "
    ") # Description desc_short = d.get("description_short") desc_html = d.get("description_html") if desc_short or desc_html: details_parts.append('
    ') if desc_short: details_parts.append(f'

    {escape(desc_short)}

    ') if desc_html: details_parts.append(f'
    {desc_html}
    ') details_parts.append("
    ") # Sections (expandable) sections = d.get("sections", []) if sections: details_parts.append('
    ') for sec in sections: details_parts.append( f'
    ' f'' f'{escape(sec.get("title", ""))}' f'' f'
    {sec.get("html", "")}
    ' ) details_parts.append("
    ") details_parts.append("
    ") return ( f'
    ' f'
    {gallery_html}{stickers_html}
    ' f'{"".join(details_parts)}
    ' ) # --------------------------------------------------------------------------- # Product meta (OpenGraph, JSON-LD) # --------------------------------------------------------------------------- def _product_meta_html(d: dict, ctx: dict) -> str: """Build product meta tags for .""" import json from quart import request title = d.get("title", "") desc_source = d.get("description_short") or "" if not desc_source and d.get("description_html"): # Strip HTML tags (simple approach) import re desc_source = re.sub(r"<[^>]+>", "", d.get("description_html", "")) description = desc_source.strip().replace("\n", " ")[:160] image_url = d.get("image") or (d.get("images", [None])[0] if d.get("images") else None) canonical = request.url if request else "" brand = d.get("brand", "") sku = d.get("sku", "") price = d.get("special_price") or d.get("regular_price") or d.get("rrp") price_currency = d.get("special_price_currency") or d.get("regular_price_currency") or d.get("rrp_currency") parts = [f"{escape(title)}"] parts.append(f'') if canonical: parts.append(f'') # OpenGraph site_title = ctx.get("base_title", "") parts.append(f'') parts.append('') parts.append(f'') parts.append(f'') if canonical: parts.append(f'') if image_url: parts.append(f'') if price and price_currency: parts.append(f'') parts.append(f'') if brand: parts.append(f'') # Twitter card_type = "summary_large_image" if image_url else "summary" parts.append(f'') parts.append(f'') parts.append(f'') if image_url: parts.append(f'') # JSON-LD jsonld = { "@context": "https://schema.org", "@type": "Product", "name": title, "image": image_url, "description": description, "sku": sku, "url": canonical, } if brand: jsonld["brand"] = {"@type": "Brand", "name": brand} if price and price_currency: jsonld["offers"] = { "@type": "Offer", "price": price, "priceCurrency": price_currency, "url": canonical, "availability": "https://schema.org/InStock", } parts.append(f'') return "\n".join(parts) # --------------------------------------------------------------------------- # Market cards (all markets / page markets) # --------------------------------------------------------------------------- def _market_card_html(market: Any, page_info: dict, *, show_page_badge: bool = True, post_slug: str = "") -> str: """Render a single market card.""" from shared.infrastructure.urls import market_url name = getattr(market, "name", "") description = getattr(market, "description", "") slug = getattr(market, "slug", "") container_id = getattr(market, "container_id", None) if show_page_badge and page_info: pi = page_info.get(container_id, {}) p_slug = pi.get("slug", "") p_title = pi.get("title", "") market_href = market_url(f"/{p_slug}/{slug}/") if p_slug else "" else: p_slug = post_slug p_title = "" market_href = market_url(f"/{post_slug}/{slug}/") if post_slug else "" parts = ['
    '] parts.append("
    ") if market_href: parts.append(f'

    {escape(name)}

    ') else: parts.append(f'

    {escape(name)}

    ') if description: parts.append(f'

    {escape(description)}

    ') parts.append("
    ") if show_page_badge and p_title: badge_href = market_url(f"/{p_slug}/") parts.append( f'
    ' f'' f'{escape(p_title)}
    ' ) parts.append("
    ") return "".join(parts) def _market_cards_html(markets: list, page_info: dict, page: int, has_more: bool, next_url: str, *, show_page_badge: bool = True, post_slug: str = "") -> str: """Render market cards with infinite scroll sentinel.""" parts = [_market_card_html(m, page_info, show_page_badge=show_page_badge, post_slug=post_slug) for m in markets] if has_more: parts.append( f'' ) return "".join(parts) # --------------------------------------------------------------------------- # OOB header helpers # --------------------------------------------------------------------------- def _oob_header_html(parent_id: str, child_id: str, row_html: str) -> str: """Wrap a header row in OOB div with child placeholder.""" return ( f'
    ' f'
    {row_html}' f'
    ' ) # =========================================================================== # PUBLIC API # =========================================================================== # --------------------------------------------------------------------------- # All markets # --------------------------------------------------------------------------- async def render_all_markets_page(ctx: dict, markets: list, has_more: bool, page_info: dict, page: int) -> str: """Full page: all markets listing.""" 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) if markets: cards = _market_cards_html(markets, page_info, page, has_more, next_url) content = f'
    {cards}
    ' else: content = ('
    ' '' '

    No markets available

    ') content += '
    ' hdr = root_header_html(ctx) return full_page(ctx, header_rows_html=hdr, content_html=content) async def render_all_markets_oob(ctx: dict, markets: list, has_more: bool, page_info: dict, page: int) -> str: """OOB response: all markets listing.""" 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) if markets: cards = _market_cards_html(markets, page_info, page, has_more, next_url) content = f'
    {cards}
    ' else: content = ('
    ' '' '

    No markets available

    ') content += '
    ' oobs = root_header_html(ctx, oob=True) return oob_page(ctx, oobs_html=oobs, content_html=content) async 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_html(markets, page_info, page, has_more, next_url) # --------------------------------------------------------------------------- # Page markets # --------------------------------------------------------------------------- async def render_page_markets_page(ctx: dict, markets: list, has_more: bool, page: int) -> str: """Full page: page-scoped markets listing.""" from quart import url_for from shared.utils import route_prefix prefix = route_prefix() post = ctx.get("post", {}) post_slug = post.get("slug", "") next_url = prefix + url_for("page_markets.markets_fragment", page=page + 1) if markets: cards = _market_cards_html(markets, {}, page, has_more, next_url, show_page_badge=False, post_slug=post_slug) content = f'
    {cards}
    ' else: content = ('
    ' '' '

    No markets for this page

    ') content += '
    ' hdr = root_header_html(ctx) hdr += sexp( '(div :id "root-header-child" :class "w-full" (raw! ph))', ph=_post_header_html(ctx), ) return full_page(ctx, header_rows_html=hdr, content_html=content) async def render_page_markets_oob(ctx: dict, markets: list, has_more: bool, page: int) -> str: """OOB response: page-scoped markets.""" from quart import url_for from shared.utils import route_prefix prefix = route_prefix() post = ctx.get("post", {}) post_slug = post.get("slug", "") next_url = prefix + url_for("page_markets.markets_fragment", page=page + 1) if markets: cards = _market_cards_html(markets, {}, page, has_more, next_url, show_page_badge=False, post_slug=post_slug) content = f'
    {cards}
    ' else: content = ('
    ' '' '

    No markets for this page

    ') content += '
    ' oobs = _oob_header_html("post-header-child", "market-header-child", "") oobs += _post_header_html(ctx, oob=True) return oob_page(ctx, oobs_html=oobs, content_html=content) async 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_html(markets, {}, page, has_more, next_url, show_page_badge=False, post_slug=post_slug) # --------------------------------------------------------------------------- # Market landing page # --------------------------------------------------------------------------- async def render_market_home_page(ctx: dict) -> str: """Full page: market landing page (post content).""" post = ctx.get("post") or {} content = _market_landing_content(post) hdr = root_header_html(ctx) hdr += sexp( '(div :id "root-header-child" :class "w-full" (raw! ph (raw! mh)))', ph=_post_header_html(ctx), mh=_market_header_html(ctx), ) menu = _mobile_nav_panel_html(ctx) return full_page(ctx, header_rows_html=hdr, content_html=content, menu_html=menu) async def render_market_home_oob(ctx: dict) -> str: """OOB response: market landing page.""" post = ctx.get("post") or {} content = _market_landing_content(post) oobs = _oob_header_html("post-header-child", "market-header-child", _market_header_html(ctx)) oobs += _post_header_html(ctx, oob=True) menu = _mobile_nav_panel_html(ctx) return oob_page(ctx, oobs_html=oobs, content_html=content, menu_html=menu) def _market_landing_content(post: dict) -> str: """Build market landing page content (excerpt + feature image + html).""" parts = ['
    '] if post.get("custom_excerpt"): parts.append(f'
    {post["custom_excerpt"]}
    ') if post.get("feature_image"): parts.append( f'
    ' f'
    ' ) if post.get("html"): parts.append(f'
    {post["html"]}
    ') parts.append('
    ') return "".join(parts) # --------------------------------------------------------------------------- # Browse page # --------------------------------------------------------------------------- async def render_browse_page(ctx: dict) -> str: """Full page: product browse with filters.""" cards_html = _product_cards_html(ctx) content = f'
    {cards_html}
    ' hdr = root_header_html(ctx) hdr += sexp( '(div :id "root-header-child" :class "w-full" (raw! ph (raw! mh)))', ph=_post_header_html(ctx), mh=_market_header_html(ctx), ) menu = _mobile_nav_panel_html(ctx) filter_html = _mobile_filter_summary_html(ctx) aside_html = _desktop_filter_html(ctx) return full_page(ctx, header_rows_html=hdr, content_html=content, menu_html=menu, filter_html=filter_html, aside_html=aside_html) async def render_browse_oob(ctx: dict) -> str: """OOB response: product browse.""" cards_html = _product_cards_html(ctx) content = f'
    {cards_html}
    ' oobs = _oob_header_html("post-header-child", "market-header-child", _market_header_html(ctx)) oobs += _post_header_html(ctx, oob=True) menu = _mobile_nav_panel_html(ctx) filter_html = _mobile_filter_summary_html(ctx) aside_html = _desktop_filter_html(ctx) return oob_page(ctx, oobs_html=oobs, content_html=content, menu_html=menu, filter_html=filter_html, aside_html=aside_html) async def render_browse_cards(ctx: dict) -> str: """Pagination fragment: product cards only.""" return _product_cards_html(ctx) # --------------------------------------------------------------------------- # Product detail # --------------------------------------------------------------------------- async def render_product_page(ctx: dict, d: dict) -> str: """Full page: product detail.""" content = _product_detail_html(d, ctx) meta = _product_meta_html(d, ctx) hdr = root_header_html(ctx) hdr += sexp( '(div :id "root-header-child" :class "w-full" (raw! ph (raw! mh (raw! prh))))', ph=_post_header_html(ctx), mh=_market_header_html(ctx), prh=_product_header_html(ctx, d), ) return full_page(ctx, header_rows_html=hdr, content_html=content, meta_html=meta) async def render_product_oob(ctx: dict, d: dict) -> str: """OOB response: product detail.""" content = _product_detail_html(d, ctx) oobs = _market_header_html(ctx, oob=True) oobs += _oob_header_html("market-header-child", "product-header-child", _product_header_html(ctx, d)) menu = _mobile_nav_panel_html(ctx) return oob_page(ctx, oobs_html=oobs, content_html=content, menu_html=menu) # --------------------------------------------------------------------------- # Product admin # --------------------------------------------------------------------------- async def render_product_admin_page(ctx: dict, d: dict) -> str: """Full page: product admin.""" content = _product_detail_html(d, ctx) hdr = root_header_html(ctx) hdr += sexp( '(div :id "root-header-child" :class "w-full" (raw! ph (raw! mh (raw! prh (raw! pah)))))', ph=_post_header_html(ctx), mh=_market_header_html(ctx), prh=_product_header_html(ctx, d), pah=_product_admin_header_html(ctx, d), ) return full_page(ctx, header_rows_html=hdr, content_html=content) async def render_product_admin_oob(ctx: dict, d: dict) -> str: """OOB response: product admin.""" content = _product_detail_html(d, ctx) oobs = _product_header_html(ctx, d, oob=True) oobs += _oob_header_html("product-header-child", "product-admin-header-child", _product_admin_header_html(ctx, d)) return oob_page(ctx, oobs_html=oobs, content_html=content) def _product_admin_header_html(ctx: dict, d: dict, *, oob: bool = False) -> str: """Build product admin header row.""" from quart import url_for slug = d.get("slug", "") link_href = url_for("market.browse.product.admin", product_slug=slug) return sexp( '(~menu-row :id "product-admin-row" :level 4' ' :link-href lh :link-label "admin!!" :icon "fa fa-cog"' ' :child-id "product-admin-header-child" :oob oob)', lh=link_href, oob=oob, ) # --------------------------------------------------------------------------- # Market admin # --------------------------------------------------------------------------- async def render_market_admin_page(ctx: dict) -> str: """Full page: market admin.""" content = "market admin" hdr = root_header_html(ctx) hdr += sexp( '(div :id "root-header-child" :class "w-full" (raw! ph (raw! mh (raw! mah))))', ph=_post_header_html(ctx), mh=_market_header_html(ctx), mah=_market_admin_header_html(ctx), ) return full_page(ctx, header_rows_html=hdr, content_html=content) async def render_market_admin_oob(ctx: dict) -> str: """OOB response: market admin.""" content = "market admin" oobs = _market_header_html(ctx, oob=True) oobs += _oob_header_html("market-header-child", "market-admin-header-child", _market_admin_header_html(ctx)) return oob_page(ctx, oobs_html=oobs, content_html=content) def _market_admin_header_html(ctx: dict, *, oob: bool = False) -> str: """Build market admin header row.""" from quart import url_for link_href = url_for("market.admin.admin") return sexp( '(~menu-row :id "market-admin-row" :level 3' ' :link-href lh :link-label "admin" :icon "fa fa-cog"' ' :child-id "market-admin-header-child" :oob oob)', lh=link_href, oob=oob, )