187 lines
8.8 KiB
Clojure
187 lines
8.8 KiB
Clojure
(ns auto-ap.ssr.grid-page-helper
|
|
(:require
|
|
[auto-ap.ssr.components :as com]
|
|
[auto-ap.ssr.ui :refer [base-page]]
|
|
[auto-ap.ssr.utils :refer [html-response]]
|
|
[hiccup2.core :as hiccup]
|
|
[bidi.bidi :as bidi]
|
|
[auto-ap.ssr-routes :as ssr-routes]
|
|
[cemerick.url :as url]
|
|
[clojure.string :as str]
|
|
[auto-ap.ssr.svg :as svg]))
|
|
|
|
(defn row* [gridspec user entity {:keys [flash? delete-after-settle?] :as options}]
|
|
(let [cells (mapv (fn [header]
|
|
(com/data-grid-cell {:class (if-let [show-starting (:show-starting header)]
|
|
(format "hidden %s:table-cell" show-starting)
|
|
(:class header))}
|
|
((:render header) entity)))
|
|
(:headers gridspec))
|
|
cells (conj cells (com/data-grid-right-stack-cell {}
|
|
(into [:form
|
|
[:input {:type :hidden :name "id" :value ((:id-fn gridspec) entity)}]]
|
|
((:row-buttons gridspec) user entity))))]
|
|
(apply com/data-grid-row
|
|
{:class (when flash?
|
|
"live-added")
|
|
"_" (hiccup/raw (when delete-after-settle?
|
|
" on htmx:afterSettle wait 400ms then remove me"))
|
|
|
|
:data-id ((:id-fn gridspec) entity)}
|
|
cells)))
|
|
|
|
(defn sort-icon [sort key]
|
|
(->> sort
|
|
(filter (comp #(= key %) :sort-key))
|
|
first
|
|
:sort-icon))
|
|
|
|
(defn sort-by-list [sort]
|
|
(if (seq sort)
|
|
(into
|
|
[:div.flex.gap-2.items-center
|
|
"sorted by"
|
|
|
|
]
|
|
(for [{:keys [name sort-icon ]} sort]
|
|
[:div.py-1.px-3.text-sm.rounded.bg-gray-100.dark:bg-gray-600.flex.items-center.gap-2.relative name [:div.h-4.w-4.mr-3 sort-icon]
|
|
[:div {:class "absolute inline-flex items-center justify-center w-6 h-6 text-xs font-bold text-white hover:scale-110 transition-all duration-300 bg-gray-400 border-2 border-white rounded-full -top-2 -right-2 dark:border-gray-900"}
|
|
[:div.h-4.w-4 svg/x]
|
|
]]
|
|
))
|
|
"default sort"))
|
|
|
|
(defn table* [grid-spec user {:keys [start per-page client flash-id sort request] :as params}]
|
|
(let [start (or start 0)
|
|
per-page (or per-page 30)
|
|
[entities total] ((:fetch-page grid-spec)
|
|
user
|
|
{:start start
|
|
:per-page per-page
|
|
:client-id (:db/id client)
|
|
:sort sort
|
|
:request request})]
|
|
(com/data-grid-card {:id (:id grid-spec)
|
|
:title (:title grid-spec)
|
|
:route (:route grid-spec)
|
|
:start start
|
|
:per-page per-page
|
|
:total total
|
|
:subtitle [:div.flex.items-center.gap-2
|
|
[:span (format "Total %s: %d, " (:entity-name grid-spec) total)]
|
|
(sort-by-list sort)]
|
|
:action-buttons ((:action-buttons grid-spec) user params)
|
|
:rows (for [entity entities]
|
|
(row* grid-spec user entity {:flash? (= flash-id ((:id-fn grid-spec) entity))}))
|
|
:thead-params {:hx-get (bidi/path-for ssr-routes/only-routes
|
|
(:route grid-spec))
|
|
:hx-target (str "#" (:id grid-spec))
|
|
:hx-trigger "sorted once"
|
|
:hx-vals "js:{\"toggle-sort\": event.detail.key || \"\"}"}
|
|
:headers
|
|
(conj
|
|
(mapv
|
|
(fn [h]
|
|
(if (:sort-key h)
|
|
(com/data-grid-sort-header {:class (if-let [show-starting (:show-starting h)]
|
|
(format "hidden %s:table-cell" show-starting)
|
|
(:class h))
|
|
:sort-key (:sort-key h)}
|
|
|
|
[:div.flex.gap-4.items-center
|
|
(:name h)
|
|
[:div.h-6.w-6.text-gray-400.dark:text-gray-500 (sort-icon sort (:sort-key h))]])
|
|
(com/data-grid-header {:class (if-let [show-starting (:show-starting h)]
|
|
(format "hidden %s:table-cell" show-starting)
|
|
(:class h))
|
|
:sort-key (:sort-key h)}
|
|
(:name h))
|
|
|
|
))
|
|
(:headers grid-spec))
|
|
(com/data-grid-header {}))})))
|
|
|
|
|
|
|
|
(defn parse-sort [grid-spec q]
|
|
(if (not-empty q)
|
|
(into []
|
|
(map (fn [k]
|
|
(let [[k v] (str/split k #":")]
|
|
{:sort-key (str k)
|
|
:asc (boolean (= "asc" v))
|
|
:name (:name (first (filter #(= (str k) (:sort-key %)) (:headers grid-spec))))
|
|
:sort-icon (if (= (boolean (= "asc" v)) true)
|
|
svg/sort-down
|
|
svg/sort-up)}))
|
|
(str/split q #",")))
|
|
[]))
|
|
|
|
(defn toggle-sort [grid-spec q k]
|
|
(if ((set (map :sort-key q)) k)
|
|
(mapv
|
|
(fn [s]
|
|
(if (= (:sort-key s)
|
|
k)
|
|
(-> s
|
|
(update :asc
|
|
#(boolean (not %)))
|
|
(update :sort-icon (fn [x]
|
|
(if (= x svg/sort-down)
|
|
svg/sort-up
|
|
svg/sort-down))))
|
|
s))
|
|
q)
|
|
(conj q {:sort-key k
|
|
:asc true
|
|
:name (:name (first (filter #(= (str k) (:sort-key %)) (:headers grid-spec))))
|
|
:sort-icon svg/sort-down})))
|
|
|
|
(defn sort->query [s]
|
|
(str/join "," (map (fn [k] (format "%s:%s" (:sort-key k) (if (= true (:asc k))
|
|
"asc"
|
|
"desc")))
|
|
s)))
|
|
|
|
(defn params->query-string [q]
|
|
(-> q
|
|
(dissoc :client :session)
|
|
(update :sort sort->query)
|
|
(url/map->query)))
|
|
|
|
(defn extract-params [grid-spec {:keys [query-params hx-query-params identity session] :as request}]
|
|
(let [{hx-start "start" hx-per-page "per-page" hx-sort "sort" } hx-query-params
|
|
{q-start "start" q-per-page "per-page" q-sort "sort" q-toggle-sort "toggle-sort"} query-params]
|
|
(cond-> {}
|
|
hx-start (assoc :start (some-> hx-start not-empty (Long/parseLong )))
|
|
q-start (assoc :start (some-> q-start not-empty (Long/parseLong )))
|
|
hx-per-page (assoc :per-page (some-> hx-per-page not-empty (Long/parseLong )))
|
|
q-per-page (assoc :per-page (some-> q-per-page not-empty (Long/parseLong )))
|
|
hx-sort (assoc :sort (parse-sort grid-spec hx-sort))
|
|
q-sort (assoc :sort (parse-sort grid-spec q-sort))
|
|
(not-empty q-toggle-sort) (update :sort #(toggle-sort grid-spec % q-toggle-sort) )
|
|
(:session request) (assoc :session (:session request))
|
|
(:client (:session request)) (assoc :client (:client (:session request))))))
|
|
|
|
(defn table [grid-spec {:keys [query-params hx-query-params identity session] :as request}]
|
|
(let [params (extract-params grid-spec request)
|
|
query-string (params->query-string params)]
|
|
(html-response (table*
|
|
grid-spec
|
|
identity
|
|
params
|
|
)
|
|
:headers {"hx-push-url" (str "?" query-string)})))
|
|
|
|
(defn page [grid-spec {:keys [identity] :as request}]
|
|
(base-page
|
|
request
|
|
(com/page {:nav (:nav grid-spec)
|
|
:active-client (:client (:session request))
|
|
:identity (:identity request)}
|
|
(apply com/breadcrumbs {} (:breadcrumbs grid-spec))
|
|
(table* grid-spec
|
|
identity
|
|
(extract-params grid-spec request)))
|
|
(:title grid-spec)))
|