(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" (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" :sx-preserve true :value (or search "") :placeholder "search" :sx-trigger "input changed delay:300ms" :sx-target "#main-panel" :sx-select (str (or hx-select "#main-panel") ", #search-mobile-wrapper, #search-desktop-wrapper") :sx-get current-local-href :sx-swap "outerHTML" :sx-push-url "true" :sx-headers search-headers-mobile :sx-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 (str search-count))))) (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" (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" :sx-preserve true :value (or search "") :placeholder "search" :sx-trigger "input changed delay:300ms" :sx-target "#main-panel" :sx-select (str (or hx-select "#main-panel") ", #search-mobile-wrapper, #search-desktop-wrapper") :sx-get current-local-href :sx-swap "outerHTML" :sx-push-url "true" :sx-headers search-headers-desktop :sx-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 (str search-count))))) (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" (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" (when filter-summary filter-summary))))) (when action-buttons action-buttons) (div :id "filter-details-mobile" :style "display:contents" (when filter-details filter-details)))) (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) :sx-get url :sx-trigger "intersect once delay:250ms" :sx-swap "outerHTML" :sx-retry "exponential:1000:30000" :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 "sx-indicator js-loading text-center text-xs text-stone-400" (str "loading\u2026 " page " / " total-pages)) (div :class "js-neterr hidden flex h-full items-center justify-center")) (div :class "hidden md:block h-[30vh] js-desktop-sentinel" (div :class "sx-indicator js-loading text-center text-xs text-stone-400" (str "loading\u2026 " page " / " total-pages)) (div :class "js-neterr hidden inset-0 grid place-items-center p-4")))) (tr (td :colspan colspan :class "px-3 py-4 text-center text-xs text-stone-400" "End of results")))) (defcomp ~shared:controls/status-pill (&key (status :as string?) (size :as string?)) (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)))