Files
integreat/src/clj/auto_ap/ssr/grid_page_helper.clj
2023-07-24 20:55:40 -07:00

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