Migrate 6 reactive demo handlers from Python f-strings to SX defhandlers
Moved flash-sale, settle-data, search-products/events/posts, and catalog endpoints from bp/pages/routes.py into sx/sx/handlers/reactive-api.sx. routes.py now contains only the SSE endpoint (async generators need Python). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
166
sx/sx/handlers/reactive-api.sx
Normal file
166
sx/sx/handlers/reactive-api.sx
Normal file
@@ -0,0 +1,166 @@
|
||||
;; ==========================================================================
|
||||
;; Reactive API endpoints — live demos for marsh/reactive-islands pages
|
||||
;;
|
||||
;; Migrated from bp/pages/routes.py (Python f-string handlers).
|
||||
;; ==========================================================================
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; Data constants
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
(define flash-sale-prices
|
||||
(list 14.99 9.99 24.99 12.49 7.99 29.99 4.99 16.50))
|
||||
|
||||
(define settle-items
|
||||
(list "Widget" "Gadget" "Sprocket" "Gizmo" "Doohickey"))
|
||||
|
||||
(define search-product-items
|
||||
(list "Artisan Widget" "Premium Gadget" "Handcrafted Sprocket"
|
||||
"Bespoke Gizmo" "Organic Doohickey"))
|
||||
|
||||
(define search-event-items
|
||||
(list "Summer Workshop" "Craft Fair" "Open Studio"
|
||||
"Artist Talk" "Gallery Opening"))
|
||||
|
||||
(define search-post-items
|
||||
(list "On Craft and Code" "The SX Manifesto" "Islands and Lakes"
|
||||
"Reactive Marshes" "Self-Hosting Spec"))
|
||||
|
||||
(define catalog-items
|
||||
(list
|
||||
(dict "name" "Artisan Widget" "price" "19.99" "desc" "Hand-crafted with care")
|
||||
(dict "name" "Premium Gadget" "price" "34.50" "desc" "Top-of-the-line quality")
|
||||
(dict "name" "Vintage Sprocket" "price" "12.99" "desc" "Classic design")
|
||||
(dict "name" "Custom Gizmo" "price" "27.00" "desc" "Made to order")))
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; Helper: search results panel
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~reactive-api/search-results (&key label color items query)
|
||||
(let ((matches (if (= query "")
|
||||
items
|
||||
(filter (fn (item) (string-contains? (lower item) (lower query)))
|
||||
items))))
|
||||
(let ((shown (slice matches 0 3)))
|
||||
(div :class "space-y-1"
|
||||
(p :class (str "text-xs font-semibold uppercase " color) label)
|
||||
(ul :class "list-disc pl-4"
|
||||
(map (fn (m) (li :class "text-sm text-stone-600" m)) shown))
|
||||
(p :class "text-xs text-stone-400"
|
||||
(str (length matches) " result(s)"))))))
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; Flash Sale
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
(defhandler reactive-flash-sale
|
||||
:path "/sx/(geography.(reactive.(api.flash-sale)))"
|
||||
:method :get
|
||||
:returns "element"
|
||||
(&key)
|
||||
(let ((idx (random-int 0 (- (length flash-sale-prices) 1)))
|
||||
(now (helper "now" "%H:%M:%S")))
|
||||
(let ((price (nth flash-sale-prices idx)))
|
||||
(<>
|
||||
(div :class "space-y-2"
|
||||
(p :class "text-sm text-emerald-600 font-medium"
|
||||
(str "⚡ Flash sale! Price: $" price))
|
||||
(p :class "text-xs text-stone-400" (str "at " now)))
|
||||
(script :type "text/sx" :data-init ""
|
||||
(str "(reset! (use-store \"demo-price\") " price ")"))))))
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; Settle Data
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
(defhandler reactive-settle-data
|
||||
:path "/sx/(geography.(reactive.(api.settle-data)))"
|
||||
:method :get
|
||||
:returns "element"
|
||||
(&key)
|
||||
(let ((idx (random-int 0 (- (length settle-items) 1)))
|
||||
(now (helper "now" "%H:%M:%S")))
|
||||
(let ((item (nth settle-items idx)))
|
||||
(div :class "space-y-1"
|
||||
(p :class "text-sm font-medium text-stone-700" (str "Fetched: " item))
|
||||
(p :class "text-xs text-stone-400" (str "at " now))))))
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; Search Products
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
(defhandler reactive-search-products
|
||||
:path "/sx/(geography.(reactive.(api.search-products)))"
|
||||
:method :get
|
||||
:returns "element"
|
||||
(&key)
|
||||
(let ((q (helper "request-arg" "q" "")))
|
||||
(~reactive-api/search-results
|
||||
:label "Products" :color "text-violet-600"
|
||||
:items search-product-items :query q)))
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; Search Events
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
(defhandler reactive-search-events
|
||||
:path "/sx/(geography.(reactive.(api.search-events)))"
|
||||
:method :get
|
||||
:returns "element"
|
||||
(&key)
|
||||
(let ((q (helper "request-arg" "q" "")))
|
||||
(~reactive-api/search-results
|
||||
:label "Events" :color "text-emerald-600"
|
||||
:items search-event-items :query q)))
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; Search Posts
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
(defhandler reactive-search-posts
|
||||
:path "/sx/(geography.(reactive.(api.search-posts)))"
|
||||
:method :get
|
||||
:returns "element"
|
||||
(&key)
|
||||
(let ((q (helper "request-arg" "q" "")))
|
||||
(~reactive-api/search-results
|
||||
:label "Posts" :color "text-amber-600"
|
||||
:items search-post-items :query q)))
|
||||
|
||||
|
||||
;; --------------------------------------------------------------------------
|
||||
;; Catalog
|
||||
;; --------------------------------------------------------------------------
|
||||
|
||||
(defhandler reactive-catalog
|
||||
:path "/sx/(geography.(reactive.(api.catalog)))"
|
||||
:method :get
|
||||
:returns "element"
|
||||
(&key)
|
||||
(let ((now (helper "now" "%H:%M:%S"))
|
||||
(idx (random-int 0 3)))
|
||||
;; Rotate items by a random offset to simulate shuffle
|
||||
(let ((rotated (append (slice catalog-items idx (length catalog-items))
|
||||
(slice catalog-items 0 idx))))
|
||||
(let ((items-sx
|
||||
(str "(list "
|
||||
(reduce (fn (acc item)
|
||||
(str acc
|
||||
"(dict \"name\" \"" (get item "name")
|
||||
"\" \"price\" \"" (get item "price")
|
||||
"\" \"desc\" \"" (get item "desc") "\") "))
|
||||
"" rotated)
|
||||
")")))
|
||||
(<>
|
||||
(p :class "text-sm text-emerald-600 font-medium"
|
||||
(str "Catalog loaded: " (length rotated) " items (shuffled at " now ")"))
|
||||
(script :type "text/sx" :data-init ""
|
||||
(str "(reset! (use-store \"catalog-items\") " items-sx ")")))))))
|
||||
Reference in New Issue
Block a user