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:
@@ -6,26 +6,26 @@
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
;; Auth section nav items (newsletters link + account_nav slot)
|
||||
(defcomp ~auth-nav-items (&key (account-url :as string?) (select-colours :as string?) account-nav)
|
||||
(defcomp ~shared:auth/nav-items (&key (account-url :as string?) (select-colours :as string?) account-nav)
|
||||
(<>
|
||||
(~nav-link :href (str (or account-url "") "/newsletters/")
|
||||
(~shared:layout/nav-link :href (str (or account-url "") "/newsletters/")
|
||||
:label "newsletters"
|
||||
:select-colours (or select-colours ""))
|
||||
(when account-nav account-nav)))
|
||||
|
||||
;; Auth header row — wraps ~menu-row-sx for account section
|
||||
(defcomp ~auth-header-row (&key (account-url :as string?) (select-colours :as string?) account-nav (oob :as boolean?))
|
||||
(~menu-row-sx :id "auth-row" :level 1 :colour "sky"
|
||||
;; Auth header row — wraps ~shared:layout/menu-row-sx for account section
|
||||
(defcomp ~shared:auth/header-row (&key (account-url :as string?) (select-colours :as string?) account-nav (oob :as boolean?))
|
||||
(~shared:layout/menu-row-sx :id "auth-row" :level 1 :colour "sky"
|
||||
:link-href (str (or account-url "") "/")
|
||||
:link-label "account" :icon "fa-solid fa-user"
|
||||
:nav (~auth-nav-items :account-url account-url
|
||||
:nav (~shared:auth/nav-items :account-url account-url
|
||||
:select-colours select-colours
|
||||
:account-nav account-nav)
|
||||
:child-id "auth-header-child" :oob oob))
|
||||
|
||||
;; Auth header row without nav (for cart service)
|
||||
(defcomp ~auth-header-row-simple (&key (account-url :as string?) (oob :as boolean?))
|
||||
(~menu-row-sx :id "auth-row" :level 1 :colour "sky"
|
||||
(defcomp ~shared:auth/header-row-simple (&key (account-url :as string?) (oob :as boolean?))
|
||||
(~shared:layout/menu-row-sx :id "auth-row" :level 1 :colour "sky"
|
||||
:link-href (str (or account-url "") "/")
|
||||
:link-label "account" :icon "fa-solid fa-user"
|
||||
:child-id "auth-header-child" :oob oob))
|
||||
@@ -34,26 +34,26 @@
|
||||
;; Expands inline (defmacro) so IO calls resolve in _aser mode.
|
||||
(defmacro ~auth-header-row-auto (oob)
|
||||
(quasiquote
|
||||
(~auth-header-row :account-url (app-url "account" "")
|
||||
(~shared:auth/header-row :account-url (app-url "account" "")
|
||||
:select-colours (select-colours)
|
||||
:account-nav (account-nav-ctx)
|
||||
:oob (unquote oob))))
|
||||
|
||||
(defmacro ~auth-header-row-simple-auto (oob)
|
||||
(quasiquote
|
||||
(~auth-header-row-simple :account-url (app-url "account" "")
|
||||
(~shared:auth/header-row-simple :account-url (app-url "account" "")
|
||||
:oob (unquote oob))))
|
||||
|
||||
;; Auto-fetching auth nav items — for mobile menus
|
||||
(defmacro ~auth-nav-items-auto ()
|
||||
(quasiquote
|
||||
(~auth-nav-items :account-url (app-url "account" "")
|
||||
(~shared:auth/nav-items :account-url (app-url "account" "")
|
||||
:select-colours (select-colours)
|
||||
:account-nav (account-nav-ctx))))
|
||||
|
||||
;; Orders header row
|
||||
(defcomp ~orders-header-row (&key (list-url :as string))
|
||||
(~menu-row-sx :id "orders-row" :level 2 :colour "sky"
|
||||
(defcomp ~shared:auth/orders-header-row (&key (list-url :as string))
|
||||
(~shared:layout/menu-row-sx :id "orders-row" :level 2 :colour "sky"
|
||||
:link-href list-url :link-label "Orders" :icon "fa fa-gbp"
|
||||
:child-id "orders-header-child"))
|
||||
|
||||
@@ -61,12 +61,12 @@
|
||||
;; Auth forms — login flow, check email
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~auth-error-banner (&key (error :as string?))
|
||||
(defcomp ~shared:auth/error-banner (&key (error :as string?))
|
||||
(when error
|
||||
(div :class "bg-red-50 border border-red-200 text-red-700 p-3 rounded mb-4"
|
||||
error)))
|
||||
|
||||
(defcomp ~auth-login-form (&key error (action :as string) (csrf-token :as string) (email :as string?))
|
||||
(defcomp ~shared:auth/login-form (&key error (action :as string) (csrf-token :as string) (email :as string?))
|
||||
(div :class "py-8 max-w-md mx-auto"
|
||||
(h1 :class "text-2xl font-bold mb-6" "Sign in")
|
||||
error
|
||||
@@ -80,12 +80,12 @@
|
||||
:class "w-full bg-stone-800 text-white py-2 px-4 rounded hover:bg-stone-700 transition"
|
||||
"Send magic link"))))
|
||||
|
||||
(defcomp ~auth-check-email-error (&key (error :as string?))
|
||||
(defcomp ~shared:auth/check-email-error (&key (error :as string?))
|
||||
(when error
|
||||
(div :class "bg-yellow-50 border border-yellow-200 text-yellow-700 p-3 rounded mt-4"
|
||||
error)))
|
||||
|
||||
(defcomp ~auth-check-email (&key (email :as string) error)
|
||||
(defcomp ~shared:auth/check-email (&key (email :as string) error)
|
||||
(div :class "py-8 max-w-md mx-auto text-center"
|
||||
(h1 :class "text-2xl font-bold mb-4" "Check your email")
|
||||
(p :class "text-stone-600 mb-2" "We sent a sign-in link to " (strong email) ".")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
(defcomp ~post-card (&key (title :as string) (slug :as string) (href :as string) (feature-image :as string?)
|
||||
(defcomp ~shared:cards/post-card (&key (title :as string) (slug :as string) (href :as string) (feature-image :as string?)
|
||||
(excerpt :as string?) (status :as string?) (published-at :as string?) (updated-at :as string?)
|
||||
(publish-requested :as boolean?) (hx-select :as string?) like widgets at-bar)
|
||||
(article :class "border-b pb-6 last:border-b-0 relative"
|
||||
@@ -31,13 +31,13 @@
|
||||
(when widgets widgets)
|
||||
(when at-bar at-bar)))
|
||||
|
||||
(defcomp ~order-summary-card (&key (order-id :as string) (created-at :as string?) (description :as string?)
|
||||
(defcomp ~shared:cards/order-summary-card (&key (order-id :as string) (created-at :as string?) (description :as string?)
|
||||
(status :as string?) (currency :as string?) (total-amount :as string?))
|
||||
(div :class "rounded-2xl border border-stone-200 bg-white/80 p-4 sm:p-6 space-y-2 text-xs sm:text-sm text-stone-800"
|
||||
(p (span :class "font-medium" "Order ID:") " " (span :class "font-mono" (str "#" order-id)))
|
||||
(p (span :class "font-medium" "Created:") " " (or created-at "\u2014"))
|
||||
(p (span :class "font-medium" "Description:") " " (or description "\u2013"))
|
||||
(p (span :class "font-medium" "Status:") " " (~status-pill :status (or status "pending") :size "[11px]"))
|
||||
(p (span :class "font-medium" "Status:") " " (~shared:controls/status-pill :status (or status "pending") :size "[11px]"))
|
||||
(p (span :class "font-medium" "Currency:") " " (or currency "GBP"))
|
||||
(p (span :class "font-medium" "Total:") " "
|
||||
(if total-amount
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
(defcomp ~search-mobile (&key (current-local-href :as string) (search :as string?) (search-count :as number?)
|
||||
(defcomp ~shared:controls/search-mobile (&key (current-local-href :as string) (search :as string?) (search-count :as number?)
|
||||
(hx-select :as string?) (search-headers-mobile :as string?))
|
||||
(div :id "search-mobile-wrapper"
|
||||
:class "flex flex-row gap-2 items-center flex-1 min-w-0 pr-2"
|
||||
@@ -21,7 +21,7 @@
|
||||
:class (if (not search-count) "text-xl text-red-500" "")
|
||||
(when search (str search-count)))))
|
||||
|
||||
(defcomp ~search-desktop (&key (current-local-href :as string) (search :as string?) (search-count :as number?)
|
||||
(defcomp ~shared:controls/search-desktop (&key (current-local-href :as string) (search :as string?) (search-count :as number?)
|
||||
(hx-select :as string?) (search-headers-desktop :as string?))
|
||||
(div :id "search-desktop-wrapper"
|
||||
:class "flex flex-row gap-2 items-center"
|
||||
@@ -44,7 +44,7 @@
|
||||
:class (if (not search-count) "text-xl text-red-500" "")
|
||||
(when search (str search-count)))))
|
||||
|
||||
(defcomp ~mobile-filter (&key filter-summary action-buttons filter-details)
|
||||
(defcomp ~shared:controls/mobile-filter (&key filter-summary action-buttons filter-details)
|
||||
(details :class "group/filter p-2 md:hidden" :data-toggle-group "mobile-panels"
|
||||
(summary :class "bg-white/90"
|
||||
(div :class "flex flex-row items-start"
|
||||
@@ -64,7 +64,7 @@
|
||||
(div :id "filter-details-mobile" :style "display:contents"
|
||||
(when filter-details filter-details))))
|
||||
|
||||
(defcomp ~infinite-scroll (&key (url :as string) (page :as number) (total-pages :as number)
|
||||
(defcomp ~shared:controls/infinite-scroll (&key (url :as string) (page :as number) (total-pages :as number)
|
||||
(id-prefix :as string) (colspan :as number))
|
||||
(if (< page total-pages)
|
||||
(tr :id (str id-prefix "-sentinel-" page)
|
||||
@@ -85,7 +85,7 @@
|
||||
(tr (td :colspan colspan :class "px-3 py-4 text-center text-xs text-stone-400"
|
||||
"End of results"))))
|
||||
|
||||
(defcomp ~status-pill (&key (status :as string?) (size :as string?))
|
||||
(defcomp ~shared:controls/status-pill (&key (status :as string?) (size :as string?))
|
||||
(let* ((s (or status "pending"))
|
||||
(lower (lower s))
|
||||
(sz (or size "xs"))
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
(defcomp ~link-card (&key (link :as string) (title :as string) (image :as string?) (icon :as string?)
|
||||
(defcomp ~shared:fragments/link-card (&key (link :as string) (title :as string) (image :as string?) (icon :as string?)
|
||||
(subtitle :as string?) (detail :as string?) (data-app :as string?))
|
||||
(a :href link
|
||||
:class "block rounded border border-stone-200 bg-white hover:bg-stone-50 transition-colors no-underline"
|
||||
@@ -17,7 +17,7 @@
|
||||
(when detail
|
||||
(div :class "text-xs text-stone-400 mt-1" detail))))))
|
||||
|
||||
(defcomp ~cart-mini (&key (cart-count :as number) (blog-url :as string) (cart-url :as string) (oob :as string?))
|
||||
(defcomp ~shared:fragments/cart-mini (&key (cart-count :as number) (blog-url :as string) (cart-url :as string) (oob :as string?))
|
||||
(div :id "cart-mini"
|
||||
:sx-swap-oob oob
|
||||
(if (= cart-count 0)
|
||||
@@ -34,7 +34,7 @@
|
||||
(span :class "absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 inline-flex items-center justify-center rounded-full bg-emerald-600 text-white text-sm w-5 h-5"
|
||||
cart-count)))))
|
||||
|
||||
(defcomp ~auth-menu (&key (user-email :as string?) (account-url :as string))
|
||||
(defcomp ~shared:fragments/auth-menu (&key (user-email :as string?) (account-url :as string))
|
||||
(<>
|
||||
(span :id "auth-menu-desktop" :class "hidden md:inline-flex"
|
||||
(if user-email
|
||||
@@ -66,7 +66,7 @@
|
||||
(i :class "fa-solid fa-key")
|
||||
(span "sign in or register"))))))
|
||||
|
||||
(defcomp ~account-nav-item (&key (href :as string) (label :as string))
|
||||
(defcomp ~shared:fragments/account-nav-item (&key (href :as string) (label :as string))
|
||||
(div :class "relative nav-group"
|
||||
(a :href href
|
||||
:class "justify-center cursor-pointer flex flex-row items-center gap-2 rounded bg-stone-200 text-black p-3"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
(defcomp ~app-body (&key header-rows filter aside menu content)
|
||||
(defcomp ~shared:layout/app-body (&key header-rows filter aside menu content)
|
||||
(div :class "max-w-screen-2xl mx-auto py-1 px-1"
|
||||
(when header-rows
|
||||
(div :class "w-full"
|
||||
@@ -24,7 +24,7 @@
|
||||
(when content content)
|
||||
(div :class "pb-8")))))))
|
||||
|
||||
(defcomp ~oob-sx (&key oobs filter aside menu content)
|
||||
(defcomp ~shared:layout/oob-sx (&key oobs filter aside menu content)
|
||||
(<>
|
||||
(when oobs oobs)
|
||||
(div :id "filter" :sx-swap-oob "outerHTML"
|
||||
@@ -38,7 +38,7 @@
|
||||
:class "flex-1 md:h-full md:min-h-0 overflow-y-auto overscroll-contain js-grid-viewport"
|
||||
(when content content))))
|
||||
|
||||
(defcomp ~hamburger ()
|
||||
(defcomp ~shared:layout/hamburger ()
|
||||
(div :class "md:hidden bg-stone-200 rounded"
|
||||
(svg :class "h-12 w-12 transition-transform group-open/root:hidden block self-start"
|
||||
:viewBox "0 0 24 24" :fill "none" :stroke "currentColor"
|
||||
@@ -48,17 +48,17 @@
|
||||
:class "w-12 h-12 rotate-180 transition-transform group-open/root:block hidden self-start"
|
||||
(path :d "M6 9l6 6 6-6" :fill "currentColor"))))
|
||||
|
||||
(defcomp ~post-label (&key (feature-image :as string?) (title :as string))
|
||||
(defcomp ~shared:layout/post-label (&key (feature-image :as string?) (title :as string))
|
||||
(<> (when feature-image
|
||||
(img :src feature-image :class "h-8 w-8 rounded-full object-cover border border-stone-300 flex-shrink-0"))
|
||||
(span title)))
|
||||
|
||||
(defcomp ~page-cart-badge (&key (href :as string) (count :as string))
|
||||
(defcomp ~shared:layout/page-cart-badge (&key (href :as string) (count :as string))
|
||||
(a :href href :class "relative inline-flex items-center gap-1.5 px-3 py-1.5 text-sm rounded-full border border-emerald-300 bg-emerald-50 text-emerald-800 hover:bg-emerald-100 transition"
|
||||
(i :class "fa fa-shopping-cart" :aria-hidden "true")
|
||||
(span count)))
|
||||
|
||||
(defcomp ~header-row-sx (&key cart-mini (blog-url :as string?) (site-title :as string?)
|
||||
(defcomp ~shared:layout/header-row-sx (&key cart-mini (blog-url :as string?) (site-title :as string?)
|
||||
(app-label :as string?) nav-tree auth-menu nav-panel
|
||||
(settings-url :as string?) (is-admin :as boolean?) (oob :as boolean?))
|
||||
(<>
|
||||
@@ -79,13 +79,13 @@
|
||||
(when (and is-admin settings-url)
|
||||
(a :href settings-url :class "justify-center cursor-pointer flex flex-row items-center gap-2 rounded bg-stone-200 text-black p-3"
|
||||
(i :class "fa fa-cog" :aria-hidden "true"))))
|
||||
(~hamburger)))
|
||||
(~shared:layout/hamburger)))
|
||||
(div :class "block md:hidden text-md font-bold"
|
||||
(when auth-menu auth-menu))))
|
||||
|
||||
; @css bg-sky-400 bg-sky-300 bg-sky-200 bg-sky-100 bg-violet-400 bg-violet-300 bg-violet-200 bg-violet-100
|
||||
; @css aria-selected:bg-violet-200 aria-selected:text-violet-900 aria-selected:bg-stone-500 aria-selected:text-white
|
||||
(defcomp ~menu-row-sx (&key (id :as string) (level :as number?) (colour :as string?)
|
||||
(defcomp ~shared:layout/menu-row-sx (&key (id :as string) (level :as number?) (colour :as string?)
|
||||
(link-href :as string) (link-label :as string?) link-label-content
|
||||
(icon :as string?) (selected :as string?) (hx-select :as string?)
|
||||
nav (child-id :as string?) child (oob :as boolean?) (external :as boolean?))
|
||||
@@ -117,11 +117,11 @@
|
||||
(div :id child-id :class "flex flex-col w-full items-center"
|
||||
(when child child))))))
|
||||
|
||||
(defcomp ~oob-header-sx (&key (parent-id :as string) row)
|
||||
(defcomp ~shared:layout/oob-header-sx (&key (parent-id :as string) row)
|
||||
(div :id parent-id :sx-swap-oob "outerHTML" :class "flex flex-col w-full items-center"
|
||||
row))
|
||||
|
||||
(defcomp ~header-child-sx (&key (id :as string?) inner)
|
||||
(defcomp ~shared:layout/header-child-sx (&key (id :as string?) inner)
|
||||
(div :id (or id "root-header-child") :class "flex flex-col w-full items-center" inner))
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
@@ -129,7 +129,7 @@
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
;; Labelled section: colour bar header + vertical nav items
|
||||
(defcomp ~mobile-menu-section (&key (label :as string) (href :as string?) (colour :as string?)
|
||||
(defcomp ~shared:layout/mobile-menu-section (&key (label :as string) (href :as string?) (colour :as string?)
|
||||
(level :as number?) items)
|
||||
(let* ((c (or colour "sky"))
|
||||
(lv (or level 1))
|
||||
@@ -143,7 +143,7 @@
|
||||
items))))
|
||||
|
||||
;; Root-level mobile nav: site nav items + auth links
|
||||
(defcomp ~mobile-root-nav (&key nav-tree auth-menu)
|
||||
(defcomp ~shared:layout/mobile-root-nav (&key nav-tree auth-menu)
|
||||
(<>
|
||||
(when nav-tree
|
||||
(div :class "flex flex-col gap-2 p-3 text-sm" nav-tree))
|
||||
@@ -156,27 +156,27 @@
|
||||
;; nested component calls in _aser are serialized without expansion.
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~root-header (&key cart-mini (blog-url :as string?) (site-title :as string?)
|
||||
(defcomp ~shared:layout/root-header (&key cart-mini (blog-url :as string?) (site-title :as string?)
|
||||
(app-label :as string?) nav-tree auth-menu nav-panel
|
||||
(settings-url :as string?) (is-admin :as boolean?) (oob :as boolean?))
|
||||
(~header-row-sx :cart-mini cart-mini :blog-url blog-url :site-title site-title
|
||||
(~shared:layout/header-row-sx :cart-mini cart-mini :blog-url blog-url :site-title site-title
|
||||
:app-label app-label :nav-tree nav-tree :auth-menu auth-menu
|
||||
:nav-panel nav-panel :settings-url settings-url :is-admin is-admin
|
||||
:oob oob))
|
||||
|
||||
(defcomp ~root-mobile (&key nav-tree auth-menu)
|
||||
(~mobile-root-nav :nav-tree nav-tree :auth-menu auth-menu))
|
||||
(defcomp ~shared:layout/root-mobile (&key nav-tree auth-menu)
|
||||
(~shared:layout/mobile-root-nav :nav-tree nav-tree :auth-menu auth-menu))
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Auto-fetching header/mobile macros — use IO primitives to self-populate.
|
||||
;; These expand inline so IO calls resolve in _aser mode within layout bodies.
|
||||
;; Replaces the 10-parameter ~root-header boilerplate in layout defcomps.
|
||||
;; Replaces the 10-parameter ~shared:layout/root-header boilerplate in layout defcomps.
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defmacro ~root-header-auto (oob)
|
||||
(quasiquote
|
||||
(let ((__rhctx (root-header-ctx)))
|
||||
(~header-row-sx :cart-mini (get __rhctx "cart-mini")
|
||||
(~shared:layout/header-row-sx :cart-mini (get __rhctx "cart-mini")
|
||||
:blog-url (get __rhctx "blog-url")
|
||||
:site-title (get __rhctx "site-title")
|
||||
:app-label (get __rhctx "app-label")
|
||||
@@ -190,7 +190,7 @@
|
||||
(defmacro ~root-mobile-auto ()
|
||||
(quasiquote
|
||||
(let ((__rhctx (root-header-ctx)))
|
||||
(~mobile-root-nav :nav-tree (get __rhctx "nav-tree")
|
||||
(~shared:layout/mobile-root-nav :nav-tree (get __rhctx "nav-tree")
|
||||
:auth-menu (get __rhctx "auth-menu")))))
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
@@ -198,31 +198,31 @@
|
||||
;; These use ~root-header-auto / ~root-mobile-auto macros (IO primitives).
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~layout-root-full ()
|
||||
(defcomp ~shared:layout/root-full ()
|
||||
(~root-header-auto))
|
||||
|
||||
(defcomp ~layout-root-oob ()
|
||||
(~oob-header-sx :parent-id "root-header-child"
|
||||
(defcomp ~shared:layout/root-oob ()
|
||||
(~shared:layout/oob-header-sx :parent-id "root-header-child"
|
||||
:row (~root-header-auto true)))
|
||||
|
||||
(defcomp ~layout-root-mobile ()
|
||||
(defcomp ~shared:layout/root-mobile ()
|
||||
(~root-mobile-auto))
|
||||
|
||||
;; Post layout — root + post header
|
||||
(defcomp ~layout-post-full ()
|
||||
(defcomp ~shared:layout/post-full ()
|
||||
(<> (~root-header-auto)
|
||||
(~header-child-sx :inner (~post-header-auto))))
|
||||
(~shared:layout/header-child-sx :inner (~post-header-auto))))
|
||||
|
||||
(defcomp ~layout-post-oob ()
|
||||
(defcomp ~shared:layout/post-oob ()
|
||||
(<> (~post-header-auto true)
|
||||
(~oob-header-sx :parent-id "post-header-child" :row "")))
|
||||
(~shared:layout/oob-header-sx :parent-id "post-header-child" :row "")))
|
||||
|
||||
(defcomp ~layout-post-mobile ()
|
||||
(defcomp ~shared:layout/post-mobile ()
|
||||
(let ((__phctx (post-header-ctx))
|
||||
(__rhctx (root-header-ctx)))
|
||||
(<>
|
||||
(when (get __phctx "slug")
|
||||
(~mobile-menu-section
|
||||
(~shared:layout/mobile-menu-section
|
||||
:label (slice (get __phctx "title") 0 40)
|
||||
:href (get __phctx "link-href")
|
||||
:level 1
|
||||
@@ -230,35 +230,35 @@
|
||||
(~root-mobile-auto))))
|
||||
|
||||
;; Post-admin layout — root + post header with nested admin row
|
||||
(defcomp ~layout-post-admin-full (&key (selected :as string?))
|
||||
(defcomp ~shared:layout/post-admin-full (&key (selected :as string?))
|
||||
(let ((__admin-hdr (~post-admin-header-auto nil selected)))
|
||||
(<> (~root-header-auto)
|
||||
(~header-child-sx
|
||||
(~shared:layout/header-child-sx
|
||||
:inner (~post-header-auto nil)))))
|
||||
|
||||
(defcomp ~layout-post-admin-oob (&key (selected :as string?))
|
||||
(defcomp ~shared:layout/post-admin-oob (&key (selected :as string?))
|
||||
(<> (~post-header-auto true)
|
||||
(~oob-header-sx :parent-id "post-header-child"
|
||||
(~shared:layout/oob-header-sx :parent-id "post-header-child"
|
||||
:row (~post-admin-header-auto nil selected))))
|
||||
|
||||
(defcomp ~layout-post-admin-mobile (&key (selected :as string?))
|
||||
(defcomp ~shared:layout/post-admin-mobile (&key (selected :as string?))
|
||||
(let ((__phctx (post-header-ctx)))
|
||||
(<>
|
||||
(when (get __phctx "slug")
|
||||
(~mobile-menu-section
|
||||
(~shared:layout/mobile-menu-section
|
||||
:label "admin"
|
||||
:href (get __phctx "admin-href")
|
||||
:level 2
|
||||
:items (~post-admin-nav-auto selected)))
|
||||
(when (get __phctx "slug")
|
||||
(~mobile-menu-section
|
||||
(~shared:layout/mobile-menu-section
|
||||
:label (slice (get __phctx "title") 0 40)
|
||||
:href (get __phctx "link-href")
|
||||
:level 1
|
||||
:items (~post-nav-auto)))
|
||||
(~root-mobile-auto))))
|
||||
|
||||
(defcomp ~error-content (&key (errnum :as string) (message :as string) (image :as string?))
|
||||
(defcomp ~shared:layout/error-content (&key (errnum :as string) (message :as string) (image :as string?))
|
||||
(div :class "text-center p-8 max-w-lg mx-auto"
|
||||
(div :class "font-bold text-2xl md:text-4xl text-red-500 mb-4" errnum)
|
||||
(div :class "text-stone-600 mb-4" message)
|
||||
@@ -266,7 +266,7 @@
|
||||
(div :class "flex justify-center"
|
||||
(img :src image :width "300" :height "300")))))
|
||||
|
||||
(defcomp ~clear-oob-div (&key (id :as string))
|
||||
(defcomp ~shared:layout/clear-oob-div (&key (id :as string))
|
||||
(div :id id :sx-swap-oob "outerHTML"))
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
@@ -280,12 +280,12 @@
|
||||
(when (get __phctx "slug")
|
||||
(<>
|
||||
(when (> (get __phctx "page-cart-count") 0)
|
||||
(~page-cart-badge :href (get __phctx "cart-href")
|
||||
(~shared:layout/page-cart-badge :href (get __phctx "cart-href")
|
||||
:count (str (get __phctx "page-cart-count"))))
|
||||
(when (get __phctx "container-nav")
|
||||
(~container-nav-wrapper :content (get __phctx "container-nav")))
|
||||
(~shared:layout/container-nav-wrapper :content (get __phctx "container-nav")))
|
||||
(when (get __phctx "is-admin")
|
||||
(~admin-cog-button :href (get __phctx "admin-href")
|
||||
(~shared:layout/admin-cog-button :href (get __phctx "admin-href")
|
||||
:is-admin-page (get __phctx "is-admin-page"))))))))
|
||||
|
||||
(defmacro ~post-header-auto (oob)
|
||||
@@ -293,9 +293,9 @@
|
||||
(quasiquote
|
||||
(let ((__phctx (post-header-ctx)))
|
||||
(when (get __phctx "slug")
|
||||
(~menu-row-sx :id "post-row" :level 1
|
||||
(~shared:layout/menu-row-sx :id "post-row" :level 1
|
||||
:link-href (get __phctx "link-href")
|
||||
:link-label-content (~post-label
|
||||
:link-label-content (~shared:layout/post-label
|
||||
:feature-image (get __phctx "feature-image")
|
||||
:title (get __phctx "title"))
|
||||
:nav (~post-nav-auto)
|
||||
@@ -310,28 +310,28 @@
|
||||
(let ((__slug (get __phctx "slug"))
|
||||
(__sc (get __phctx "select-colours")))
|
||||
(<>
|
||||
(~nav-link :href (app-url "events" (str "/" __slug "/admin/"))
|
||||
(~shared:layout/nav-link :href (app-url "events" (str "/" __slug "/admin/"))
|
||||
:label "calendars" :select-colours __sc
|
||||
:is-selected (when (= (unquote selected) "calendars") "true"))
|
||||
(~nav-link :href (app-url "market" (str "/" __slug "/admin/"))
|
||||
(~shared:layout/nav-link :href (app-url "market" (str "/" __slug "/admin/"))
|
||||
:label "markets" :select-colours __sc
|
||||
:is-selected (when (= (unquote selected) "markets") "true"))
|
||||
(~nav-link :href (app-url "cart" (str "/" __slug "/admin/payments/"))
|
||||
(~shared:layout/nav-link :href (app-url "cart" (str "/" __slug "/admin/payments/"))
|
||||
:label "payments" :select-colours __sc
|
||||
:is-selected (when (= (unquote selected) "payments") "true"))
|
||||
(~nav-link :href (app-url "blog" (str "/" __slug "/admin/entries/"))
|
||||
(~shared:layout/nav-link :href (app-url "blog" (str "/" __slug "/admin/entries/"))
|
||||
:label "entries" :select-colours __sc
|
||||
:is-selected (when (= (unquote selected) "entries") "true"))
|
||||
(~nav-link :href (app-url "blog" (str "/" __slug "/admin/data/"))
|
||||
(~shared:layout/nav-link :href (app-url "blog" (str "/" __slug "/admin/data/"))
|
||||
:label "data" :select-colours __sc
|
||||
:is-selected (when (= (unquote selected) "data") "true"))
|
||||
(~nav-link :href (app-url "blog" (str "/" __slug "/admin/preview/"))
|
||||
(~shared:layout/nav-link :href (app-url "blog" (str "/" __slug "/admin/preview/"))
|
||||
:label "preview" :select-colours __sc
|
||||
:is-selected (when (= (unquote selected) "preview") "true"))
|
||||
(~nav-link :href (app-url "blog" (str "/" __slug "/admin/edit/"))
|
||||
(~shared:layout/nav-link :href (app-url "blog" (str "/" __slug "/admin/edit/"))
|
||||
:label "edit" :select-colours __sc
|
||||
:is-selected (when (= (unquote selected) "edit") "true"))
|
||||
(~nav-link :href (app-url "blog" (str "/" __slug "/admin/settings/"))
|
||||
(~shared:layout/nav-link :href (app-url "blog" (str "/" __slug "/admin/settings/"))
|
||||
:label "settings" :select-colours __sc
|
||||
:is-selected (when (= (unquote selected) "settings") "true"))))))))
|
||||
|
||||
@@ -340,9 +340,9 @@
|
||||
(quasiquote
|
||||
(let ((__phctx (post-header-ctx)))
|
||||
(when (get __phctx "slug")
|
||||
(~menu-row-sx :id "post-admin-row" :level 2
|
||||
(~shared:layout/menu-row-sx :id "post-admin-row" :level 2
|
||||
:link-href (get __phctx "admin-href")
|
||||
:link-label-content (~post-admin-label
|
||||
:link-label-content (~shared:layout/post-admin-label
|
||||
:selected (unquote selected))
|
||||
:nav (~post-admin-nav-auto (unquote selected))
|
||||
:child-id "post-admin-header-child"
|
||||
@@ -352,27 +352,27 @@
|
||||
;; Shared nav helpers — used by post_header_sx / post_admin_header_sx
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~container-nav-wrapper (&key content)
|
||||
(defcomp ~shared:layout/container-nav-wrapper (&key content)
|
||||
(div :id "entries-calendars-nav-wrapper"
|
||||
:class "flex flex-col sm:flex-row sm:items-center gap-2 border-r border-stone-200 mr-2 sm:max-w-2xl"
|
||||
content))
|
||||
|
||||
; @css justify-center cursor-pointer flex flex-row items-center gap-2 rounded bg-stone-200 text-black p-3 !bg-stone-500 !text-white
|
||||
(defcomp ~admin-cog-button (&key (href :as string) (is-admin-page :as boolean?))
|
||||
(defcomp ~shared:layout/admin-cog-button (&key (href :as string) (is-admin-page :as boolean?))
|
||||
(div :class "relative nav-group"
|
||||
(a :href href
|
||||
:class (str "justify-center cursor-pointer flex flex-row items-center gap-2 rounded bg-stone-200 text-black p-3 "
|
||||
(if is-admin-page "!bg-stone-500 !text-white" ""))
|
||||
(i :class "fa fa-cog" :aria-hidden "true"))))
|
||||
|
||||
(defcomp ~post-admin-label (&key (selected :as string?))
|
||||
(defcomp ~shared:layout/post-admin-label (&key (selected :as string?))
|
||||
(<>
|
||||
(i :class "fa fa-shield-halved" :aria-hidden "true")
|
||||
" admin"
|
||||
(when selected
|
||||
(span :class "text-white" selected))))
|
||||
|
||||
(defcomp ~nav-link (&key (href :as string) (hx-select :as string?) (label :as string?) (icon :as string?)
|
||||
(defcomp ~shared:layout/nav-link (&key (href :as string) (hx-select :as string?) (label :as string?) (icon :as string?)
|
||||
(aclass :as string?) (select-colours :as string?) (is-selected :as string?))
|
||||
(div :class "relative nav-group"
|
||||
(a :href href
|
||||
|
||||
@@ -2,33 +2,33 @@
|
||||
|
||||
;; The single place where raw! lives — for CMS content (Ghost post body,
|
||||
;; product descriptions, etc.) that arrives as pre-rendered HTML.
|
||||
(defcomp ~rich-text (&key (html :as string))
|
||||
(defcomp ~shared:misc/rich-text (&key (html :as string))
|
||||
(raw! html))
|
||||
|
||||
(defcomp ~error-inline (&key (message :as string))
|
||||
(defcomp ~shared:misc/error-inline (&key (message :as string))
|
||||
(div :class "text-red-600 text-sm" message))
|
||||
|
||||
(defcomp ~notification-badge (&key (count :as number))
|
||||
(defcomp ~shared:misc/notification-badge (&key (count :as number))
|
||||
(span :class "bg-red-500 text-white text-xs rounded-full px-1.5 py-0.5" count))
|
||||
|
||||
(defcomp ~cache-cleared (&key (time-str :as string))
|
||||
(defcomp ~shared:misc/cache-cleared (&key (time-str :as string))
|
||||
(span :class "text-green-600 font-bold" "Cache cleared at " time-str))
|
||||
|
||||
(defcomp ~error-list (&key (items :as list?))
|
||||
(defcomp ~shared:misc/error-list (&key (items :as list?))
|
||||
(ul :class "list-disc pl-5 space-y-1 text-sm text-red-600"
|
||||
(when items items)))
|
||||
|
||||
(defcomp ~error-list-item (&key (message :as string))
|
||||
(defcomp ~shared:misc/error-list-item (&key (message :as string))
|
||||
(li message))
|
||||
|
||||
(defcomp ~fragment-error (&key (service :as string))
|
||||
(defcomp ~shared:misc/fragment-error (&key (service :as string))
|
||||
(p :class "text-sm text-red-600" "Service " (b service) " is unavailable."))
|
||||
|
||||
(defcomp ~htmx-sentinel (&key (id :as string) (hx-get :as string) (hx-trigger :as string)
|
||||
(defcomp ~shared:misc/htmx-sentinel (&key (id :as string) (hx-get :as string) (hx-trigger :as string)
|
||||
(hx-swap :as string) (class :as string?) extra-attrs)
|
||||
(div :id id :sx-get hx-get :sx-trigger hx-trigger :sx-swap hx-swap :class class))
|
||||
|
||||
(defcomp ~nav-group-link (&key (href :as string) (hx-select :as string?) (nav-class :as string?) (label :as string))
|
||||
(defcomp ~shared:misc/nav-group-link (&key (href :as string) (hx-select :as string?) (nav-class :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"
|
||||
@@ -39,7 +39,7 @@
|
||||
;; Shared sentinel components — infinite scroll triggers
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~sentinel-mobile (&key (id :as string) (next-url :as string) (hyperscript :as string?))
|
||||
(defcomp ~shared:misc/sentinel-mobile (&key (id :as string) (next-url :as string) (hyperscript :as string?))
|
||||
(div :id id :class "block md:hidden h-[60vh] opacity-0 pointer-events-none js-mobile-sentinel"
|
||||
:sx-get next-url :sx-trigger "intersect once delay:250ms, sentinelmobile:retry"
|
||||
:sx-swap "outerHTML" :_ hyperscript
|
||||
@@ -50,7 +50,7 @@
|
||||
(i :class "fa fa-exclamation-triangle text-2xl")
|
||||
(p :class "mt-2" "Loading failed \u2014 retrying\u2026"))))
|
||||
|
||||
(defcomp ~sentinel-desktop (&key (id :as string) (next-url :as string) (hyperscript :as string?))
|
||||
(defcomp ~shared:misc/sentinel-desktop (&key (id :as string) (next-url :as string) (hyperscript :as string?))
|
||||
(div :id id :class "hidden md:block h-4 opacity-0 pointer-events-none"
|
||||
:sx-get next-url :sx-trigger "intersect once delay:250ms, sentinel:retry"
|
||||
:sx-swap "outerHTML" :_ hyperscript
|
||||
@@ -59,20 +59,20 @@
|
||||
(div :class "animate-spin h-6 w-6 border-2 border-stone-300 border-t-stone-600 rounded-full"))
|
||||
(div :class "js-neterr hidden text-center py-2 text-stone-400 text-sm" "Retry\u2026")))
|
||||
|
||||
(defcomp ~sentinel-simple (&key (id :as string) (next-url :as string))
|
||||
(defcomp ~shared:misc/sentinel-simple (&key (id :as string) (next-url :as string))
|
||||
(div :id id :class "h-4 opacity-0 pointer-events-none"
|
||||
:sx-get next-url :sx-trigger "intersect once delay:250ms" :sx-swap "outerHTML"
|
||||
:role "status" :aria-hidden "true"
|
||||
(div :class "text-center text-xs text-stone-400" "loading...")))
|
||||
|
||||
(defcomp ~end-of-results (&key (cls :as string?))
|
||||
(defcomp ~shared:misc/end-of-results (&key (cls :as string?))
|
||||
(div :class (or cls "col-span-full mt-4 text-center text-xs text-stone-400") "End of results"))
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Shared empty state — icon + message + optional action
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~empty-state (&key (icon :as string?) (message :as string) (cls :as string?) &rest children)
|
||||
(defcomp ~shared:misc/empty-state (&key (icon :as string?) (message :as string) (cls :as string?) &rest children)
|
||||
(div :class (or cls "p-8 text-center text-stone-400")
|
||||
(when icon (div (i :class (str icon " text-4xl mb-2") :aria-hidden "true")))
|
||||
(p message)
|
||||
@@ -82,7 +82,7 @@
|
||||
;; Shared badge — inline pill with configurable colours
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~badge (&key (label :as string) (cls :as string?))
|
||||
(defcomp ~shared:misc/badge (&key (label :as string) (cls :as string?))
|
||||
(span :class (str "inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium " (or cls "bg-stone-100 text-stone-700"))
|
||||
label))
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
;; Shared delete button with confirm dialog
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~delete-btn (&key (url :as string) (trigger-target :as string) (title :as string?)
|
||||
(defcomp ~shared:misc/delete-btn (&key (url :as string) (trigger-target :as string) (title :as string?)
|
||||
(text :as string?) (confirm-text :as string?) (cancel-text :as string?)
|
||||
(sx-headers :as string?) (cls :as string?))
|
||||
(button :type "button"
|
||||
@@ -110,7 +110,7 @@
|
||||
;; Shared price display — special + regular with strikethrough
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~price (&key (special-price :as string?) (regular-price :as string?))
|
||||
(defcomp ~shared:misc/price (&key (special-price :as string?) (regular-price :as string?))
|
||||
(div :class "mt-1 flex items-baseline gap-2 justify-center"
|
||||
(when special-price (div :class "text-lg font-semibold text-emerald-700" special-price))
|
||||
(when (and special-price regular-price) (div :class "text-sm line-through text-stone-500" regular-price))
|
||||
@@ -120,7 +120,7 @@
|
||||
;; Shared image-or-placeholder
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~img-or-placeholder (&key (src :as string?) (alt :as string?) (size-cls :as string?)
|
||||
(defcomp ~shared:misc/img-or-placeholder (&key (src :as string?) (alt :as string?) (size-cls :as string?)
|
||||
(placeholder-icon :as string?) (placeholder-text :as string?))
|
||||
(if src
|
||||
(img :src src :alt (or alt "") :class (or size-cls "w-12 h-12 rounded-full object-cover flex-shrink-0"))
|
||||
@@ -133,35 +133,35 @@
|
||||
;; Shared view toggle — list/tile view switcher
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~list-svg ()
|
||||
(defcomp ~shared:misc/list-svg ()
|
||||
(svg :xmlns "http://www.w3.org/2000/svg" :class "h-5 w-5" :fill "none" :viewBox "0 0 24 24"
|
||||
:stroke "currentColor" :stroke-width "2"
|
||||
(path :stroke-linecap "round" :stroke-linejoin "round" :d "M4 6h16M4 12h16M4 18h16")))
|
||||
|
||||
(defcomp ~tile-svg ()
|
||||
(defcomp ~shared:misc/tile-svg ()
|
||||
(svg :xmlns "http://www.w3.org/2000/svg" :class "h-5 w-5" :fill "none" :viewBox "0 0 24 24"
|
||||
:stroke "currentColor" :stroke-width "2"
|
||||
(path :stroke-linecap "round" :stroke-linejoin "round"
|
||||
:d "M4 5a1 1 0 011-1h4a1 1 0 011 1v4a1 1 0 01-1 1H5a1 1 0 01-1-1V5zM14 5a1 1 0 011-1h4a1 1 0 011 1v4a1 1 0 01-1 1h-4a1 1 0 01-1-1V5zM4 15a1 1 0 011-1h4a1 1 0 011 1v4a1 1 0 01-1 1H5a1 1 0 01-1-1v-4zM14 15a1 1 0 011-1h4a1 1 0 011 1v4a1 1 0 01-1 1h-4a1 1 0 01-1-1v-4z")))
|
||||
|
||||
(defcomp ~view-toggle (&key (list-href :as string) (tile-href :as string) (hx-select :as string?)
|
||||
(defcomp ~shared:misc/view-toggle (&key (list-href :as string) (tile-href :as string) (hx-select :as string?)
|
||||
(list-cls :as string?) (tile-cls :as string?) (storage-key :as string?)
|
||||
list-svg tile-svg)
|
||||
(div :class "hidden md:flex justify-end px-3 pt-3 gap-1"
|
||||
(a :href list-href :sx-get list-href :sx-target "#main-panel" :sx-select hx-select
|
||||
:sx-swap "outerHTML" :sx-push-url "true" :class (str "p-1.5 rounded " list-cls) :title "List view"
|
||||
:_ (str "on click js localStorage.removeItem('" (or storage-key "view") "') end")
|
||||
(or list-svg (~list-svg)))
|
||||
(or list-svg (~shared:misc/list-svg)))
|
||||
(a :href tile-href :sx-get tile-href :sx-target "#main-panel" :sx-select hx-select
|
||||
:sx-swap "outerHTML" :sx-push-url "true" :class (str "p-1.5 rounded " tile-cls) :title "Tile view"
|
||||
:_ (str "on click js localStorage.setItem('" (or storage-key "view") "','tile') end")
|
||||
(or tile-svg (~tile-svg)))))
|
||||
(or tile-svg (~shared:misc/tile-svg)))))
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Shared CRUD admin panel — for calendars, markets, etc.
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~crud-create-form (&key (create-url :as string) (csrf :as string) (errors-id :as string?)
|
||||
(defcomp ~shared:misc/crud-create-form (&key (create-url :as string) (csrf :as string) (errors-id :as string?)
|
||||
(list-id :as string?) (placeholder :as string?) (label :as string?)
|
||||
(btn-label :as string?))
|
||||
(<>
|
||||
@@ -177,12 +177,12 @@
|
||||
:placeholder (or placeholder "Name")))
|
||||
(button :type "submit" :class "border rounded px-3 py-2" (or btn-label "Add")))))
|
||||
|
||||
(defcomp ~crud-panel (&key form list (list-id :as string?))
|
||||
(defcomp ~shared:misc/crud-panel (&key form list (list-id :as string?))
|
||||
(section :class "p-4"
|
||||
form
|
||||
(div :id (or list-id "crud-list") :class "mt-6" list)))
|
||||
|
||||
(defcomp ~crud-item (&key (href :as string) (name :as string) (slug :as string) (del-url :as string)
|
||||
(defcomp ~shared:misc/crud-item (&key (href :as string) (name :as string) (slug :as string) (del-url :as string)
|
||||
(csrf-hdr :as string) (list-id :as string?) (confirm-title :as string?)
|
||||
(confirm-text :as string?))
|
||||
(div :class "mt-6 border rounded-lg p-4"
|
||||
@@ -206,7 +206,7 @@
|
||||
;; checkout prefix) used by blog, events, and cart admin panels.
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~sumup-settings-form (&key (update-url :as string) (csrf :as string?) (merchant-code :as string?)
|
||||
(defcomp ~shared:misc/sumup-settings-form (&key (update-url :as string) (csrf :as string?) (merchant-code :as string?)
|
||||
(placeholder :as string?) (input-cls :as string?)
|
||||
(sumup-configured :as boolean?) (checkout-prefix :as string?)
|
||||
(panel-id :as string?) (sx-select :as string?))
|
||||
@@ -241,7 +241,7 @@
|
||||
;; Shared avatar — image or initial-letter placeholder
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~avatar (&key (src :as string?) (cls :as string?) (initial :as string?))
|
||||
(defcomp ~shared:misc/avatar (&key (src :as string?) (cls :as string?) (initial :as string?))
|
||||
(if src
|
||||
(img :src src :alt "" :class cls)
|
||||
(div :class cls initial)))
|
||||
@@ -250,7 +250,7 @@
|
||||
;; Shared scroll-nav wrapper — horizontal scrollable nav with arrows
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~scroll-nav-wrapper (&key (wrapper-id :as string) (container-id :as string) (arrow-cls :as string?)
|
||||
(defcomp ~shared:misc/scroll-nav-wrapper (&key (wrapper-id :as string) (container-id :as string) (arrow-cls :as string?)
|
||||
(left-hs :as string?) (scroll-hs :as string?) (right-hs :as string?)
|
||||
items (oob :as boolean?))
|
||||
(div :class "flex flex-col sm:flex-row sm:items-center gap-2 border-r border-stone-200 mr-2 sm:max-w-2xl"
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
;; Blog navigation components
|
||||
|
||||
(defcomp ~blog-nav-empty (&key wrapper-id)
|
||||
(defcomp ~shared:nav/blog-nav-empty (&key wrapper-id)
|
||||
(div :id wrapper-id :sx-swap-oob "outerHTML"))
|
||||
|
||||
(defcomp ~blog-nav-item-link (&key href hx-get selected nav-cls img label)
|
||||
(defcomp ~shared:nav/blog-nav-item-link (&key href hx-get selected nav-cls img label)
|
||||
(div (a :href href :sx-get hx-get :sx-target "#main-panel"
|
||||
:sx-swap "outerHTML" :sx-push-url "true"
|
||||
:aria-selected selected :class nav-cls
|
||||
img (span label))))
|
||||
|
||||
(defcomp ~blog-nav-item-plain (&key href selected nav-cls img label)
|
||||
(defcomp ~shared:nav/blog-nav-item-plain (&key href selected nav-cls img label)
|
||||
(div (a :href href :aria-selected selected :class nav-cls
|
||||
img (span label))))
|
||||
|
||||
;; Nav entries
|
||||
|
||||
(defcomp ~blog-nav-entries-empty ()
|
||||
(defcomp ~shared:nav/blog-nav-entries-empty ()
|
||||
(div :id "entries-calendars-nav-wrapper" :sx-swap-oob "true"))
|
||||
|
||||
(defcomp ~blog-nav-calendar-item (&key href nav-cls name)
|
||||
(defcomp ~shared:nav/blog-nav-calendar-item (&key href nav-cls name)
|
||||
(a :href href :class nav-cls
|
||||
(i :class "fa fa-calendar" :aria-hidden "true")
|
||||
(div name)))
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
(defcomp ~calendar-entry-nav (&key (href :as string) (name :as string) (date-str :as string) (nav-class :as string?))
|
||||
(defcomp ~shared:navigation/calendar-entry-nav (&key (href :as string) (name :as string) (date-str :as string) (nav-class :as string?))
|
||||
(a :href href :class nav-class
|
||||
(div :class "w-8 h-8 rounded bg-stone-200 flex-shrink-0")
|
||||
(div :class "flex-1 min-w-0"
|
||||
(div :class "font-medium truncate" name)
|
||||
(div :class "text-xs text-stone-600 truncate" date-str))))
|
||||
|
||||
(defcomp ~calendar-link-nav (&key (href :as string) (name :as string) (nav-class :as string?)
|
||||
(defcomp ~shared:navigation/calendar-link-nav (&key (href :as string) (name :as string) (nav-class :as string?)
|
||||
(is-selected :as string?) (select-colours :as string?))
|
||||
(a :href href
|
||||
:sx-get href
|
||||
@@ -18,13 +18,13 @@
|
||||
(i :class "fa fa-calendar" :aria-hidden "true")
|
||||
(span name)))
|
||||
|
||||
(defcomp ~market-link-nav (&key (href :as string) (name :as string) (nav-class :as string?)
|
||||
(defcomp ~shared:navigation/market-link-nav (&key (href :as string) (name :as string) (nav-class :as string?)
|
||||
(select-colours :as string?))
|
||||
(a :href href :class (str (or nav-class "") " " (or select-colours ""))
|
||||
(i :class "fa fa-shopping-bag" :aria-hidden "true")
|
||||
(span name)))
|
||||
|
||||
(defcomp ~relation-nav (&key (href :as string) (name :as string) (icon :as string?)
|
||||
(defcomp ~shared:navigation/relation-nav (&key (href :as string) (name :as string) (icon :as string?)
|
||||
(nav-class :as string?) (relation-type :as string?))
|
||||
(a :href href :class (or nav-class "flex items-center gap-3 rounded-lg py-2 px-3 text-sm text-stone-700 hover:bg-stone-100 transition-colors")
|
||||
(when icon
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
;; Order table rows
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~order-row-desktop (&key (oid :as string) (created :as string) (desc :as string) (total :as string)
|
||||
(defcomp ~shared:orders/row-desktop (&key (oid :as string) (created :as string) (desc :as string) (total :as string)
|
||||
(pill :as string) (status :as string) (url :as string))
|
||||
(tr :class "hidden sm:table-row border-t border-stone-100 hover:bg-stone-50/60"
|
||||
(td :class "px-3 py-2 align-top" (span :class "font-mono text-[11px] sm:text-xs" oid))
|
||||
@@ -17,7 +17,7 @@
|
||||
(td :class "px-3 py-0.5 align-top text-right"
|
||||
(a :href url :class "inline-flex items-center px-3 py-1.5 text-xs sm:text-sm rounded-full border border-stone-300 bg-white hover:bg-stone-50 transition" "View"))))
|
||||
|
||||
(defcomp ~order-row-mobile (&key (oid :as string) (created :as string) (total :as string)
|
||||
(defcomp ~shared:orders/row-mobile (&key (oid :as string) (created :as string) (total :as string)
|
||||
(pill :as string) (status :as string) (url :as string))
|
||||
(tr :class "sm:hidden border-t border-stone-100"
|
||||
(td :colspan "5" :class "px-3 py-3"
|
||||
@@ -30,16 +30,16 @@
|
||||
(div :class "font-medium text-stone-800" total)
|
||||
(a :href url :class "inline-flex items-center px-2 py-1 text-[11px] rounded-full border border-stone-300 bg-white hover:bg-stone-50 transition shrink-0" "View"))))))
|
||||
|
||||
(defcomp ~order-end-row ()
|
||||
(defcomp ~shared:orders/end-row ()
|
||||
(tr (td :colspan "5" :class "px-3 py-4 text-center text-xs text-stone-400"
|
||||
(~end-of-results :cls "text-center text-xs text-stone-400"))))
|
||||
(~shared:misc/end-of-results :cls "text-center text-xs text-stone-400"))))
|
||||
|
||||
(defcomp ~order-empty-state ()
|
||||
(defcomp ~shared:orders/empty-state ()
|
||||
(div :class "max-w-full px-3 py-3 space-y-3"
|
||||
(div :class "rounded-2xl border border-dashed border-stone-300 bg-white/80 p-4 sm:p-6 text-sm text-stone-700"
|
||||
"No orders yet.")))
|
||||
|
||||
(defcomp ~order-table (&key rows)
|
||||
(defcomp ~shared:orders/table (&key rows)
|
||||
(div :class "max-w-full px-3 py-3 space-y-3"
|
||||
(div :class "overflow-x-auto rounded-2xl border border-stone-200 bg-white/80"
|
||||
(table :class "min-w-full text-xs sm:text-sm"
|
||||
@@ -53,7 +53,7 @@
|
||||
(th :class "px-3 py-2 text-left font-medium" "")))
|
||||
(tbody rows)))))
|
||||
|
||||
(defcomp ~order-list-header (&key search-mobile)
|
||||
(defcomp ~shared:orders/list-header (&key search-mobile)
|
||||
(header :class "mb-6 sm:mb-8 flex flex-col sm:flex-row sm:items-center justify-between gap-3 sm:gap-4"
|
||||
(div :class "space-y-1"
|
||||
(p :class "text-xs sm:text-sm text-stone-600" "Recent orders placed via the checkout."))
|
||||
@@ -63,13 +63,13 @@
|
||||
;; Order detail panels
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~order-item-image (&key (src :as string) (alt :as string))
|
||||
(defcomp ~shared:orders/item-image (&key (src :as string) (alt :as string))
|
||||
(img :src src :alt alt :class "w-full h-full object-contain object-center" :loading "lazy" :decoding "async"))
|
||||
|
||||
(defcomp ~order-item-no-image ()
|
||||
(defcomp ~shared:orders/item-no-image ()
|
||||
(div :class "w-full h-full flex items-center justify-center text-[9px] text-stone-400" "No image"))
|
||||
|
||||
(defcomp ~order-item-row (&key (href :as string) img (title :as string) (pid :as string)
|
||||
(defcomp ~shared:orders/item-row (&key (href :as string) img (title :as string) (pid :as string)
|
||||
(qty :as string) (price :as string))
|
||||
(li (a :class "w-full py-2 flex gap-3" :href href
|
||||
(div :class "w-12 h-12 sm:w-14 sm:h-14 rounded-md bg-stone-100 flex-shrink-0 overflow-hidden" img)
|
||||
@@ -81,12 +81,12 @@
|
||||
(p qty)
|
||||
(p price))))))
|
||||
|
||||
(defcomp ~order-items-panel (&key items)
|
||||
(defcomp ~shared:orders/items-panel (&key items)
|
||||
(div :class "rounded-2xl border border-stone-200 bg-white/80 p-4 sm:p-6"
|
||||
(h2 :class "text-sm sm:text-base font-semibold mb-3" "Items")
|
||||
(ul :class "divide-y divide-stone-100 text-xs sm:text-sm" items)))
|
||||
|
||||
(defcomp ~order-calendar-entry (&key (name :as string) (pill :as string) (status :as string)
|
||||
(defcomp ~shared:orders/calendar-entry (&key (name :as string) (pill :as string) (status :as string)
|
||||
(date-str :as string) (cost :as string))
|
||||
(li :class "px-4 py-3 flex items-start justify-between text-sm"
|
||||
(div (div :class "font-medium flex items-center gap-2"
|
||||
@@ -94,19 +94,19 @@
|
||||
(div :class "text-xs text-stone-500" date-str))
|
||||
(div :class "ml-4 font-medium" cost)))
|
||||
|
||||
(defcomp ~order-calendar-section (&key items)
|
||||
(defcomp ~shared:orders/calendar-section (&key items)
|
||||
(section :class "mt-6 space-y-3"
|
||||
(h2 :class "text-base sm:text-lg font-semibold" "Calendar bookings in this order")
|
||||
(ul :class "divide-y divide-stone-200 rounded-2xl border border-stone-200 bg-white/80" items)))
|
||||
|
||||
(defcomp ~order-detail-panel (&key summary items calendar)
|
||||
(defcomp ~shared:orders/detail-panel (&key summary items calendar)
|
||||
(div :class "max-w-full px-3 py-3 space-y-4" summary items calendar))
|
||||
|
||||
(defcomp ~order-pay-btn (&key (url :as string))
|
||||
(defcomp ~shared:orders/pay-btn (&key (url :as string))
|
||||
(a :href url :class "inline-flex items-center px-3 py-2 text-xs sm:text-sm rounded-full border border-emerald-600 bg-emerald-600 text-white hover:bg-emerald-700 transition"
|
||||
(i :class "fa fa-credit-card mr-2" :aria-hidden "true") "Open payment page"))
|
||||
|
||||
(defcomp ~order-detail-filter (&key (info :as string) (list-url :as string) (recheck-url :as string)
|
||||
(defcomp ~shared:orders/detail-filter (&key (info :as string) (list-url :as string) (recheck-url :as string)
|
||||
(csrf :as string) pay)
|
||||
(header :class "mb-6 sm:mb-8 flex flex-col sm:flex-row sm:items-center justify-between gap-3 sm:gap-4"
|
||||
(div :class "space-y-1"
|
||||
@@ -120,43 +120,43 @@
|
||||
(i :class "fa-solid fa-rotate mr-2" :aria-hidden "true") "Re-check status"))
|
||||
pay)))
|
||||
|
||||
(defcomp ~order-detail-header-stack (&key auth orders order)
|
||||
(~header-child-sx :inner
|
||||
(<> auth (~header-child-sx :id "auth-header-child" :inner
|
||||
(<> orders (~header-child-sx :id "orders-header-child" :inner order))))))
|
||||
(defcomp ~shared:orders/detail-header-stack (&key auth orders order)
|
||||
(~shared:layout/header-child-sx :inner
|
||||
(<> auth (~shared:layout/header-child-sx :id "auth-header-child" :inner
|
||||
(<> orders (~shared:layout/header-child-sx :id "orders-header-child" :inner order))))))
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Data-driven order rows (replaces Python loop)
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~order-rows-from-data (&key (orders :as list?) (page :as number) (total-pages :as number)
|
||||
(defcomp ~shared:orders/rows-from-data (&key (orders :as list?) (page :as number) (total-pages :as number)
|
||||
(next-url :as string?))
|
||||
(<>
|
||||
(map (lambda (o)
|
||||
(<>
|
||||
(~order-row-desktop :oid (get o "oid") :created (get o "created")
|
||||
(~shared:orders/row-desktop :oid (get o "oid") :created (get o "created")
|
||||
:desc (get o "desc") :total (get o "total")
|
||||
:pill (get o "pill_desktop") :status (get o "status") :url (get o "url"))
|
||||
(~order-row-mobile :oid (get o "oid") :created (get o "created")
|
||||
(~shared:orders/row-mobile :oid (get o "oid") :created (get o "created")
|
||||
:total (get o "total") :pill (get o "pill_mobile")
|
||||
:status (get o "status") :url (get o "url"))))
|
||||
(or orders (list)))
|
||||
(if next-url
|
||||
(~infinite-scroll :url next-url :page page :total-pages total-pages
|
||||
(~shared:controls/infinite-scroll :url next-url :page page :total-pages total-pages
|
||||
:id-prefix "orders" :colspan 5)
|
||||
(~order-end-row))))
|
||||
(~shared:orders/end-row))))
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Data-driven order items (replaces Python loop)
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~order-items-from-data (&key (items :as list?))
|
||||
(~order-items-panel
|
||||
(defcomp ~shared:orders/items-from-data (&key (items :as list?))
|
||||
(~shared:orders/items-panel
|
||||
:items (<> (map (lambda (item)
|
||||
(let* ((img (if (get item "product_image")
|
||||
(~order-item-image :src (get item "product_image") :alt (or (get item "product_title") "Product image"))
|
||||
(~order-item-no-image))))
|
||||
(~order-item-row
|
||||
(~shared:orders/item-image :src (get item "product_image") :alt (or (get item "product_title") "Product image"))
|
||||
(~shared:orders/item-no-image))))
|
||||
(~shared:orders/item-row
|
||||
:href (get item "href") :img img
|
||||
:title (or (get item "product_title") "Unknown product")
|
||||
:pid (str "Product ID: " (get item "product_id"))
|
||||
@@ -168,10 +168,10 @@
|
||||
;; Data-driven calendar entries (replaces Python loop)
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~order-calendar-from-data (&key (entries :as list?))
|
||||
(~order-calendar-section
|
||||
(defcomp ~shared:orders/calendar-from-data (&key (entries :as list?))
|
||||
(~shared:orders/calendar-section
|
||||
:items (<> (map (lambda (e)
|
||||
(~order-calendar-entry
|
||||
(~shared:orders/calendar-entry
|
||||
:name (get e "name") :pill (get e "pill")
|
||||
:status (get e "status") :date-str (get e "date_str")
|
||||
:cost (get e "cost")))
|
||||
@@ -186,7 +186,7 @@
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
;; Status pill class mapping
|
||||
(defcomp ~order-status-pill-cls (&key (status :as string?))
|
||||
(defcomp ~shared:orders/status-pill-cls (&key (status :as string?))
|
||||
(let* ((sl (lower (or status ""))))
|
||||
(cond
|
||||
((= sl "paid") "border-emerald-300 bg-emerald-50 text-emerald-700")
|
||||
@@ -194,46 +194,46 @@
|
||||
(true "border-stone-300 bg-stone-50 text-stone-700"))))
|
||||
|
||||
;; Single order row pair (desktop + mobile) — takes serialized order data dict
|
||||
(defcomp ~order-row-pair (&key (order :as dict) (detail-url-prefix :as string))
|
||||
(defcomp ~shared:orders/row-pair (&key (order :as dict) (detail-url-prefix :as string))
|
||||
(let* ((status (or (get order "status") "pending"))
|
||||
(pill-base (~order-status-pill-cls :status status))
|
||||
(pill-base (~shared:orders/status-pill-cls :status status))
|
||||
(oid (str "#" (get order "id")))
|
||||
(created (or (get order "created_at_formatted") "\u2014"))
|
||||
(desc (or (get order "description") ""))
|
||||
(total (str (or (get order "currency") "GBP") " " (or (get order "total_formatted") "0.00")))
|
||||
(url (str detail-url-prefix (get order "id") "/")))
|
||||
(<>
|
||||
(~order-row-desktop
|
||||
(~shared:orders/row-desktop
|
||||
:oid oid :created created :desc desc :total total
|
||||
:pill (str "inline-flex items-center rounded-full border px-2 py-0.5 text-[11px] sm:text-xs " pill-base)
|
||||
:status status :url url)
|
||||
(~order-row-mobile
|
||||
(~shared:orders/row-mobile
|
||||
:oid oid :created created :total total
|
||||
:pill (str "inline-flex items-center rounded-full border px-2 py-0.5 text-[11px] " pill-base)
|
||||
:status status :url url))))
|
||||
|
||||
;; Assembled orders list content
|
||||
(defcomp ~orders-list-content (&key (orders :as list) (page :as number) (total-pages :as number)
|
||||
(defcomp ~shared:orders/list-content (&key (orders :as list) (page :as number) (total-pages :as number)
|
||||
(rows-url :as string) (detail-url-prefix :as string))
|
||||
(if (empty? orders)
|
||||
(~order-empty-state)
|
||||
(~order-table
|
||||
(~shared:orders/empty-state)
|
||||
(~shared:orders/table
|
||||
:rows (<>
|
||||
(map (lambda (order)
|
||||
(~order-row-pair :order order :detail-url-prefix detail-url-prefix))
|
||||
(~shared:orders/row-pair :order order :detail-url-prefix detail-url-prefix))
|
||||
orders)
|
||||
(if (< page total-pages)
|
||||
(~infinite-scroll
|
||||
(~shared:controls/infinite-scroll
|
||||
:url (str rows-url "?page=" (inc page))
|
||||
:page page :total-pages total-pages
|
||||
:id-prefix "orders" :colspan 5)
|
||||
(~order-end-row))))))
|
||||
(~shared:orders/end-row))))))
|
||||
|
||||
;; Assembled order detail content — replaces Python _order_main_sx
|
||||
(defcomp ~order-detail-content (&key (order :as dict) (calendar-entries :as list?))
|
||||
(defcomp ~shared:orders/detail-content (&key (order :as dict) (calendar-entries :as list?))
|
||||
(let* ((items (get order "items")))
|
||||
(~order-detail-panel
|
||||
:summary (~order-summary-card
|
||||
(~shared:orders/detail-panel
|
||||
:summary (~shared:cards/order-summary-card
|
||||
:order-id (get order "id")
|
||||
:created-at (get order "created_at_formatted")
|
||||
:description (get order "description")
|
||||
@@ -241,21 +241,21 @@
|
||||
:currency (get order "currency")
|
||||
:total-amount (get order "total_formatted"))
|
||||
:items (when (not (empty? (or items (list))))
|
||||
(~order-items-panel
|
||||
(~shared:orders/items-panel
|
||||
:items (map (lambda (item)
|
||||
(~order-item-row
|
||||
(~shared:orders/item-row
|
||||
:href (get item "product_url")
|
||||
:img (if (get item "product_image")
|
||||
(~order-item-image :src (get item "product_image")
|
||||
(~shared:orders/item-image :src (get item "product_image")
|
||||
:alt (or (get item "product_title") "Product image"))
|
||||
(~order-item-no-image))
|
||||
(~shared:orders/item-no-image))
|
||||
:title (or (get item "product_title") "Unknown product")
|
||||
:pid (str "Product ID: " (get item "product_id"))
|
||||
:qty (str "Qty: " (get item "quantity"))
|
||||
:price (str (or (get item "currency") (get order "currency") "GBP") " " (or (get item "unit_price_formatted") "0.00"))))
|
||||
items)))
|
||||
:calendar (when (not (empty? (or calendar-entries (list))))
|
||||
(~order-calendar-section
|
||||
(~shared:orders/calendar-section
|
||||
:items (map (lambda (e)
|
||||
(let* ((st (or (get e "state") ""))
|
||||
(pill (cond
|
||||
@@ -263,7 +263,7 @@
|
||||
((= st "provisional") "bg-amber-100 text-amber-800")
|
||||
((= st "ordered") "bg-blue-100 text-blue-800")
|
||||
(true "bg-stone-100 text-stone-700"))))
|
||||
(~order-calendar-entry
|
||||
(~shared:orders/calendar-entry
|
||||
:name (get e "name")
|
||||
:pill (str "inline-flex items-center rounded-full px-2 py-0.5 text-[11px] font-medium " pill)
|
||||
:status (upper (slice st 0 1))
|
||||
@@ -272,33 +272,33 @@
|
||||
calendar-entries))))))
|
||||
|
||||
;; Assembled order detail filter — replaces Python _order_filter_sx
|
||||
(defcomp ~order-detail-filter-content (&key (order :as dict) (list-url :as string) (recheck-url :as string)
|
||||
(defcomp ~shared:orders/detail-filter-content (&key (order :as dict) (list-url :as string) (recheck-url :as string)
|
||||
(pay-url :as string) (csrf :as string))
|
||||
(let* ((status (or (get order "status") "pending"))
|
||||
(created (or (get order "created_at_formatted") "\u2014")))
|
||||
(~order-detail-filter
|
||||
(~shared:orders/detail-filter
|
||||
:info (str "Placed " created " \u00b7 Status: " status)
|
||||
:list-url list-url
|
||||
:recheck-url recheck-url
|
||||
:csrf csrf
|
||||
:pay (when (!= status "paid")
|
||||
(~order-pay-btn :url pay-url)))))
|
||||
(~shared:orders/pay-btn :url pay-url)))))
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Checkout return components
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~checkout-return-header (&key (status :as string))
|
||||
(defcomp ~shared:orders/checkout-return-header (&key (status :as string))
|
||||
(header :class "mb-6 sm:mb-8"
|
||||
(h1 :class "text-xl sm:text-2xl md:text-3xl font-semibold tracking-tight" "Payment complete")
|
||||
(p :class "text-xs sm:text-sm text-stone-600"
|
||||
(str "Your checkout session is " status "."))))
|
||||
|
||||
(defcomp ~checkout-return-missing ()
|
||||
(defcomp ~shared:orders/checkout-return-missing ()
|
||||
(div :class "max-w-full px-3 py-3 space-y-4"
|
||||
(p :class "text-sm text-stone-600" "Order not found.")))
|
||||
|
||||
(defcomp ~checkout-return-ticket (&key (name :as string) (pill :as string) (state :as string)
|
||||
(defcomp ~shared:orders/checkout-return-ticket (&key (name :as string) (pill :as string) (state :as string)
|
||||
(type-name :as string?) (date-str :as string) (code :as string?)
|
||||
(price :as string))
|
||||
(li :class "px-4 py-3 flex items-start justify-between text-sm"
|
||||
@@ -310,23 +310,23 @@
|
||||
(when code (div :class "font-mono text-xs text-stone-400" code)))
|
||||
(div :class "ml-4 font-medium" price)))
|
||||
|
||||
(defcomp ~checkout-return-tickets (&key items)
|
||||
(defcomp ~shared:orders/checkout-return-tickets (&key items)
|
||||
(section :class "mt-6 space-y-3"
|
||||
(h2 :class "text-base sm:text-lg font-semibold" "Tickets")
|
||||
(ul :class "divide-y divide-stone-200 rounded-2xl border border-stone-200 bg-white/80" items)))
|
||||
|
||||
(defcomp ~checkout-return-failed (&key (order-id :as string?))
|
||||
(defcomp ~shared:orders/checkout-return-failed (&key (order-id :as string?))
|
||||
(div :class "rounded-lg border border-rose-200 bg-rose-50 p-4 text-sm text-rose-900"
|
||||
(p :class "font-medium" "Payment failed")
|
||||
(p "Please try again or contact support."
|
||||
(when order-id (span " Order #" (str order-id))))))
|
||||
|
||||
(defcomp ~checkout-return-paid ()
|
||||
(defcomp ~shared:orders/checkout-return-paid ()
|
||||
(div :class "rounded-lg border border-emerald-200 bg-emerald-50 p-4 text-sm text-emerald-900"
|
||||
(p :class "font-medium" "Payment successful!")
|
||||
(p "Your order has been confirmed.")))
|
||||
|
||||
(defcomp ~checkout-return-content (&key summary items calendar tickets status-message)
|
||||
(defcomp ~shared:orders/checkout-return-content (&key summary items calendar tickets status-message)
|
||||
(div :class "max-w-full px-3 py-3 space-y-4"
|
||||
status-message summary items calendar tickets))
|
||||
|
||||
@@ -334,15 +334,15 @@
|
||||
;; Checkout error screens
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~checkout-error-header ()
|
||||
(defcomp ~shared:orders/checkout-error-header ()
|
||||
(header :class "mb-6 sm:mb-8"
|
||||
(h1 :class "text-xl sm:text-2xl md:text-3xl font-semibold tracking-tight" "Checkout error")
|
||||
(p :class "text-xs sm:text-sm text-stone-600" "We tried to start your payment with SumUp but hit a problem.")))
|
||||
|
||||
(defcomp ~checkout-error-order-id (&key (oid :as string))
|
||||
(defcomp ~shared:orders/checkout-error-order-id (&key (oid :as string))
|
||||
(p :class "text-xs text-rose-800/80" "Order ID: " (span :class "font-mono" oid)))
|
||||
|
||||
(defcomp ~checkout-error-content (&key (msg :as string) order (back-url :as string))
|
||||
(defcomp ~shared:orders/checkout-error-content (&key (msg :as string) order (back-url :as string))
|
||||
(div :class "max-w-full px-3 py-3 space-y-4"
|
||||
(div :class "rounded-2xl border border-rose-200 bg-rose-50/80 p-4 sm:p-6 text-sm text-rose-900 space-y-2"
|
||||
(p :class "font-medium" "Something went wrong.")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
(defcomp ~base-shell (&key (title :as string) (asset-url :as string) &rest children)
|
||||
(defcomp ~shared:pages/base-shell (&key (title :as string) (asset-url :as string) &rest children)
|
||||
(<>
|
||||
(raw! "<!doctype html>")
|
||||
(html :lang "en"
|
||||
@@ -23,14 +23,14 @@
|
||||
;; <script>__sxResolve("id", "(resolved sx ...)")</script>
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~suspense (&key (id :as string) fallback &rest children)
|
||||
(defcomp ~shared:pages/suspense (&key (id :as string) fallback &rest children)
|
||||
(div :id (str "sx-suspense-" id)
|
||||
:data-suspense id
|
||||
:style "display:contents"
|
||||
(if (not (empty? children)) children fallback)))
|
||||
|
||||
(defcomp ~error-page (&key (title :as string) (message :as string) (image :as string?) (asset-url :as string))
|
||||
(~base-shell :title title :asset-url asset-url
|
||||
(defcomp ~shared:pages/error-page (&key (title :as string) (message :as string) (image :as string?) (asset-url :as string))
|
||||
(~shared:pages/base-shell :title title :asset-url asset-url
|
||||
(div :class "text-center p-8 max-w-lg mx-auto"
|
||||
(div :class "font-bold text-2xl md:text-4xl text-red-500 mb-4"
|
||||
(div message))
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
(defcomp ~relation-attach (&key (create-url :as string) (label :as string?) (icon :as string?))
|
||||
(defcomp ~shared:relations/attach (&key (create-url :as string) (label :as string?) (icon :as string?))
|
||||
(a :href create-url
|
||||
:sx-get create-url
|
||||
:sx-target "#main-panel"
|
||||
@@ -8,7 +8,7 @@
|
||||
(when icon (i :class icon))
|
||||
(span (or label "Add"))))
|
||||
|
||||
(defcomp ~relation-detach (&key (detach-url :as string) (name :as string?))
|
||||
(defcomp ~shared:relations/detach (&key (detach-url :as string) (name :as string?))
|
||||
(button :sx-delete detach-url
|
||||
:sx-confirm (str "Remove " (or name "this item") "?")
|
||||
:class "text-red-500 hover:text-red-700 text-sm p-1 rounded hover:bg-red-50 transition-colors"
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
;; - Pre-rendered meta HTML from callers
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~sx-page-shell (&key (title :as string) (meta-html :as string?) (csrf :as string)
|
||||
(defcomp ~shared:shell/sx-page-shell (&key (title :as string) (meta-html :as string?) (csrf :as string)
|
||||
(sx-css :as string?) (sx-css-classes :as string?)
|
||||
(component-hash :as string?) (component-defs :as string?)
|
||||
(pages-sx :as string?) (page-sx :as string?)
|
||||
|
||||
Reference in New Issue
Block a user