Files
rose-ash/market/sx/cards.sx
giles c0d369eb8e
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 6m0s
Refactor SX templates: shared components, Python migration, cleanup
- Extract shared components (empty-state, delete-btn, sentinel, crud-*,
  view-toggle, img-or-placeholder, avatar, sumup-settings-form, auth
  forms, order tables/detail/checkout)
- Migrate all Python sx_call() callers to use shared components directly
- Remove 55+ thin wrapper defcomps from domain .sx files
- Remove trivial passthrough wrappers (blog-header-label, market-card-text, etc)
- Unify duplicate auth flows (account + federation) into shared/sx/templates/auth.sx
- Unify duplicate order views (cart + orders) into shared/sx/templates/orders.sx
- Disable static file caching in dev (SEND_FILE_MAX_AGE_DEFAULT=0)
- Add SX response validation and debug headers

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 20:34:34 +00:00

98 lines
5.3 KiB
Plaintext

;; Market card components — pure data, no raw! HTML injection
(defcomp ~market-label-overlay (&key src)
(img :src src :alt ""
:class "pointer-events-none absolute inset-0 w-full h-full object-contain object-top"))
(defcomp ~market-card-image (&key image labels brand brand-highlight)
(div :class "w-full aspect-square bg-stone-100 relative"
(figure :class "inline-block w-full h-full"
(div :class "relative w-full h-full"
(img :src image :alt "no image" :class "absolute inset-0 w-full h-full object-contain object-top" :loading "lazy" :decoding "async" :fetchpriority "low")
(when labels (map (lambda (src) (~market-label-overlay :src src)) labels)))
(figcaption :class (str "mt-2 text-sm text-center" brand-highlight " text-stone-600") brand))))
(defcomp ~market-card-no-image (&key labels brand)
(div :class "w-full aspect-square bg-stone-100 relative"
(div :class "p-2 flex flex-col items-center justify-center gap-2 text-red-500 h-full relative"
(div :class "text-stone-400 text-xs" "No image")
(when labels (ul :class "flex flex-row gap-1" (map (lambda (l) (li l)) labels)))
(div :class "text-stone-900 text-center line-clamp-3 break-words [overflow-wrap:anywhere]" brand))))
(defcomp ~market-card-sticker (&key src name ring-cls)
(img :src src :alt name :class (str "w-6 h-6" ring-cls)))
(defcomp ~market-card-stickers (&key stickers)
(div :class "flex flex-row justify-center gap-2 p-2"
(map (lambda (s) (~market-card-sticker :src (get s "src") :name (get s "name") :ring-cls (get s "ring-cls"))) stickers)))
(defcomp ~market-card-highlight (&key pre mid post)
(<> pre (mark mid) post))
;; Price — delegates to shared ~price
(defcomp ~market-card-price (&key special-price regular-price)
(~price :special-price special-price :regular-price regular-price))
;; Main product card — accepts pure data, composes sub-components
(defcomp ~market-product-card (&key href hx-select
has-like liked slug csrf like-action
image labels brand brand-highlight
special-price regular-price
cart-action quantity cart-href
stickers
title has-highlight search-pre search-mid search-post)
(div :class "flex flex-col rounded-xl bg-white shadow hover:shadow-md transition overflow-hidden relative"
(when has-like
(~market-like-button :form-id (str "like-" slug) :action like-action :slug slug :csrf csrf
:icon-cls (if liked "fa-solid fa-heart text-red-500" "fa-regular fa-heart text-stone-400")))
(a :href href :sx-get href :sx-target "#main-panel"
:sx-select hx-select :sx-swap "outerHTML" :sx-push-url "true"
(if image
(~market-card-image :image image :labels labels :brand brand :brand-highlight brand-highlight)
(~market-card-no-image :labels labels :brand brand))
(~market-card-price :special-price special-price :regular-price regular-price))
(div :class "flex justify-center"
(if quantity
(~market-cart-add-quantity :cart-id (str "cart-" slug) :action cart-action :csrf csrf
:minus-val (str (- quantity 1)) :plus-val (str (+ quantity 1))
:quantity (str quantity) :cart-href cart-href)
(~market-cart-add-empty :cart-id (str "cart-" slug) :action cart-action :csrf csrf)))
(a :href href :sx-get href :sx-target "#main-panel"
:sx-select hx-select :sx-swap "outerHTML" :sx-push-url "true"
(when stickers (~market-card-stickers :stickers stickers))
(div :class "text-sm font-medium text-stone-800 text-center line-clamp-3 break-words [overflow-wrap:anywhere]"
(if has-highlight
(~market-card-highlight :pre search-pre :mid search-mid :post search-post)
title)))))
(defcomp ~market-like-button (&key form-id action slug csrf icon-cls)
(div :class "absolute top-2 right-2 z-10 text-6xl md:text-xl"
(form :id form-id :action action :method "post"
:sx-post action :sx-target (str "#like-" slug) :sx-swap "outerHTML"
(input :type "hidden" :name "csrf_token" :value csrf)
(button :type "submit" :class "cursor-pointer"
(i :class icon-cls :aria-hidden "true")))))
(defcomp ~market-market-card-title-link (&key href name)
(a :href href :class "hover:text-emerald-700"
(h2 :class "text-lg font-semibold text-stone-900" name)))
(defcomp ~market-market-card-title (&key name)
(h2 :class "text-lg font-semibold text-stone-900" name))
(defcomp ~market-market-card-desc (&key description)
(p :class "text-sm text-stone-600 mt-1 line-clamp-2" description))
(defcomp ~market-market-card-badge (&key href title)
(div :class "flex flex-wrap items-center gap-1.5 mt-3"
(a :href href :class "inline-block px-2 py-0.5 rounded-full text-xs font-medium bg-amber-100 text-amber-800 hover:bg-amber-200"
title)))
(defcomp ~market-market-card (&key title-content desc-content badge-content title desc badge)
(article :class "rounded-xl bg-white shadow-sm border border-stone-200 p-5 flex flex-col justify-between hover:border-stone-400 transition-colors"
(div
(if title-content title-content (when title title))
(if desc-content desc-content (when desc desc)))
(if badge-content badge-content (when badge badge))))