Rename all 1,169 components to path-based names with namespace support
Component names now reflect filesystem location using / as path separator and : as namespace separator for shared components: ~sx-header → ~layouts/header ~layout-app-body → ~shared:layout/app-body ~blog-admin-dashboard → ~admin/dashboard 209 files, 4,941 replacements across all services. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,40 +1,40 @@
|
||||
;; Market card components — pure data, no raw! HTML injection
|
||||
|
||||
(defcomp ~market-label-overlay (&key (src :as string))
|
||||
(defcomp ~cards/label-overlay (&key (src :as string))
|
||||
(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 :as string) (labels :as list?) (brand :as string) (brand-highlight :as string?))
|
||||
(defcomp ~cards/image (&key (image :as string) (labels :as list?) (brand :as string) (brand-highlight :as string?))
|
||||
(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)))
|
||||
(when labels (map (lambda (src) (~cards/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 :as list?) (brand :as string))
|
||||
(defcomp ~cards/no-image (&key (labels :as list?) (brand :as string))
|
||||
(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 :as string) (name :as string) (ring-cls :as string?))
|
||||
(defcomp ~cards/sticker (&key (src :as string) (name :as string) (ring-cls :as string?))
|
||||
(img :src src :alt name :class (str "w-6 h-6" ring-cls)))
|
||||
|
||||
(defcomp ~market-card-stickers (&key (stickers :as list))
|
||||
(defcomp ~cards/stickers (&key (stickers :as list))
|
||||
(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)))
|
||||
(map (lambda (s) (~cards/sticker :src (get s "src") :name (get s "name") :ring-cls (get s "ring-cls"))) stickers)))
|
||||
|
||||
(defcomp ~market-card-highlight (&key (pre :as string) (mid :as string) (post :as string))
|
||||
(defcomp ~cards/highlight (&key (pre :as string) (mid :as string) (post :as string))
|
||||
(<> pre (mark mid) post))
|
||||
|
||||
;; Price — delegates to shared ~price
|
||||
(defcomp ~market-card-price (&key (special-price :as string?) (regular-price :as string?))
|
||||
(~price :special-price special-price :regular-price regular-price))
|
||||
;; Price — delegates to shared ~shared:misc/price
|
||||
(defcomp ~cards/price (&key (special-price :as string?) (regular-price :as string?))
|
||||
(~shared:misc/price :special-price special-price :regular-price regular-price))
|
||||
|
||||
;; Main product card — accepts pure data, composes sub-components
|
||||
(defcomp ~market-product-card (&key (href :as string) (hx-select :as string)
|
||||
(defcomp ~cards/product-card (&key (href :as string) (hx-select :as string)
|
||||
(has-like :as boolean) (liked :as boolean?) (slug :as string) (csrf :as string) (like-action :as string?)
|
||||
(image :as string?) (labels :as list?) (brand :as string) (brand-highlight :as string?)
|
||||
(special-price :as string?) (regular-price :as string?)
|
||||
@@ -43,29 +43,29 @@
|
||||
(title :as string) (has-highlight :as boolean) (search-pre :as string?) (search-mid :as string?) (search-post :as string?))
|
||||
(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
|
||||
(~cards/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))
|
||||
(~cards/image :image image :labels labels :brand brand :brand-highlight brand-highlight)
|
||||
(~cards/no-image :labels labels :brand brand))
|
||||
(~cards/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
|
||||
(~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)))
|
||||
(~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))
|
||||
(when stickers (~cards/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)
|
||||
(~cards/highlight :pre search-pre :mid search-mid :post search-post)
|
||||
title)))))
|
||||
|
||||
(defcomp ~market-like-button (&key (form-id :as string) (action :as string) (slug :as string) (csrf :as string) (icon-cls :as string))
|
||||
(defcomp ~cards/like-button (&key (form-id :as string) (action :as string) (slug :as string) (csrf :as string) (icon-cls :as string))
|
||||
(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"
|
||||
@@ -73,22 +73,22 @@
|
||||
(button :type "submit" :class "cursor-pointer"
|
||||
(i :class icon-cls :aria-hidden "true")))))
|
||||
|
||||
(defcomp ~market-market-card-title-link (&key (href :as string) (name :as string))
|
||||
(defcomp ~cards/market-card-title-link (&key (href :as string) (name :as string))
|
||||
(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 :as string))
|
||||
(defcomp ~cards/market-card-title (&key (name :as string))
|
||||
(h2 :class "text-lg font-semibold text-stone-900" name))
|
||||
|
||||
(defcomp ~market-market-card-desc (&key (description :as string))
|
||||
(defcomp ~cards/market-card-desc (&key (description :as string))
|
||||
(p :class "text-sm text-stone-600 mt-1 line-clamp-2" description))
|
||||
|
||||
(defcomp ~market-market-card-badge (&key (href :as string) (title :as string))
|
||||
(defcomp ~cards/market-card-badge (&key (href :as string) (title :as string))
|
||||
(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 :as list?) (desc-content :as list?) (badge-content :as list?) (title :as string?) (desc :as string?) (badge :as string?))
|
||||
(defcomp ~cards/market-card (&key (title-content :as list?) (desc-content :as list?) (badge-content :as list?) (title :as string?) (desc :as string?) (badge :as string?))
|
||||
(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))
|
||||
@@ -101,11 +101,11 @@
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
;; Product cards grid with infinite scroll sentinels
|
||||
(defcomp ~market-product-cards-content (&key (products :as list) (page :as number) (total-pages :as number) (next-url :as string)
|
||||
(defcomp ~cards/product-cards-content (&key (products :as list) (page :as number) (total-pages :as number) (next-url :as string)
|
||||
(mobile-sentinel-hs :as string?) (desktop-sentinel-hs :as string?))
|
||||
(<>
|
||||
(map (lambda (p)
|
||||
(~market-product-card
|
||||
(~cards/product-card
|
||||
:href (get p "href") :hx-select (get p "hx-select") :slug (get p "slug")
|
||||
:image (get p "image") :brand (get p "brand") :brand-highlight (get p "brand-highlight")
|
||||
:special-price (get p "special-price") :regular-price (get p "regular-price")
|
||||
@@ -119,39 +119,39 @@
|
||||
:search-post (get p "search-post")))
|
||||
products)
|
||||
(if (< page total-pages)
|
||||
(<> (~sentinel-mobile :id (str "sentinel-" page "-m") :next-url next-url
|
||||
(<> (~shared:misc/sentinel-mobile :id (str "sentinel-" page "-m") :next-url next-url
|
||||
:hyperscript mobile-sentinel-hs)
|
||||
(~sentinel-desktop :id (str "sentinel-" page "-d") :next-url next-url
|
||||
(~shared:misc/sentinel-desktop :id (str "sentinel-" page "-d") :next-url next-url
|
||||
:hyperscript desktop-sentinel-hs))
|
||||
(~end-of-results))))
|
||||
(~shared:misc/end-of-results))))
|
||||
|
||||
;; Single market card from data (handles conditional title/desc/badge)
|
||||
(defcomp ~market-card-from-data (&key (name :as string) (description :as string?) (href :as string?) (show-badge :as boolean) (badge-href :as string?) (badge-title :as string?))
|
||||
(~market-market-card
|
||||
(defcomp ~cards/from-data (&key (name :as string) (description :as string?) (href :as string?) (show-badge :as boolean) (badge-href :as string?) (badge-title :as string?))
|
||||
(~cards/market-card
|
||||
:title-content (if href
|
||||
(~market-market-card-title-link :href href :name name)
|
||||
(~market-market-card-title :name name))
|
||||
(~cards/market-card-title-link :href href :name name)
|
||||
(~cards/market-card-title :name name))
|
||||
:desc-content (when description
|
||||
(~market-market-card-desc :description description))
|
||||
(~cards/market-card-desc :description description))
|
||||
:badge-content (when (and show-badge badge-title)
|
||||
(~market-market-card-badge :href badge-href :title badge-title))))
|
||||
(~cards/market-card-badge :href badge-href :title badge-title))))
|
||||
|
||||
;; Market cards list with infinite scroll sentinel
|
||||
(defcomp ~market-cards-content (&key (markets :as list) (page :as number) (has-more :as boolean) (next-url :as string))
|
||||
(defcomp ~cards/content (&key (markets :as list) (page :as number) (has-more :as boolean) (next-url :as string))
|
||||
(<>
|
||||
(map (lambda (m)
|
||||
(~market-card-from-data
|
||||
(~cards/from-data
|
||||
:name (get m "name") :description (get m "description")
|
||||
:href (get m "href") :show-badge (get m "show-badge")
|
||||
:badge-href (get m "badge-href") :badge-title (get m "badge-title")))
|
||||
markets)
|
||||
(when has-more
|
||||
(~sentinel-simple :id (str "sentinel-" page) :next-url next-url))))
|
||||
(~shared:misc/sentinel-simple :id (str "sentinel-" page) :next-url next-url))))
|
||||
|
||||
;; Market landing page content from data
|
||||
(defcomp ~market-landing-from-data (&key (excerpt :as string?) (feature-image :as string?) (html :as string?))
|
||||
(~market-landing-content :inner
|
||||
(<> (when excerpt (~market-landing-excerpt :text excerpt))
|
||||
(when feature-image (~market-landing-image :src feature-image))
|
||||
(when html (~market-landing-html :html html)))))
|
||||
(defcomp ~cards/landing-from-data (&key (excerpt :as string?) (feature-image :as string?) (html :as string?))
|
||||
(~detail/landing-content :inner
|
||||
(<> (when excerpt (~detail/landing-excerpt :text excerpt))
|
||||
(when feature-image (~detail/landing-image :src feature-image))
|
||||
(when html (~detail/landing-html :html html)))))
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;; Market cart components
|
||||
|
||||
(defcomp ~market-cart-add-empty (&key cart-id action csrf)
|
||||
(defcomp ~cart/add-empty (&key cart-id action csrf)
|
||||
(div :id cart-id
|
||||
(form :action action :method "post" :sx-post action :sx-target "#cart-mini" :sx-swap "outerHTML" :class "rounded flex items-center"
|
||||
(input :type "hidden" :name "csrf_token" :value csrf)
|
||||
@@ -9,7 +9,7 @@
|
||||
(span :class "relative inline-flex items-center justify-center"
|
||||
(i :class "fa fa-cart-plus text-4xl" :aria-hidden "true"))))))
|
||||
|
||||
(defcomp ~market-cart-add-quantity (&key cart-id action csrf minus-val plus-val quantity cart-href)
|
||||
(defcomp ~cart/add-quantity (&key cart-id action csrf minus-val plus-val quantity cart-href)
|
||||
(div :id cart-id
|
||||
(div :class "rounded flex items-center gap-2"
|
||||
(form :action action :method "post" :sx-post action :sx-target "#cart-mini" :sx-swap "outerHTML"
|
||||
@@ -26,7 +26,7 @@
|
||||
(input :type "hidden" :name "count" :value plus-val)
|
||||
(button :type "submit" :class "inline-flex items-center justify-center w-8 h-8 text-sm font-medium rounded-full border border-emerald-600 text-emerald-700 hover:bg-emerald-50 text-xl" "+")))))
|
||||
|
||||
(defcomp ~market-cart-mini-count (&key href count)
|
||||
(defcomp ~cart/mini-count (&key href count)
|
||||
(div :id "cart-mini" :sx-swap-oob "outerHTML"
|
||||
(a :href href :class "relative inline-flex items-center justify-center"
|
||||
(span :class "relative inline-flex items-center justify-center"
|
||||
@@ -35,25 +35,25 @@
|
||||
(span :class "flex items-center justify-center bg-emerald-500 text-white rounded-full min-w-[1.25rem] h-5 text-xs font-bold px-1"
|
||||
count))))))
|
||||
|
||||
(defcomp ~market-cart-mini-empty (&key href logo)
|
||||
(defcomp ~cart/mini-empty (&key href logo)
|
||||
(div :id "cart-mini" :sx-swap-oob "outerHTML"
|
||||
(a :href href :class "relative inline-flex items-center justify-center"
|
||||
(img :src logo :class "h-8 w-8 rounded-full object-cover border border-stone-300" :alt ""))))
|
||||
|
||||
(defcomp ~market-cart-add-oob (&key id content inner)
|
||||
(defcomp ~cart/add-oob (&key id content inner)
|
||||
(div :id id :sx-swap-oob "outerHTML"
|
||||
(if content content (when inner inner))))
|
||||
|
||||
;; Cart added response — composes cart mini + add/remove OOB in sx
|
||||
(defcomp ~market-cart-added-response (&key has-count cart-href blog-href logo
|
||||
(defcomp ~cart/added-response (&key has-count cart-href blog-href logo
|
||||
slug action csrf quantity minus-val plus-val)
|
||||
(<>
|
||||
(if has-count
|
||||
(~market-cart-mini-count :href cart-href :count (str has-count))
|
||||
(~market-cart-mini-empty :href blog-href :logo logo))
|
||||
(~market-cart-add-oob :id (str "cart-add-" slug)
|
||||
(~cart/mini-count :href cart-href :count (str has-count))
|
||||
(~cart/mini-empty :href blog-href :logo logo))
|
||||
(~cart/add-oob :id (str "cart-add-" slug)
|
||||
:inner (if (= (or quantity "0") "0")
|
||||
(~market-cart-add-empty :cart-id (str "cart-" slug) :action action :csrf csrf)
|
||||
(~market-cart-add-quantity :cart-id (str "cart-" slug) :action action :csrf csrf
|
||||
(~cart/add-empty :cart-id (str "cart-" slug) :action action :csrf csrf)
|
||||
(~cart/add-quantity :cart-id (str "cart-" slug) :action action :csrf csrf
|
||||
:minus-val minus-val :plus-val plus-val
|
||||
:quantity quantity :cart-href cart-href)))))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;; Market product detail components
|
||||
|
||||
(defcomp ~market-detail-gallery-inner (&key (like :as list?) (image :as string) (alt :as string) (labels :as list?) (brand :as string))
|
||||
(defcomp ~detail/gallery-inner (&key (like :as list?) (image :as string) (alt :as string) (labels :as list?) (brand :as string))
|
||||
(<> like
|
||||
(figure :class "inline-block"
|
||||
(div :class "relative w-full aspect-square"
|
||||
@@ -9,7 +9,7 @@
|
||||
labels)
|
||||
(figcaption :class "mt-2 text-sm text-stone-600 text-center" brand))))
|
||||
|
||||
(defcomp ~market-detail-nav-buttons ()
|
||||
(defcomp ~detail/nav-buttons ()
|
||||
(<>
|
||||
(button :type "button" :data-prev ""
|
||||
:class "absolute left-2 top-1/2 -translate-y-1/2 z-10 grid place-items-center w-12 h-12 md:w-14 md:h-14 rounded-full bg-white/90 hover:bg-white shadow-lg text-3xl md:text-4xl"
|
||||
@@ -18,79 +18,79 @@
|
||||
:class "absolute right-2 top-1/2 -translate-y-1/2 z-10 grid place-items-center w-12 h-12 md:w-14 md:h-14 rounded-full bg-white/90 hover:bg-white shadow-lg text-3xl md:text-4xl"
|
||||
:title "Next" "\u203a")))
|
||||
|
||||
(defcomp ~market-detail-gallery (&key (inner :as list) (nav :as list?))
|
||||
(defcomp ~detail/gallery (&key (inner :as list) (nav :as list?))
|
||||
(div :class "relative rounded-xl overflow-hidden bg-stone-100"
|
||||
inner nav))
|
||||
|
||||
(defcomp ~market-detail-thumb (&key (title :as string) (src :as string) (alt :as string))
|
||||
(defcomp ~detail/thumb (&key (title :as string) (src :as string) (alt :as string))
|
||||
(<> (button :type "button" :data-thumb ""
|
||||
:class "shrink-0 rounded-lg overflow-hidden bg-stone-100 hover:opacity-90 ring-offset-2"
|
||||
:title title
|
||||
(img :src src :class "h-16 w-16 object-contain" :alt alt :loading "lazy" :decoding "async"))
|
||||
(span :data-image-src src :class "hidden")))
|
||||
|
||||
(defcomp ~market-detail-thumbs (&key (thumbs :as list))
|
||||
(defcomp ~detail/thumbs (&key (thumbs :as list))
|
||||
(div :class "flex flex-row justify-center"
|
||||
(div :class "mt-3 flex gap-2 overflow-x-auto no-scrollbar" thumbs)))
|
||||
|
||||
(defcomp ~market-detail-no-image (&key (like :as list?))
|
||||
(defcomp ~detail/no-image (&key (like :as list?))
|
||||
(div :class "relative aspect-square bg-stone-100 rounded-xl flex items-center justify-center text-stone-400"
|
||||
like "No image"))
|
||||
|
||||
(defcomp ~market-detail-sticker (&key (src :as string) (name :as string))
|
||||
(defcomp ~detail/sticker (&key (src :as string) (name :as string))
|
||||
(img :src src :alt name :class "w-10 h-10"))
|
||||
|
||||
(defcomp ~market-detail-stickers (&key (items :as list))
|
||||
(defcomp ~detail/stickers (&key (items :as list))
|
||||
(div :class "p-2 flex flex-row justify-center gap-2" items))
|
||||
|
||||
(defcomp ~market-detail-unit-price (&key (price :as string))
|
||||
(defcomp ~detail/unit-price (&key (price :as string))
|
||||
(div (str "Unit price: " price)))
|
||||
|
||||
(defcomp ~market-detail-case-size (&key (size :as string))
|
||||
(defcomp ~detail/case-size (&key (size :as string))
|
||||
(div (str "Case size: " size)))
|
||||
|
||||
(defcomp ~market-detail-extras (&key (inner :as list))
|
||||
(defcomp ~detail/extras (&key (inner :as list))
|
||||
(div :class "mt-2 space-y-1 text-sm text-stone-600" inner))
|
||||
|
||||
(defcomp ~market-detail-desc-short (&key (text :as string))
|
||||
(defcomp ~detail/desc-short (&key (text :as string))
|
||||
(p :class "leading-relaxed text-lg" text))
|
||||
|
||||
(defcomp ~market-detail-desc-html (&key (html :as string))
|
||||
(defcomp ~detail/desc-html (&key (html :as string))
|
||||
(div :class "max-w-none text-sm leading-relaxed" (~rich-text :html html)))
|
||||
|
||||
(defcomp ~market-detail-desc-wrapper (&key (inner :as list))
|
||||
(defcomp ~detail/desc-wrapper (&key (inner :as list))
|
||||
(div :class "mt-4 text-stone-800 space-y-3" inner))
|
||||
|
||||
(defcomp ~market-detail-section (&key (title :as string) (html :as string))
|
||||
(defcomp ~detail/section (&key (title :as string) (html :as string))
|
||||
(details :class "group rounded-xl border bg-white shadow-sm open:shadow p-0"
|
||||
(summary :class "cursor-pointer select-none px-4 py-3 flex items-center justify-between"
|
||||
(span :class "font-medium" title)
|
||||
(span :class "ml-2 text-xl transition-transform group-open:rotate-180" "\u2304"))
|
||||
(div :class "px-4 pb-4 max-w-none text-sm leading-relaxed" (~rich-text :html html))))
|
||||
|
||||
(defcomp ~market-detail-sections (&key (items :as list))
|
||||
(defcomp ~detail/sections (&key (items :as list))
|
||||
(div :class "mt-8 space-y-3" items))
|
||||
|
||||
(defcomp ~market-detail-right-col (&key (inner :as list))
|
||||
(defcomp ~detail/right-col (&key (inner :as list))
|
||||
(div :class "md:col-span-3" inner))
|
||||
|
||||
(defcomp ~market-detail-layout (&key (gallery :as list) (stickers :as list?) (details :as list))
|
||||
(defcomp ~detail/layout (&key (gallery :as list) (stickers :as list?) (details :as list))
|
||||
(<> (div :class "mt-3 grid grid-cols-1 md:grid-cols-5 gap-6" :data-gallery-root ""
|
||||
(div :class "md:col-span-2" gallery stickers)
|
||||
details)
|
||||
(div :class "pb-8")))
|
||||
|
||||
(defcomp ~market-landing-excerpt (&key (text :as string))
|
||||
(defcomp ~detail/landing-excerpt (&key (text :as string))
|
||||
(div :class "w-full text-center italic text-3xl p-2" text))
|
||||
|
||||
(defcomp ~market-landing-image (&key (src :as string))
|
||||
(defcomp ~detail/landing-image (&key (src :as string))
|
||||
(div :class "mb-3 flex justify-center"
|
||||
(img :src src :alt "" :class "rounded-lg w-full md:w-3/4 object-cover")))
|
||||
|
||||
(defcomp ~market-landing-html (&key (html :as string))
|
||||
(defcomp ~detail/landing-html (&key (html :as string))
|
||||
(div :class "blog-content p-2" (~rich-text :html html)))
|
||||
|
||||
(defcomp ~market-landing-content (&key (inner :as list))
|
||||
(defcomp ~detail/landing-content (&key (inner :as list))
|
||||
(<> (article :class "relative w-full" inner) (div :class "pb-8")))
|
||||
|
||||
|
||||
@@ -99,64 +99,64 @@
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
;; Gallery section from pre-computed data
|
||||
(defcomp ~market-detail-gallery-from-data (&key (images :as list?) (labels :as list?) (brand :as string) (like-data :as dict?) (has-nav-buttons :as boolean) (thumbs :as list?))
|
||||
(defcomp ~detail/gallery-from-data (&key (images :as list?) (labels :as list?) (brand :as string) (like-data :as dict?) (has-nav-buttons :as boolean) (thumbs :as list?))
|
||||
(let ((like-sx (when like-data
|
||||
(~market-like-button
|
||||
(~cards/like-button
|
||||
:form-id (get like-data "form-id") :action (get like-data "action")
|
||||
:slug (get like-data "slug") :csrf (get like-data "csrf")
|
||||
:icon-cls (get like-data "icon-cls")))))
|
||||
(if images
|
||||
(<>
|
||||
(~market-detail-gallery
|
||||
:inner (~market-detail-gallery-inner
|
||||
(~detail/gallery
|
||||
:inner (~detail/gallery-inner
|
||||
:like like-sx
|
||||
:image (get (first images) "src") :alt (get (first images) "alt")
|
||||
:labels (when labels
|
||||
(<> (map (lambda (src) (~market-label-overlay :src src)) labels)))
|
||||
(<> (map (lambda (src) (~cards/label-overlay :src src)) labels)))
|
||||
:brand brand)
|
||||
:nav (when has-nav-buttons (~market-detail-nav-buttons)))
|
||||
:nav (when has-nav-buttons (~detail/nav-buttons)))
|
||||
(when thumbs
|
||||
(~market-detail-thumbs :thumbs
|
||||
(~detail/thumbs :thumbs
|
||||
(<> (map (lambda (t)
|
||||
(~market-detail-thumb
|
||||
(~detail/thumb
|
||||
:title (get t "title") :src (get t "src") :alt (get t "alt")))
|
||||
thumbs)))))
|
||||
(~market-detail-no-image :like like-sx))))
|
||||
(~detail/no-image :like like-sx))))
|
||||
|
||||
;; Right column details from data
|
||||
(defcomp ~market-detail-info-from-data (&key (extras :as list?) (desc-short :as string?) (desc-html :as string?) (sections :as list?))
|
||||
(~market-detail-right-col :inner
|
||||
(defcomp ~detail/info-from-data (&key (extras :as list?) (desc-short :as string?) (desc-html :as string?) (sections :as list?))
|
||||
(~detail/right-col :inner
|
||||
(<>
|
||||
(when extras
|
||||
(~market-detail-extras :inner
|
||||
(~detail/extras :inner
|
||||
(<> (map (lambda (e)
|
||||
(if (= (get e "type") "unit-price")
|
||||
(~market-detail-unit-price :price (get e "value"))
|
||||
(~market-detail-case-size :size (get e "value"))))
|
||||
(~detail/unit-price :price (get e "value"))
|
||||
(~detail/case-size :size (get e "value"))))
|
||||
extras))))
|
||||
(when (or desc-short desc-html)
|
||||
(~market-detail-desc-wrapper :inner
|
||||
(<> (when desc-short (~market-detail-desc-short :text desc-short))
|
||||
(when desc-html (~market-detail-desc-html :html desc-html)))))
|
||||
(~detail/desc-wrapper :inner
|
||||
(<> (when desc-short (~detail/desc-short :text desc-short))
|
||||
(when desc-html (~detail/desc-html :html desc-html)))))
|
||||
(when sections
|
||||
(~market-detail-sections :items
|
||||
(~detail/sections :items
|
||||
(<> (map (lambda (s)
|
||||
(~market-detail-section :title (get s "title") :html (get s "html")))
|
||||
(~detail/section :title (get s "title") :html (get s "html")))
|
||||
sections)))))))
|
||||
|
||||
;; Full product detail layout from data
|
||||
(defcomp ~market-product-detail-from-data (&key (images :as list?) (labels :as list?) (brand :as string) (like-data :as dict?)
|
||||
(defcomp ~detail/product-detail-from-data (&key (images :as list?) (labels :as list?) (brand :as string) (like-data :as dict?)
|
||||
(has-nav-buttons :as boolean) (thumbs :as list?) (sticker-items :as list?)
|
||||
(extras :as list?) (desc-short :as string?) (desc-html :as string?) (sections :as list?))
|
||||
(~market-detail-layout
|
||||
:gallery (~market-detail-gallery-from-data
|
||||
(~detail/layout
|
||||
:gallery (~detail/gallery-from-data
|
||||
:images images :labels labels :brand brand :like-data like-data
|
||||
:has-nav-buttons has-nav-buttons :thumbs thumbs)
|
||||
:stickers (when sticker-items
|
||||
(~market-detail-stickers :items
|
||||
(~detail/stickers :items
|
||||
(<> (map (lambda (s)
|
||||
(~market-detail-sticker :src (get s "src") :name (get s "name")))
|
||||
(~detail/sticker :src (get s "src") :name (get s "name")))
|
||||
sticker-items))))
|
||||
:details (~market-detail-info-from-data
|
||||
:details (~detail/info-from-data
|
||||
:extras extras :desc-short desc-short :desc-html desc-html
|
||||
:sections sections)))
|
||||
|
||||
@@ -1,73 +1,73 @@
|
||||
;; Market filter components
|
||||
|
||||
(defcomp ~market-filter-sort-item (&key href hx-select ring-cls src label)
|
||||
(defcomp ~filters/sort-item (&key href hx-select ring-cls src label)
|
||||
(a :href href :sx-get href :sx-target "#main-panel"
|
||||
:sx-select hx-select :sx-swap "outerHTML" :sx-push-url "true"
|
||||
:class (str "flex flex-col items-center gap-1 p-1 cursor-pointer" ring-cls)
|
||||
(img :src src :alt label :class "w-10 h-10")
|
||||
(span :class "text-xs" label)))
|
||||
|
||||
(defcomp ~market-filter-sort-row (&key items)
|
||||
(defcomp ~filters/sort-row (&key items)
|
||||
(div :class "flex flex-row gap-2 justify-center p-1"
|
||||
items))
|
||||
|
||||
(defcomp ~market-filter-like (&key href hx-select icon-cls size-cls)
|
||||
(defcomp ~filters/like (&key href hx-select icon-cls size-cls)
|
||||
(a :href href :sx-get href :sx-target "#main-panel"
|
||||
:sx-select hx-select :sx-swap "outerHTML" :sx-push-url "true"
|
||||
:class "flex flex-col items-center gap-1 p-1 cursor-pointer"
|
||||
(i :aria-hidden "true" :class (str icon-cls " " size-cls " leading-none"))))
|
||||
|
||||
(defcomp ~market-filter-label-item (&key href hx-select ring-cls src name)
|
||||
(defcomp ~filters/label-item (&key href hx-select ring-cls src name)
|
||||
(a :href href :sx-get href :sx-target "#main-panel"
|
||||
:sx-select hx-select :sx-swap "outerHTML" :sx-push-url "true"
|
||||
:class (str "flex flex-col items-center gap-1 p-1 cursor-pointer" ring-cls)
|
||||
(img :src src :alt name :class "w-10 h-10")))
|
||||
|
||||
(defcomp ~market-filter-sticker-item (&key href hx-select ring-cls src name count-cls count)
|
||||
(defcomp ~filters/sticker-item (&key href hx-select ring-cls src name count-cls count)
|
||||
(a :href href :sx-get href :sx-target "#main-panel"
|
||||
:sx-select hx-select :sx-swap "outerHTML" :sx-push-url "true"
|
||||
:class (str "flex flex-col items-center gap-1 p-1 cursor-pointer" ring-cls)
|
||||
(img :src src :alt name :class "w-6 h-6")
|
||||
(span :class count-cls count)))
|
||||
|
||||
(defcomp ~market-filter-stickers-row (&key items)
|
||||
(defcomp ~filters/stickers-row (&key items)
|
||||
(div :class "flex flex-wrap gap-2 justify-center p-1"
|
||||
items))
|
||||
|
||||
(defcomp ~market-filter-brand-item (&key href hx-select bg-cls name-cls name count)
|
||||
(defcomp ~filters/brand-item (&key href hx-select bg-cls name-cls name count)
|
||||
(a :href href :sx-get href :sx-target "#main-panel"
|
||||
:sx-select hx-select :sx-swap "outerHTML" :sx-push-url "true"
|
||||
:class (str "flex flex-row items-center gap-2 px-2 py-1 rounded hover:bg-stone-100" bg-cls)
|
||||
(div :class name-cls name) (div :class name-cls count)))
|
||||
|
||||
(defcomp ~market-filter-brands-panel (&key items)
|
||||
(defcomp ~filters/brands-panel (&key items)
|
||||
(div :class "space-y-1 p-2"
|
||||
items))
|
||||
|
||||
(defcomp ~market-filter-category-label (&key label)
|
||||
(defcomp ~filters/category-label (&key label)
|
||||
(div :class "mb-4" (div :class "text-2xl uppercase tracking-wide text-black-500" label)))
|
||||
|
||||
(defcomp ~market-filter-like-labels-nav (&key content inner)
|
||||
(defcomp ~filters/like-labels-nav (&key content inner)
|
||||
(nav :aria-label "like" :class "flex flex-row justify-center w-full p-0 m-0 border bg-white shadow-sm rounded-xl gap-1"
|
||||
(if content content (when inner inner))))
|
||||
|
||||
(defcomp ~market-desktop-category-summary (&key content inner)
|
||||
(defcomp ~filters/desktop-category-summary (&key content inner)
|
||||
(div :id "category-summary-desktop" :hxx-swap-oob "outerHTML"
|
||||
(if content content (when inner inner))))
|
||||
|
||||
(defcomp ~market-desktop-brand-summary (&key inner)
|
||||
(defcomp ~filters/desktop-brand-summary (&key inner)
|
||||
(div :id "filter-summary-desktop" :hxx-swap-oob "outerHTML" inner))
|
||||
|
||||
(defcomp ~market-filter-subcategory-item (&key href hx-select active-cls name)
|
||||
(defcomp ~filters/subcategory-item (&key href hx-select active-cls name)
|
||||
(a :href href :sx-get href :sx-target "#main-panel"
|
||||
:sx-select hx-select :sx-swap "outerHTML" :sx-push-url "true"
|
||||
:class (str "block px-2 py-1 rounded hover:bg-stone-100" active-cls)
|
||||
name))
|
||||
|
||||
(defcomp ~market-filter-subcategory-panel (&key items)
|
||||
(defcomp ~filters/subcategory-panel (&key items)
|
||||
(div :class "mt-4 space-y-1" items))
|
||||
|
||||
(defcomp ~market-mobile-clear-filters (&key href hx-select)
|
||||
(defcomp ~filters/mobile-clear-filters (&key href hx-select)
|
||||
(div :class "flex flex-row justify-center"
|
||||
(a :href href :sx-get href :sx-target "#main-panel"
|
||||
:sx-select hx-select :sx-swap "outerHTML" :sx-push-url "true"
|
||||
@@ -75,10 +75,10 @@
|
||||
:class "flex flex-col items-center justify-start p-1 rounded bg-stone-200 text-black cursor-pointer"
|
||||
(span :class "mt-1 leading-none tabular-nums" "clear filters"))))
|
||||
|
||||
(defcomp ~market-mobile-like-labels-row (&key inner)
|
||||
(defcomp ~filters/mobile-like-labels-row (&key inner)
|
||||
(div :class "flex flex-row gap-2 justify-center items-center" inner))
|
||||
|
||||
(defcomp ~market-mobile-filter-summary (&key search-bar chips filter)
|
||||
(defcomp ~filters/mobile-filter-summary (&key search-bar chips filter)
|
||||
(details :class "md:hidden group" :id "/filter"
|
||||
(summary :class "cursor-pointer select-none" :id "filter-summary-mobile"
|
||||
search-bar
|
||||
@@ -87,40 +87,40 @@
|
||||
(div :id "filter-details-mobile" :style "display:contents"
|
||||
filter)))
|
||||
|
||||
(defcomp ~market-mobile-chips-row (&key inner)
|
||||
(defcomp ~filters/mobile-chips-row (&key inner)
|
||||
(div :class "flex flex-row items-start gap-2" inner))
|
||||
|
||||
(defcomp ~market-mobile-chip-sort (&key src label)
|
||||
(defcomp ~filters/mobile-chip-sort (&key src label)
|
||||
(ul :class "relative inline-flex items-center justify-center gap-2"
|
||||
(li :role "listitem" (img :src src :alt label :class "w-10 h-10"))))
|
||||
|
||||
(defcomp ~market-mobile-chip-liked-icon ()
|
||||
(defcomp ~filters/mobile-chip-liked-icon ()
|
||||
(i :aria-hidden "true" :class "fa-solid fa-heart text-red-500 text-[40px] leading-none"))
|
||||
|
||||
(defcomp ~market-mobile-chip-count (&key cls count)
|
||||
(defcomp ~filters/mobile-chip-count (&key cls count)
|
||||
(div :class (str cls " mt-1 leading-none tabular-nums") count))
|
||||
|
||||
(defcomp ~market-mobile-chip-liked (&key inner)
|
||||
(defcomp ~filters/mobile-chip-liked (&key inner)
|
||||
(div :class "flex flex-col items-center gap-1 pb-1" inner))
|
||||
|
||||
(defcomp ~market-mobile-chip-image (&key src name)
|
||||
(defcomp ~filters/mobile-chip-image (&key src name)
|
||||
(img :src src :alt name :class "w-10 h-10"))
|
||||
|
||||
(defcomp ~market-mobile-chip-item (&key inner)
|
||||
(defcomp ~filters/mobile-chip-item (&key inner)
|
||||
(li :role "listitem" :class "flex flex-col items-center gap-1 pb-1" inner))
|
||||
|
||||
(defcomp ~market-mobile-chip-list (&key items)
|
||||
(defcomp ~filters/mobile-chip-list (&key items)
|
||||
(ul :class "relative inline-flex items-center justify-center gap-2" items))
|
||||
|
||||
(defcomp ~market-mobile-chip-brand (&key name count)
|
||||
(defcomp ~filters/mobile-chip-brand (&key name count)
|
||||
(li :role "listitem" :class "flex flex-row items-center gap-2"
|
||||
(div :class "text-md" name) (div :class "text-md" count)))
|
||||
|
||||
(defcomp ~market-mobile-chip-brand-zero (&key name)
|
||||
(defcomp ~filters/mobile-chip-brand-zero (&key name)
|
||||
(li :role "listitem" :class "flex flex-row items-center gap-2"
|
||||
(div :class "text-md text-red-500" name) (div :class "text-xl text-red-500" "0")))
|
||||
|
||||
(defcomp ~market-mobile-chip-brand-list (&key items)
|
||||
(defcomp ~filters/mobile-chip-brand-list (&key items)
|
||||
(ul items))
|
||||
|
||||
|
||||
@@ -129,160 +129,160 @@
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
;; Sort option stickers from data
|
||||
(defcomp ~market-filter-sort-from-data (&key items)
|
||||
(~market-filter-sort-row :items
|
||||
(defcomp ~filters/sort-from-data (&key items)
|
||||
(~filters/sort-row :items
|
||||
(<> (map (lambda (s)
|
||||
(~market-filter-sort-item
|
||||
(~filters/sort-item
|
||||
:href (get s "href") :hx-select (get s "hx-select")
|
||||
:ring-cls (get s "ring-cls") :src (get s "src") :label (get s "label")))
|
||||
items))))
|
||||
|
||||
;; Like filter from data
|
||||
(defcomp ~market-filter-like-from-data (&key href hx-select liked mobile)
|
||||
(~market-filter-like
|
||||
(defcomp ~filters/like-from-data (&key href hx-select liked mobile)
|
||||
(~filters/like
|
||||
:href href :hx-select hx-select
|
||||
:icon-cls (if liked "fa-solid fa-heart text-red-500" "fa-regular fa-heart text-stone-400")
|
||||
:size-cls (if mobile "text-[40px]" "text-2xl")))
|
||||
|
||||
;; Label filter items from data
|
||||
(defcomp ~market-filter-labels-from-data (&key items hx-select)
|
||||
(defcomp ~filters/labels-from-data (&key items hx-select)
|
||||
(<> (map (lambda (lb)
|
||||
(~market-filter-label-item
|
||||
(~filters/label-item
|
||||
:href (get lb "href") :hx-select hx-select
|
||||
:ring-cls (get lb "ring-cls") :src (get lb "src") :name (get lb "name")))
|
||||
items)))
|
||||
|
||||
;; Sticker filter items from data
|
||||
(defcomp ~market-filter-stickers-from-data (&key items hx-select)
|
||||
(~market-filter-stickers-row :items
|
||||
(defcomp ~filters/stickers-from-data (&key items hx-select)
|
||||
(~filters/stickers-row :items
|
||||
(<> (map (lambda (st)
|
||||
(~market-filter-sticker-item
|
||||
(~filters/sticker-item
|
||||
:href (get st "href") :hx-select hx-select
|
||||
:ring-cls (get st "ring-cls") :src (get st "src") :name (get st "name")
|
||||
:count-cls (get st "count-cls") :count (get st "count")))
|
||||
items))))
|
||||
|
||||
;; Brand filter items from data
|
||||
(defcomp ~market-filter-brands-from-data (&key items hx-select)
|
||||
(~market-filter-brands-panel :items
|
||||
(defcomp ~filters/brands-from-data (&key items hx-select)
|
||||
(~filters/brands-panel :items
|
||||
(<> (map (lambda (br)
|
||||
(~market-filter-brand-item
|
||||
(~filters/brand-item
|
||||
:href (get br "href") :hx-select hx-select
|
||||
:bg-cls (get br "bg-cls") :name-cls (get br "name-cls")
|
||||
:name (get br "name") :count (get br "count")))
|
||||
items))))
|
||||
|
||||
;; Subcategory selector from data
|
||||
(defcomp ~market-filter-subcategories-from-data (&key items hx-select all-href current-sub)
|
||||
(~market-filter-subcategory-panel :items
|
||||
(defcomp ~filters/subcategories-from-data (&key items hx-select all-href current-sub)
|
||||
(~filters/subcategory-panel :items
|
||||
(<>
|
||||
(~market-filter-subcategory-item
|
||||
(~filters/subcategory-item
|
||||
:href all-href :hx-select hx-select
|
||||
:active-cls (if (not current-sub) " bg-stone-200 font-medium" "")
|
||||
:name "All")
|
||||
(map (lambda (sub)
|
||||
(~market-filter-subcategory-item
|
||||
(~filters/subcategory-item
|
||||
:href (get sub "href") :hx-select hx-select
|
||||
:active-cls (get sub "active-cls") :name (get sub "name")))
|
||||
items))))
|
||||
|
||||
;; Desktop filter panel from data
|
||||
(defcomp ~market-desktop-filter-from-data (&key search-sx category-label
|
||||
(defcomp ~filters/desktop-filter-from-data (&key search-sx category-label
|
||||
sort-data like-data label-data
|
||||
sticker-data brand-data sub-data hx-select)
|
||||
(<>
|
||||
search-sx
|
||||
(~market-desktop-category-summary :inner
|
||||
(~filters/desktop-category-summary :inner
|
||||
(<>
|
||||
(~market-filter-category-label :label category-label)
|
||||
(when sort-data (~market-filter-sort-from-data :items sort-data))
|
||||
(~market-filter-like-labels-nav :inner
|
||||
(~filters/category-label :label category-label)
|
||||
(when sort-data (~filters/sort-from-data :items sort-data))
|
||||
(~filters/like-labels-nav :inner
|
||||
(<>
|
||||
(~market-filter-like-from-data
|
||||
(~filters/like-from-data
|
||||
:href (get like-data "href") :hx-select hx-select
|
||||
:liked (get like-data "liked") :mobile false)
|
||||
(when label-data
|
||||
(~market-filter-labels-from-data :items label-data :hx-select hx-select))))
|
||||
(~filters/labels-from-data :items label-data :hx-select hx-select))))
|
||||
(when sticker-data
|
||||
(~market-filter-stickers-from-data :items sticker-data :hx-select hx-select))
|
||||
(~filters/stickers-from-data :items sticker-data :hx-select hx-select))
|
||||
(when sub-data
|
||||
(~market-filter-subcategories-from-data
|
||||
(~filters/subcategories-from-data
|
||||
:items (get sub-data "items") :hx-select hx-select
|
||||
:all-href (get sub-data "all-href")
|
||||
:current-sub (get sub-data "current-sub")))))
|
||||
(~market-desktop-brand-summary
|
||||
(~filters/desktop-brand-summary
|
||||
:inner (when brand-data
|
||||
(~market-filter-brands-from-data :items brand-data :hx-select hx-select)))))
|
||||
(~filters/brands-from-data :items brand-data :hx-select hx-select)))))
|
||||
|
||||
;; Mobile filter chips from active filter data
|
||||
(defcomp ~market-mobile-chips-from-data (&key sort-chip liked-chip label-chips sticker-chips brand-chips)
|
||||
(~market-mobile-chips-row :inner
|
||||
(defcomp ~filters/mobile-chips-from-data (&key sort-chip liked-chip label-chips sticker-chips brand-chips)
|
||||
(~filters/mobile-chips-row :inner
|
||||
(<>
|
||||
(when sort-chip
|
||||
(~market-mobile-chip-sort :src (get sort-chip "src") :label (get sort-chip "label")))
|
||||
(~filters/mobile-chip-sort :src (get sort-chip "src") :label (get sort-chip "label")))
|
||||
(when liked-chip
|
||||
(~market-mobile-chip-liked :inner
|
||||
(~filters/mobile-chip-liked :inner
|
||||
(<>
|
||||
(~market-mobile-chip-liked-icon)
|
||||
(~filters/mobile-chip-liked-icon)
|
||||
(when (get liked-chip "count")
|
||||
(~market-mobile-chip-count
|
||||
(~filters/mobile-chip-count
|
||||
:cls (get liked-chip "count-cls") :count (get liked-chip "count"))))))
|
||||
(when label-chips
|
||||
(~market-mobile-chip-list :items
|
||||
(~filters/mobile-chip-list :items
|
||||
(<> (map (lambda (lc)
|
||||
(~market-mobile-chip-item :inner
|
||||
(~filters/mobile-chip-item :inner
|
||||
(<>
|
||||
(~market-mobile-chip-image :src (get lc "src") :name (get lc "name"))
|
||||
(~filters/mobile-chip-image :src (get lc "src") :name (get lc "name"))
|
||||
(when (get lc "count")
|
||||
(~market-mobile-chip-count :cls (get lc "count-cls") :count (get lc "count"))))))
|
||||
(~filters/mobile-chip-count :cls (get lc "count-cls") :count (get lc "count"))))))
|
||||
label-chips))))
|
||||
(when sticker-chips
|
||||
(~market-mobile-chip-list :items
|
||||
(~filters/mobile-chip-list :items
|
||||
(<> (map (lambda (sc)
|
||||
(~market-mobile-chip-item :inner
|
||||
(~filters/mobile-chip-item :inner
|
||||
(<>
|
||||
(~market-mobile-chip-image :src (get sc "src") :name (get sc "name"))
|
||||
(~filters/mobile-chip-image :src (get sc "src") :name (get sc "name"))
|
||||
(when (get sc "count")
|
||||
(~market-mobile-chip-count :cls (get sc "count-cls") :count (get sc "count"))))))
|
||||
(~filters/mobile-chip-count :cls (get sc "count-cls") :count (get sc "count"))))))
|
||||
sticker-chips))))
|
||||
(when brand-chips
|
||||
(~market-mobile-chip-brand-list :items
|
||||
(~filters/mobile-chip-brand-list :items
|
||||
(<> (map (lambda (bc)
|
||||
(if (get bc "has-count")
|
||||
(~market-mobile-chip-brand :name (get bc "name") :count (get bc "count"))
|
||||
(~market-mobile-chip-brand-zero :name (get bc "name"))))
|
||||
(~filters/mobile-chip-brand :name (get bc "name") :count (get bc "count"))
|
||||
(~filters/mobile-chip-brand-zero :name (get bc "name"))))
|
||||
brand-chips)))))))
|
||||
|
||||
;; Mobile filter content (expanded panel) from data
|
||||
(defcomp ~market-mobile-filter-content-from-data (&key sort-data like-data label-data
|
||||
(defcomp ~filters/mobile-filter-content-from-data (&key sort-data like-data label-data
|
||||
sticker-data brand-data clear-href hx-select)
|
||||
(<>
|
||||
(when sort-data (~market-filter-sort-from-data :items sort-data))
|
||||
(when sort-data (~filters/sort-from-data :items sort-data))
|
||||
(when clear-href
|
||||
(~market-mobile-clear-filters :href clear-href :hx-select hx-select))
|
||||
(~market-mobile-like-labels-row :inner
|
||||
(~filters/mobile-clear-filters :href clear-href :hx-select hx-select))
|
||||
(~filters/mobile-like-labels-row :inner
|
||||
(<>
|
||||
(~market-filter-like-from-data
|
||||
(~filters/like-from-data
|
||||
:href (get like-data "href") :hx-select hx-select
|
||||
:liked (get like-data "liked") :mobile true)
|
||||
(when label-data
|
||||
(~market-filter-labels-from-data :items label-data :hx-select hx-select))))
|
||||
(~filters/labels-from-data :items label-data :hx-select hx-select))))
|
||||
(when sticker-data
|
||||
(~market-filter-stickers-from-data :items sticker-data :hx-select hx-select))
|
||||
(~filters/stickers-from-data :items sticker-data :hx-select hx-select))
|
||||
(when brand-data
|
||||
(~market-filter-brands-from-data :items brand-data :hx-select hx-select))))
|
||||
(~filters/brands-from-data :items brand-data :hx-select hx-select))))
|
||||
|
||||
;; Composite mobile filter — eliminates SxExpr nesting in Python (M2)
|
||||
(defcomp ~market-mobile-filter-from-data (&key search-bar
|
||||
(defcomp ~filters/mobile-filter-from-data (&key search-bar
|
||||
sort-chip liked-chip label-chips sticker-chips brand-chips
|
||||
sort-data like-data label-data sticker-data brand-data
|
||||
clear-href hx-select)
|
||||
(~market-mobile-filter-summary
|
||||
(~filters/mobile-filter-summary
|
||||
:search-bar search-bar
|
||||
:chips (~market-mobile-chips-from-data
|
||||
:chips (~filters/mobile-chips-from-data
|
||||
:sort-chip sort-chip :liked-chip liked-chip
|
||||
:label-chips label-chips :sticker-chips sticker-chips :brand-chips brand-chips)
|
||||
:filter (~market-mobile-filter-content-from-data
|
||||
:filter (~filters/mobile-filter-content-from-data
|
||||
:sort-data sort-data :like-data like-data
|
||||
:label-data label-data :sticker-data sticker-data :brand-data brand-data
|
||||
:clear-href clear-href :hx-select hx-select)))
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
;; Market grid and layout components
|
||||
|
||||
(defcomp ~market-markets-grid (&key cards)
|
||||
(defcomp ~grids/markets-grid (&key cards)
|
||||
(<> (div :class "max-w-full px-3 py-3 grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4" cards) (div :class "pb-8")))
|
||||
|
||||
(defcomp ~market-product-grid (&key cards)
|
||||
(defcomp ~grids/product-grid (&key cards)
|
||||
(<> (div :class "grid grid-cols-1 sm:grid-cols-3 md:grid-cols-6 gap-3" cards) (div :class "pb-8")))
|
||||
|
||||
(defcomp ~market-admin-content-wrap (&key inner)
|
||||
(defcomp ~grids/admin-content-wrap (&key inner)
|
||||
(div :id "main-panel" inner))
|
||||
|
||||
(defcomp ~market-like-toggle-button (&key colour action hx-headers label icon-cls)
|
||||
(defcomp ~grids/like-toggle-button (&key colour action hx-headers label icon-cls)
|
||||
(button :class (str "flex items-center gap-1 " colour " hover:text-red-600 transition-colors w-[1em] h-[1em]")
|
||||
:sx-post action :sx-target "this" :sx-swap "outerHTML" :sx-push-url "false"
|
||||
:sx-headers hx-headers
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
(sel-colours (or (jinja-global "select_colours") "")))
|
||||
(<> (map (fn (m)
|
||||
(let ((href (app-url "market" (str "/" slug "/" (get m "slug") "/"))))
|
||||
(~market-link-nav
|
||||
(~shared:navigation/market-link-nav
|
||||
:href href
|
||||
:name (get m "name")
|
||||
:nav-class nav-class
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
(if (get product "regular_price")
|
||||
(str (get product "regular_price"))
|
||||
""))))
|
||||
(~link-card
|
||||
(~shared:fragments/link-card
|
||||
:title (get product "title")
|
||||
:image (get product "image")
|
||||
:subtitle subtitle
|
||||
@@ -35,7 +35,7 @@
|
||||
(if (get product "regular_price")
|
||||
(str (get product "regular_price"))
|
||||
""))))
|
||||
(~link-card
|
||||
(~shared:fragments/link-card
|
||||
:title (get product "title")
|
||||
:image (get product "image")
|
||||
:subtitle subtitle
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
;; Market header components
|
||||
|
||||
(defcomp ~market-shop-label (&key title top-slug sub-div)
|
||||
(defcomp ~headers/shop-label (&key title top-slug sub-div)
|
||||
(div :class "font-bold text-xl flex-shrink-0 flex gap-2 items-center"
|
||||
(div (i :class "fa fa-shop") " " title)
|
||||
(div :class "flex flex-col md:flex-row md:gap-2 text-xs"
|
||||
(div top-slug) (when sub-div (div sub-div)))))
|
||||
|
||||
(defcomp ~market-product-label (&key title)
|
||||
(defcomp ~headers/product-label (&key title)
|
||||
(<> (i :class "fa fa-shopping-bag" :aria-hidden "true") (div title)))
|
||||
|
||||
(defcomp ~market-admin-link (&key href hx-select)
|
||||
(defcomp ~headers/admin-link (&key href hx-select)
|
||||
(a :href href :sx-get href :sx-target "#main-panel"
|
||||
:sx-select hx-select :sx-swap "outerHTML" :sx-push-url "true"
|
||||
:class "px-2 py-1 text-stone-500 hover:text-stone-700"
|
||||
@@ -21,42 +21,42 @@
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
;; Desktop category nav from pre-computed category data
|
||||
(defcomp ~market-desktop-nav-from-data (&key categories hx-select select-colours
|
||||
(defcomp ~headers/desktop-nav-from-data (&key categories hx-select select-colours
|
||||
all-href all-active admin-href)
|
||||
(~market-desktop-category-nav
|
||||
(~navigation/desktop-category-nav
|
||||
:links (<>
|
||||
(~market-category-link :href all-href :hx-select hx-select
|
||||
(~navigation/category-link :href all-href :hx-select hx-select
|
||||
:active all-active :select-colours select-colours :label "All")
|
||||
(map (lambda (cat)
|
||||
(~market-category-link
|
||||
(~navigation/category-link
|
||||
:href (get cat "href") :hx-select hx-select
|
||||
:active (get cat "active") :select-colours select-colours
|
||||
:label (get cat "label"))) categories))
|
||||
:admin (when admin-href
|
||||
(~market-admin-link :href admin-href :hx-select hx-select))))
|
||||
(~headers/admin-link :href admin-href :hx-select hx-select))))
|
||||
|
||||
;; Market-level header row from data
|
||||
(defcomp ~market-header-from-data (&key market-title top-slug sub-slug link-href
|
||||
(defcomp ~headers/from-data (&key market-title top-slug sub-slug link-href
|
||||
categories hx-select select-colours
|
||||
all-href all-active admin-href oob)
|
||||
(~menu-row-sx :id "market-row" :level 2
|
||||
(~shared:layout/menu-row-sx :id "market-row" :level 2
|
||||
:link-href link-href
|
||||
:link-label-content (~market-shop-label
|
||||
:link-label-content (~headers/shop-label
|
||||
:title market-title :top-slug (or top-slug "") :sub-div sub-slug)
|
||||
:nav (~market-desktop-nav-from-data
|
||||
:nav (~headers/desktop-nav-from-data
|
||||
:categories categories :hx-select hx-select :select-colours select-colours
|
||||
:all-href all-href :all-active all-active :admin-href admin-href)
|
||||
:child-id "market-header-child"
|
||||
:oob oob))
|
||||
|
||||
;; Product-level header row from data
|
||||
(defcomp ~market-product-header-from-data (&key title link-href hx-select
|
||||
(defcomp ~headers/product-header-from-data (&key title link-href hx-select
|
||||
price-data admin-href oob)
|
||||
(~menu-row-sx :id "product-row" :level 3
|
||||
(~shared:layout/menu-row-sx :id "product-row" :level 3
|
||||
:link-href link-href
|
||||
:link-label-content (~market-product-label :title title)
|
||||
:link-label-content (~headers/product-label :title title)
|
||||
:nav (<>
|
||||
(~market-prices-header-from-data
|
||||
(~prices/header-from-data
|
||||
:cart-id (get price-data "cart-id")
|
||||
:cart-action (get price-data "cart-action")
|
||||
:csrf (get price-data "csrf")
|
||||
@@ -66,13 +66,13 @@
|
||||
:rp-val (get price-data "rp-val") :rp-str (get price-data "rp-str")
|
||||
:rrp-str (get price-data "rrp-str"))
|
||||
(when admin-href
|
||||
(~market-admin-link :href admin-href :hx-select hx-select)))
|
||||
(~headers/admin-link :href admin-href :hx-select hx-select)))
|
||||
:child-id "product-header-child"
|
||||
:oob oob))
|
||||
|
||||
;; Product admin header row from data
|
||||
(defcomp ~market-product-admin-header-from-data (&key link-href oob)
|
||||
(~menu-row-sx :id "product-admin-row" :level 4
|
||||
(defcomp ~headers/product-admin-header-from-data (&key link-href oob)
|
||||
(~shared:layout/menu-row-sx :id "product-admin-row" :level 4
|
||||
:link-href link-href :link-label "admin!!" :icon "fa fa-cog"
|
||||
:child-id "product-admin-header-child" :oob oob))
|
||||
|
||||
|
||||
@@ -9,13 +9,13 @@
|
||||
"Market header row using (market-header-ctx)."
|
||||
(quasiquote
|
||||
(let ((__mctx (market-header-ctx)))
|
||||
(~menu-row-sx :id "market-row" :level 2
|
||||
(~shared:layout/menu-row-sx :id "market-row" :level 2
|
||||
:link-href (get __mctx "link-href")
|
||||
:link-label-content (~market-shop-label
|
||||
:link-label-content (~headers/shop-label
|
||||
:title (get __mctx "market-title")
|
||||
:top-slug (get __mctx "top-slug")
|
||||
:sub-div (get __mctx "sub-slug"))
|
||||
:nav (~market-desktop-nav-from-data
|
||||
:nav (~headers/desktop-nav-from-data
|
||||
:categories (get __mctx "categories")
|
||||
:hx-select (get __mctx "hx-select")
|
||||
:select-colours (get __mctx "select-colours")
|
||||
@@ -29,44 +29,44 @@
|
||||
;; OOB clear helpers
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~market-clear-oob ()
|
||||
(defcomp ~layouts/clear-oob ()
|
||||
"Clear OOB divs for browse level."
|
||||
(<>
|
||||
(~clear-oob-div :id "product-admin-row")
|
||||
(~clear-oob-div :id "product-admin-header-child")
|
||||
(~clear-oob-div :id "product-row")
|
||||
(~clear-oob-div :id "product-header-child")
|
||||
(~clear-oob-div :id "market-admin-row")
|
||||
(~clear-oob-div :id "market-admin-header-child")
|
||||
(~clear-oob-div :id "post-admin-row")
|
||||
(~clear-oob-div :id "post-admin-header-child")))
|
||||
(~shared:layout/clear-oob-div :id "product-admin-row")
|
||||
(~shared:layout/clear-oob-div :id "product-admin-header-child")
|
||||
(~shared:layout/clear-oob-div :id "product-row")
|
||||
(~shared:layout/clear-oob-div :id "product-header-child")
|
||||
(~shared:layout/clear-oob-div :id "market-admin-row")
|
||||
(~shared:layout/clear-oob-div :id "market-admin-header-child")
|
||||
(~shared:layout/clear-oob-div :id "post-admin-row")
|
||||
(~shared:layout/clear-oob-div :id "post-admin-header-child")))
|
||||
|
||||
(defcomp ~market-clear-oob-admin ()
|
||||
(defcomp ~layouts/clear-oob-admin ()
|
||||
"Clear OOB divs for admin level."
|
||||
(<>
|
||||
(~clear-oob-div :id "product-admin-row")
|
||||
(~clear-oob-div :id "product-admin-header-child")
|
||||
(~clear-oob-div :id "product-row")
|
||||
(~clear-oob-div :id "product-header-child")))
|
||||
(~shared:layout/clear-oob-div :id "product-admin-row")
|
||||
(~shared:layout/clear-oob-div :id "product-admin-header-child")
|
||||
(~shared:layout/clear-oob-div :id "product-row")
|
||||
(~shared:layout/clear-oob-div :id "product-header-child")))
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Browse layout: root + post + market (self-contained)
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~market-browse-layout-full ()
|
||||
(defcomp ~layouts/browse-layout-full ()
|
||||
(<> (~root-header-auto)
|
||||
(~header-child-sx
|
||||
(~shared:layout/header-child-sx
|
||||
:inner (<> (~post-header-auto nil)
|
||||
(~market-header-auto nil)))))
|
||||
|
||||
(defcomp ~market-browse-layout-oob ()
|
||||
(defcomp ~layouts/browse-layout-oob ()
|
||||
(<> (~post-header-auto true)
|
||||
(~oob-header-sx :parent-id "post-header-child"
|
||||
(~shared:layout/oob-header-sx :parent-id "post-header-child"
|
||||
:row (~market-header-auto nil))
|
||||
(~market-clear-oob)
|
||||
(~layouts/clear-oob)
|
||||
(~root-header-auto true)))
|
||||
|
||||
(defcomp ~market-browse-layout-mobile ()
|
||||
(defcomp ~layouts/browse-layout-mobile ()
|
||||
(let ((__mctx (market-header-ctx)))
|
||||
(get __mctx "mobile-nav")))
|
||||
|
||||
@@ -74,18 +74,18 @@
|
||||
;; Market admin layout: root + post + market + post-admin (self-contained)
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~market-admin-layout-full (&key selected)
|
||||
(defcomp ~layouts/admin-layout-full (&key selected)
|
||||
(<> (~root-header-auto)
|
||||
(~header-child-sx
|
||||
(~shared:layout/header-child-sx
|
||||
:inner (<> (~post-header-auto nil)
|
||||
(~market-header-auto nil)
|
||||
(~post-admin-header-auto nil selected)))))
|
||||
|
||||
(defcomp ~market-admin-layout-oob (&key selected)
|
||||
(defcomp ~layouts/admin-layout-oob (&key selected)
|
||||
(<> (~market-header-auto true)
|
||||
(~oob-header-sx :parent-id "market-header-child"
|
||||
(~shared:layout/oob-header-sx :parent-id "market-header-child"
|
||||
:row (~post-admin-header-auto nil selected))
|
||||
(~market-clear-oob-admin)
|
||||
(~layouts/clear-oob-admin)
|
||||
(~root-header-auto true)))
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
@@ -93,46 +93,46 @@
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
;; Product layout: root + post + market + product
|
||||
(defcomp ~market-product-layout-full (&key post-header market-header product-header)
|
||||
(defcomp ~layouts/product-layout-full (&key post-header market-header product-header)
|
||||
(<> (~root-header-auto)
|
||||
(~header-child-sx :inner (<> post-header market-header product-header))))
|
||||
(~shared:layout/header-child-sx :inner (<> post-header market-header product-header))))
|
||||
|
||||
;; Product admin layout: root + post + market + product + admin
|
||||
(defcomp ~market-product-admin-layout-full (&key post-header market-header product-header admin-header)
|
||||
(defcomp ~layouts/product-admin-layout-full (&key post-header market-header product-header admin-header)
|
||||
(<> (~root-header-auto)
|
||||
(~header-child-sx :inner (<> post-header market-header product-header admin-header))))
|
||||
(~shared:layout/header-child-sx :inner (<> post-header market-header product-header admin-header))))
|
||||
|
||||
;; OOB wrappers — compose headers + clear divs in sx (no Python concatenation)
|
||||
|
||||
(defcomp ~market-oob-wrap (&key parts)
|
||||
(defcomp ~layouts/oob-wrap (&key parts)
|
||||
(<> parts))
|
||||
|
||||
(defcomp ~market-clear-product-oob ()
|
||||
(defcomp ~layouts/clear-product-oob ()
|
||||
"Clear admin-level OOB divs when rendering product detail."
|
||||
(<>
|
||||
(~clear-oob-div :id "product-admin-row")
|
||||
(~clear-oob-div :id "product-admin-header-child")
|
||||
(~clear-oob-div :id "market-admin-row")
|
||||
(~clear-oob-div :id "market-admin-header-child")
|
||||
(~clear-oob-div :id "post-admin-row")
|
||||
(~clear-oob-div :id "post-admin-header-child")))
|
||||
(~shared:layout/clear-oob-div :id "product-admin-row")
|
||||
(~shared:layout/clear-oob-div :id "product-admin-header-child")
|
||||
(~shared:layout/clear-oob-div :id "market-admin-row")
|
||||
(~shared:layout/clear-oob-div :id "market-admin-header-child")
|
||||
(~shared:layout/clear-oob-div :id "post-admin-row")
|
||||
(~shared:layout/clear-oob-div :id "post-admin-header-child")))
|
||||
|
||||
(defcomp ~market-clear-product-admin-oob ()
|
||||
(defcomp ~layouts/clear-product-admin-oob ()
|
||||
"Clear deeper OOB divs when rendering product admin."
|
||||
(<>
|
||||
(~clear-oob-div :id "market-admin-row")
|
||||
(~clear-oob-div :id "market-admin-header-child")
|
||||
(~clear-oob-div :id "post-admin-row")
|
||||
(~clear-oob-div :id "post-admin-header-child")))
|
||||
(~shared:layout/clear-oob-div :id "market-admin-row")
|
||||
(~shared:layout/clear-oob-div :id "market-admin-header-child")
|
||||
(~shared:layout/clear-oob-div :id "post-admin-row")
|
||||
(~shared:layout/clear-oob-div :id "post-admin-header-child")))
|
||||
|
||||
(defcomp ~market-product-oob (&key market-header oob-header)
|
||||
(defcomp ~layouts/product-oob (&key market-header oob-header)
|
||||
"Product detail OOB: market header + product header + clear deeper."
|
||||
(<> market-header oob-header (~market-clear-product-oob)))
|
||||
(<> market-header oob-header (~layouts/clear-product-oob)))
|
||||
|
||||
(defcomp ~market-product-admin-oob (&key product-header oob-header)
|
||||
(defcomp ~layouts/product-admin-oob (&key product-header oob-header)
|
||||
"Product admin OOB: product header + admin header + clear deeper."
|
||||
(<> product-header oob-header (~market-clear-product-admin-oob)))
|
||||
(<> product-header oob-header (~layouts/clear-product-admin-oob)))
|
||||
|
||||
;; Content wrappers
|
||||
(defcomp ~market-content-padded (&key content)
|
||||
(defcomp ~layouts/content-padded (&key content)
|
||||
(<> content (div :class "pb-8")))
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
;; Market meta/SEO components
|
||||
|
||||
(defcomp ~market-meta-title (&key (title :as string))
|
||||
(defcomp ~meta/title (&key (title :as string))
|
||||
(title title))
|
||||
|
||||
(defcomp ~market-meta-description (&key (description :as string))
|
||||
(defcomp ~meta/description (&key (description :as string))
|
||||
(meta :name "description" :content description))
|
||||
|
||||
(defcomp ~market-meta-canonical (&key (href :as string))
|
||||
(defcomp ~meta/canonical (&key (href :as string))
|
||||
(link :rel "canonical" :href href))
|
||||
|
||||
(defcomp ~market-meta-og (&key (property :as string) (content :as string))
|
||||
(defcomp ~meta/og (&key (property :as string) (content :as string))
|
||||
(meta :property property :content content))
|
||||
|
||||
(defcomp ~market-meta-twitter (&key (name :as string) (content :as string))
|
||||
(defcomp ~meta/twitter (&key (name :as string) (content :as string))
|
||||
(meta :name name :content content))
|
||||
|
||||
(defcomp ~market-meta-jsonld (&key (json :as string))
|
||||
(defcomp ~meta/jsonld (&key (json :as string))
|
||||
(script :type "application/ld+json" (~rich-text :html json)))
|
||||
|
||||
|
||||
@@ -23,30 +23,30 @@
|
||||
;; Composition: all product meta tags from data
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~market-product-meta-from-data (&key (title :as string) (description :as string) (canonical :as string?)
|
||||
(defcomp ~meta/product-meta-from-data (&key (title :as string) (description :as string) (canonical :as string?)
|
||||
(image-url :as string?)
|
||||
(site-title :as string) (brand :as string?) (price :as string?) (price-currency :as string?)
|
||||
(jsonld-json :as string))
|
||||
(<>
|
||||
(~market-meta-title :title title)
|
||||
(~market-meta-description :description description)
|
||||
(when canonical (~market-meta-canonical :href canonical))
|
||||
(~meta/title :title title)
|
||||
(~meta/description :description description)
|
||||
(when canonical (~meta/canonical :href canonical))
|
||||
;; OpenGraph
|
||||
(~market-meta-og :property "og:site_name" :content site-title)
|
||||
(~market-meta-og :property "og:type" :content "product")
|
||||
(~market-meta-og :property "og:title" :content title)
|
||||
(~market-meta-og :property "og:description" :content description)
|
||||
(when canonical (~market-meta-og :property "og:url" :content canonical))
|
||||
(when image-url (~market-meta-og :property "og:image" :content image-url))
|
||||
(~meta/og :property "og:site_name" :content site-title)
|
||||
(~meta/og :property "og:type" :content "product")
|
||||
(~meta/og :property "og:title" :content title)
|
||||
(~meta/og :property "og:description" :content description)
|
||||
(when canonical (~meta/og :property "og:url" :content canonical))
|
||||
(when image-url (~meta/og :property "og:image" :content image-url))
|
||||
(when (and price price-currency)
|
||||
(<> (~market-meta-og :property "product:price:amount" :content price)
|
||||
(~market-meta-og :property "product:price:currency" :content price-currency)))
|
||||
(when brand (~market-meta-og :property "product:brand" :content brand))
|
||||
(<> (~meta/og :property "product:price:amount" :content price)
|
||||
(~meta/og :property "product:price:currency" :content price-currency)))
|
||||
(when brand (~meta/og :property "product:brand" :content brand))
|
||||
;; Twitter
|
||||
(~market-meta-twitter :name "twitter:card"
|
||||
(~meta/twitter :name "twitter:card"
|
||||
:content (if image-url "summary_large_image" "summary"))
|
||||
(~market-meta-twitter :name "twitter:title" :content title)
|
||||
(~market-meta-twitter :name "twitter:description" :content description)
|
||||
(when image-url (~market-meta-twitter :name "twitter:image" :content image-url))
|
||||
(~meta/twitter :name "twitter:title" :content title)
|
||||
(~meta/twitter :name "twitter:description" :content description)
|
||||
(when image-url (~meta/twitter :name "twitter:image" :content image-url))
|
||||
;; JSON-LD
|
||||
(~market-meta-jsonld :json jsonld-json)))
|
||||
(~meta/jsonld :json jsonld-json)))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
;; Market navigation components
|
||||
|
||||
(defcomp ~market-category-link (&key (href :as string) (hx-select :as string) (active :as boolean) (select-colours :as string) (label :as string))
|
||||
(defcomp ~navigation/category-link (&key (href :as string) (hx-select :as string) (active :as boolean) (select-colours :as string) (label :as string))
|
||||
(div :class "relative nav-group"
|
||||
(a :href href :sx-get href :sx-target "#main-panel"
|
||||
:sx-select hx-select :sx-swap "outerHTML" :sx-push-url "true"
|
||||
@@ -8,27 +8,27 @@
|
||||
:class (str "block px-2 py-1 rounded text-center whitespace-normal break-words leading-snug bg-stone-200 text-black " select-colours)
|
||||
label)))
|
||||
|
||||
(defcomp ~market-desktop-category-nav (&key (links :as list) (admin :as list?))
|
||||
(defcomp ~navigation/desktop-category-nav (&key (links :as list) (admin :as list?))
|
||||
(nav :class "hidden md:flex gap-4 text-sm ml-2 w-full justify-end items-center"
|
||||
links admin))
|
||||
|
||||
(defcomp ~market-mobile-nav-wrapper (&key (items :as list))
|
||||
(defcomp ~navigation/mobile-nav-wrapper (&key (items :as list))
|
||||
(div :class "px-4 py-2" (div :class "divide-y" items)))
|
||||
|
||||
(defcomp ~market-mobile-all-link (&key (href :as string) (hx-select :as string) (active :as boolean) (select-colours :as string))
|
||||
(defcomp ~navigation/mobile-all-link (&key (href :as string) (hx-select :as string) (active :as boolean) (select-colours :as string))
|
||||
(a :role "option" :href href :sx-get href :sx-target "#main-panel"
|
||||
:sx-select hx-select :sx-swap "outerHTML" :sx-push-url "true"
|
||||
:aria-selected (if active "true" "false")
|
||||
:class (str "block rounded-lg px-3 py-3 text-base hover:bg-stone-50 " select-colours)
|
||||
(div :class "prose prose-stone max-w-none" "All")))
|
||||
|
||||
(defcomp ~market-mobile-chevron ()
|
||||
(defcomp ~navigation/mobile-chevron ()
|
||||
(svg :class "w-4 h-4 shrink-0 transition-transform group-open/cat:rotate-180"
|
||||
:viewBox "0 0 20 20" :fill "currentColor"
|
||||
(path :fill-rule "evenodd" :clip-rule "evenodd"
|
||||
:d "M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z")))
|
||||
|
||||
(defcomp ~market-mobile-cat-summary (&key (bg-cls :as string) (href :as string) (hx-select :as string) (select-colours :as string) (cat-name :as string) (count-label :as string) (count-str :as string) (chevron :as list))
|
||||
(defcomp ~navigation/mobile-cat-summary (&key (bg-cls :as string) (href :as string) (hx-select :as string) (select-colours :as string) (cat-name :as string) (count-label :as string) (count-str :as string) (chevron :as list))
|
||||
(summary :class (str "flex items-center justify-between cursor-pointer select-none block rounded-lg px-3 py-3 text-base hover:bg-stone-50" bg-cls)
|
||||
(a :href href :sx-get href :sx-target "#main-panel"
|
||||
:sx-select hx-select :sx-swap "outerHTML" :sx-push-url "true"
|
||||
@@ -37,7 +37,7 @@
|
||||
(div :aria-label count-label count-str))
|
||||
chevron))
|
||||
|
||||
(defcomp ~market-mobile-sub-link (&key (select-colours :as string) (active :as boolean) (href :as string) (hx-select :as string) (label :as string) (count-label :as string) (count-str :as string))
|
||||
(defcomp ~navigation/mobile-sub-link (&key (select-colours :as string) (active :as boolean) (href :as string) (hx-select :as string) (label :as string) (count-label :as string) (count-str :as string))
|
||||
(a :class (str "snap-start px-2 py-3 rounded " select-colours " flex flex-row gap-2")
|
||||
:aria-selected (if active "true" "false")
|
||||
:href href :sx-get href :sx-target "#main-panel"
|
||||
@@ -45,20 +45,20 @@
|
||||
(div label)
|
||||
(div :aria-label count-label count-str)))
|
||||
|
||||
(defcomp ~market-mobile-subs-panel (&key (links :as list))
|
||||
(defcomp ~navigation/mobile-subs-panel (&key (links :as list))
|
||||
(div :class "pb-3 pl-2"
|
||||
(div :data-peek-viewport "" :data-peek-size-px "18" :data-peek-edge "bottom" :data-peek-mask "true" :class "m-2 bg-stone-100"
|
||||
(div :data-peek-inner "" :class "grid grid-cols-1 gap-1 snap-y snap-mandatory pr-1" :aria-label "Subcategories"
|
||||
links))))
|
||||
|
||||
(defcomp ~market-mobile-view-all (&key (href :as string) (hx-select :as string))
|
||||
(defcomp ~navigation/mobile-view-all (&key (href :as string) (hx-select :as string))
|
||||
(div :class "pb-3 pl-2"
|
||||
(a :class "px-2 py-1 rounded hover:bg-stone-100 block"
|
||||
:href href :sx-get href :sx-target "#main-panel"
|
||||
:sx-select hx-select :sx-swap "outerHTML" :sx-push-url "true"
|
||||
"View all")))
|
||||
|
||||
(defcomp ~market-mobile-cat-details (&key (open :as boolean) (summary :as list) (subs :as list))
|
||||
(defcomp ~navigation/mobile-cat-details (&key (open :as boolean) (summary :as list) (subs :as list))
|
||||
(details :class "group/cat py-1" :open open
|
||||
summary subs))
|
||||
|
||||
@@ -67,25 +67,25 @@
|
||||
;; Composition: mobile nav panel from pre-computed category data
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~market-mobile-nav-from-data (&key (categories :as list) (all-href :as string) (all-active :as boolean) (hx-select :as string) (select-colours :as string))
|
||||
(~market-mobile-nav-wrapper :items
|
||||
(defcomp ~navigation/mobile-nav-from-data (&key (categories :as list) (all-href :as string) (all-active :as boolean) (hx-select :as string) (select-colours :as string))
|
||||
(~navigation/mobile-nav-wrapper :items
|
||||
(<>
|
||||
(~market-mobile-all-link :href all-href :hx-select hx-select
|
||||
(~navigation/mobile-all-link :href all-href :hx-select hx-select
|
||||
:active all-active :select-colours select-colours)
|
||||
(map (lambda (cat)
|
||||
(~market-mobile-cat-details
|
||||
(~navigation/mobile-cat-details
|
||||
:open (get cat "active")
|
||||
:summary (~market-mobile-cat-summary
|
||||
:summary (~navigation/mobile-cat-summary
|
||||
:bg-cls (if (get cat "active") " bg-stone-900 text-white hover:bg-stone-900" "")
|
||||
:href (get cat "href") :hx-select hx-select
|
||||
:select-colours select-colours :cat-name (get cat "name")
|
||||
:count-label (str (get cat "count") " products")
|
||||
:count-str (str (get cat "count"))
|
||||
:chevron (~market-mobile-chevron))
|
||||
:chevron (~navigation/mobile-chevron))
|
||||
:subs (if (get cat "subs")
|
||||
(~market-mobile-subs-panel :links
|
||||
(~navigation/mobile-subs-panel :links
|
||||
(<> (map (lambda (sub)
|
||||
(~market-mobile-sub-link
|
||||
(~navigation/mobile-sub-link
|
||||
:select-colours select-colours
|
||||
:active (get sub "active")
|
||||
:href (get sub "href") :hx-select hx-select
|
||||
@@ -93,5 +93,5 @@
|
||||
:count-label (str (get sub "count") " products")
|
||||
:count-str (str (get sub "count"))))
|
||||
(get cat "subs"))))
|
||||
(~market-mobile-view-all :href (get cat "href") :hx-select hx-select))))
|
||||
(~navigation/mobile-view-all :href (get cat "href") :hx-select hx-select))))
|
||||
categories))))
|
||||
|
||||
@@ -1,36 +1,36 @@
|
||||
;; Market price display components
|
||||
|
||||
(defcomp ~market-price-special (&key (price :as string))
|
||||
(defcomp ~prices/special (&key (price :as string))
|
||||
(div :class "text-lg font-semibold text-emerald-700" price))
|
||||
|
||||
(defcomp ~market-price-regular-strike (&key (price :as string))
|
||||
(defcomp ~prices/regular-strike (&key (price :as string))
|
||||
(div :class "text-sm line-through text-stone-500" price))
|
||||
|
||||
(defcomp ~market-price-regular (&key (price :as string))
|
||||
(defcomp ~prices/regular (&key (price :as string))
|
||||
(div :class "mt-1 text-lg font-semibold" price))
|
||||
|
||||
(defcomp ~market-price-line (&key (inner :as list))
|
||||
(defcomp ~prices/line (&key (inner :as list))
|
||||
(div :class "mt-1 flex items-baseline gap-2 justify-center" inner))
|
||||
|
||||
(defcomp ~market-header-price-special-label ()
|
||||
(defcomp ~prices/header-price-special-label ()
|
||||
(div :class "text-md font-bold text-emerald-700" "Special price"))
|
||||
|
||||
(defcomp ~market-header-price-special (&key (price :as string))
|
||||
(defcomp ~prices/header-price-special (&key (price :as string))
|
||||
(div :class "text-xl font-semibold text-emerald-700" price))
|
||||
|
||||
(defcomp ~market-header-price-strike (&key (price :as string))
|
||||
(defcomp ~prices/header-price-strike (&key (price :as string))
|
||||
(div :class "text-base text-md line-through text-stone-500" price))
|
||||
|
||||
(defcomp ~market-header-price-regular-label ()
|
||||
(defcomp ~prices/header-price-regular-label ()
|
||||
(div :class "hidden md:block text-xl font-bold" "Our price"))
|
||||
|
||||
(defcomp ~market-header-price-regular (&key (price :as string))
|
||||
(defcomp ~prices/header-price-regular (&key (price :as string))
|
||||
(div :class "text-xl font-semibold" price))
|
||||
|
||||
(defcomp ~market-header-rrp (&key (rrp :as string))
|
||||
(defcomp ~prices/header-rrp (&key (rrp :as string))
|
||||
(div :class "text-base text-stone-400" (span "rrp:") " " (span rrp)))
|
||||
|
||||
(defcomp ~market-prices-row (&key (inner :as list))
|
||||
(defcomp ~prices/row (&key (inner :as list))
|
||||
(div :class "flex flex-row items-center justify-between md:gap-2 md:px-2" inner))
|
||||
|
||||
|
||||
@@ -38,31 +38,31 @@
|
||||
;; Composition: prices header + cart button from data
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~market-prices-header-from-data (&key (cart-id :as string) (cart-action :as string) (csrf :as string) (quantity :as number?)
|
||||
(defcomp ~prices/header-from-data (&key (cart-id :as string) (cart-action :as string) (csrf :as string) (quantity :as number?)
|
||||
(cart-href :as string)
|
||||
(sp-val :as number?) (sp-str :as string?) (rp-val :as number?) (rp-str :as string?) (rrp-str :as string?))
|
||||
(~market-prices-row :inner
|
||||
(~prices/row :inner
|
||||
(<>
|
||||
(if quantity
|
||||
(~market-cart-add-quantity :cart-id cart-id :action cart-action :csrf csrf
|
||||
(~cart/add-quantity :cart-id cart-id :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 cart-id :action cart-action :csrf csrf))
|
||||
(~cart/add-empty :cart-id cart-id :action cart-action :csrf csrf))
|
||||
(when sp-val
|
||||
(<> (~market-header-price-special-label)
|
||||
(~market-header-price-special :price sp-str)
|
||||
(when rp-val (~market-header-price-strike :price rp-str))))
|
||||
(<> (~prices/header-price-special-label)
|
||||
(~prices/header-price-special :price sp-str)
|
||||
(when rp-val (~prices/header-price-strike :price rp-str))))
|
||||
(when (and (not sp-val) rp-val)
|
||||
(<> (~market-header-price-regular-label)
|
||||
(~market-header-price-regular :price rp-str)))
|
||||
(when rrp-str (~market-header-rrp :rrp rrp-str)))))
|
||||
(<> (~prices/header-price-regular-label)
|
||||
(~prices/header-price-regular :price rp-str)))
|
||||
(when rrp-str (~prices/header-rrp :rrp rrp-str)))))
|
||||
|
||||
;; Card price line from data (used in product cards)
|
||||
(defcomp ~market-card-price-from-data (&key (sp-val :as number?) (sp-str :as string?) (rp-val :as number?) (rp-str :as string?))
|
||||
(~market-price-line :inner
|
||||
(defcomp ~prices/card-price-from-data (&key (sp-val :as number?) (sp-str :as string?) (rp-val :as number?) (rp-str :as string?))
|
||||
(~prices/line :inner
|
||||
(<>
|
||||
(when sp-val
|
||||
(<> (~market-price-special :price sp-str)
|
||||
(when rp-val (~market-price-regular-strike :price rp-str))))
|
||||
(<> (~prices/special :price sp-str)
|
||||
(when rp-val (~prices/regular-strike :price rp-str))))
|
||||
(when (and (not sp-val) rp-val)
|
||||
(~market-price-regular :price rp-str)))))
|
||||
(~prices/regular :price rp-str)))))
|
||||
|
||||
@@ -13,10 +13,10 @@
|
||||
:layout :root
|
||||
:data (all-markets-data)
|
||||
:content (if no-markets
|
||||
(~empty-state :icon "fa fa-store" :message "No markets available"
|
||||
(~shared:misc/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
|
||||
(~grids/markets-grid
|
||||
:cards (~cards/content
|
||||
:markets market-data :page market-page
|
||||
:has-more has-more :next-url next-url))))
|
||||
|
||||
@@ -26,10 +26,10 @@
|
||||
:layout :post
|
||||
:data (page-markets-data)
|
||||
:content (if no-markets
|
||||
(~empty-state :message "No markets for this page"
|
||||
(~shared:misc/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
|
||||
(~grids/markets-grid
|
||||
:cards (~cards/content
|
||||
:markets market-data :page market-page
|
||||
:has-more has-more :next-url next-url))))
|
||||
|
||||
@@ -38,24 +38,24 @@
|
||||
:auth :admin
|
||||
:layout (:post-admin :selected "markets")
|
||||
:data (page-admin-data)
|
||||
:content (~market-admin-content-wrap
|
||||
:inner (~crud-panel
|
||||
:content (~grids/admin-content-wrap
|
||||
:inner (~shared:misc/crud-panel
|
||||
:list-id "markets-list"
|
||||
:form (when can-create
|
||||
(~crud-create-form
|
||||
(~shared:misc/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
|
||||
(~shared:misc/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
|
||||
(~shared:misc/empty-state
|
||||
:message "No markets yet. Create one above."
|
||||
:cls "text-gray-500 mt-4")))))
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
:auth :public
|
||||
:layout :market
|
||||
:data (market-home-data)
|
||||
:content (~market-landing-from-data
|
||||
:content (~cards/landing-from-data
|
||||
:excerpt excerpt :feature-image feature-image :html html))
|
||||
|
||||
(defpage market-admin
|
||||
|
||||
Reference in New Issue
Block a user