(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)))
