"""Product/market card data builders.""" from __future__ import annotations from typing import Any from shared.sx.helpers import sx_call from .utils import _set_prices, _price_str from .filters import _MOBILE_SENTINEL_HS, _DESKTOP_SENTINEL_HS # --------------------------------------------------------------------------- # Product card data extraction # --------------------------------------------------------------------------- def _product_card_data(p: dict, ctx: dict) -> dict: """Extract data for a single product card.""" 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() # Price data 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"]) # Image labels as src URLs labels = p.get("labels", []) label_srcs = [] if p.get("image") and callable(asset_url_fn): label_srcs = [asset_url_fn("labels/" + l + ".svg") for l in labels] # Stickers as data raw_stickers = p.get("stickers", []) sticker_data = [] if raw_stickers and callable(asset_url_fn): for s in raw_stickers: ring = " ring-2 ring-emerald-500 rounded" if s in selected_stickers else "" sticker_data.append({"src": asset_url_fn(f"stickers/{s}.svg"), "name": s, "ring-cls": ring}) # Title highlighting title = p.get("title", "") has_highlight = False search_pre = search_mid = search_post = "" if search and search.lower() in title.lower(): idx = title.lower().index(search.lower()) has_highlight = True search_pre = title[:idx] search_mid = title[idx:idx + len(search)] search_post = title[idx + len(search):] # Cart quantity = sum(ci.quantity for ci in cart if ci.product.slug == slug) if cart else 0 cart_action = url_for("market.browse.product.cart", product_slug=slug) cart_url_fn = ctx.get("cart_url") cart_href = cart_url_fn("/") if callable(cart_url_fn) else "/" brand = p.get("brand", "") brand_highlight = " bg-yellow-200" if brand in selected_brands else "" d: dict[str, Any] = { "href": item_href, "hx-select": hx_select, "slug": slug, "image": p.get("image", ""), "brand": brand, "brand-highlight": brand_highlight, "special-price": sp_str, "regular-price": rp_str, "cart-action": cart_action, "quantity": quantity, "cart-href": cart_href, "csrf": csrf, "title": title, "has-like": bool(user), } if label_srcs: d["labels"] = label_srcs elif labels: d["labels"] = labels if user: d["liked"] = p.get("is_liked", False) d["like-action"] = url_for("market.browse.product.like_toggle", product_slug=slug) if sticker_data: d["stickers"] = sticker_data if has_highlight: d["has-highlight"] = True d["search-pre"] = search_pre d["search-mid"] = search_mid d["search-post"] = search_post return d def _product_cards_sx(ctx: dict) -> str: """S-expression wire format for product cards — delegates to .sx defcomp.""" 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") product_data = [_product_card_data(p, ctx) for p in products] next_url = "" if page < total_pages: 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 return sx_call("market-product-cards-content", products=product_data, page=page, total_pages=total_pages, next_url=next_url, mobile_sentinel_hs=_MOBILE_SENTINEL_HS, desktop_sentinel_hs=_DESKTOP_SENTINEL_HS) def _like_button_data(slug: str, liked: bool, csrf: str, ctx: dict) -> dict: """Extract like button data.""" 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 { "form-id": f"like-{slug}", "action": action, "slug": slug, "csrf": csrf, "icon-cls": icon_cls, } # --------------------------------------------------------------------------- # Market cards data extraction # --------------------------------------------------------------------------- def _market_card_data(market: Any, page_info: dict, *, show_page_badge: bool = True, post_slug: str = "") -> dict: """Extract data for 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) href = "" badge_href = "" badge_title = "" if show_page_badge and page_info: pi = page_info.get(container_id, {}) p_slug = pi.get("slug", "") p_title = pi.get("title", "") href = market_url(f"/{p_slug}/{slug}/") if p_slug else "" if p_title: badge_href = market_url(f"/{p_slug}/") badge_title = p_title else: p_slug = post_slug href = market_url(f"/{post_slug}/{slug}/") if post_slug else "" return { "name": name, "description": description, "href": href, "show-badge": show_page_badge, "badge-href": badge_href, "badge-title": badge_title, } def _market_cards_sx(markets: list, page_info: dict, page: int, has_more: bool, next_url: str, *, show_page_badge: bool = True, post_slug: str = "") -> str: """Build market cards as sx — delegates to .sx defcomp.""" market_data = [_market_card_data(m, page_info, show_page_badge=show_page_badge, post_slug=post_slug) for m in markets] return sx_call("market-cards-content", markets=market_data, page=page, has_more=has_more, next_url=next_url) def _no_markets_sx(message: str = "No markets available") -> str: """Empty state for markets as sx.""" return sx_call("empty-state", icon="fa fa-store", message=message, cls="px-3 py-12 text-center text-stone-400") def _market_landing_content_sx(post: dict) -> str: """Build market landing page content — delegates to .sx defcomp.""" return sx_call("market-landing-from-data", excerpt=post.get("custom_excerpt") or None, feature_image=post.get("feature_image") or None, html=post.get("html") or None)