Dropdown works well

This commit is contained in:
2024-10-18 21:00:50 -07:00
parent acaa2a7d1e
commit 2311acb9a2
6 changed files with 241 additions and 111 deletions

File diff suppressed because one or more lines are too long

View File

@@ -1,8 +1,7 @@
(ns auto-ap.ssr.components.inputs (ns auto-ap.ssr.components.inputs
(:require [auto-ap.ssr.components.buttons :as buttons] (:require [auto-ap.ssr.components.tags :as tags]
[auto-ap.ssr.components.tags :as tags]
[auto-ap.ssr.hiccup-helper :as hh] [auto-ap.ssr.hiccup-helper :as hh]
[auto-ap.ssr.hx :as hx] [auto-ap.ssr.hx :as hx :refer [js-fn]]
[auto-ap.ssr.svg :as svg] [auto-ap.ssr.svg :as svg]
[clojure.string :as str] [clojure.string :as str]
[hiccup2.core :as hiccup])) [hiccup2.core :as hiccup]))
@@ -21,7 +20,7 @@
"group-[.has-error]:dark:border-red-500"]) "group-[.has-error]:dark:border-red-500"])
(def default-checkbox-classes (def default-checkbox-classes
"w-4 h-4 bg-gray-100 border-gray-300 rounded text-primary-600 focus:ring-primary-500 dark:focus:ring-primary-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600") "w-4 h-4 bg-gray-100 indeterminate:bg-gray-300 border-gray-300 rounded text-primary-600 focus:ring-primary-500 dark:focus:ring-primary-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600")
(defn select- [params & children] (defn select- [params & children]
(into (into
@@ -38,11 +37,17 @@
(defn checkbox- [params & rest] (defn checkbox- [params & rest]
(if (seq rest) (if (seq rest)
[:label {:class "text-sm text-gray-800 dark:text-gray-300"} [:label {:class "text-sm text-gray-800 dark:text-gray-300 "}
[:input (merge params {:type "checkbox" :class (hh/add-class default-checkbox-classes (:class params ""))})] [:input (merge (dissoc params :indeterminate?)
{:type "checkbox" :class (hh/add-class default-checkbox-classes (:class params ""))}
(when (:indeterminate? params)
{:x-init "$el.indeterminate = true"}))]
[:span.ml-2 [:span.ml-2
rest]] rest]]
[:input (merge params {:type "checkbox" :class (hh/add-class default-checkbox-classes (:class params ""))}) [:input (merge (dissoc params :indeterminate params)
{:type "checkbox" :class (hh/add-class default-checkbox-classes (:class params ""))}
(when (:indeterminate? params)
{:x-init "$el.indeterminate = true"}))
])) ]))
(defn typeahead- [params] (defn typeahead- [params]
@@ -110,9 +115,10 @@
"x-init" "$el.focus(); $watch('search', s => { if($el.value.length > 2) {fetch(baseUrl + s).then(data=>data.json()).then(data => {elements = data; active=-1; tippy.popperInstance.update()}) }})"}] "x-init" "$el.focus(); $watch('search', s => { if($el.value.length > 2) {fetch(baseUrl + s).then(data=>data.json()).then(data => {elements = data; active=-1; tippy.popperInstance.update()}) }})"}]
[:div.dropdown-options {:class "rounded-b-lg overflow-hidden"} [:div.dropdown-options {:class "rounded-b-lg overflow-hidden"}
[:template {:x-for "(element, index) in elements"} [:template {:x-for "(element, index) in elements"}
[:li [:a {:class "px-4 py-2 flex gap-2 items-center outline-0 focus:bg-neutral-100 hover:bg-neutral-100 whitespace-nowrap [&.active]:bg-primary-300 [&.active]:dark:bg-primary-700 text-gray-800 dark:text-gray-100" [:li [:a {:class "px-4 py-2 flex gap-2 items-center outline-0 focus:bg-neutral-100 hover:bg-neutral-100 whitespace-nowrap [&.active]:bg-primary-500 [&.active]:dark:bg-primary-700 text-gray-800 dark:text-gray-100"
:href "#" :href "#"
":class" "active == index ? 'active' : ''" ":class" "active == index ? 'active' : ''"
"@mouseover" "active = index" "@mouseover" "active = index"
"@mouseout" "active = -1" "@mouseout" "active = -1"
"@click.prevent" "value = element; tippy.hide(); $refs.input.focus()" "@click.prevent" "value = element; tippy.hide(); $refs.input.focus()"
@@ -141,20 +147,24 @@
"@keydown.down.prevent" "active ++; active = active >= elements.length - 1 ? elements.length - 1 : active" "@keydown.down.prevent" "active ++; active = active >= elements.length - 1 ? elements.length - 1 : active"
"@keydown.up.prevent" "active --; active = active < 0 ? 0 : active" "@keydown.up.prevent" "active --; active = active < 0 ? 0 : active"
"@keydown.enter.prevent.stop" "if ($data.elements[active]) { if (value.has($data.elements[active].value)) { value.delete($data.elements[active].value) } else {value.add($data.elements[active].value); lookup[$data.elements[active].value] = $data.elements[active].label} } " "@keydown.enter.prevent.stop" "if ($data.elements[active]) { if (value.has($data.elements[active].value)) { value.delete($data.elements[active].value) } else {value.add($data.elements[active].value); lookup[$data.elements[active].value] = $data.elements[active].label} } "
"x-init" " $el.focus(); $watch('search', s => { if($el.value.length > 2) {fetch(baseUrl + s).then(data=>data.json()).then(data => {elements = data; active=-1}) }})"}]] "x-init" " $el.focus(); $watch('search', s => { if($el.value.length > 2) {fetch(baseUrl + s).then(data=>data.json()).then(data => reset_elements(data)) }})"}]]
[:div.dropdown-options {:class "overflow-hidden divide-y divide-gray-200 "} [:div.dropdown-options {:class "overflow-hidden divide-y divide-gray-200 "}
[:template {:x-for "(element, index) in elements"} [:template {:x-for "(element, index) in elements"}
[:li {":style" "index == 0 && 'border: 0 !important;'"} [:li {":style" "index == 0 && 'border: 0 !important;'"}
[:label {:class "p-3 rounded flex gap-2 items-center outline-0 focus:bg-neutral-100 hover:bg-neutral-100 whitespace-nowrap [&.active]:bg-primary-300 [&.active]:dark:bg-primary-700 text-gray-800 dark:text-gray-100 cursor-pointer" [:label {:class "p-3 group rounded flex gap-2 items-center outline-0 focus:bg-neutral-100 hover:bg-neutral-100 whitespace-nowrap [&.active]:bg-primary-300 [&.active]:dark:bg-primary-700 [&.implied]:text-gray-500 text-gray-800 dark:text-gray-100 cursor-pointer"
:href "#" :href "#"
":class" "active == index ? 'active' : ''" ":class" (hx/json {"active" (hx/js-fn "active==index")
"implied" (hx/js-fn "all_selected && index != 0")
} )
"@mouseover" "active = index" "@mouseover" "active = index"
"@mouseout" "active = -1" "@mouseout" "active = -1"
"@click.prevent" "if (value.has(element.value)) { value.delete(element.value) } else {value.add(element.value); lookup[element.value] = element.label}"} "@click.prevent" "toggle(element)"}
(checkbox- {":checked" "value.has(element.value)"} (checkbox- {":checked" "value.has(element.value) || all_selected"
) :class "group-[&.implied]:bg-green-200"
})
#_[:input {:type "checkbox" }] #_[:input {:type "checkbox" }]
[:span {"x-html" "element.label"}]]]] [:span {"x-html" "element.label"}]]]]
[:template {:x-if "elements.length == 0"} [:template {:x-if "elements.length == 0"}
@@ -164,39 +174,79 @@
(defn multi-typeahead-selected-pill- [params] (defn multi-typeahead-selected-pill- [params]
[:div.flex-grow.flex [:div.flex-grow.flex
[:template {:x-if "value.size > 0"} [:template {:x-if "value.size > 0"}
[:a.bg-blue-100.rounded-full.px-3 {:x-tooltip "{content: ()=>$refs.selected_tt.innerHTML, popperOptions: {strategy: 'fixed', modifiers: [{name: 'flip', options: {fallbackPlacements: ['top']}}]}, placement: 'bottom', theme: 'light', allowHTML: true, appendTo: $el}"} [:a.bg-blue-100.rounded-full.px-3
[:span.text-left {:x-ref "source"} [:span.text-left
[:span {"x-text" "value.size"}] [:span {"x-text" "value.has('all') ? 'All' : value.size"}]
" selected"] " selected"]
[:template {:x-ref "selected_tt"}
[:div.flex.flex-wrap.gap-4.w-64.p-4 ]]
[:template {:x-for "v in Array.from(value.values())"}
[:div.bg-green-50.rounded-full.px-3.text-ellipsis.whitespace-nowrap.shrink.overflow-hidden.text-green-800 {:x-text "lookup[v]"}]]]]]]
[:template {:x-if "value.size == 0"} [:template {:x-if "value.size == 0"}
[:span.text-left.text-gray-400 "None selected"]] [:span.text-left.text-gray-400 "None selected"]]
[:div {:class "w-4 h-4 ml-2 inline text-gray-500 self-center rounded-full bg-gray-100 text-gray-500" [:div {:class "w-4 h-4 ml-2 inline text-gray-500 self-center rounded-full bg-gray-100 text-gray-500"
"@click.prevent.stop" "value = new Set([])" "@click.prevent.stop" "value = new Set([]); all_selected=false;"
:x-show "value.size > 0"} :x-show "value.size > 0"}
svg/x]]) svg/x]])
(defn multi-typeahead- [params] (defn multi-typeahead- [params]
[:div.relative {:x-data (hx/json {:baseUrl (if (str/includes? (:url params) "?") [:div.relative {:x-data (doto (hx/json {:baseUrl (if (str/includes? (:url params) "?")
(str (:url params) "&q=") (str (:url params) "&q=")
(str (:url params) "?q=")) (str (:url params) "?q="))
:value (map (fn [v] ((:value-fn params identity) v)) :reset_elements (js-fn "function(e) {
(:value params)) this.elements = [{value: 'all', label:'All'}].concat(e);
:tippy nil this.active = -1
:lookup (into {} }")
(map (fn [v] [((:value-fn params identity) v) :toggle (js-fn "function(e) {
((:content-fn params identity) v)]) if (e.value == 'all') {
(:value params))) if (this.value.size > 0) {
:x-init (str "$watch('value', v => $dispatch('change')); ") this.value = new Set([]);
:search "" this.all_selected = false;
:active -1 } else {
:elements (if ((:value-fn params identity) (:value params)) this.value = new Set(['all']);
[{:value ((:value-fn params identity) (:value params)) :label ((:content-fn params identity) (:value params))}] this.all_selected = true;
[]) }
:x-ref "r"}) }
else {
if (this.all_selected) {
this.value.delete('all')
this.all_selected = false;
}
if (this.value.has(e.value)) {
this.value.delete(e.value)
} else {
this.value.add(e.value); this.lookup[e.value] = e.label
}
}
}")
:all_selected (boolean (= (:value params) :all)),
:value (cond
(= :all (:value params))
["all"]
(sequential? (:value params))
(map (fn [v] ((:value-fn params identity) v))
(:value params))
:else
[])
:tippy nil
:lookup (into {}
(when (sequential? (:value params))
(map (fn [v] [((:value-fn params identity) v)
((:content-fn params identity) v)])
(:value params))))
:x-init (str "$watch('value', v => $dispatch('change')); ")
:search ""
:active -1
:elements (cond-> [{:value "all" :label "All"}]
(sequential? (:value params))
(into (map (fn [v]
{:value ((:value-fn params identity) v)
:label ((:content-fn params identity) v)})
(:value params))))
:x-ref "r"})
println)
;; :x-modelable "value.value" TODO ;; :x-modelable "value.value" TODO
;; :x-model (:x-model params) TODO ;; :x-model (:x-model params) TODO
:x-init "value=new Set(value || []); "} :x-init "value=new Set(value || []); "}
@@ -355,4 +405,4 @@
[:label {:class "inline-flex items-center cursor-pointer"} [:label {:class "inline-flex items-center cursor-pointer"}
[:input (merge {:type "checkbox", :class "sr-only peer"} params)] [:input (merge {:type "checkbox", :class "sr-only peer"} params)]
[:div {:class "relative w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600"}] [:div {:class "relative w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600"}]
[:span {:class "ms-3 text-sm font-medium text-gray-900 dark:text-gray-300"} children]]) [:span {:class "ms-3 text-sm font-medium text-gray-900 dark:text-gray-300"} children]])

View File

@@ -1,15 +1,42 @@
(ns auto-ap.ssr.hx (ns auto-ap.ssr.hx
(:require [cheshire.core :as cheshire] (:require [auto-ap.ssr.hiccup-helper :as hh]
[clojure.string :as str] [cheshire.core :as cheshire]
[auto-ap.ssr.hiccup-helper :as hh])) [cheshire.generate :refer [add-encoder]]
[clojure.string :as str]))
(defn vals [m] (defn vals [m]
(cheshire/generate-string m)) (cheshire/generate-string m))
(deftype jsfn [body name])
(defn jsf [t jq]
(.writeString jq (str "__" (.-name t) "__" (.-body t) "__end__")))
(add-encoder jsfn jsf)
(defn json [m] (defn json [m]
(cheshire/generate-string m)) (let [starting-point (cheshire/generate-string m)]
(if (map? m)
(reduce
(fn [starting-point [k v]]
(if (instance? jsfn v)
(-> (str/replace starting-point (re-pattern (str "(?s)\"__" (.-name v) "__(.*?)__end__\"")) "$1" )
(str/replace "\\n" "\n"))
starting-point))
starting-point
m)
starting-point)))
(defn random-alpha-string []
(let [alpha-chars (concat (map char (range 65 91)) ; A-Z
(map char (range 97 123))) ; a-z
random-char (fn [] (rand-nth alpha-chars))]
(apply str (repeatedly 10 random-char))))
(defn js-fn [s]
(jsfn. s (random-alpha-string)))
(defn trigger-field-change [& {:keys [name (defn trigger-field-change [& {:keys [name
from]}] from]}]

View File

@@ -1,40 +1,42 @@
(ns auto-ap.ssr.ledger.balance-sheet (ns auto-ap.ssr.ledger.balance-sheet
(:require [amazonica.aws.s3 :as s3] (:require
[auto-ap.datomic [amazonica.aws.s3 :as s3]
[auto-ap.datomic
:refer [conn pull-many]] :refer [conn pull-many]]
[auto-ap.graphql.utils :refer [assert-can-see-client]] [auto-ap.graphql.utils :refer [assert-can-see-client]]
[auto-ap.ledger :refer [build-account-lookup]] [auto-ap.ledger :refer [build-account-lookup]]
[auto-ap.ledger.reports :as l-reports] [auto-ap.ledger.reports :as l-reports]
[auto-ap.logging :as alog] [auto-ap.logging :as alog]
[auto-ap.pdf.ledger :refer [table->pdf]] [auto-ap.pdf.ledger :refer [table->pdf]]
[auto-ap.permissions :refer [wrap-must]] [auto-ap.permissions :refer [wrap-must]]
[auto-ap.routes.ledger :as route] [auto-ap.routes.ledger :as route]
[auto-ap.routes.utils [auto-ap.routes.utils
:refer [wrap-client-redirect-unauthenticated]] :refer [wrap-client-redirect-unauthenticated]]
[auto-ap.ssr-routes :as ssr-routes] [auto-ap.ssr-routes :as ssr-routes]
[auto-ap.ssr.components :as com] [auto-ap.ssr.components :as com]
[auto-ap.ssr.form-cursor :as fc] [auto-ap.ssr.form-cursor :as fc]
[auto-ap.ssr.hx :as hx] [auto-ap.ssr.hx :as hx]
[auto-ap.ssr.svg :as svg] [auto-ap.ssr.svg :as svg]
[auto-ap.ssr.ui :refer [base-page]] [auto-ap.ssr.ui :refer [base-page]]
[auto-ap.ssr.utils [auto-ap.ssr.utils
:refer [apply-middleware-to-all-handlers clj-date-schema :refer [apply-middleware-to-all-handlers clj-date-schema
html-response modal-response wrap-form-4xx-2 html-response modal-response unspecified-transformer
wrap-merge-prior-hx wrap-schema-enforce]] wrap-form-4xx-2 wrap-merge-prior-hx wrap-schema-enforce]]
[auto-ap.time :as atime] [auto-ap.time :as atime]
[bidi.bidi :as bidi] [bidi.bidi :as bidi]
[clj-pdf.core :as pdf] [clj-pdf.core :as pdf]
[clj-time.coerce :as coerce] [clj-time.coerce :as coerce]
[clj-time.core :as t] [clj-time.core :as t]
[clojure.java.io :as io] [clojure.java.io :as io]
[clojure.string :as str] [clojure.string :as str]
[config.core :refer [env] :as env] [config.core :refer [env] :as env]
[datomic.api :as dc] [datomic.api :as dc]
[hiccup.util :as hu] [hiccup.util :as hu]
[iol-ion.utils :refer [by]] [iol-ion.utils :refer [by]]
[malli.core :as mc]) [malli.core :as mc])
(:import [java.util UUID] (:import
[org.apache.commons.io.output ByteArrayOutputStream])) [java.util UUID]
[org.apache.commons.io.output ByteArrayOutputStream]))
@@ -65,16 +67,18 @@
(def query-schema (mc/schema (def query-schema (mc/schema
[:maybe [:and [:map [:maybe [:and [:map
[:client {} [:client {:unspecified/value :all}
[:vector {:coerce? true :min 1 } [:or
[:entity-map {:pull [:db/id :client/name]}]]] [:enum :all]
[:date {} [:vector {:coerce? true :min 1 }
[:entity-map {:pull [:db/id :client/name]}]]
]]
[:date {:unspecified/fn atime/local-now}
clj-date-schema] clj-date-schema]
[:comparison-date {:optional true} [:comparison-date {:optional true}
[:maybe clj-date-schema]] [:maybe clj-date-schema]]
[:include-comparison {:optional true :default false} [:include-comparison {:optional true :default false}
[ :boolean {:decode/string {:enter #(if (= % "on") true [ :boolean {:decode/string {:enter #(if (= % "on") true
(boolean %))}}]]] (boolean %))}}]]]
[:fn {:error/message "required" [:fn {:error/message "required"
:error/path [:comparison-date]} :error/path [:comparison-date]}
@@ -86,8 +90,6 @@
(defn cell [{:keys [width investigate-url other-style]} c] (defn cell [{:keys [width investigate-url other-style]} c]
(let [cell-contents (cond (let [cell-contents (cond
@@ -212,7 +214,8 @@
(defn get-report [{ {:keys [date comparison-date include-comparison client] :as qp} :query-params :as request}] (defn get-report [{ {:keys [date comparison-date include-comparison client] :as qp} :query-params :as request}]
(when (and date client) (when (and date client)
(let [client-ids (map :db/id client) (let [client (if (= :all client) (take 5 (:clients request)) client)
client-ids (map :db/id client)
_ (doseq [client-id client-ids] _ (doseq [client-id client-ids]
(assert-can-see-client (:identity request) client-id)) (assert-can-see-client (:identity request) client-id))
@@ -258,7 +261,6 @@
:account-type (:account_type account) :account-type (:account_type account)
:numeric-code (:numeric_code account) :numeric-code (:numeric_code account)
:name (:name account) })) :name (:name account) }))
client-ids))) client-ids)))
) )
args (assoc (:query-params request) args (assoc (:query-params request)
@@ -280,10 +282,18 @@
{:data report {:data report
:report report}))) :report report})))
(defn maybe-trim-clients [request client ]
(if (= :all client)
(cond-> {:client (take 5 (:clients request))}
(> (count (:clients request)) 20)
(assoc :warning "You requested a report with more than 20 clients. This report will only contain the first 20."))
{:client client}))
(defn balance-sheet* [{ {:keys [date comparison-date include-comparison client] } :query-params :as request}] (defn balance-sheet* [{ {:keys [date comparison-date include-comparison client] } :query-params :as request}]
[:div#report [:div#report
(when (and date client) (when (and date client)
(let [{:keys [data report]} (get-report request) (let [{:keys [client warning]} (maybe-trim-clients request client)
{:keys [data report]} (get-report (assoc-in request [:query-params :client] client))
client-count (count (set (map :client-id (:data data)))) ] client-count (count (set (map :client-id (:data data)))) ]
(list (list
[:div.text-2xl.font-bold.text-gray-600 (str "Balance Sheet - " (str/join ", " (map :client/name client))) ] [:div.text-2xl.font-bold.text-gray-600 (str "Balance Sheet - " (str/join ", " (map :client/name client))) ]
@@ -291,7 +301,7 @@
(:include-comparison (:args data)) (into (repeat 13 (* 2 client-count)))) (:include-comparison (:args data)) (into (repeat 13 (* 2 client-count))))
:investigate-url (bidi.bidi/path-for ssr-routes/only-routes ::route/investigate) :investigate-url (bidi.bidi/path-for ssr-routes/only-routes ::route/investigate)
:table report :table report
:warning (:warning report)} ))))]) :warning (not-empty (str/join "\n " (filter not-empty [warning (:warning report)])))} ))))])
(defn form* [request] (defn form* [request]
(let [params (merge (:query-params request) (:form-params request) {})] (let [params (merge (:query-params request) (:form-params request) {})]
@@ -314,8 +324,7 @@
:class "w-64" :class "w-64"
:id "client" :id "client"
:url (bidi/path-for ssr-routes/only-routes :company-search) :url (bidi/path-for ssr-routes/only-routes :company-search)
:value (or (fc/field-value) :value (fc/field-value)
(take 5 (:clients request)))
:value-fn :db/id :value-fn :db/id
:content-fn :client/name}))) :content-fn :client/name})))
(fc/with-field :date (fc/with-field :date
@@ -332,7 +341,6 @@
(fc/with-field :comparison-date (fc/with-field :comparison-date
(com/validated-inline-field {:label "Previous Date" (com/validated-inline-field {:label "Previous Date"
:errors (fc/field-errors)} :errors (fc/field-errors)}
(com/date-input {:placeholder "12/21/2020" (com/date-input {:placeholder "12/21/2020"
:name (fc/field-name) :name (fc/field-name)
:value (some-> (or (fc/field-value) (t/plus (t/now) (t/years -1))) :value (some-> (or (fc/field-value) (t/plus (t/now) (t/years -1)))
@@ -453,6 +461,6 @@
) )
(fn [h] (fn [h]
(-> h (-> h
(wrap-merge-prior-hx) #_(wrap-merge-prior-hx)
(wrap-must {:activity :import :subject :ledger}) (wrap-must {:activity :read :subject :balance-sheet})
(wrap-client-redirect-unauthenticated))))) (wrap-client-redirect-unauthenticated)))))

View File

@@ -359,18 +359,39 @@
(mt2/transformer {:encoders e (mt2/transformer {:encoders e
:decoders e}))) :decoders e})))
(def unspecified-transformer
(mt2/transformer
{:decoders {:map {:compile (fn [x g]
(fn [value]
(let [specified-keys (set (keys value))]
(reduce
(fn [value [k params]]
(cond (and (:unspecified/fn params)
(not (get specified-keys k)))
(assoc value k ((:unspecified/fn params)))
(and (:unspecified/value params)
(not (get specified-keys k)))
(assoc value k (:unspecified/value params))
:else
value
))
value
(m/children x)))))}}}))
(def main-transformer (def main-transformer
(mt2/transformer (mt2/transformer
parse-empty-as-nil
date-transformer date-transformer
(mt2/key-transformer {:encode keyword->str :decode str->keyword}) (mt2/key-transformer {:encode keyword->str :decode str->keyword})
mt2/string-transformer mt2/string-transformer
mt2/json-transformer mt2/json-transformer
parse-empty-as-nil
unspecified-transformer
(mt2/transformer {:name :arbitrary}) (mt2/transformer {:name :arbitrary})
coerce-vector coerce-vector
date-range-transformer date-range-transformer
pull-transformer pull-transformer
mt2/default-value-transformer)) mt2/default-value-transformer
))
(defn strip [s] (defn strip [s]
(cond (and (string? s) (str/blank? s)) (cond (and (string? s) (str/blank? s))
@@ -439,9 +460,9 @@
(and query-schema query-params) (and query-schema query-params)
(assoc :query-params (assoc :query-params
(mc/coerce (mc/coerce
query-schema query-schema
query-params query-params
main-transformer))) main-transformer)))
(catch Exception e (catch Exception e
(alog/warn ::validation-error :error e) (alog/warn ::validation-error :error e)
@@ -638,4 +659,4 @@
(defn wrap-implied-route-param [handler & {:as route-params}] (defn wrap-implied-route-param [handler & {:as route-params}]
(fn [request] (fn [request]
(handler (update-in request [:route-params] merge route-params)))) (handler (update-in request [:route-params] merge route-params))))

View File

@@ -52,6 +52,12 @@
(= [:vendor :edit] [subject activity]) (= [:vendor :edit] [subject activity])
true true
(= [:ledger :read] [subject activity])
true
(= [:balance-sheet :read] [subject activity])
true
:else false) :else false)
(#{:user-role/manager "manager"} role) (#{:user-role/manager "manager"} role)
@@ -77,40 +83,58 @@
(= [:invoice :delete] [subject activity]) (= [:invoice :delete] [subject activity])
true true
(= [:ledger :read] [subject activity])
true
(= [:balance-sheet :read] [subject activity])
true
:else false) :else false)
(#{:user-role/read-only "read-only"} role) (#{:user-role/read-only "read-only"} role)
(cond (cond
(= :ledger-page subject) true (= :ledger-page subject) true
(= [:ledger :read] [subject activity])
true
(= [:balance-sheet :read] [subject activity])
true
:else false) :else false)
(#{:user-role/user "user"} role) (#{:user-role/user "user"} role)
(cond (cond
(#{:invoice-page :payment-page :my-company-page :transaction-page :ledger-page} subject) (#{:invoice-page :payment-page :my-company-page :transaction-page :ledger-page} subject)
true true
(= [:vendor :create] [subject activity]) (= [:vendor :create] [subject activity])
true true
(= [:vendor :edit] [subject activity]) (= [:vendor :edit] [subject activity])
true true
(= [:signature :edit] [subject activity]) (= [:signature :edit] [subject activity])
true true
(= [:invoice :create] [subject activity]) (= [:invoice :create] [subject activity])
true true
(= [:invoice :pay] [subject activity]) (= [:invoice :pay] [subject activity])
true true
(= [:invoice :edit] [subject activity]) (= [:invoice :edit] [subject activity])
true true
(= [:invoice :delete] [subject activity]) (= [:invoice :delete] [subject activity])
true true
(= [:ledger :read] [subject activity])
true
(= [:balance-sheet :read] [subject activity])
true
:else false) :else false)