All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m13s
Move 24 defcomp definitions from Python string constants in components.py to 7 grouped .sexp files under shared/sexp/templates/. Add load_sexp_dir() to jinja_bridge.py for file-based loading. Migrate events and market link-card fragment handlers from render_template to sexp. Delete 9 superseded Jinja HTML fragment templates. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
127 lines
6.5 KiB
Common Lisp
127 lines
6.5 KiB
Common Lisp
(defcomp ~search-mobile (&key current-local-href search search-count hx-select search-headers-mobile)
|
|
(div :id "search-mobile-wrapper"
|
|
:class "flex flex-row gap-2 items-center flex-1 min-w-0 pr-2"
|
|
(input :id "search-mobile"
|
|
:type "text" :name "search" :aria-label "search"
|
|
:class "text-base md:text-sm col-span-5 rounded-md px-3 py-2 mb-2 w-full min-w-0 max-w-full border-2 border-stone-200 placeholder-shown:border-stone-200 [&:not(:placeholder-shown)]:border-yellow-200"
|
|
:hx-preserve true
|
|
:value (or search "")
|
|
:placeholder "search"
|
|
:hx-trigger "input changed delay:300ms"
|
|
:hx-target "#main-panel"
|
|
:hx-select (str (or hx-select "#main-panel") ", #search-mobile-wrapper, #search-desktop-wrapper")
|
|
:hx-get current-local-href
|
|
:hx-swap "outerHTML"
|
|
:hx-push-url "true"
|
|
:hx-headers search-headers-mobile
|
|
:hx-sync "this:replace"
|
|
:autocomplete "off")
|
|
(div :id "search-count-mobile" :aria-label "search count"
|
|
:class (if (not search-count) "text-xl text-red-500" "")
|
|
(when search (raw! (str search-count))))))
|
|
|
|
(defcomp ~search-desktop (&key current-local-href search search-count hx-select search-headers-desktop)
|
|
(div :id "search-desktop-wrapper"
|
|
:class "flex flex-row gap-2 items-center"
|
|
(input :id "search-desktop"
|
|
:type "text" :name "search" :aria-label "search"
|
|
:class "w-full mx-1 my-3 px-3 py-2 text-md rounded-xl border-2 shadow-sm border-white placeholder-shown:border-white [&:not(:placeholder-shown)]:border-yellow-200"
|
|
:hx-preserve true
|
|
:value (or search "")
|
|
:placeholder "search"
|
|
:hx-trigger "input changed delay:300ms"
|
|
:hx-target "#main-panel"
|
|
:hx-select (str (or hx-select "#main-panel") ", #search-mobile-wrapper, #search-desktop-wrapper")
|
|
:hx-get current-local-href
|
|
:hx-swap "outerHTML"
|
|
:hx-push-url "true"
|
|
:hx-headers search-headers-desktop
|
|
:hx-sync "this:replace"
|
|
:autocomplete "off")
|
|
(div :id "search-count-desktop" :aria-label "search count"
|
|
:class (if (not search-count) "text-xl text-red-500" "")
|
|
(when search (raw! (str search-count))))))
|
|
|
|
(defcomp ~mobile-filter (&key filter-summary-html action-buttons-html filter-details-html)
|
|
(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"
|
|
(div
|
|
(div :class "md:hidden mx-2 bg-stone-200 rounded"
|
|
(span :class "flex items-center justify-center text-stone-600 text-lg h-12 w-12 transition-transform group-open/filter:hidden self-start"
|
|
(i :class "fa-solid fa-filter"))
|
|
(span
|
|
(svg :aria-hidden "true" :viewBox "0 0 24 24"
|
|
:class "w-12 h-12 rotate-180 transition-transform group-open/filter:block hidden self-start"
|
|
(path :d "M6 9l6 6 6-6" :fill "currentColor")))))
|
|
(div :id "filter-summary-mobile"
|
|
:class "flex-1 md:hidden grid grid-cols-12 items-center gap-3"
|
|
(div :class "flex flex-col items-start gap-2"
|
|
(raw! filter-summary-html)))))
|
|
(raw! (or action-buttons-html ""))
|
|
(div :id "filter-details-mobile" :style "display:contents"
|
|
(raw! (or filter-details-html "")))))
|
|
|
|
(defcomp ~infinite-scroll (&key url page total-pages id-prefix colspan)
|
|
(if (< page total-pages)
|
|
(raw! (str
|
|
"<tr id=\"" id-prefix "-sentinel-" page "\""
|
|
" hx-get=\"" url "\""
|
|
" hx-trigger=\"intersect once delay:250ms, sentinel:retry\""
|
|
" hx-swap=\"outerHTML\""
|
|
" _=\""
|
|
"init "
|
|
"if not me.dataset.retryMs then set me.dataset.retryMs to 1000 end "
|
|
"on sentinel:retry "
|
|
"remove .hidden from .js-loading in me "
|
|
"add .hidden to .js-neterr in me "
|
|
"set me.style.pointerEvents to 'none' "
|
|
"set me.style.opacity to '0' "
|
|
"trigger htmx:consume on me "
|
|
"call htmx.trigger(me, 'intersect') "
|
|
"end "
|
|
"def backoff() "
|
|
"add .hidden to .js-loading in me "
|
|
"remove .hidden from .js-neterr in me "
|
|
"set myMs to Number(me.dataset.retryMs) "
|
|
"if myMs < 10000 then set me.dataset.retryMs to myMs * 2 end "
|
|
"js setTimeout(() => htmx.trigger(me, 'sentinel:retry'), myMs) "
|
|
"end "
|
|
"on htmx:beforeRequest "
|
|
"set me.style.pointerEvents to 'none' "
|
|
"set me.style.opacity to '0' "
|
|
"end "
|
|
"on htmx:afterSwap set me.dataset.retryMs to 1000 end "
|
|
"on htmx:sendError call backoff() "
|
|
"on htmx:responseError call backoff() "
|
|
"on htmx:timeout call backoff()"
|
|
"\""
|
|
" role=\"status\" aria-live=\"polite\" aria-hidden=\"true\">"
|
|
"<td colspan=\"" colspan "\" class=\"px-3 py-4\">"
|
|
"<div class=\"block md:hidden h-[60vh] js-mobile-sentinel\">"
|
|
"<div class=\"js-loading text-center text-xs text-stone-400\">loading\u2026 " page " / " total-pages "</div>"
|
|
"<div class=\"js-neterr hidden flex h-full items-center justify-center\"></div>"
|
|
"</div>"
|
|
"<div class=\"hidden md:block h-[30vh] js-desktop-sentinel\">"
|
|
"<div class=\"js-loading text-center text-xs text-stone-400\">loading\u2026 " page " / " total-pages "</div>"
|
|
"<div class=\"js-neterr hidden inset-0 grid place-items-center p-4\"></div>"
|
|
"</div>"
|
|
"</td></tr>"))
|
|
(raw! (str
|
|
"<tr><td colspan=\"" colspan "\" class=\"px-3 py-4 text-center text-xs text-stone-400\">End of results</td></tr>"))))
|
|
|
|
(defcomp ~status-pill (&key status size)
|
|
(let* ((s (or status "pending"))
|
|
(lower (lower s))
|
|
(sz (or size "xs"))
|
|
(colours (cond
|
|
(= lower "paid") "border-emerald-300 bg-emerald-50 text-emerald-700"
|
|
(= lower "confirmed") "border-emerald-300 bg-emerald-50 text-emerald-700"
|
|
(= lower "checked_in") "border-blue-300 bg-blue-50 text-blue-700"
|
|
(or (= lower "failed") (= lower "cancelled")) "border-rose-300 bg-rose-50 text-rose-700"
|
|
(= lower "provisional") "border-amber-300 bg-amber-50 text-amber-700"
|
|
(= lower "ordered") "border-blue-300 bg-blue-50 text-blue-700"
|
|
true "border-stone-300 bg-stone-50 text-stone-700")))
|
|
(span :class (str "inline-flex items-center rounded-full border px-2 py-0.5 text-" sz " font-medium " colours)
|
|
s)))
|