ccount modal

This commit is contained in:
2023-10-24 16:59:43 -07:00
parent d7edf0221c
commit f5fd532a31
5 changed files with 167 additions and 180 deletions

View File

@@ -29,8 +29,10 @@
html-response
main-transformer
many-entity
modal-response
ref->enum-schema
ref->select-options
strip
temp-id
wrap-entity
wrap-form-4xx-2
@@ -44,8 +46,8 @@
[:form {"hx-trigger" "change delay:500ms, keyup changed from:.hot-filter delay:1000ms"
"hx-get" (bidi/path-for ssr-routes/only-routes
:admin-account-table)
"hx-target" "#account-table"
"hx-indicator" "#account-table"}
"hx-target" "#entity-table"
"hx-indicator" "#entity-table"}
[:fieldset.space-y-6
(com/field {:label "Name"}
@@ -129,7 +131,7 @@
matching-count]))
(def grid-page
(helper/build {:id "account-table"
(helper/build {:id "entity-table"
:nav (com/admin-aside-nav)
:page-specific-nav filters
:fetch-page fetch-page
@@ -139,16 +141,12 @@
:action-buttons (fn [_]
[(com/button {:hx-get (str (bidi/path-for ssr-routes/only-routes
:admin-account-new-dialog))
:hx-target "#modal-content"
:hx-swap "innerHTML"
:color :primary}
"New Account")])
:row-buttons (fn [_ entity]
[(com/icon-button {:hx-get (str (bidi/path-for ssr-routes/only-routes
:admin-account-edit-dialog
:db/id (:db/id entity)))
:hx-target "#modal-content"
:hx-swap "innerHTML"}
:db/id (:db/id entity)))}
svg/pencil)])
:breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes
:admin)}
@@ -181,31 +179,29 @@
(def table* (partial helper/table* grid-page))
(defn account-save [{:keys [form-params request-method] :as request}]
(let [entity (cond-> form-params
(= :post request-method) (assoc :db/id "new"))
_ (cond (= :post request-method)
(when (seq (dc/q '[:find ?x :in $ ?nc :where [?x :account/numeric-code ?nc]] (dc/db conn) (:account/numeric-code entity)))
(field-validation-error (format "The code %d is already in use." (:account/numeric-code entity))
[:account/numeric-code]
:form form-params))
)
_ (some->> form-params
:account/client-overrides
(group-by :account-client-override/client)
(filter (fn [[_ overrides]]
(> (count overrides) 1)))
(map first)
seq
(#(form-validation-error (format "Client(s) %s have more than one override."
(str/join ", "
(map (fn [client]
(format "'%s'" (pull-attr (dc/db conn)
:client/name
(-> client)))
) %)))
:form form-params))
)
(let [entity (cond-> form-params
(= :post request-method) (assoc :db/id "new"))
_ (cond (= :post request-method)
(when (seq (dc/q '[:find ?x :in $ ?nc :where [?x :account/numeric-code ?nc]] (dc/db conn) (:account/numeric-code entity)))
(field-validation-error (format "The code %d is already in use." (:account/numeric-code entity))
[:account/numeric-code]
:form form-params)))
_ (some->> form-params
:account/client-overrides
(group-by :account-client-override/client)
(filter (fn [[_ overrides]]
(> (count overrides) 1)))
(map first)
seq
(#(form-validation-error (format "Client(s) %s have more than one override."
(str/join ", "
(map (fn [client]
(format "'%s'" (pull-attr (dc/db conn)
:client/name
(-> client)))
) %)))
:form form-params)) ;; TODO shouldnt need to bubble this through. See if we can eliminate the passing of form and last-form.
)
{:keys [tempids]} (audit-transact [[:upsert-entity (cond-> entity
(:account/numeric-code entity) (assoc :account/code (str (:account/numeric-code entity))))]]
(:identity request))
@@ -231,8 +227,10 @@
"account_client_override_id" (:db/id o)})))
(html-response
(row* identity updated-account {:flash? true})
:headers {"hx-trigger" "modalclose"
"hx-retarget" (format "#account-table tr[data-id=\"%d\"]" (:db/id updated-account))})))
:headers (cond-> {"hx-trigger" "modalclose"}
(= :post request-method) (assoc "hx-retarget" "#entity-table tbody"
"hx-reswap" "afterbegin")
(= :put request-method) (assoc "hx-retarget" (format "#entity-table tr[data-id=\"%d\"]" (:db/id updated-account)))))))
;; TODO decide when cursors are used. other cases it's not, some are
(defn client-override* [override]
@@ -377,14 +375,14 @@
:index (count (fc/field-value))
:hx-get (bidi/path-for ssr-routes/only-routes
:admin-account-client-override-new)}
"New override"))))
]
"New override"))))]
[:div
(com/form-errors {:errors (:errors fc/*form-errors*)})
(com/validated-save-button {:errors (seq form-errors)}
"Save account")])]])]))
;; TODO saving new row should att it to the tbody
(defn new-client-override [{ {:keys [index]} :query-params}]
(html-response
(fc/start-form-with-prefix
@@ -398,8 +396,8 @@
[:map
[:db/id {:optional true} [:maybe entity-id]]
[:account/numeric-code {:optional true} [:maybe :int]]
[:account/name [:string {:min 1}]]
[:account/location [:maybe :string]]
[:account/name [:string {:min 1 :decode/string strip}]]
[:account/location {:optional true} [:maybe [:string {:decode/string strip}]]]
[:account/type (ref->enum-schema "account-type")]
[:account/applicability (ref->enum-schema "account-applicability")] ;
[:account/invoice-allowance (ref->enum-schema "allowance")]
@@ -409,17 +407,16 @@
(many-entity {}
[:db/id [:or entity-id temp-id]]
[:account-client-override/client entity-id]
[:account-client-override/name [:string {:min 2}]])]]]))
[:account-client-override/name [:string {:min 2 :decode/string strip}]])]]]))
(defn account-dialog [{:keys [entity form-params form-errors]}]
(html-response (dialog* {:entity entity
(modal-response (dialog* {:entity entity
:form-params (or (when (seq form-params)
form-params)
(when entity
(mc/decode form-schema entity main-transformer))
{})
:form-errors form-errors})
:headers {"hx-trigger" "modalopen"}))
:form-errors form-errors})))

View File

@@ -31,6 +31,7 @@
html-response
main-transformer
many-entity
modal-response
money
percentage
ref->enum-schema
@@ -44,21 +45,14 @@
[bidi.bidi :as bidi]
[clojure.string :as str]
[datomic.api :as dc]
[iol-ion.query :refer [ident]]
[malli.core :as mc]))
;; TODO with dependencies, I really don't like that you have to be ultra specific in what
;; you want to include, and generating the routes and interconnection is weird too.
;; I'm tempted to say to include a full snapshot of the form, and the indicator
;; as to which one to generate.
(defn filters [request]
[:form {"hx-trigger" "change delay:500ms, keyup changed from:.hot-filter delay:1000ms"
"hx-get" (bidi/path-for ssr-routes/only-routes
:admin-transaction-rule-table)
"hx-target" "#transaction-rule-table"
"hx-indicator" "#transaction-rule-table"}
"hx-target" "#entity-table"
"hx-indicator" "#entity-table"}
[:fieldset.space-y-6
(com/field {:label "Vendor"}
@@ -180,7 +174,7 @@
matching-count]))
(def grid-page
(helper/build {:id "transaction-rule-table"
(helper/build {:id "entity-table"
:nav (com/admin-aside-nav)
:page-specific-nav filters
:fetch-page fetch-page
@@ -190,16 +184,12 @@
:action-buttons (fn [request]
[(com/button {:hx-get (str (bidi/path-for ssr-routes/only-routes
:admin-transaction-rule-new-dialog))
:hx-target "#modal-content"
:hx-swap "innerHTML"
:color :primary}
"New Transaction Rule")])
:row-buttons (fn [request entity]
[(com/icon-button {:hx-get (str (bidi/path-for ssr-routes/only-routes
:admin-transaction-rule-edit-dialog
:db/id (:db/id entity)))
:hx-target "#modal-content"
:hx-swap "innerHTML"}
:db/id (:db/id entity)))}
svg/pencil)])
:breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes
:admin)}
@@ -269,6 +259,7 @@
bank-account-id))
(defn transaction-rule-save [{:keys [form-params request-method identity] :as request}]
(clojure.pprint/pprint form-params)
(let [entity (cond-> form-params
(= :post request-method) (assoc :db/id "new")
true (assoc :transaction-rule/note (entity->note form-params)))
@@ -297,13 +288,15 @@
{:keys [tempids]} (audit-transact [[:upsert-entity entity]]
(:identity request))
updated-account (dc/pull (dc/db conn)
updated-rule (dc/pull (dc/db conn)
default-read
(or (get tempids (:db/id entity)) (:db/id entity)))]
(html-response
(row* identity updated-account {:flash? true})
:headers {"hx-trigger" "modalclose"
"hx-retarget" (format "#transaction-rule-table tr[data-id=\"%d\"]" (:db/id updated-account))})))
(row* identity updated-rule {:flash? true})
:headers (cond-> {"hx-trigger" "modalclose"}
(= :post request-method) (assoc "hx-retarget" "#entity-table tbody"
"hx-reswap" "afterbegin")
(= :put request-method) (assoc "hx-retarget" (format "#entity-table tr[data-id=\"%d\"]" (:db/id updated-rule)))))))
@@ -327,81 +320,84 @@
[{:keys [name value client-id x-model]}]
[:div.flex.flex-col
(com/typeahead-2 {:name name
:placeholder "Search..."
:url (str (bidi/path-for ssr-routes/only-routes :account-search) "?client-id=" client-id)
:id name
:x-model x-model
:value value
:content-fn (fn [value]
(:account/name (d-accounts/clientize (dc/pull (dc/db conn) d-accounts/default-read value)
client-id)))})])
:placeholder "Search..."
:url (str (bidi/path-for ssr-routes/only-routes :account-search) "?client-id=" client-id)
:id name
:x-model x-model
:value value
:content-fn (fn [value]
(:account/name (d-accounts/clientize (dc/pull (dc/db conn) d-accounts/default-read value)
client-id)))})])
;; TODO something is making the accountId not change the location for only existing ones?
(defn- transaction-rule-account-row*
[transaction-rule account]
(com/data-grid-row (-> {:x-data (hx/json {:accountId (or (:db/id (fc/field-value (:transaction-rule-account/account account)))
(fc/field-value (:transaction-rule-account/account account)))
:show (boolean (not (fc/field-value (:new? account))))})
:data-key "show"
:x-ref "p"}
hx/alpine-mount-then-appear)
(let [account-name (fc/field-name (:transaction-rule-account/account account))]
(list
(fc/with-field :db/id
(com/hidden {:name (fc/field-name)
:value (fc/field-value)}))
(fc/with-field :transaction-rule-account/account
(com/data-grid-cell
{}
(com/validated-field
{:errors (fc/field-errors)}
[:div {:hx-trigger "changed"
:hx-target "next div"
:hx-vals (format "js:{name: '%s', 'client-id': event.detail.clientId}" account-name)
:hx-get (str (bidi/path-for ssr-routes/only-routes :admin-transaction-rule-account-typeahead))
:x-init "$watch('clientId', cid => $dispatch('changed', $data))"}]
(account-typeahead* {:value (fc/field-value)
:client-id (:db/id (:transaction-rule/client transaction-rule))
:name (fc/field-name)
:x-model "accountId"}))))
(fc/with-field :transaction-rule-account/location
(com/data-grid-cell
{}
(com/validated-field
{:errors (fc/field-errors)
:x-data (hx/json {:location (fc/field-value)})}
[:div {:hx-trigger "changed"
:hx-target "next *"
:hx-vals (format "js:{name: '%s', 'client-id': event.detail.clientId || '', 'account-id': event.detail.accountId || ''}" (fc/field-name) )
:hx-get (bidi/path-for ssr-routes/only-routes :admin-transaction-rule-location-select)
:x-init "$watch('clientId', cid => $dispatch('changed', $data)); $watch('accountId', cid => $dispatch('changed', $data))"}]
(location-select* {:name (fc/field-name)
:account-location (:account/location (cond->> (:transaction-rule-account/account @account)
(nat-int? (:transaction-rule-account/account @account)) (dc/pull (dc/db conn)
'[:account/location])))
:client-locations (:client/locations (:transaction-rule/client transaction-rule))
:hx-model "location"
:value (fc/field-value)}))))
(fc/with-field :transaction-rule-account/percentage
(com/data-grid-cell
{}
(com/validated-field
{:errors (fc/field-errors)}
(com/money-input {:name (fc/field-name)
:class "w-16"
:value (some-> (fc/field-value)
(* 100 )
(long ))}))))))
(com/data-grid-cell {:class "align-top"}
(com/a-icon-button {"@click.prevent.stop" "show=false; setTimeout(() => $refs.p.remove(), 500)"} svg/x))))
(com/data-grid-row
(-> {:x-data (hx/json {:accountId (or (:db/id (fc/field-value (:transaction-rule-account/account account)))
(fc/field-value (:transaction-rule-account/account account)))
:show (boolean (not (fc/field-value (:new? account))))})
:data-key "show"
:x-ref "p"}
hx/alpine-mount-then-appear)
(let [account-name (fc/field-name (:transaction-rule-account/account account))]
(list
(fc/with-field :db/id
(com/hidden {:name (fc/field-name)
:value (fc/field-value)}))
(fc/with-field :transaction-rule-account/account
(com/data-grid-cell
{}
(com/validated-field
{:errors (fc/field-errors)}
[:div {:hx-trigger "changed"
:hx-target "next div"
:hx-vals (format "js:{name: '%s', 'client-id': event.detail.clientId}" account-name)
:hx-get (str (bidi/path-for ssr-routes/only-routes :admin-transaction-rule-account-typeahead))
:x-init "$watch('clientId', cid => $dispatch('changed', $data))"}]
(account-typeahead* {:value (fc/field-value)
:client-id (:db/id (:transaction-rule/client transaction-rule))
:name (fc/field-name)
:x-model "accountId"}))))
(fc/with-field :transaction-rule-account/location
(com/data-grid-cell
{}
(com/validated-field
{:errors (fc/field-errors)
:x-data (hx/json {:location (fc/field-value)})}
[:div {:hx-trigger "changed"
:hx-target "next *"
:hx-vals (format "js:{name: '%s', 'client-id': event.detail.clientId || '', 'account-id': event.detail.accountId || ''}" (fc/field-name) )
:hx-get (bidi/path-for ssr-routes/only-routes :admin-transaction-rule-location-select)
:x-init "$watch('clientId', cid => $dispatch('changed', $data)); $watch('accountId', cid => $dispatch('changed', $data) )"}]
(location-select* {:name (fc/field-name)
:account-location (:account/location (cond->> (:transaction-rule-account/account @account)
(nat-int? (:transaction-rule-account/account @account)) (dc/pull (dc/db conn)
'[:account/location])))
:client-locations (:client/locations (:transaction-rule/client transaction-rule))
:hx-model "location"
:value (fc/field-value)}))))
(fc/with-field :transaction-rule-account/percentage
(com/data-grid-cell
{}
(com/validated-field
{:errors (fc/field-errors)}
(println "FIELD VALUE IS" (fc/field-value) (some-> (fc/field-value)
(* 100 )
(long )))
(com/money-input {:name (fc/field-name)
:class "w-16"
:value (some-> (fc/field-value)
(* 100 )
(long ))}))))))
(com/data-grid-cell {:class "align-top"}
(com/a-icon-button {"@click.prevent.stop" "show=false; setTimeout(() => $refs.p.remove(), 500)"} svg/x))))
;; TODO background jobs and company 1099
(defn dialog* [{:keys [entity form-params form-errors]}]
(fc/start-form form-params form-errors
(com/modal
{:modal-class "max-w-2xl"
:hx-target "this"}
:hx-target "this"}
[:form {:hx-ext "response-targets"
:hx-swap "outerHTML swap:300ms"
@@ -415,7 +411,6 @@
{}
[:div.flex [:div.p-2 "Transaction Rule"]]
[:fieldset {:class "hx-disable"
:hx-disinherit "hx-target" ;; TODO why disinherit
:x-data (hx/json {:clientId (or (:db/id (:transaction-rule/client form-params))
(:transaction-rule/client form-params)
(:db/id (:transaction-rule/client entity)))})}
@@ -542,8 +537,7 @@
(com/data-grid {:headers [(com/data-grid-header {} "Account")
(com/data-grid-header {:class "w-32"} "Location")
(com/data-grid-header {:class "w-16"} "%")
(com/data-grid-header {:class "w-16"})]
:id "transaction-rule-account-table"}
(com/data-grid-header {:class "w-16"})]}
(fc/cursor-map #(transaction-rule-account-row* form-params %))
(com/data-grid-new-row {:colspan 4
:hx-get (bidi/path-for ssr-routes/only-routes
@@ -604,7 +598,8 @@
client-id client-id]
(html-response (account-typeahead* {:name name
:value account
:client-id client-id}))))
:client-id client-id
:x-model "accountId"}))))
(def form-schema (mc/schema
[:map
@@ -627,14 +622,13 @@
[:transaction-rule-account/percentage percentage])]]))
(defn transaction-dialog [{:keys [entity form-params form-errors]}]
(html-response (dialog* {:entity entity
(modal-response (dialog* {:entity entity
:form-params (or (when (seq form-params)
form-params)
(when entity
(mc/decode form-schema entity main-transformer)) ;; TODO coerce into form params
(mc/decode form-schema entity main-transformer))
{})
:form-errors form-errors})
:headers {"hx-trigger" "modalopen"}))
:form-errors form-errors})))