user progress

This commit is contained in:
2022-07-22 09:44:19 -07:00
parent 3c11891c45
commit 7f5a2ea353
10 changed files with 233 additions and 208 deletions

View File

@@ -234,7 +234,7 @@
:user :user
{:fields {:id {:type :id} {:fields {:id {:type :id}
:name {:type 'String} :name {:type 'String}
:role {:type 'String} :role {:type :role}
:clients {:type '(list :client)}}} :clients {:type '(list :client)}}}
:account_client_override :account_client_override
@@ -415,7 +415,7 @@
:edit_user :edit_user
{:fields {:id {:type :id} {:fields {:id {:type :id}
:name {:type 'String} :name {:type 'String}
:role {:type 'String} :role {:type :role}
:clients {:type '(list String)}}} :clients {:type '(list String)}}}
:add_contact :add_contact
@@ -520,6 +520,11 @@
:applicability {:values [{:enum-value :global} :applicability {:values [{:enum-value :global}
{:enum-value :optional} {:enum-value :optional}
{:enum-value :customized}]} {:enum-value :customized}]}
:role {:values [{:enum-value :none}
{:enum-value :user}
{:enum-value :manager}
{:enum-value :power_user}
{:enum-value :admin}]}
:account_type {:values [{:enum-value :dividend} :account_type {:values [{:enum-value :dividend}
{:enum-value :expense} {:enum-value :expense}
{:enum-value :asset} {:enum-value :asset}

View File

@@ -1,59 +1,49 @@
(ns auto-ap.graphql.transaction-rules (ns auto-ap.graphql.transaction-rules
(:require [auto-ap.datomic (:require
:refer [auto-ap.datomic
[audit-transact merge-query remove-nils replace-nils-with-retract uri conn]] :refer [audit-transact
[auto-ap.datomic.transaction-rules :as tr] conn
[auto-ap.datomic.transactions :as d-transactions] merge-query
[auto-ap.graphql.utils remove-nils
:refer replace-nils-with-retract]]
[->graphql [auto-ap.datomic.transaction-rules :as tr]
<-graphql [auto-ap.datomic.transactions :as d-transactions]
assert-admin [auto-ap.graphql.utils
ident->enum-f :refer [->graphql
limited-clients <-graphql
result->page assert-admin
snake->kebab]] ident->enum-f
[auto-ap.rule-matching :as rm] limited-clients
[auto-ap.utils :refer [dollars=]] result->page
[clj-time.coerce :as coerce] snake->kebab]]
[clojure.set :as set] [auto-ap.rule-matching :as rm]
[clojure.string :as str] [auto-ap.utils :refer [dollars=]]
[clojure.tools.logging :as log] [clj-time.coerce :as c]
[datomic.api :as d] [clojure.set :as set]
[clj-time.coerce :as c]) [clojure.string :as str]
(:import java.time.temporal.ChronoField)) [datomic.api :as d]))
(defn get-transaction-rule-page [context args value] (defn get-transaction-rule-page [context args _]
(let [args (assoc args :id (:id context)) (let [args (assoc args :id (:id context))
[journal-entries journal-entries-count] (tr/get-graphql (<-graphql args))] [journal-entries journal-entries-count] (tr/get-graphql (<-graphql args))]
(result->page (->> journal-entries (result->page (->> journal-entries
(map (ident->enum-f :transaction-rule/transaction-approval-status))) (map (ident->enum-f :transaction-rule/transaction-approval-status)))
journal-entries-count :transaction_rules args))) journal-entries-count :transaction_rules args)))
(defn get-transaction-rule-matches [context args value] (defn get-transaction-rule-matches [context args _]
(if (= "admin" (:user/role (:id context))) (if (= "admin" (:user/role (:id context)))
(let [all-rules (tr/get-all) (let [all-rules (tr/get-all)
transaction (update (d-transactions/get-by-id (:transaction_id args)) :transaction/date coerce/to-date)] transaction (update (d-transactions/get-by-id (:transaction_id args)) :transaction/date c/to-date)]
(map ->graphql (rm/get-matching-rules transaction all-rules))) (map ->graphql (rm/get-matching-rules transaction all-rules)))
nil)) nil))
(defn deleted-accounts [transaction accounts]
(let [current-accounts (:transaction-rule/accounts transaction)
specified-ids (->> accounts
(map :id)
set)
existing-ids (->> current-accounts
(map :db/id)
set)]
(set/difference existing-ids specified-ids)))
(defn transaction-rule-account->entity [{:keys [id account_id percentage location]}] (defn transaction-rule-account->entity [{:keys [id account_id percentage location]}]
(remove-nils #:transaction-rule-account {:percentage percentage (remove-nils #:transaction-rule-account {:percentage percentage
:db/id id :db/id id
:account account_id :account account_id
:location location})) :location location}))
(defn delete-transaction-rule [context {:keys [transaction_rule_id ]} value] (defn delete-transaction-rule [context {:keys [transaction_rule_id ]} _]
(assert-admin (:id context)) (assert-admin (:id context))
(let [existing-transaction-rule (tr/get-by-id transaction_rule_id)] (let [existing-transaction-rule (tr/get-by-id transaction_rule_id)]
(when-not (:transaction-rule/description existing-transaction-rule) (when-not (:transaction-rule/description existing-transaction-rule)
@@ -62,10 +52,9 @@
(audit-transact [[:db/retractEntity transaction_rule_id]] (:id context)) (audit-transact [[:db/retractEntity transaction_rule_id]] (:id context))
transaction_rule_id)) transaction_rule_id))
(defn upsert-transaction-rule [context {{:keys [id description yodlee_merchant_id note client_id bank_account_id amount_lte amount_gte vendor_id accounts transaction_approval_status dom_gte dom_lte]} :transaction_rule :as z} value] (defn upsert-transaction-rule [context {{:keys [id description yodlee_merchant_id note client_id bank_account_id amount_lte amount_gte vendor_id accounts transaction_approval_status dom_gte dom_lte]} :transaction_rule} _]
(assert-admin (:id context)) (assert-admin (:id context))
(let [existing-transaction (tr/get-by-id id) (let [existing-transaction (tr/get-by-id id)
deleted (deleted-accounts existing-transaction accounts)
account-total (reduce + 0 (map (fn [x] (:percentage x)) accounts)) account-total (reduce + 0 (map (fn [x] (:percentage x)) accounts))
_ (when-not (dollars= 1.0 account-total) _ (when-not (dollars= 1.0 account-total)
(let [error (str "Account total (" account-total ") does not reach 100%")] (let [error (str "Account total (" account-total ") does not reach 100%")]
@@ -75,7 +64,7 @@
(let [error (str "You must provide a description or a yodlee merchant")] (let [error (str "You must provide a description or a yodlee merchant")]
(throw (ex-info error {:validation-error error})))) (throw (ex-info error {:validation-error error}))))
_ (doseq [a accounts _ (doseq [a accounts
:let [{:keys [:account/location :account/name] :as account} (d/entity (d/db conn) (:account_id a)) :let [{:keys [:account/location :account/name]} (d/entity (d/db conn) (:account_id a))
client (d/entity (d/db conn) client_id) client (d/entity (d/db conn) client_id)
]] ]]
(when (and location (not= location (:location a))) (when (and location (not= location (:location a)))
@@ -120,7 +109,7 @@
(defn tr [z x] (defn tr [z x]
(re-find (re-pattern z) x)) (re-find (re-pattern z) x))
(defn -test-transaction-rule [id {:keys [:transaction-rule/description :transaction-rule/note :transaction-rule/client :transaction-rule/bank-account :transaction-rule/amount-lte :transaction-rule/amount-gte :transaction-rule/dom-lte :transaction-rule/dom-gte :transaction-rule/yodlee-merchant]} include-coded? count] (defn -test-transaction-rule [id {:keys [:transaction-rule/description :transaction-rule/client :transaction-rule/bank-account :transaction-rule/amount-lte :transaction-rule/amount-gte :transaction-rule/dom-lte :transaction-rule/dom-gte :transaction-rule/yodlee-merchant]} include-coded? count]
(->> (->>
(d/query (d/query
(cond-> {:query {:find ['(pull ?e [* {:transaction/client [:client/name] (cond-> {:query {:find ['(pull ?e [* {:transaction/client [:client/name]
@@ -204,7 +193,7 @@
(map ->graphql)) (map ->graphql))
conj []))) conj [])))
(defn test-transaction-rule [{:keys [id]} {{:keys [description note client_id bank_account_id amount_lte amount_gte dom_lte dom_gte yodlee_merchant_id]} :transaction_rule :as z} value] (defn test-transaction-rule [{:keys [id]} {{:keys [description client_id bank_account_id amount_lte amount_gte dom_lte dom_gte yodlee_merchant_id]} :transaction_rule} _]
(assert-admin id) (assert-admin id)
(-test-transaction-rule id #:transaction-rule {:description description (-test-transaction-rule id #:transaction-rule {:description description
:client (when client_id {:db/id client_id}) :client (when client_id {:db/id client_id})
@@ -217,6 +206,6 @@
true 15)) true 15))
(defn run-transaction-rule [{:keys [id]} {:keys [transaction_rule_id count]} value] (defn run-transaction-rule [{:keys [id]} {:keys [transaction_rule_id count]} _]
(assert-admin id) (assert-admin id)
(-test-transaction-rule id (tr/get-by-id transaction_rule_id) false count)) (-test-transaction-rule id (tr/get-by-id transaction_rule_id) false count))

View File

@@ -3,11 +3,11 @@
[auto-ap.datomic.users :as d-users] [auto-ap.datomic.users :as d-users]
[auto-ap.graphql.utils :refer [->graphql assert-admin]])) [auto-ap.graphql.utils :refer [->graphql assert-admin]]))
(def role->datomic-role {":none" :user-role/none (def role->datomic-role {:none :user-role/none
":admin" :user-role/admin :admin :user-role/admin
":power_user" :user-role/power-user :power_user :user-role/power-user
":manager" :user-role/manager :manager :user-role/manager
":user" :user-role/user}) :user :user-role/user})
(defn edit-user [context {:keys [edit_user] :as args} value] (defn edit-user [context {:keys [edit_user] :as args} value]
(assert-admin (:id context)) (assert-admin (:id context))

View File

@@ -41,6 +41,11 @@
:complete-listener complete-listener}) :complete-listener complete-listener})
(assoc-in [::status/status form] nil)))) (assoc-in [::status/status form] nil))))
(re-frame/reg-event-db
::start-form
(fn [db [_ id data]]
(start-form db id data)))
(defn triggers-saved [form data-key] (defn triggers-saved [form data-key]
(i/->interceptor (i/->interceptor
:id :triggers-saved :id :triggers-saved
@@ -81,6 +86,11 @@
db db
(partition 2 path-pairs)))) (partition 2 path-pairs))))
(re-frame/reg-event-db
::reset
(fn [db [_ form v]]
(assoc-in db [::forms form :data] v)))
(re-frame/reg-event-db (re-frame/reg-event-db
::visited ::visited
(fn [db [_ form & paths]] (fn [db [_ form & paths]]
@@ -90,7 +100,12 @@
(re-frame/reg-event-db (re-frame/reg-event-db
::check-problems ::check-problems
(fn [db [_ form schema]] (fn [db [_ form schema]]
(println "Checking problems") (println "Checking problems"
form
(get-in db [::forms form :data])
(keys (get-in db [::forms]))
schema
(m/explain schema (get-in db [::forms form :data])))
(assoc-in db [::forms form :problems] (assoc-in db [::forms form :problems]
(when schema (m/explain schema (get-in db [::forms form :data])))))) (when schema (m/explain schema (get-in db [::forms form :data]))))))
@@ -116,6 +131,7 @@
(re-frame/reg-event-db (re-frame/reg-event-db
::save-error ::save-error
(fn [db [_ form result]] (fn [db [_ form result]]
(println result)
(-> db (-> db
(assoc-in [::forms form :status] :error) (assoc-in [::forms form :status] :error)
(assoc-in [::forms form :error] (or (:message (first result)) (assoc-in [::forms form :error] (or (:message (first result))

View File

@@ -53,7 +53,7 @@
(let [data-sub (or data-sub [::forms/form id]) (let [data-sub (or data-sub [::forms/form id])
change-event (when-not on-change change-event (when-not on-change
(or change-event [::forms/change id])) (or change-event [::forms/change id]))
{:keys [data visited attempted-submit? problems] form-key :id} @(re-frame/subscribe data-sub) {:keys [data visited attempted-submit? problems error] form-key :id} @(re-frame/subscribe data-sub)
data (or value data) data (or value data)
status @(re-frame/subscribe [::status/single id]) status @(re-frame/subscribe [::status/single id])
can-submit (if can-submit @(re-frame/subscribe can-submit) can-submit (if can-submit @(re-frame/subscribe can-submit)
@@ -68,7 +68,7 @@
:submit-event submit-event :submit-event submit-event
:problems problems :problems problems
:attempted-submit? attempted-submit? :attempted-submit? attempted-submit?
:error (-> status :error first :message) :error (or error (-> status :error first :message))
:status status :status status
:id id :id id
:data data :data data
@@ -91,7 +91,8 @@
)))) ))))
(defn virtual-builder [] (defn virtual-builder []
(let [key (r/atom (random-uuid))] (let [starting-key (random-uuid)
key (r/atom starting-key)]
(fn [{:keys [value on-change can-submit error-messages fullwidth? schema]}] (fn [{:keys [value on-change can-submit error-messages fullwidth? schema]}]
(let [data-sub [::forms/form @key] (let [data-sub [::forms/form @key]
{:keys [data error problems visited]} @(re-frame/subscribe data-sub) {:keys [data error problems visited]} @(re-frame/subscribe data-sub)
@@ -99,8 +100,12 @@
(r/create-element Provider #js {:value #js {:can-submit can-submit (r/create-element Provider #js {:value #js {:can-submit can-submit
:error-messages (or error-messages :error-messages (or error-messages
nil) nil)
:on-change on-change ;; wrap to make sure raw form updates too
:blur-event [::blurred schema @key] :on-change (fn [v]
(re-frame/dispatch-sync [::forms/reset @key v])
(on-change v))
:blur-event [::blurred schema @key ]
:problems problems
:visited visited :visited visited
:error error :error error
:id @key :id @key
@@ -201,6 +206,7 @@
nil) nil)
visited? (get visited full-field-path) visited? (get visited full-field-path)
value (get-in data full-field-path)] value (get-in data full-field-path)]
(println "VISITED " visited full-field-path problems)
(-> child-props (-> child-props
(assoc :on-change (assoc :on-change
(if on-change (if on-change

View File

@@ -3,7 +3,8 @@
[clojure.string :as str] [clojure.string :as str]
[auto-ap.views.components.multi :as multi] [auto-ap.views.components.multi :as multi]
[auto-ap.views.components.money-field :as money] [auto-ap.views.components.money-field :as money]
[auto-ap.views.components.number :as number])) [auto-ap.views.components.number :as number]
[auto-ap.views.components.typeahead.vendor :as typeahead]))
(defn checkbox [{:keys [on-change (defn checkbox [{:keys [on-change
@@ -53,3 +54,8 @@
(def number-input number/number-input) (def number-input number/number-input)
(def money-input money/field) (def money-input money/field)
(def search-backed-typeahead typeahead/search-backed-typeahead)
(def entity-typeahead typeahead/typeahead-v3)
(def button-radio-input auto-ap.views.components.button-radio/button-radio)

View File

@@ -302,20 +302,22 @@
locations)) locations))
:disabled (boolean (:location account)) :disabled (boolean (:location account))
:allow-nil? true}]]]]] :allow-nil? true}]]]]]
(if percentage-only?
[left-stack [form-builder/raw-field-v2 {:field [index :amount-percentage]}
[:div.field.has-addons [percentage-field {}]]
[form-builder/raw-field-v2 {:field [index :amount-mode]} [left-stack
[button-radio/button-radio {:options [["$" "Amount"] [:div.field.has-addons
["%" "Percent"]]}]] [form-builder/raw-field-v2 {:field [index :amount-mode]}
(if (= "$" amount-mode) [button-radio/button-radio {:options [["$" "Amount"]
[form-builder/raw-field-v2 {:field [index :amount]} ["%" "Percent"]]}]]
[money-field {}] (if (= "$" amount-mode)
] [form-builder/raw-field-v2 {:field [index :amount]}
[form-builder/raw-field-v2 {:field [index :amount-percentage]} [money-field {}]
[percentage-field {}]])] ]
(when (= "%" amount-mode) [form-builder/raw-field-v2 {:field [index :amount-percentage]}
[:div.tag.is-primary.is-light (gstring/format "$%.2f" (or amount 0) )])]]])) [percentage-field {}]])]
(when (= "%" amount-mode)
[:div.tag.is-primary.is-light (gstring/format "$%.2f" (or amount 0) )])])]]))
(when-not disabled (when-not disabled
[:p.buttons [:p.buttons
[:a.button {:on-click (fn [] [:a.button {:on-click (fn []

View File

@@ -1,26 +1,22 @@
(ns auto-ap.views.pages.admin.rules.form (ns auto-ap.views.pages.admin.rules.form
(:require (:require
[auto-ap.entities.transaction-rule :as entity]
[auto-ap.events :as events] [auto-ap.events :as events]
[auto-ap.forms :as forms] [auto-ap.forms :as forms]
[auto-ap.forms.builder :as form-builder] [auto-ap.forms.builder :as form-builder]
[auto-ap.schema :as schema]
[auto-ap.status :as status] [auto-ap.status :as status]
[auto-ap.subs :as subs] [auto-ap.subs :as subs]
[auto-ap.views.components.level :refer [left-stack]] [auto-ap.views.components :as com]
[auto-ap.views.components.button-radio :refer [button-radio]]
[auto-ap.views.components.expense-accounts-field [auto-ap.views.components.expense-accounts-field
:as expense-accounts-field :as expense-accounts-field
:refer [expense-accounts-field]] :refer [expense-accounts-field-v2]]
[auto-ap.views.components.layouts :as layouts] [auto-ap.views.components.layouts :as layouts]
[auto-ap.views.components.money-field :refer [money-field]] [auto-ap.views.components.level :refer [left-stack]]
[auto-ap.views.components.typeahead :refer [typeahead-v3]]
[auto-ap.views.components.typeahead.vendor
:refer [search-backed-typeahead]]
[auto-ap.views.pages.admin.rules.common :refer [default-read]] [auto-ap.views.pages.admin.rules.common :refer [default-read]]
[auto-ap.views.pages.admin.rules.results-modal :as results-modal] [auto-ap.views.pages.admin.rules.results-modal :as results-modal]
[auto-ap.views.utils :refer [dispatch-event with-user]] [auto-ap.views.utils :refer [coerce-float dispatch-event with-user]]
[clojure.spec.alpha :as s]
[clojure.string :as str] [clojure.string :as str]
[malli.core :as m]
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[reagent.core :as r] [reagent.core :as r]
[vimsical.re-frame.cofx.inject :as inject] [vimsical.re-frame.cofx.inject :as inject]
@@ -49,12 +45,6 @@
(when dom-lte (when dom-lte
(str "<" dom-lte))))])))) (str "<" dom-lte))))]))))
(re-frame/reg-sub
::can-submit
:<- [::forms/form ::form]
(fn [{:keys [data]} _]
(s/valid? ::entity/transaction-rule data)))
(re-frame/reg-sub (re-frame/reg-sub
::query ::query
:<- [::forms/form ::form] :<- [::forms/form ::form]
@@ -91,10 +81,6 @@
(assoc :bank-account-id (:id (:bank-account data))))} (assoc :bank-account-id (:id (:bank-account data))))}
default-read]}]})) default-read]}]}))
(defn ungraphql-transaction-rule [x]
(-> x
(update :amount-lte #(some-> % js/parseFloat))
(update :amount-gte #(some-> % js/parseFloat))))
(re-frame/reg-sub (re-frame/reg-sub
::test-query ::test-query
@@ -162,6 +148,9 @@
:accounts :accounts
:yodlee-merchant :yodlee-merchant
:transaction-approval-status]) :transaction-approval-status])
(update :amount-lte coerce-float)
(update :amount-gte coerce-float)
(update :accounts (fn [xs] (update :accounts (fn [xs]
(mapv #(-> % (mapv #(-> %
(assoc :amount-percentage (* (:percentage %) 100.0))) (assoc :amount-percentage (* (:percentage %) 100.0)))
@@ -210,9 +199,9 @@
{:graphql {:graphql
{:token user {:token user
:query-obj query :query-obj query
:owns-state {:single ::form}
:on-success (fn [result] :on-success (fn [result]
[::updated (:upsert-transaction-rule result)]) [::updated (:upsert-transaction-rule result)])}}))
:on-error [::forms/save-error ::form]}}))
(re-frame/reg-event-fx (re-frame/reg-event-fx
::test-clicked ::test-clicked
@@ -228,7 +217,7 @@
(re-frame/reg-event-fx (re-frame/reg-event-fx
::updated ::updated
[(forms/triggers-stop ::form)] [(forms/triggers-stop ::form)]
(fn [{:keys [db]} [_ {:keys [rule-saved]} _]] (fn [{:keys [db]} _]
{:db (forms/start-form db ::form {:client @(re-frame/subscribe [::subs/client])})})) {:db (forms/start-form db ::form {:client @(re-frame/subscribe [::subs/client])})}))
(re-frame/reg-event-fx (re-frame/reg-event-fx
@@ -261,117 +250,107 @@
{::track/dispose [{:id ::client} {::track/dispose [{:id ::client}
{:id ::vendor-change}]})) {:id ::vendor-change}]}))
(defn form-contents [params] (def rule-schema
(m/schema [:map
[:client {:optional true}
[:maybe schema/reference]]
[:bank-account {:optional true}
[:maybe schema/reference]]
[:description
schema/not-empty-string]
[:amount-gte {:optional true}
[:maybe schema/money]]
[:amount-lte {:optional true}
[:maybe schema/money]]
[:dom-gte {:optional true}
[:maybe [:int {:min 1 :max 31}]]]
[:dom-lte {:optional true}
[:maybe [:int {:min 1 :max 31}]]]
[:vendor {:optional true}
[:maybe schema/reference]]
[:transaction-approval-status {:optional true}
[:maybe [:enum :unapproved :requires-feedback :approved :excluded]]]
[:note {:optional true}
[:maybe :string]]]))
(defn form-contents []
[layouts/side-bar {:on-close (dispatch-event [::forms/form-closing ::form ])} [layouts/side-bar {:on-close (dispatch-event [::forms/form-closing ::form ])}
(let [{:keys [data id]} @(re-frame/subscribe [::forms/form ::form]) (let [{:keys [data]} @(re-frame/subscribe [::forms/form ::form])
default-note @(re-frame/subscribe [::default-note]) default-note @(re-frame/subscribe [::default-note])
test-state @(re-frame/subscribe [::status/single ::test])] test-state @(re-frame/subscribe [::status/single ::test])]
[form-builder/builder {:can-submit [::can-submit] [form-builder/builder {:change-event [::changed]
:change-event [::changed]
:submit-event [::saving ] :submit-event [::saving ]
:id ::form} :id ::form
:schema rule-schema}
[form-builder/section {:title "Transaction Rule"} [form-builder/section {:title "Transaction Rule"}
[form-builder/field {:required? true} [form-builder/field-v2 {:required? true
:field :client}
"Client" "Client"
[typeahead-v3 {:entities @(re-frame/subscribe [::subs/clients]) [com/entity-typeahead {:entities @(re-frame/subscribe [::subs/clients])
:auto-focus true :auto-focus true
:entity->text :name :entity->text :name}]]
:type "typeahead-v3"
:field [:client]
:spec ::entity/client}]]
[form-builder/field [form-builder/field-v2 {:field [:bank-account]}
"Bank account" "Bank account"
[typeahead-v3 {:entities @(re-frame/subscribe [::subs/real-bank-accounts-for-client (:client data)]) [com/entity-typeahead {:entities @(re-frame/subscribe [::subs/real-bank-accounts-for-client (:client data)])
:entity->text :name :entity->text :name}]]
:type "typeahead-v3"
:field [:bank-account]
:spec ::entity/bank-account}]]
#_[form-builder/field [form-builder/field-v2 {:field :description
"Yodlee Merchant" :required? true}
[typeahead-v3 {:entities @(re-frame/subscribe [::subs/yodlee-merchants])
:entity->text #(str (:name %) " - " (:yodlee-id %))
:type "typeahead-v3"
:field [:yodlee-merchant]}]]
[form-builder/field
[:span "Description (" [:a {:href "https://regex101.com" :target "_new"} "regex tester"] ")" ] [:span "Description (" [:a {:href "https://regex101.com" :target "_new"} "regex tester"] ")" ]
[:input.input {:type "text" [:input.input {:type "text"}]]
:field [:description]
:spec ::entity/description}]]
[:div.field [:div.field
[:p.help "Amount"] [:p.help "Amount"]
[left-stack [left-stack
[form-builder/raw-field [form-builder/raw-field-v2 {:field :amount-gte}
[money-field {:type "money" [com/money-input {:placeholder ">="}]]
:placeholder ">="
:field [:amount-gte]
:spec ::entity/amount-gte}]]
"-" "-"
[form-builder/raw-field [form-builder/raw-field-v2 {:field :amount-lte}
[money-field {:type "money" [com/money-input {:placeholder "<="}]]]]
:placeholder "<="
:field [:amount-lte]
:spec ::entity/amount-lte}]]]]
[:div.field [:div.field
[:p.help "Day of Month"] [:p.help "Day of month"]
[:div.control [left-stack
[:div.columns [form-builder/raw-field-v2 {:field :dom-gte}
[:div.column [com/number-input {:placeholder ">="
[form-builder/raw-field :style {:width "7em"}}]]
[:input.input {:type "number" "-"
:placeholder ">=" [form-builder/raw-field-v2 {:field :dom-lte}
:field [:dom-gte] [com/number-input {:placeholder "<="
:spec ::entity/dom-gte :style {:width "7em"}}]]]]
:precision 0
:step "1"}]]]
[:div.column
[form-builder/raw-field
[:input.input {:type "number"
:placeholder "<="
:field [:dom-lte]
:spec ::entity/dom-lte
:precision 0
:step "1"}]]]]]]
[:h2.title.is-4 "Outcomes"] [:h2.title.is-4 "Outcomes"]
[form-builder/field "Assign Vendor" [form-builder/field-v2 {:field :vendor}
[search-backed-typeahead {:search-query (fn [i] "Assign Vendor"
[com/search-backed-typeahead {:search-query (fn [i]
[:search_vendor [:search_vendor
{:query i} {:query i}
[:name :id]]) [:name :id]])}]]
:type "typeahead-v3"
:field [:vendor]}]]
[form-builder/raw-field [form-builder/field-v2 {:field :accounts}
[expense-accounts-field {:type "expense-accounts" "Accounts"
:descriptor "account asssignment" [expense-accounts-field-v2 {:descriptor "account asssignment"
:percentage-only? true :percentage-only? true
:client (:client data) :client (:client data)
:locations (into ["Shared"] @(re-frame/subscribe [::subs/locations-for-client-or-bank-account (:id (:client data)) (:id (:bank-account data))])) :locations (into ["Shared"] @(re-frame/subscribe [::subs/locations-for-client-or-bank-account (:id (:client data)) (:id (:bank-account data))]))
:max 100 :max 100}]]
:field [:accounts]}]]
[form-builder/field [form-builder/field-v2 {:field :transaction-approval-status}
"Approval Status" "Approval Status"
[button-radio [com/button-radio-input
{:type "button-radio" {:options [[:unapproved "Unapproved"]
:field [:transaction-approval-status]
:options [[:unapproved "Unapproved"]
[:requires-feedback "Client Review"] [:requires-feedback "Client Review"]
[:approved "Approved"] [:approved "Approved"]
[:excluded "Excluded from Ledger"]]}]] [:excluded "Excluded from Ledger"]]}]]
[form-builder/field "Note" [form-builder/field-v2 {:field :note}
"Note"
[:input.input {:type "text" [:input.input {:type "text"
:field [:note] :placeholder default-note}]]
:placeholder default-note
:spec (s/nilable ::entity/note)}]]
[:div.is-divider] [:div.is-divider]
[form-builder/error-notification] [form-builder/error-notification]

View File

@@ -1,18 +1,15 @@
(ns auto-ap.views.pages.admin.users.form (ns auto-ap.views.pages.admin.users.form
(:require (:require
[auto-ap.entities.clients :as entity]
[auto-ap.forms :as forms] [auto-ap.forms :as forms]
[auto-ap.forms.builder :as form-builder] [auto-ap.forms.builder :as form-builder]
[auto-ap.schema :as schema]
[auto-ap.status :as status] [auto-ap.status :as status]
[auto-ap.subs :as subs] [auto-ap.subs :as subs]
[auto-ap.views.components :as com]
[auto-ap.views.components.modal :as modal] [auto-ap.views.components.modal :as modal]
[auto-ap.views.components.typeahead :refer [typeahead-v3]] [auto-ap.views.utils :refer [dispatch-event with-user]]
[auto-ap.views.utils :refer [dispatch-event multi-field with-user]] [malli.core :as m]
[react-popper :refer [usePopper] :as react-popper] [re-frame.core :as re-frame]))
[re-frame.core :as re-frame]
[react :as react]
[reagent.core :as r]))
(re-frame/reg-event-fx (re-frame/reg-event-fx
::saving ::saving
@@ -37,41 +34,52 @@
{:dispatch [::modal/modal-closed]})) {:dispatch [::modal/modal-closed]}))
(def client-schema
(m/schema [:map [:client schema/reference]]))
(def user-schema
(m/schema
[:map
[:name schema/not-empty-string]
[:role [:enum :none :user :manager :power_user :admin]]
[:clients {:optional true}
[:maybe
[:sequential client-schema]]]]))
(defn form [] (defn form []
(let [{:keys [data]} @(re-frame/subscribe [::forms/form ::form]) (let [{:keys [data]} @(re-frame/subscribe [::forms/form ::form])
clients @(re-frame/subscribe [::subs/clients])] clients @(re-frame/subscribe [::subs/clients])]
(println data)
[:<> [:<>
[form-builder/builder {:submit-event [::saving] [form-builder/builder {:submit-event [::saving]
:id ::form} :id ::form
[form-builder/field :schema user-schema}
[form-builder/field-v2 {:required? true
:field :name}
"Name" "Name"
[:input.input {:type "text" [:input.input {:type "text"}]]
:field [:name] [form-builder/field-v2 {:required? true
:spec ::entity/name}]] :field :role}
[:div.field "Role"
[:p.help "Role"] [com/select-field {:options [[:none "None"]
[:div.control [:user "User"]
[:div.select [:manager "Manager"]
[form-builder/raw-field [:power_user "Power User"]
[:select {:type "select" [:admin "Admin"]]
:field [:role]} :allow-nil? false
[:option {:value ":none"} "None"] :keywordize? true}]]
[:option {:value ":user"} "User"] (when (#{:user :manager :power_user} (:role data))
[:option {:value ":manager"} "Manager"] [form-builder/field-v2 {:field :clients}
[:option {:value ":power_user"} "Power User"]
[:option {:value ":admin"} "Admin"]]]]]]
(when (#{":user" ":manager" ":power_user"} (:role data))
[form-builder/field
"Client" "Client"
[multi-field {:type "multi-field" [com/multi-field-v2 {:template [[form-builder/raw-field-v2 {:field :client}
:field [:clients] [com/entity-typeahead
:template [[typeahead-v3 {:entities clients {:entities clients
:entity->text :name :entity->text :name
:style {:width "13em"} :style {:width "13em"}}]]]
:type "typeahead-v3" :key-fn :id
:field [:client]}]]}]]) :schema [:sequential client-schema]
:new-text "Grant access to client"}]])
[form-builder/hidden-submit-button]]])) [form-builder/hidden-submit-button]]]))
@@ -79,7 +87,7 @@
::editing ::editing
(fn [{:keys [db]} [_ d]] (fn [{:keys [db]} [_ d]]
{:db (-> db {:db (-> db
(forms/start-form ::form (update d :clients #(map (fn [x] {:client x}) %)))) (forms/start-form ::form (update d :clients #(mapv (fn [x] {:client x :id (random-uuid)}) %))))
:dispatch [::modal/modal-requested {:title (str "Edit user " (:name d)) :dispatch [::modal/modal-requested {:title (str "Edit user " (:name d))
:body [form] :body [form]
:cancel? false :cancel? false

View File

@@ -622,3 +622,17 @@
second second
base64/decodeString)] base64/decodeString)]
(js->clj (.parse js/JSON json) :keywordize-keys true))) (js->clj (.parse js/JSON json) :keywordize-keys true)))
(defn coerce-float [f]
(cond (str/blank? f)
nil
(float? f)
f
(and (string? f)
(not (js/Number.isNaN (js/parseFloat f))))
(js/parseFloat f)
:else
nil))