Move SX construction from Python to .sx defcomps (phases 0-4)

Eliminate Python s-expression string building across account, orders,
federation, and cart services. Visual rendering logic now lives entirely
in .sx defcomp components; Python files contain only data serialization,
header/layout wiring, and thin wrappers that call defcomps.

Phase 0: Shared DRY extraction — auth/orders header defcomps, format-decimal/
pluralize/escape/route-prefix primitives.
Phase 1: Account — dashboard, newsletters, login/device/check-email content.
Phase 2: Orders — order list, detail, filter, checkout return assembled defcomps.
Phase 3: Federation — social nav, post cards, timeline, search, actors,
notifications, compose, profile assembled defcomps.
Phase 4: Cart — overview, page cart items/calendar/tickets/summary, admin,
payments assembled defcomps; orders rendering reuses Phase 2 shared defcomps.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-03 22:36:34 +00:00
parent 03f0929fdf
commit 193578ef88
23 changed files with 1824 additions and 1795 deletions

View File

@@ -39,3 +39,56 @@
(defcomp ~cart-overview-panel (&key cards)
(div :class "max-w-full px-3 py-3 space-y-3"
(div :class "space-y-4" cards)))
(defcomp ~cart-empty ()
(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-6 sm:p-8 text-center"
(~empty-state :icon "fa fa-shopping-cart" :message "Your cart is empty" :cls "text-center"))))
;; Assembled page group card — replaces Python _page_group_card_sx
(defcomp ~cart-page-group-card-from-data (&key grp cart-url-base)
(let* ((post (get grp "post"))
(product-count (or (get grp "product_count") 0))
(calendar-count (or (get grp "calendar_count") 0))
(ticket-count (or (get grp "ticket_count") 0))
(total (or (get grp "total") 0))
(market-place (get grp "market_place"))
(badges (<>
(when (> product-count 0)
(~cart-badge :icon "fa fa-box-open"
:text (str product-count " item" (pluralize product-count))))
(when (> calendar-count 0)
(~cart-badge :icon "fa fa-calendar"
:text (str calendar-count " booking" (pluralize calendar-count))))
(when (> ticket-count 0)
(~cart-badge :icon "fa fa-ticket"
:text (str ticket-count " ticket" (pluralize ticket-count)))))))
(if post
(let* ((slug (or (get post "slug") ""))
(title (or (get post "title") ""))
(feature-image (get post "feature_image"))
(mp-name (if market-place (or (get market-place "name") "") ""))
(display-title (if (!= mp-name "") mp-name title)))
(~cart-group-card
:href (str cart-url-base "/" slug "/")
:img (if feature-image
(~cart-group-card-img :src feature-image :alt title)
(~img-or-placeholder :src nil :size-cls "h-16 w-16 rounded-xl"
:placeholder-icon "fa fa-store text-xl"))
:display-title display-title
:subtitle (when (!= mp-name "")
(~cart-mp-subtitle :title title))
:badges (~cart-badges-wrap :badges badges)
:total (str "\u00a3" (format-decimal total 2))))
(~cart-orphan-card
:badges (~cart-badges-wrap :badges badges)
:total (str "\u00a3" (format-decimal total 2))))))
;; Assembled cart overview content — replaces Python _overview_main_panel_sx
(defcomp ~cart-overview-content (&key page-groups cart-url-base)
(if (empty? page-groups)
(~cart-empty)
(~cart-overview-panel
:cards (map (lambda (grp)
(~cart-page-group-card-from-data :grp grp :cart-url-base cart-url-base))
page-groups))))