Simplified forms considerably
This commit is contained in:
@@ -84,6 +84,10 @@
|
|||||||
.modal.wide .modal-card {
|
.modal.wide .modal-card {
|
||||||
width: 1024px;
|
width: 1024px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.modal.semi-wide .modal-card {
|
||||||
|
width: 700px;
|
||||||
|
}
|
||||||
@keyframes grow-width {
|
@keyframes grow-width {
|
||||||
from {
|
from {
|
||||||
width: 0px;
|
width: 0px;
|
||||||
@@ -347,10 +351,6 @@ nav.navbar .navbar-item.is-active {
|
|||||||
.modal {
|
.modal {
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-card-body {
|
|
||||||
overflow: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
.buttons .dropdown:not(:last-child):not(.is-fullwidth) .button {
|
.buttons .dropdown:not(:last-child):not(.is-fullwidth) .button {
|
||||||
margin-right: 0.5em;
|
margin-right: 0.5em;
|
||||||
|
|||||||
@@ -266,24 +266,30 @@
|
|||||||
(throw (ex-info "Payment can't be undone because it isn't cleared." {:validation-error "Payment can't be undone because it isn't cleared."})))
|
(throw (ex-info "Payment can't be undone because it isn't cleared." {:validation-error "Payment can't be undone because it isn't cleared."})))
|
||||||
(if is-autopay-payment?
|
(if is-autopay-payment?
|
||||||
(audit-transact
|
(audit-transact
|
||||||
(->> [{:db/id (:db/id payment)
|
(cond-> [{:db/id (:db/id payment)
|
||||||
:payment/status :payment-status/pending}
|
:payment/status :payment-status/pending}
|
||||||
{:db/id transaction-id
|
{:db/id transaction-id
|
||||||
:transaction/approval-status :transaction-approval-status/unapproved}
|
:transaction/approval-status :transaction-approval-status/unapproved}
|
||||||
|
|
||||||
[:db/retractEntity (:db/id payment) ]
|
[:db/retractEntity (:db/id payment) ]
|
||||||
[:db/retract transaction-id :transaction/payment (:db/id payment)]
|
[:db/retract transaction-id :transaction/payment (:db/id payment)]
|
||||||
[:db/retract transaction-id :transaction/vendor (:db/id (:transaction/vendor transaction))]
|
[:db/retract transaction-id :transaction/vendor (:db/id (:transaction/vendor transaction))]]
|
||||||
[:db/retract transaction-id :transaction/location (:transaction/location transaction)]]
|
|
||||||
(into (map (fn [a]
|
(:transaction/location transaction)
|
||||||
[:db/retract transaction-id :transaction/accounts (:db/id a)])
|
(conj [:db/retract transaction-id :transaction/location (:transaction/location transaction)])
|
||||||
(:transaction/accounts transaction)))
|
|
||||||
(into (map (fn [[invoice-payment]]
|
(seq (:transaction/accounts transaction))
|
||||||
[:db/retractEntity invoice-payment])
|
(into (map (fn [a]
|
||||||
(d/query {:query {:find ['?ip]
|
[:db/retract transaction-id :transaction/accounts (:db/id a)])
|
||||||
:in ['$ '?p]
|
(:transaction/accounts transaction)))
|
||||||
:where ['[?ip :invoice-payment/payment ?p]]}
|
|
||||||
:args [(d/db conn) (:db/id payment)]} ))))
|
true
|
||||||
|
(into (map (fn [[invoice-payment]]
|
||||||
|
[:db/retractEntity invoice-payment])
|
||||||
|
(d/query {:query {:find ['?ip]
|
||||||
|
:in ['$ '?p]
|
||||||
|
:where ['[?ip :invoice-payment/payment ?p]]}
|
||||||
|
:args [(d/db conn) (:db/id payment)]} ))))
|
||||||
(:id context))
|
(:id context))
|
||||||
(audit-transact
|
(audit-transact
|
||||||
(into (cond-> [{:db/id (:db/id payment)
|
(into (cond-> [{:db/id (:db/id payment)
|
||||||
|
|||||||
@@ -5,12 +5,13 @@
|
|||||||
[auto-ap.graphql.utils
|
[auto-ap.graphql.utils
|
||||||
:refer [->graphql
|
:refer [->graphql
|
||||||
<-graphql
|
<-graphql
|
||||||
cleanse-query
|
|
||||||
assert-admin
|
assert-admin
|
||||||
assert-failure
|
assert-failure
|
||||||
|
cleanse-query
|
||||||
enum->keyword
|
enum->keyword
|
||||||
is-admin?
|
is-admin?
|
||||||
result->page]]
|
result->page]]
|
||||||
|
[auto-ap.utils :refer [by]]
|
||||||
[clojure.set :as set]
|
[clojure.set :as set]
|
||||||
[clojure.string :as str]
|
[clojure.string :as str]
|
||||||
[clojure.tools.logging :as log]
|
[clojure.tools.logging :as log]
|
||||||
@@ -31,9 +32,30 @@
|
|||||||
(set (map :db/id (:user/clients id)))))))
|
(set (map :db/id (:user/clients id)))))))
|
||||||
|
|
||||||
(defn upsert-vendor [context {{:keys [id name hidden terms code print_as primary_contact secondary_contact address default_account_id invoice_reminder_schedule schedule_payment_dom terms_overrides account_overrides] :as in} :vendor} value]
|
(defn upsert-vendor [context {{:keys [id name hidden terms code print_as primary_contact secondary_contact address default_account_id invoice_reminder_schedule schedule_payment_dom terms_overrides account_overrides] :as in} :vendor} value]
|
||||||
|
|
||||||
(when (and id (not (can-user-edit-vendor? id (:id context))))
|
(when (and id (not (can-user-edit-vendor? id (:id context))))
|
||||||
(assert-failure "This vendor is managed by Integreat. Please reach out to ben@integreatconsult.com for your changes."))
|
(assert-failure "This vendor is managed by Integreat. Please reach out to ben@integreatconsult.com for your changes."))
|
||||||
|
|
||||||
|
(when (->> schedule_payment_dom
|
||||||
|
(group-by :client_id)
|
||||||
|
vals
|
||||||
|
(filter #(> (count %) 1))
|
||||||
|
seq)
|
||||||
|
(assert-failure "Only one schedule payment override allowed per client."))
|
||||||
|
|
||||||
|
(when (->> terms_overrides
|
||||||
|
(group-by :client_id)
|
||||||
|
vals
|
||||||
|
(filter #(> (count %) 1))
|
||||||
|
seq)
|
||||||
|
(assert-failure "Only one terms override allowed per client."))
|
||||||
|
|
||||||
|
(when (->> account_overrides
|
||||||
|
(group-by :client_id)
|
||||||
|
vals
|
||||||
|
(filter #(> (count %) 1))
|
||||||
|
seq)
|
||||||
|
(assert-failure "Only one account override allowed per client."))
|
||||||
|
|
||||||
(let [
|
(let [
|
||||||
hidden (if (is-admin? (:id context))
|
hidden (if (is-admin? (:id context))
|
||||||
hidden
|
hidden
|
||||||
|
|||||||
@@ -18,23 +18,26 @@
|
|||||||
(defn builder [{:keys [can-submit data-sub change-event submit-event id fullwidth?] :as z}]
|
(defn builder [{:keys [can-submit data-sub change-event submit-event id fullwidth?] :as z}]
|
||||||
(let [data-sub (or data-sub [::forms/form id])
|
(let [data-sub (or data-sub [::forms/form id])
|
||||||
change-event (or change-event [::forms/change id])
|
change-event (or change-event [::forms/change id])
|
||||||
{:keys [data error]} @(re-frame/subscribe data-sub)]
|
{:keys [data error]} @(re-frame/subscribe data-sub)
|
||||||
|
status @(re-frame/subscribe [::status/single id])]
|
||||||
(r/create-element Provider #js {:value #js {:can-submit @(re-frame/subscribe can-submit)
|
(r/create-element Provider #js {:value #js {:can-submit @(re-frame/subscribe can-submit)
|
||||||
:change-event change-event
|
:change-event change-event
|
||||||
:submit-event submit-event
|
:submit-event submit-event
|
||||||
:error error
|
:error error
|
||||||
:status @(re-frame/subscribe [::status/single id])
|
:status status
|
||||||
:id id
|
:id id
|
||||||
:data data
|
:data data
|
||||||
:fullwidth? fullwidth?}}
|
:fullwidth? fullwidth?}}
|
||||||
(r/as-element
|
(r/as-element
|
||||||
(into [:form {:on-submit (fn [e]
|
[:form {:on-submit (fn [e]
|
||||||
(when (.-stopPropagation e)
|
(when (.-stopPropagation e)
|
||||||
(.stopPropagation e)
|
(.stopPropagation e)
|
||||||
(.preventDefault e))
|
(.preventDefault e))
|
||||||
(when can-submit
|
(when can-submit
|
||||||
(re-frame/dispatch-sync (vec (conj submit-event {})))))}]
|
(re-frame/dispatch-sync (vec (conj submit-event {})))))}
|
||||||
(r/children (r/current-component)))))))
|
(into [:fieldset {:disabled (boolean (= :loading (:state status)))}]
|
||||||
|
(r/children (r/current-component)))]
|
||||||
|
))))
|
||||||
|
|
||||||
(defn raw-field []
|
(defn raw-field []
|
||||||
(let [[child] (r/children (r/current-component))]
|
(let [[child] (r/children (r/current-component))]
|
||||||
@@ -55,6 +58,7 @@
|
|||||||
|
|
||||||
:else
|
:else
|
||||||
nil)))
|
nil)))
|
||||||
|
|
||||||
(assoc-in [1 :subscription] (aget consume-form "data"))
|
(assoc-in [1 :subscription] (aget consume-form "data"))
|
||||||
(assoc-in [1 :event] (aget consume-form "change-event")))]))]))]))
|
(assoc-in [1 :event] (aget consume-form "change-event")))]))]))]))
|
||||||
|
|
||||||
@@ -62,6 +66,16 @@
|
|||||||
(r/create-element FormScopeProvider #js {:value scope}
|
(r/create-element FormScopeProvider #js {:value scope}
|
||||||
(r/as-element (into [:<>]
|
(r/as-element (into [:<>]
|
||||||
(r/children (r/current-component))))))
|
(r/children (r/current-component))))))
|
||||||
|
(defn vertical-control [{:keys [is-small?]}]
|
||||||
|
(let [[label & children] (r/children (r/current-component))]
|
||||||
|
[:> Consumer {}
|
||||||
|
(fn [consume]
|
||||||
|
(r/as-element
|
||||||
|
[:div.field
|
||||||
|
(when label (if (or (aget consume "fullwidth?")
|
||||||
|
is-small?) [:p.help label]
|
||||||
|
[:label.label label]))
|
||||||
|
(into [:div.control ] children)]))]))
|
||||||
|
|
||||||
(defn field []
|
(defn field []
|
||||||
(let [[label child] (r/children (r/current-component))]
|
(let [[label child] (r/children (r/current-component))]
|
||||||
@@ -111,6 +125,16 @@
|
|||||||
fullwidth? (conj "is-fullwidth")) }
|
fullwidth? (conj "is-fullwidth")) }
|
||||||
child])))]))
|
child])))]))
|
||||||
|
|
||||||
|
(defn hidden-submit-button []
|
||||||
|
[:> Consumer {}
|
||||||
|
(fn [consume]
|
||||||
|
(let [status (aget consume "status")
|
||||||
|
can-submit (aget consume "can-submit")]
|
||||||
|
(r/as-element
|
||||||
|
[:div {:style {:display "none"}}
|
||||||
|
[:button.button.is-medium.is-primary {:disabled (or (status/disabled-for status)
|
||||||
|
(not can-submit))}]])))])
|
||||||
|
|
||||||
(defn error-notification []
|
(defn error-notification []
|
||||||
(let [[child] (r/children (r/current-component))]
|
(let [[child] (r/children (r/current-component))]
|
||||||
[:> Consumer {}
|
[:> Consumer {}
|
||||||
|
|||||||
@@ -65,7 +65,7 @@
|
|||||||
[horizontal-field
|
[horizontal-field
|
||||||
nil
|
nil
|
||||||
[:div.control
|
[:div.control
|
||||||
[:p.help "Address"]
|
[:p.help "Street Address"]
|
||||||
[form-builder/raw-field
|
[form-builder/raw-field
|
||||||
[:input.input.is-expanded {:type "text"
|
[:input.input.is-expanded {:type "text"
|
||||||
:placeholder "1700 Pennsylvania Ave"
|
:placeholder "1700 Pennsylvania Ave"
|
||||||
|
|||||||
9
src/cljs/auto_ap/views/components/level.cljs
Normal file
9
src/cljs/auto_ap/views/components/level.cljs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
(ns auto-ap.views.components.level
|
||||||
|
(:require [reagent.core :as r]))
|
||||||
|
|
||||||
|
(defn left-stack []
|
||||||
|
(let [children (r/children (r/current-component))]
|
||||||
|
[:div.level (r/props (r/current-component))
|
||||||
|
(into [:div.level-left]
|
||||||
|
(for [c children]
|
||||||
|
[:div.level-item c]))]))
|
||||||
@@ -33,7 +33,8 @@
|
|||||||
class (assoc :class class))
|
class (assoc :class class))
|
||||||
[:div.modal-background {:on-click (dispatch-event [::modal-closed])}]
|
[:div.modal-background {:on-click (dispatch-event [::modal-closed])}]
|
||||||
|
|
||||||
[:div.modal-card
|
[:div.modal-card (cond-> {}
|
||||||
|
class (assoc :class class))
|
||||||
[:header.modal-card-head
|
[:header.modal-card-head
|
||||||
[:p.modal-card-title
|
[:p.modal-card-title
|
||||||
title]
|
title]
|
||||||
|
|||||||
@@ -1,91 +1,7 @@
|
|||||||
(ns auto-ap.views.components.typeahead
|
(ns auto-ap.views.components.typeahead
|
||||||
(:require [reagent.core :as r]
|
(:require
|
||||||
[reagent.ratom :as ra]
|
[auto-ap.views.components.typeahead.vendor :as internal]))
|
||||||
[clojure.string :as str]
|
|
||||||
[clojure.set :as set]
|
|
||||||
[downshift :as ds :refer [useCombobox]]
|
|
||||||
[react]))
|
|
||||||
|
|
||||||
(set! *warn-on-infer* true)
|
(set! *warn-on-infer* true)
|
||||||
|
|
||||||
;; TODO: This avoids the use of inferred externs by using aget. You could just use the ^js tag though
|
(def typeahead-v3 internal/typeahead-v3)
|
||||||
(defn state-reducer [state actions-and-changes]
|
|
||||||
(cond
|
|
||||||
(= (aget actions-and-changes "type") (aget (aget useCombobox "stateChangeTypes" ) "InputChange"))
|
|
||||||
(doto (aget actions-and-changes "changes") (aset "selectedItem" nil))
|
|
||||||
|
|
||||||
(and (= (aget actions-and-changes "type") (aget (aget useCombobox "stateChangeTypes") "InputBlur"))
|
|
||||||
(not (aget state "selectedItem")))
|
|
||||||
(doto (aget actions-and-changes "changes" ) (aset "inputValue" nil))
|
|
||||||
|
|
||||||
:else
|
|
||||||
(aget actions-and-changes "changes")))
|
|
||||||
|
|
||||||
(defn typeahead-v3-internal [{:keys [class style disabled entities ^js entity->text entities-by-id entity-index on-change disabled value name auto-focus] :or {disabled false} :as i}]
|
|
||||||
(let [[items set-items] (react/useState (map clj->js entities))
|
|
||||||
[getLabelProps getMenuProps getComboboxProps getToggleButtonProps getInputProps getItemProps isOpen highlightedIndex selectItem selectedItem setInputValue]
|
|
||||||
(as-> (useCombobox (clj->js {:items items
|
|
||||||
:defaultHighlightedIndex 0
|
|
||||||
:defaultSelectedItem value
|
|
||||||
:onInputValueChange (fn [input]
|
|
||||||
(if entities-by-id
|
|
||||||
(do
|
|
||||||
(->> (.search entity-index (or (aget input "inputValue") "") #js {:fuzzy 0.2} )
|
|
||||||
clj->js
|
|
||||||
(take 10)
|
|
||||||
(set-items)))
|
|
||||||
|
|
||||||
(set-items (map clj->js (take 10 (filter (fn [x] (str/includes? (or (some-> (entity->text x) str/lower-case) "")
|
|
||||||
(or (some-> (aget input "inputValue") str/lower-case) "")))
|
|
||||||
entities))))))
|
|
||||||
:stateReducer state-reducer
|
|
||||||
:onSelectedItemChange (fn [z]
|
|
||||||
(when on-change
|
|
||||||
(on-change (js->clj (aget z "selectedItem") :keywordize-keys true))))})) $
|
|
||||||
(map #(aget $ %) ["getLabelProps" "getMenuProps" "getComboboxProps" "getToggleButtonProps" "getInputProps" "getItemProps" "isOpen" "highlightedIndex" "selectItem" "selectedItem" "setInputValue"]))]
|
|
||||||
[:<>
|
|
||||||
[:div.typeahead (assoc (js->clj (getComboboxProps))
|
|
||||||
:style style)
|
|
||||||
(if selectedItem
|
|
||||||
^{:key "typeahead"} [:div.input (assoc (js->clj (getInputProps #js {:disabled (if disabled
|
|
||||||
"disabled"
|
|
||||||
"")}))
|
|
||||||
:on-key-up (fn [e]
|
|
||||||
(when (= 8 (aget e "keyCode" ))
|
|
||||||
(selectItem nil)
|
|
||||||
(setInputValue nil)
|
|
||||||
(when on-change
|
|
||||||
(on-change nil))))
|
|
||||||
:class class
|
|
||||||
:tab-index "0")
|
|
||||||
[:div.control
|
|
||||||
[:div.tags.has-addons
|
|
||||||
[:span.tag (entity->text (js->clj selectedItem :keywordize-keys true))]
|
|
||||||
(when name
|
|
||||||
[:input {:type "hidden" :name name :value (:id (js->clj selectedItem :keywordize-keys true))}])
|
|
||||||
(when-not disabled
|
|
||||||
[:a.tag.is-delete {:on-click (fn []
|
|
||||||
(setInputValue nil)
|
|
||||||
(selectItem nil)
|
|
||||||
(when on-change
|
|
||||||
(on-change nil)))}])]]]
|
|
||||||
^{:key "typeahead"} [:input.input (js->clj
|
|
||||||
(getInputProps #js {:disabled (if disabled
|
|
||||||
"disabled"
|
|
||||||
"")
|
|
||||||
:autoFocus (if auto-focus
|
|
||||||
"autoFocus"
|
|
||||||
"")}))])
|
|
||||||
[:div {:class (if (and isOpen (seq items)) "typeahead-menu")}
|
|
||||||
[:ul (js->clj (getMenuProps))
|
|
||||||
(if (and isOpen (seq items))
|
|
||||||
(for [[index item] (map vector (range) (js->clj items :keywordize-keys true))]
|
|
||||||
^{:key item}
|
|
||||||
[:li.typeahead-suggestion (assoc (js->clj (getItemProps #js {:item item :index index}))
|
|
||||||
:class (if (= index highlightedIndex)
|
|
||||||
"typeahead-highlighted"))
|
|
||||||
(entity->text item)]))]]]]))
|
|
||||||
|
|
||||||
(defn typeahead-v3 [{:keys [class disabled entities entity->text entities-by-id entity-index on-change value ] :as props}]
|
|
||||||
[:div
|
|
||||||
[:f> typeahead-v3-internal props]])
|
|
||||||
|
|||||||
@@ -3,26 +3,12 @@
|
|||||||
[downshift :as ds :refer [useCombobox]]
|
[downshift :as ds :refer [useCombobox]]
|
||||||
[re-frame.core :as re-frame]
|
[re-frame.core :as re-frame]
|
||||||
[auto-ap.views.utils :refer [with-user]]
|
[auto-ap.views.utils :refer [with-user]]
|
||||||
[react]))
|
|
||||||
|
[clojure.string :as str]
|
||||||
|
[react :as react]))
|
||||||
|
|
||||||
(set! *warn-on-infer* true)
|
(set! *warn-on-infer* true)
|
||||||
|
|
||||||
;; TODO: This avoids the use of inferred externs by using aget. You could just use the ^js tag though
|
|
||||||
(defn state-reducer [^js/FakeStateObject state ^js/FakeActionsAndChanges actions-and-changes]
|
|
||||||
(let [useCombobox ^js/Downshift useCombobox]
|
|
||||||
(cond
|
|
||||||
(= (.-type actions-and-changes) (.-InputChange (.-stateChangeTypes ^js/Downshift useCombobox)))
|
|
||||||
(set! (.-selectedItem (.-changes actions-and-changes)) nil)
|
|
||||||
|
|
||||||
(and (= (.-type actions-and-changes) (.-InputBlur (.-stateChangeTypes ^js/Downshift useCombobox)))
|
|
||||||
(not (.-selectedItem state)))
|
|
||||||
(set! (.-inputValue (.-changes actions-and-changes ))
|
|
||||||
nil)
|
|
||||||
|
|
||||||
:else
|
|
||||||
nil))
|
|
||||||
(.-changes actions-and-changes))
|
|
||||||
|
|
||||||
(re-frame/reg-event-fx
|
(re-frame/reg-event-fx
|
||||||
::search-completed
|
::search-completed
|
||||||
(fn [_ [_ set-items set-loading-status result]]
|
(fn [_ [_ set-items set-loading-status result]]
|
||||||
@@ -41,13 +27,30 @@
|
|||||||
(when (> (count input-value) 2)
|
(when (> (count input-value) 2)
|
||||||
(set-loading-status :loading)
|
(set-loading-status :loading)
|
||||||
|
|
||||||
|
|
||||||
{:graphql {:token user
|
{:graphql {:token user
|
||||||
:query-obj {:venia/queries [{:query/data (search-query input-value )
|
:query-obj {:venia/queries [{:query/data (search-query input-value )
|
||||||
:query/alias :search-results}]}
|
:query/alias :search-results}]}
|
||||||
:on-success [::search-completed set-items set-loading-status]
|
:on-success [::search-completed set-items set-loading-status]
|
||||||
:on-error [::search-failed set-loading-status]}})))
|
:on-error [::search-failed set-loading-status]}})))
|
||||||
|
|
||||||
|
;; TODO: This avoids the use of inferred externs by using aget. You could just use the ^js tag though
|
||||||
|
(defn state-reducer [^js/FakeStateObject state ^js/FakeActionsAndChanges actions-and-changes]
|
||||||
|
(let [useCombobox ^js/Downshift useCombobox]
|
||||||
|
(cond
|
||||||
|
(= (.-type actions-and-changes) (.-InputChange (.-stateChangeTypes ^js/Downshift useCombobox)))
|
||||||
|
(set! (.-selectedItem (.-changes actions-and-changes)) nil)
|
||||||
|
|
||||||
|
(and (= (.-type actions-and-changes) (.-InputBlur (.-stateChangeTypes ^js/Downshift useCombobox)))
|
||||||
|
(not (.-selectedItem state)))
|
||||||
|
(set! (.-inputValue (.-changes actions-and-changes ))
|
||||||
|
nil)
|
||||||
|
|
||||||
|
:else
|
||||||
|
nil))
|
||||||
|
(.-changes actions-and-changes))
|
||||||
|
|
||||||
|
|
||||||
(re-frame/reg-event-fx
|
(re-frame/reg-event-fx
|
||||||
::input-value-changed
|
::input-value-changed
|
||||||
(fn [_ [_ input-value search-query set-items set-loading-status]]
|
(fn [_ [_ input-value search-query set-items set-loading-status]]
|
||||||
@@ -58,73 +61,116 @@
|
|||||||
:time 250
|
:time 250
|
||||||
:key ::input-value-settled}})))
|
:key ::input-value-settled}})))
|
||||||
|
|
||||||
(defn typeahead-v3-internal [{:keys [class style ^js on-change disabled value name search-query auto-focus] :or {disabled false} :as i}]
|
(defn typeahead-v3-internal [{:keys [class entity->text entities on-input-change style ^js on-change disabled value name auto-focus] :or {disabled false} :as i}]
|
||||||
(let [[items set-items] (react/useState [])
|
(let [[items set-items] (react/useState (or (clj->js entities)
|
||||||
|
[]))
|
||||||
|
[focused set-focus] (react/useState (boolean auto-focus))
|
||||||
[loading-status set-loading-status] (react/useState false)
|
[loading-status set-loading-status] (react/useState false)
|
||||||
[getLabelProps getMenuProps getComboboxProps getToggleButtonProps getInputProps getItemProps isOpen highlightedIndex selectItem selectedItem setInputValue]
|
[getMenuProps getComboboxProps getInputProps getItemProps isOpen highlightedIndex selectItem selectedItem setInputValue]
|
||||||
(as-> (useCombobox (clj->js {:items items
|
(as-> (useCombobox (clj->js {:items items
|
||||||
:defaultHighlightedIndex 0
|
:defaultHighlightedIndex 0
|
||||||
:defaultSelectedItem value
|
:defaultSelectedItem value
|
||||||
|
:itemToString (fn []
|
||||||
|
;; once an item is selected, you just use empty text
|
||||||
|
"")
|
||||||
:onInputValueChange (fn [input]
|
:onInputValueChange (fn [input]
|
||||||
(re-frame/dispatch [::input-value-changed (aget input "inputValue") search-query set-items set-loading-status])
|
(on-input-change input set-items set-loading-status))
|
||||||
true)
|
|
||||||
:stateReducer state-reducer
|
:stateReducer state-reducer
|
||||||
:onSelectedItemChange (fn [z]
|
:onSelectedItemChange (fn [z]
|
||||||
(when on-change
|
(when on-change
|
||||||
(on-change (js->clj (aget z "selectedItem") :keywordize-keys true))))})) $
|
(on-change (js->clj (aget z "selectedItem") :keywordize-keys true))))})) $
|
||||||
(map #(aget $ %) ["getLabelProps" "getMenuProps" "getComboboxProps" "getToggleButtonProps" "getInputProps" "getItemProps" "isOpen" "highlightedIndex" "selectItem" "selectedItem" "setInputValue"]))]
|
(map #(aget $ %) ["getMenuProps" "getComboboxProps" "getInputProps" "getItemProps" "isOpen" "highlightedIndex" "selectItem" "selectedItem" "setInputValue"]))]
|
||||||
[:<>
|
[:<>
|
||||||
[:div.typeahead (assoc (js->clj (getComboboxProps))
|
[:div.typeahead (assoc (js->clj (getComboboxProps))
|
||||||
:style style)
|
:style style)
|
||||||
(cond
|
[:div.input {:on-key-up (when selectedItem (fn [e]
|
||||||
selectedItem
|
(when (= 8 (aget e "keyCode" ))
|
||||||
^{:key "typeahead"}
|
(selectItem nil)
|
||||||
[:div.input (assoc (js->clj (getInputProps #js {:disabled (if disabled
|
(setInputValue nil)
|
||||||
"disabled"
|
(when on-change
|
||||||
"")}))
|
(on-change nil)))))
|
||||||
:on-key-up (fn [e]
|
:disabled (cond disabled
|
||||||
(when (= 8 (aget e "keyCode" ))
|
"disabled"
|
||||||
(selectItem nil)
|
|
||||||
(setInputValue nil)
|
|
||||||
(when on-change
|
|
||||||
(on-change nil))))
|
|
||||||
:class (if (= :loading loading-status)
|
|
||||||
"is-loading"
|
|
||||||
class)
|
|
||||||
:tab-index "0")
|
|
||||||
[:div.control
|
|
||||||
[:div.tags.has-addons
|
|
||||||
[:span.tag (:name (js->clj selectedItem :keywordize-keys true))]
|
|
||||||
(when name
|
|
||||||
[:input {:type "hidden" :name name :value (:id (js->clj selectedItem :keywordize-keys true))}])
|
|
||||||
(when-not disabled
|
|
||||||
[:a.tag.is-delete {:on-click (fn []
|
|
||||||
(setInputValue nil)
|
|
||||||
(selectItem nil)
|
|
||||||
(when on-change
|
|
||||||
(on-change nil)))}])]]]
|
|
||||||
|
|
||||||
:else
|
:else
|
||||||
^{:key "typeahead"} [:div.control {:class (when (= :loading loading-status)
|
"")
|
||||||
"is-loading")}
|
:class
|
||||||
[:input.input (js->clj
|
(cond-> []
|
||||||
(getInputProps #js {:disabled (if disabled
|
(sequential? class)
|
||||||
"disabled"
|
(into class)
|
||||||
"")
|
|
||||||
:autoFocus (if auto-focus
|
(not (sequential? class))
|
||||||
"autoFocus"
|
(conj class)
|
||||||
"")}))]])
|
|
||||||
|
focused
|
||||||
|
(conj "is-focused")
|
||||||
|
)}
|
||||||
|
(when selectedItem
|
||||||
|
^{:key "hidden"}
|
||||||
|
[:div.level-item
|
||||||
|
[:div.control
|
||||||
|
[:div.tags.has-addons
|
||||||
|
[:span.tag (:name (js->clj selectedItem :keywordize-keys true))]
|
||||||
|
(when name
|
||||||
|
[:input {:type "hidden" :name name :value (:id (js->clj selectedItem :keywordize-keys true))}])
|
||||||
|
(when-not disabled
|
||||||
|
[:a.tag.is-delete {:on-click (fn []
|
||||||
|
(setInputValue nil)
|
||||||
|
(selectItem nil)
|
||||||
|
(when on-change
|
||||||
|
(on-change nil)))}])]]])
|
||||||
|
^{:key "main"}
|
||||||
|
[:div.control {:class (when (= :loading loading-status)
|
||||||
|
"is-loading")
|
||||||
|
:style {:padding "0px"
|
||||||
|
:width "100%"
|
||||||
|
:height "2em"
|
||||||
|
:margin "0px"}}
|
||||||
|
[:input (js->clj (getInputProps #js {:style #js {:border "0px"
|
||||||
|
:height "2em"
|
||||||
|
:width "100%"
|
||||||
|
"outline" "none"}
|
||||||
|
:disabled disabled
|
||||||
|
|
||||||
|
:onFocus #(set-focus true)
|
||||||
|
:onBlur #(set-focus false)
|
||||||
|
:autoFocus (if auto-focus
|
||||||
|
"autoFocus"
|
||||||
|
"")}))]]]
|
||||||
[:div {:class (when (and isOpen (seq items))
|
[:div {:class (when (and isOpen (seq items))
|
||||||
"typeahead-menu")}
|
"typeahead-menu")}
|
||||||
[:ul (js->clj (getMenuProps))
|
[:ul (js->clj (getMenuProps))
|
||||||
(if (and isOpen (seq items))
|
(when (and isOpen (seq items))
|
||||||
(for [[index item] (map vector (range) (js->clj items :keywordize-keys true))]
|
(for [[index item] (map vector (range) (js->clj items :keywordize-keys true))]
|
||||||
^{:key item}
|
^{:key item}
|
||||||
[:li.typeahead-suggestion (assoc (js->clj (getItemProps #js {:item item :index index}))
|
[:li.typeahead-suggestion (assoc (js->clj (getItemProps #js {:item item :index index}))
|
||||||
:class (if (= index highlightedIndex)
|
:class (when (= index highlightedIndex)
|
||||||
"typeahead-highlighted"))
|
"typeahead-highlighted"))
|
||||||
(:name item)]))]]]]))
|
(if entity->text
|
||||||
|
(entity->text item)
|
||||||
|
(:name item))]))]]]]))
|
||||||
|
|
||||||
(defn search-backed-typeahead [props]
|
(defn search-backed-typeahead [{:keys [search-query] :as props}]
|
||||||
[:div
|
[:div
|
||||||
[:f> typeahead-v3-internal props]])
|
[:f> typeahead-v3-internal (assoc props
|
||||||
|
:on-input-change
|
||||||
|
(fn [input set-items set-loading-status]
|
||||||
|
(re-frame/dispatch [::input-value-changed (aget input "inputValue") search-query set-items set-loading-status])
|
||||||
|
true))]])
|
||||||
|
|
||||||
|
|
||||||
|
(defn typeahead-v3 [{:keys [entities ^js entity->text entities-by-id entity-index] :as props}]
|
||||||
|
[:div
|
||||||
|
[:f> typeahead-v3-internal (assoc props
|
||||||
|
:on-input-change
|
||||||
|
(fn [input set-items]
|
||||||
|
(if entities-by-id
|
||||||
|
(do
|
||||||
|
(->> (.search entity-index (or (aget input "inputValue") "") #js {:fuzzy 0.2} )
|
||||||
|
clj->js
|
||||||
|
(take 10)
|
||||||
|
(set-items)))
|
||||||
|
|
||||||
|
(set-items (map clj->js (take 10 (filter (fn [x] (str/includes? (or (some-> (entity->text x) str/lower-case) "")
|
||||||
|
(or (some-> (aget input "inputValue") str/lower-case) "")))
|
||||||
|
entities)))))))]])
|
||||||
|
|||||||
@@ -3,23 +3,24 @@
|
|||||||
[auto-ap.entities.contact :as contact]
|
[auto-ap.entities.contact :as contact]
|
||||||
[auto-ap.entities.vendors :as entity]
|
[auto-ap.entities.vendors :as entity]
|
||||||
[auto-ap.forms :as forms]
|
[auto-ap.forms :as forms]
|
||||||
|
[auto-ap.forms.builder :as form-builder]
|
||||||
[auto-ap.status :as status]
|
[auto-ap.status :as status]
|
||||||
|
[auto-ap.views.components.level :refer [left-stack]]
|
||||||
[auto-ap.subs :as subs]
|
[auto-ap.subs :as subs]
|
||||||
[auto-ap.views.components.address :refer [address2-field]]
|
[auto-ap.views.components.address :refer [address2-field]]
|
||||||
[auto-ap.views.components.typeahead.vendor
|
|
||||||
:refer [search-backed-typeahead]]
|
|
||||||
[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.components.typeahead :refer [typeahead-v3]]
|
||||||
|
[auto-ap.views.components.typeahead.vendor
|
||||||
|
:refer [search-backed-typeahead]]
|
||||||
[auto-ap.views.pages.admin.vendors.common :as common]
|
[auto-ap.views.pages.admin.vendors.common :as common]
|
||||||
[auto-ap.views.utils
|
[auto-ap.views.utils
|
||||||
:refer [bind-field
|
:refer [dispatch-event multi-field str->int with-is-admin? with-user]]
|
||||||
dispatch-event
|
|
||||||
horizontal-field
|
|
||||||
with-is-admin?
|
|
||||||
with-user]]
|
|
||||||
[clojure.spec.alpha :as s]
|
[clojure.spec.alpha :as s]
|
||||||
[re-frame.core :as re-frame]
|
[re-frame.core :as re-frame]
|
||||||
[auto-ap.forms.builder :as form-builder]))
|
[reagent.core :as r]))
|
||||||
|
|
||||||
|
;; Remaining cleanup todos:
|
||||||
|
;; test minification
|
||||||
|
|
||||||
(re-frame/reg-sub
|
(re-frame/reg-sub
|
||||||
::can-submit
|
::can-submit
|
||||||
@@ -27,37 +28,6 @@
|
|||||||
(fn [form]
|
(fn [form]
|
||||||
(s/valid? ::entity/vendor (:data form))))
|
(s/valid? ::entity/vendor (:data form))))
|
||||||
|
|
||||||
(re-frame/reg-event-db
|
|
||||||
::removed-override
|
|
||||||
[(forms/in-form ::vendor-form)]
|
|
||||||
(fn [form [_ override-key index]]
|
|
||||||
|
|
||||||
(update-in form [:data override-key]
|
|
||||||
(fn [overrides]
|
|
||||||
(reduce
|
|
||||||
(fn [overrides [i override]]
|
|
||||||
(if (= i index)
|
|
||||||
overrides
|
|
||||||
(conj overrides override)))
|
|
||||||
[]
|
|
||||||
(map vector (range) overrides))))))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(re-frame/reg-event-db
|
|
||||||
::changed
|
|
||||||
[(forms/settles {:key ::vendor-form
|
|
||||||
:time 500
|
|
||||||
:event [::settled]})]
|
|
||||||
(forms/change-handler
|
|
||||||
::vendor-form
|
|
||||||
(fn [data field _]
|
|
||||||
(let [[override-key? i?] field]
|
|
||||||
(if (and (#{:account-overrides :terms-overrides :schedule-payment-dom} override-key?)
|
|
||||||
(nil? (get-in data [override-key? i? :key])))
|
|
||||||
[[override-key? i? :key] (random-uuid)]
|
|
||||||
[])))))
|
|
||||||
|
|
||||||
(re-frame/reg-event-fx
|
(re-frame/reg-event-fx
|
||||||
::save-complete
|
::save-complete
|
||||||
@@ -74,7 +44,8 @@
|
|||||||
{:vendor (cond-> {:id id
|
{:vendor (cond-> {:id id
|
||||||
:name name
|
:name name
|
||||||
:print-as print-as
|
:print-as print-as
|
||||||
:terms terms
|
:terms (or (str->int terms)
|
||||||
|
0)
|
||||||
:default-account-id (:id default-account)
|
:default-account-id (:id default-account)
|
||||||
:address address
|
:address address
|
||||||
:primary-contact primary-contact
|
:primary-contact primary-contact
|
||||||
@@ -82,28 +53,29 @@
|
|||||||
:invoice-reminder-schedule invoice-reminder-schedule}
|
:invoice-reminder-schedule invoice-reminder-schedule}
|
||||||
is-admin? (assoc :hidden hidden
|
is-admin? (assoc :hidden hidden
|
||||||
:terms-overrides (mapv
|
:terms-overrides (mapv
|
||||||
(fn [{:keys [client override id]}]
|
(fn [{:keys [client terms id]}]
|
||||||
{:id id
|
{:id id
|
||||||
:client-id (:id client)
|
:client-id (:id client)
|
||||||
:terms override})
|
:terms (or (str->int terms) 0)})
|
||||||
terms-overrides)
|
terms-overrides)
|
||||||
:account-overrides (mapv
|
:account-overrides (mapv
|
||||||
(fn [{:keys [client override id]}]
|
(fn [{:keys [client account id]}]
|
||||||
{:id id
|
{:id id
|
||||||
:client-id (:id client)
|
:client-id (:id client)
|
||||||
:account-id (:id override)})
|
:account-id (:id account)})
|
||||||
account-overrides)
|
account-overrides)
|
||||||
:schedule-payment-dom (mapv
|
:schedule-payment-dom (mapv
|
||||||
(fn [{:keys [client override id]}]
|
(fn [{:keys [client dom id]}]
|
||||||
{:id id
|
{:id id
|
||||||
:client-id (:id client)
|
:client-id (:id client)
|
||||||
:dom override})
|
:dom (or (str->int dom)
|
||||||
|
0)})
|
||||||
schedule-payment-dom)
|
schedule-payment-dom)
|
||||||
:automatically-paid-when-due (mapv
|
:automatically-paid-when-due (mapv
|
||||||
:id
|
(comp :id :client)
|
||||||
automatically-paid-when-due)
|
automatically-paid-when-due)
|
||||||
:legal-entity-first-name legal-entity-first-name
|
:legal-entity-first-name legal-entity-first-name
|
||||||
:legal-entity-middle-name legal-entity-middle-name
|
:legal-entity-middle-name legal-entity-middle-name
|
||||||
:legal-entity-last-name legal-entity-last-name
|
:legal-entity-last-name legal-entity-last-name
|
||||||
:legal-entity-tin legal-entity-tin
|
:legal-entity-tin legal-entity-tin
|
||||||
:legal-entity-tin-type (some-> legal-entity-tin-type clojure.core/name not-empty keyword)
|
:legal-entity-tin-type (some-> legal-entity-tin-type clojure.core/name not-empty keyword)
|
||||||
@@ -118,239 +90,176 @@
|
|||||||
:operation/name "UpsertVendor"} :venia/queries [{:query/data query}]}
|
:operation/name "UpsertVendor"} :venia/queries [{:query/data query}]}
|
||||||
:on-success [::save-complete]}}))))
|
:on-success [::save-complete]}}))))
|
||||||
|
|
||||||
(defn client-list [{:keys [override-key data]}]
|
(defn pull-left []
|
||||||
(let [clients @(re-frame/subscribe [::subs/clients])]
|
(into [:div {:style {:position "relative"
|
||||||
[form-builder/horizontal-control
|
:left "-40px"}}]
|
||||||
"Client"
|
(r/children (r/current-component))))
|
||||||
(doall
|
|
||||||
(for [[i override] (map vector (range) (conj (override-key data) {:key (random-uuid)}))]
|
|
||||||
^{:key (or (:id override)
|
|
||||||
(:key override))}
|
|
||||||
[:div.level
|
|
||||||
[:div.level-left
|
|
||||||
[:div.level-item
|
|
||||||
[form-builder/raw-field
|
|
||||||
[typeahead-v3 {:entities clients
|
|
||||||
:entity->text :name
|
|
||||||
:style {:width "13em"}
|
|
||||||
:type "typeahead-v3"
|
|
||||||
:field [override-key i]}]]]
|
|
||||||
|
|
||||||
[:div.level-item
|
|
||||||
[:a.button {:on-click (dispatch-event [::removed-override override-key i])} [:span.icon [:span.icon-remove]]]]]]))]))
|
|
||||||
|
|
||||||
(defn default-with-overrides [{:keys [override-key default-key data mandatory?]} template]
|
(defn contact-field [{:keys [name field]}]
|
||||||
(let [clients @(re-frame/subscribe [::subs/clients])]
|
[form-builder/with-scope {:scope field}
|
||||||
[:div
|
[form-builder/vertical-control
|
||||||
[form-builder/horizontal-control
|
name
|
||||||
[:span "Default"
|
[left-stack
|
||||||
(when mandatory?
|
[form-builder/vertical-control {:is-small? true}
|
||||||
[:span.has-text-danger " *"])]
|
"Name"
|
||||||
(template default-key nil)]
|
[:div.control.has-icons-left
|
||||||
[form-builder/horizontal-control
|
[form-builder/raw-field
|
||||||
"Overrides"
|
[:input.input.is-expanded {:type "text"
|
||||||
(doall
|
:field :name
|
||||||
(for [[i override] (map vector (range) (conj (override-key data) {:key (random-uuid)}))]
|
:spec ::contact/name}]]
|
||||||
^{:key (or
|
[:span.icon.is-small.is-left
|
||||||
(:id override)
|
[:i.fa.fa-user]]]]
|
||||||
(:key override)
|
[form-builder/vertical-control {:is-small? true}
|
||||||
)}
|
"Email"
|
||||||
[:div.level
|
|
||||||
[:div.level-left
|
[:div.control.has-icons-left
|
||||||
[:div.level-item
|
[:span.icon.is-small.is-left
|
||||||
[form-builder/raw-field
|
[:i.fa.fa-envelope]]
|
||||||
[typeahead-v3 {:entities clients
|
[form-builder/raw-field
|
||||||
:entity->text :name
|
[:input.input {:type "email"
|
||||||
:style {:width "13em"}
|
:field :email
|
||||||
:type "typeahead-v3"
|
:spec ::contact/email}]]]]
|
||||||
:field [override-key i :client]}]]]
|
[form-builder/vertical-control {:is-small? true}
|
||||||
[:div.level-item
|
"Phone"
|
||||||
(template
|
[:div.control.has-icons-left
|
||||||
[override-key i :override]
|
[form-builder/raw-field
|
||||||
(get-in data [override-key i :client]))]
|
[:input.input {:type "phone"
|
||||||
[:div.level-item
|
:field :phone
|
||||||
[:a.button {:on-click (dispatch-event [::removed-override override-key i])} [:span.icon [:span.icon-remove]]]]]]))]]))
|
:spec ::contact/phone}]]
|
||||||
|
[:span.icon.is-small.is-left
|
||||||
|
[:i.fa.fa-phone]]]]]]])
|
||||||
|
|
||||||
(defn client-overrides [{:keys [override-key data]} template]
|
|
||||||
(let [clients @(re-frame/subscribe [::subs/clients])]
|
|
||||||
[form-builder/horizontal-control
|
|
||||||
"Overrides"
|
|
||||||
(doall
|
|
||||||
(for [[i override] (map vector (range) (conj (override-key data) {:key (random-uuid)}))]
|
|
||||||
^{:key (or
|
|
||||||
(:id override)
|
|
||||||
(:key override))}
|
|
||||||
[:div.level
|
|
||||||
[:div.level-left
|
|
||||||
[:div.level-item
|
|
||||||
[form-builder/raw-field
|
|
||||||
[typeahead-v3 {:entities clients
|
|
||||||
:entity->text :name
|
|
||||||
:style {:width "13em"}
|
|
||||||
:type "typeahead-v3"
|
|
||||||
:field [override-key i :client]}]]]
|
|
||||||
[:div.level-item
|
|
||||||
(template
|
|
||||||
[override-key i :override]
|
|
||||||
(get-in data [override-key i :client]))]
|
|
||||||
[:div.level-item
|
|
||||||
[:a.button {:on-click (dispatch-event [::removed-override override-key i])} [:span.icon [:span.icon-remove]]]]]]))]))
|
|
||||||
|
|
||||||
(defn form-content [{:keys [data]}]
|
(defn form-content [{:keys [data]}]
|
||||||
|
(let [is-admin? @(re-frame/subscribe [::subs/is-admin?])
|
||||||
(let [is-admin? @(re-frame/subscribe [::subs/is-admin?])]
|
clients @(re-frame/subscribe [::subs/clients])]
|
||||||
[form-builder/builder {:submit-event [::save]
|
[form-builder/builder {:submit-event [::save]
|
||||||
:can-submit [::can-submit]
|
:can-submit [::can-submit]
|
||||||
:change-event [::changed]
|
|
||||||
:id ::vendor-form}
|
:id ::vendor-form}
|
||||||
[:div
|
[form-builder/field
|
||||||
[form-builder/horizontal-field
|
[:span "Name " [:span.has-text-danger "*"]]
|
||||||
[:span "Name " [:span.has-text-danger "*"]]
|
[:input.input {:type "text"
|
||||||
[:input.input {:type "text"
|
:auto-focus true
|
||||||
:auto-focus true
|
:field :name
|
||||||
:field :name
|
:spec ::entity/name}]]
|
||||||
:spec ::entity/name}]]
|
|
||||||
|
|
||||||
[form-builder/horizontal-field
|
[form-builder/field
|
||||||
"Print Checks As"
|
"Print Checks As"
|
||||||
[:input.input {:type "text"
|
[:input.input {:type "text"
|
||||||
:field :print-as
|
:field :print-as
|
||||||
:spec ::entity/print-as}]]
|
:spec ::entity/print-as}]]
|
||||||
|
(when is-admin?
|
||||||
|
[:div.field
|
||||||
|
[:label.checkbox
|
||||||
|
[form-builder/raw-field
|
||||||
|
[:input {:type "checkbox"
|
||||||
|
:field [:hidden]
|
||||||
|
:spec ::entity/hidden}]]
|
||||||
|
" Hidden"]])
|
||||||
|
|
||||||
|
[form-builder/section {:title "Terms"}
|
||||||
|
[form-builder/field
|
||||||
|
"Terms"
|
||||||
|
[:input.input {:type "number"
|
||||||
|
:step "1"
|
||||||
|
:style {:width "4em"}
|
||||||
|
:field [:terms]
|
||||||
|
:size 3
|
||||||
|
:spec ::entity/terms}]]
|
||||||
(when is-admin?
|
(when is-admin?
|
||||||
[form-builder/horizontal-field
|
[form-builder/field
|
||||||
"Hidden"
|
"Overrides"
|
||||||
[:input {:type "checkbox"
|
[multi-field {:type "multi-field"
|
||||||
:field :hidden
|
:field [:terms-overrides]
|
||||||
:spec ::entity/hidden}]])
|
:template [[typeahead-v3 {:entities clients
|
||||||
|
:entity->text :name
|
||||||
|
:style {:width "13em"}
|
||||||
|
:type "typeahead-v3"
|
||||||
|
:field [:client]}]
|
||||||
|
[:input.input {:type "number"
|
||||||
|
:step "1"
|
||||||
|
:style {:width "4em"}
|
||||||
|
:field [:terms]
|
||||||
|
:size 3
|
||||||
|
:spec ::entity/terms}]]}]])
|
||||||
|
]
|
||||||
|
(when is-admin?
|
||||||
|
[form-builder/section {:title "Schedule payment when due"}
|
||||||
|
[form-builder/field
|
||||||
|
"Client"
|
||||||
|
[multi-field {:type "multi-field"
|
||||||
|
:field [:automatically-paid-when-due]
|
||||||
|
:template [[typeahead-v3 {:entities clients
|
||||||
|
:entity->text :name
|
||||||
|
:style {:width "13em"}
|
||||||
|
:type "typeahead-v3"
|
||||||
|
:field [:client]}]]}]]])
|
||||||
|
|
||||||
(if is-admin?
|
(when is-admin?
|
||||||
[:<>
|
[form-builder/section {:title "Schedule payment on day of month"}
|
||||||
[form-builder/section {:title "Terms"}
|
[form-builder/field
|
||||||
[default-with-overrides {:data data
|
"Overrides"
|
||||||
:default-key :terms
|
[multi-field {:type "multi-field"
|
||||||
:override-key :terms-overrides}
|
:field [:schedule-payment-dom]
|
||||||
(fn [field _]
|
:template [[typeahead-v3 {:entities clients
|
||||||
[form-builder/raw-field
|
:entity->text :name
|
||||||
[:input.input {:type "number"
|
:style {:width "13em"}
|
||||||
:step "1"
|
:type "typeahead-v3"
|
||||||
:style {:width "4em"}
|
:field [:client]}]
|
||||||
:field field
|
[:input.input {:type "number"
|
||||||
:size 3
|
:step "1"
|
||||||
:spec ::entity/terms}]])]]]
|
:style {:width "4em"}
|
||||||
|
:field [:dom]
|
||||||
[form-builder/horizontal-field
|
:size 3
|
||||||
[:span "Terms"]
|
:spec ::entity/terms}]]}]]])
|
||||||
[:input.input {:type "number"
|
[form-builder/section {:title "Expense Accounts"}
|
||||||
:step "1"
|
[form-builder/field
|
||||||
:style {:width "4em"}
|
"Default *"
|
||||||
:field :terms
|
[search-backed-typeahead {:search-query (fn [i]
|
||||||
:size 3
|
[:search_account
|
||||||
:spec ::entity/terms}]])
|
{:query i}
|
||||||
|
[:name :id]])
|
||||||
|
:type "typeahead-v3"
|
||||||
|
:style {:width "19em"}
|
||||||
|
:field [:default-account]}]]
|
||||||
|
|
||||||
(when is-admin?
|
(when is-admin?
|
||||||
[form-builder/section {:title "Schedule payment when due"}
|
[form-builder/field
|
||||||
[client-list {:data data
|
"Overrides"
|
||||||
:override-key :automatically-paid-when-due}]])
|
[multi-field {:type "multi-field"
|
||||||
|
:field [:account-overrides]
|
||||||
|
:template (fn [entity]
|
||||||
|
[[typeahead-v3 {:entities clients
|
||||||
|
:entity->text :name
|
||||||
|
:style {:width "19em"}
|
||||||
|
:type "typeahead-v3"
|
||||||
|
:field [:client]}]
|
||||||
|
[search-backed-typeahead {:search-query (fn [i]
|
||||||
|
[:search_account
|
||||||
|
{:query i
|
||||||
|
:client_id (:id (:client entity))}
|
||||||
|
[:name :id]])
|
||||||
|
:type "typeahead-v3"
|
||||||
|
:style {:width "15em"}
|
||||||
|
:field [:account]}]])}]])]
|
||||||
|
|
||||||
(when is-admin?
|
[form-builder/with-scope {:scope [:address ]}
|
||||||
[form-builder/section {:title "Schedule payment on day of month"}
|
[form-builder/section {:title "Address"}
|
||||||
[client-overrides {:data data
|
[:div {:style {:width "30em"}}
|
||||||
:mandatory? true
|
[address2-field]]]]
|
||||||
:override-key :schedule-payment-dom}
|
|
||||||
(fn [field _]
|
[form-builder/section {:title "Contact"}
|
||||||
[form-builder/raw-field
|
[contact-field {:name "Primary"
|
||||||
[:input.input {:type "number"
|
:field [:primary-contact]}]
|
||||||
:step "1"
|
[contact-field {:name "Secondary"
|
||||||
:style {:width "5em"}
|
:field [:secondary-contact]}]]
|
||||||
:field field
|
|
||||||
:size 3
|
|
||||||
:spec ::entity/dom}]])]])
|
|
||||||
|
|
||||||
(if is-admin?
|
|
||||||
[form-builder/section {:title "Expense Accounts"}
|
|
||||||
[default-with-overrides {:data data
|
|
||||||
:mandatory? true
|
|
||||||
:default-key :default-account
|
|
||||||
:override-key :account-overrides}
|
|
||||||
(fn [field client]
|
|
||||||
[form-builder/raw-field
|
|
||||||
[search-backed-typeahead {:search-query (fn [i]
|
|
||||||
[:search_account
|
|
||||||
{:query i
|
|
||||||
:client-id (:id client)}
|
|
||||||
[:name :id]])
|
|
||||||
:type "typeahead-v3"
|
|
||||||
:style {:width "15em"}
|
|
||||||
:field field}]])]]
|
|
||||||
|
|
||||||
[form-builder/horizontal-field
|
(when is-admin?
|
||||||
"Expense Account"
|
[form-builder/section {:title "Legal Entity"}
|
||||||
[search-backed-typeahead {:search-query (fn [i]
|
[form-builder/vertical-control
|
||||||
[:search_account
|
"Name"
|
||||||
{:query i}
|
[left-stack
|
||||||
[:name :id]])
|
|
||||||
:type "typeahead-v3"
|
|
||||||
:field :default-account}]])
|
|
||||||
|
|
||||||
[form-builder/with-scope {:scope [:address ]}
|
|
||||||
[form-builder/section {:title "Address"}
|
|
||||||
[address2-field]]]
|
|
||||||
|
|
||||||
[form-builder/section {:title "Contact"}
|
|
||||||
[form-builder/horizontal-control
|
|
||||||
"Primary"
|
|
||||||
[:div.control.has-icons-left
|
|
||||||
[form-builder/raw-field
|
|
||||||
[:input.input.is-expanded {:type "text"
|
|
||||||
:field [:primary-contact :name]
|
|
||||||
:spec ::contact/name}]]
|
|
||||||
[:span.icon.is-small.is-left
|
|
||||||
[:i.fa.fa-user]]]
|
|
||||||
|
|
||||||
[:div.control.has-icons-left
|
|
||||||
[:span.icon.is-small.is-left
|
|
||||||
[:i.fa.fa-envelope]]
|
|
||||||
[form-builder/raw-field
|
|
||||||
[:input.input {:type "email"
|
|
||||||
:field [:primary-contact :email]
|
|
||||||
:spec ::contact/email}]]]
|
|
||||||
|
|
||||||
[:div.control.has-icons-left
|
|
||||||
[form-builder/raw-field
|
|
||||||
[:input.input {:type "phone"
|
|
||||||
:field [:primary-contact :phone]
|
|
||||||
:spec ::contact/phone}]]
|
|
||||||
[:span.icon.is-small.is-left
|
|
||||||
[:i.fa.fa-phone]]]]
|
|
||||||
[form-builder/horizontal-control
|
|
||||||
"Secondary"
|
|
||||||
[:div.control.has-icons-left
|
|
||||||
[form-builder/raw-field
|
|
||||||
[:input.input.is-expanded {:type "text"
|
|
||||||
:field [:secondary-contact :name]
|
|
||||||
:spec ::contact/name}]]
|
|
||||||
[:span.icon.is-small.is-left
|
|
||||||
[:i.fa.fa-user]]]
|
|
||||||
[:div.control.has-icons-left
|
|
||||||
[:span.icon.is-small.is-left
|
|
||||||
[:i.fa.fa-envelope]]
|
|
||||||
[form-builder/raw-field
|
|
||||||
[:input.input {:type "email"
|
|
||||||
:field [:secondary-contact :email]
|
|
||||||
:spec ::contact/email}]]]
|
|
||||||
[:div.control.has-icons-left
|
|
||||||
[form-builder/raw-field
|
|
||||||
[:input.input {:type "phone"
|
|
||||||
:field [:secondary-contact :phone]
|
|
||||||
:spec ::contact/phone}]]
|
|
||||||
[:span.icon.is-small.is-left
|
|
||||||
[:i.fa.fa-phone]]]]]
|
|
||||||
|
|
||||||
|
|
||||||
(when is-admin?
|
|
||||||
[form-builder/section {:title "Legal Entity"}
|
|
||||||
[form-builder/horizontal-control
|
|
||||||
"Name"
|
|
||||||
[:div.control
|
[:div.control
|
||||||
[form-builder/raw-field
|
[form-builder/raw-field
|
||||||
[:input.input {:type "text"
|
[:input.input {:type "text"
|
||||||
@@ -370,16 +279,16 @@
|
|||||||
[:input.input {:type "text"
|
[:input.input {:type "text"
|
||||||
:placeholder "Last Name"
|
:placeholder "Last Name"
|
||||||
:field [:legal-entity-last-name]
|
:field [:legal-entity-last-name]
|
||||||
:spec ::contact/name}]]]]
|
:spec ::contact/name}]]]]]
|
||||||
[form-builder/horizontal-control
|
[form-builder/vertical-control
|
||||||
"TIN"
|
"TIN"
|
||||||
[:div.control
|
[left-stack
|
||||||
[form-builder/raw-field
|
[form-builder/raw-field
|
||||||
[:input.input {:type "text"
|
[:input.input {:type "text"
|
||||||
:placeholder "SSN or EIN"
|
:placeholder "SSN or EIN"
|
||||||
:field [:legal-entity-tin]
|
:field [:legal-entity-tin]
|
||||||
:size "12"
|
:size "12"
|
||||||
:spec ::contact/name}]]]
|
:spec ::contact/name}]]
|
||||||
|
|
||||||
[:div.control
|
[:div.control
|
||||||
[:div.select
|
[:div.select
|
||||||
@@ -388,19 +297,20 @@
|
|||||||
:field [:legal-entity-tin-type]}
|
:field [:legal-entity-tin-type]}
|
||||||
[:option {:value nil} ""]
|
[:option {:value nil} ""]
|
||||||
[:option {:value "ein"} "EIN"]
|
[:option {:value "ein"} "EIN"]
|
||||||
[:option {:value "ssn"} "SSN"]]]]]]
|
[:option {:value "ssn"} "SSN"]]]]]]]
|
||||||
|
|
||||||
[form-builder/horizontal-control
|
[form-builder/vertical-control
|
||||||
"1099 Type"
|
"1099 Type"
|
||||||
[:div.control
|
[:div.control
|
||||||
[:div.select
|
[:div.select
|
||||||
[form-builder/raw-field
|
[form-builder/raw-field
|
||||||
[:select {:type "select"
|
[:select {:type "select"
|
||||||
:field [:legal-entity-1099-type]}
|
:field [:legal-entity-1099-type]}
|
||||||
[:option {:value nil} ""]
|
[:option {:value nil} ""]
|
||||||
[:option {:value "none"} "Don't 1099"]
|
[:option {:value "none"} "Don't 1099"]
|
||||||
[:option {:value "misc"} "Misc"]
|
[:option {:value "misc"} "Misc"]
|
||||||
[:option {:value "landlord"} "Landlord"]]]]]]])]]))
|
[:option {:value "landlord"} "Landlord"]]]]]]])
|
||||||
|
[form-builder/hidden-submit-button]]))
|
||||||
|
|
||||||
(defn vendor-dialog [ ]
|
(defn vendor-dialog [ ]
|
||||||
(let [{:keys [data]} @(re-frame/subscribe [::forms/form ::vendor-form])]
|
(let [{:keys [data]} @(re-frame/subscribe [::forms/form ::vendor-form])]
|
||||||
@@ -417,7 +327,6 @@
|
|||||||
common/default-read]]}
|
common/default-read]]}
|
||||||
:owns-state {:single ::select-vendor-form}
|
:owns-state {:single ::select-vendor-form}
|
||||||
:on-success (fn [r]
|
:on-success (fn [r]
|
||||||
(println (:vendor-by-id r))
|
|
||||||
[::started (:vendor-by-id r)])}}))
|
[::started (:vendor-by-id r)])}}))
|
||||||
|
|
||||||
(re-frame/reg-sub
|
(re-frame/reg-sub
|
||||||
@@ -442,7 +351,7 @@
|
|||||||
:type "typeahead-v3"
|
:type "typeahead-v3"
|
||||||
:auto-focus true
|
:auto-focus true
|
||||||
:field [:vendor]}]]
|
:field [:vendor]}]]
|
||||||
#_[form-builder/submit-button "Save"]])
|
[form-builder/hidden-submit-button]])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -450,33 +359,17 @@
|
|||||||
::started
|
::started
|
||||||
(fn [{:keys [db]} [_ vendor]]
|
(fn [{:keys [db]} [_ vendor]]
|
||||||
{:db (-> db (forms/start-form ::vendor-form (-> vendor
|
{:db (-> db (forms/start-form ::vendor-form (-> vendor
|
||||||
(update :account-overrides #(mapv
|
(update :automatically-paid-when-due #(mapv (fn [apwd]
|
||||||
(fn [ao]
|
{:id (:id apwd)
|
||||||
{:id (:id ao)
|
:client apwd})
|
||||||
:client (:client ao)
|
%))
|
||||||
:override (:account ao)})
|
|
||||||
%))
|
|
||||||
|
|
||||||
(update :schedule-payment-dom #(mapv
|
|
||||||
(fn [spdom]
|
|
||||||
{:id (:id spdom)
|
|
||||||
:client (:client spdom)
|
|
||||||
:override (:dom spdom)})
|
|
||||||
%))
|
|
||||||
(update :terms-overrides #(mapv
|
|
||||||
(fn [to]
|
|
||||||
{:id (:id to)
|
|
||||||
:client (:client to)
|
|
||||||
:override (:terms to)})
|
|
||||||
%))
|
|
||||||
(update :automatically-paid-when-due #(mapv identity %))
|
|
||||||
(update :hidden #(if (nil? %)
|
(update :hidden #(if (nil? %)
|
||||||
false
|
false
|
||||||
%)))))
|
%)))))
|
||||||
:dispatch [::modal/modal-requested
|
:dispatch [::modal/modal-requested
|
||||||
{:title "Vendor"
|
{:title "Vendor"
|
||||||
:class "is-wide"
|
|
||||||
:body [vendor-dialog]
|
:body [vendor-dialog]
|
||||||
|
:class "semi-wide"
|
||||||
:confirm {:value "Save Vendor"
|
:confirm {:value "Save Vendor"
|
||||||
:status-from [::status/single ::vendor-form]
|
:status-from [::status/single ::vendor-form]
|
||||||
:class "is-primary"
|
:class "is-primary"
|
||||||
@@ -490,7 +383,6 @@
|
|||||||
{:db (-> db (forms/start-form ::select-vendor-form {}))
|
{:db (-> db (forms/start-form ::select-vendor-form {}))
|
||||||
:dispatch [::modal/modal-requested
|
:dispatch [::modal/modal-requested
|
||||||
{:title "Select Vendor"
|
{:title "Select Vendor"
|
||||||
:class "is-wide"
|
|
||||||
:body [select-vendor-form-content]
|
:body [select-vendor-form-content]
|
||||||
:confirm {:value "Choose a vendor"
|
:confirm {:value "Choose a vendor"
|
||||||
:status-from [::status/single ::select-vendor-form]
|
:status-from [::status/single ::select-vendor-form]
|
||||||
|
|||||||
@@ -139,12 +139,12 @@
|
|||||||
[multi-field {:type "multi-field"
|
[multi-field {:type "multi-field"
|
||||||
:field [:client-overrides]
|
:field [:client-overrides]
|
||||||
:template [[typeahead-v3 {:entities @(re-frame/subscribe [::subs/clients])
|
:template [[typeahead-v3 {:entities @(re-frame/subscribe [::subs/clients])
|
||||||
:style {:width "20em"}
|
:style {:width "13em"}
|
||||||
:entity->text :name
|
:entity->text :name
|
||||||
:type "typeahead-v3"
|
:type "typeahead-v3"
|
||||||
:field [:client]}]
|
:field [:client]}]
|
||||||
[:input.input {:type "text"
|
[:input.input {:type "text"
|
||||||
:style {:width "20em"}
|
:style {:width "15em"}
|
||||||
:placeholder "Bubblegum"
|
:placeholder "Bubblegum"
|
||||||
:field [:name]}]
|
:field [:name]}]
|
||||||
]}])
|
]}])
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
[auto-ap.views.components.address :refer [address2-field]]
|
[auto-ap.views.components.address :refer [address2-field]]
|
||||||
[react-signature-canvas]
|
[react-signature-canvas]
|
||||||
[auto-ap.views.components.typeahead :refer [typeahead-v3]]
|
[auto-ap.views.components.typeahead :refer [typeahead-v3]]
|
||||||
|
[auto-ap.views.components.level :refer [left-stack]]
|
||||||
[auto-ap.views.components.typeahead.vendor
|
[auto-ap.views.components.typeahead.vendor
|
||||||
:refer [search-backed-typeahead]]
|
:refer [search-backed-typeahead]]
|
||||||
[auto-ap.views.utils
|
[auto-ap.views.utils
|
||||||
@@ -350,8 +351,6 @@
|
|||||||
(#{:credit ":credit"} type) [:span.icon-credit-card-1]
|
(#{:credit ":credit"} type) [:span.icon-credit-card-1]
|
||||||
|
|
||||||
:else [:span.icon-accounting-bill])]]
|
:else [:span.icon-accounting-bill])]]
|
||||||
#_[:div.level-item
|
|
||||||
]
|
|
||||||
[:div.level-item code ": " name]]
|
[:div.level-item code ": " name]]
|
||||||
[:div.level-right
|
[:div.level-right
|
||||||
[:div.level-item
|
[:div.level-item
|
||||||
@@ -647,7 +646,7 @@
|
|||||||
:style { :width "15em"}}]
|
:style { :width "15em"}}]
|
||||||
[:input.input {:field [:location]
|
[:input.input {:field [:location]
|
||||||
:placeholder "DT"
|
:placeholder "DT"
|
||||||
:maxlength 2
|
:max-length 2
|
||||||
:style { :width "4em"}}]]}]]])
|
:style { :width "4em"}}]]}]]])
|
||||||
|
|
||||||
(defn bank-accounts-section []
|
(defn bank-accounts-section []
|
||||||
@@ -674,41 +673,35 @@
|
|||||||
|
|
||||||
[form-builder/section {:title "Cash Flow"}
|
[form-builder/section {:title "Cash Flow"}
|
||||||
[:label.label (str "Week A (" next-week-a ")")]
|
[:label.label (str "Week A (" next-week-a ")")]
|
||||||
[:div.level
|
[left-stack
|
||||||
[:div.level-left
|
[form-builder/field
|
||||||
[:div.level-item
|
"Regular Credits"
|
||||||
[form-builder/field
|
[:input.input {:type "number"
|
||||||
"Regular Credits"
|
:style {:width "10em"}
|
||||||
[:input.input {:type "number"
|
:placeholder "500.00"
|
||||||
:style {:width "10em"}
|
:field [:week-a-credits]
|
||||||
:placeholder "500.00"
|
:step "0.01"}]]
|
||||||
:field [:week-a-credits]
|
[form-builder/field
|
||||||
:step "0.01"}]]]
|
"Regular Debits"
|
||||||
[:div.level-item
|
[:input.input {:type "number"
|
||||||
[form-builder/field
|
:style {:width "10em"}
|
||||||
"Regular Debits"
|
:placeholder "150.00"
|
||||||
[:input.input {:type "number"
|
:field [:week-a-debits]
|
||||||
:style {:width "10em"}
|
:step "0.01"}]]]
|
||||||
:placeholder "150.00"
|
|
||||||
:field [:week-a-debits]
|
|
||||||
:step "0.01"}]]]]]
|
|
||||||
[:label.label (str "Week B (" next-week-b ")")]
|
[:label.label (str "Week B (" next-week-b ")")]
|
||||||
[:div.level
|
[left-stack
|
||||||
[:div.level-left
|
[form-builder/field "Regular Credits"
|
||||||
[:div.level-item
|
[:input.input {:type "number"
|
||||||
[form-builder/field "Regular Credits"
|
:style {:width "10em"}
|
||||||
[:input.input {:type "number"
|
:placeholder "1000.00"
|
||||||
:style {:width "10em"}
|
:field [:week-b-credits]
|
||||||
:placeholder "1000.00"
|
:step "0.01"}]]
|
||||||
:field [:week-b-credits]
|
[form-builder/field "Regular Debits"
|
||||||
:step "0.01"}]]]
|
[:input.input {:type "number"
|
||||||
[:div.level-item
|
:style {:width "10em"}
|
||||||
[form-builder/field "Regular Debits"
|
:placeholder "250.00"
|
||||||
[:input.input {:type "number"
|
:field [:week-b-debits]
|
||||||
:style {:width "10em"}
|
:step "0.01"}]]]
|
||||||
:placeholder "250.00"
|
|
||||||
:field [:week-b-debits]
|
|
||||||
:step "0.01"}]]]]]
|
|
||||||
[:div.field
|
[:div.field
|
||||||
[:label.label "Forecasted transactions"]
|
[:label.label "Forecasted transactions"]
|
||||||
|
|
||||||
|
|||||||
@@ -148,55 +148,59 @@
|
|||||||
value
|
value
|
||||||
(conj value {:key (random-uuid)
|
(conj value {:key (random-uuid)
|
||||||
:new? true}))]
|
:new? true}))]
|
||||||
[:div {:style {:margin-bottom "1em"}}
|
[:div {:style {:margin-bottom "0.25em"}}
|
||||||
(for [[i override] (map vector (range) value)
|
(for [[i override] (map vector (range) value)
|
||||||
:let [is-disabled? (if (= false allow-change?)
|
:let [is-disabled? (if (= false allow-change?)
|
||||||
(not (boolean (:new? override)))
|
(not (boolean (:new? override)))
|
||||||
nil)]
|
nil)]
|
||||||
]
|
]
|
||||||
^{:key (:key override)}
|
^{:key (:key override)}
|
||||||
[:div.level
|
[:div.level {:style {:margin-bottom "0.25em"}}
|
||||||
[:div.level-left {:style (when (and (= i (dec (count value)))
|
[:div.level-left {:style {:padding "0.5em 1em"}
|
||||||
(:new? override))
|
:class (cond
|
||||||
{:background "#EEE"
|
(and (= i (dec (count value)))
|
||||||
:padding "0.25em 1em 0.25em 0em"})}
|
(:new? override))
|
||||||
[:div.level-item
|
"has-background-light"
|
||||||
(if (:new? override)
|
|
||||||
|
|
||||||
[:div.icon.is-medium {:class (when (not= i (dec (count value)))
|
|
||||||
"has-text-info")}
|
|
||||||
[:i.fa.fa-plus]]
|
|
||||||
[:div.icon.is-medium])]
|
|
||||||
[:<> (for [[idx template] (map vector (range ) template)]
|
|
||||||
^{:key idx}
|
|
||||||
|
|
||||||
[:div.level-item
|
(:new? override)
|
||||||
(update template 1 assoc
|
"has-background-info-light"
|
||||||
:value (let [value (get-in override (get-in template [1 :field])) ;; TODO this is really ugly to support maps or strings
|
:else
|
||||||
value (if (map? value)
|
"")}
|
||||||
(dissoc value :key :new?)
|
(let [template (if (fn? template)
|
||||||
value)]
|
(template override)
|
||||||
(if (= value {})
|
template)]
|
||||||
nil
|
|
||||||
value))
|
[:<> (for [[idx template] (map vector (range ) template)]
|
||||||
:disabled (or is-disabled? (get-in template [1 :disabled]))
|
^{:key idx}
|
||||||
:on-change (fn [e]
|
|
||||||
(reset! value-repr
|
[:div.level-item
|
||||||
(into []
|
(update template 1 assoc
|
||||||
(filter (fn [r]
|
:value (let [value (get-in override (get-in template [1 :field])) ;; TODO this is really ugly to support maps or strings
|
||||||
(not= [:key :new?] (keys r)))
|
value (if (map? value)
|
||||||
(assoc-in value (into [i] (get-in template [1 :field]))
|
(dissoc value :key :new?)
|
||||||
(let [this-value (if (and e (.. e -target))
|
value)]
|
||||||
(.. e -target -value )
|
(if (= value {})
|
||||||
e)]
|
nil
|
||||||
(if (map? this-value)
|
value))
|
||||||
(update this-value :key (fnil identity (random-uuid)))
|
:disabled (or is-disabled? (get-in template [1 :disabled]))
|
||||||
this-value)) ))))
|
:on-change (fn [e]
|
||||||
(on-change (mapv
|
(reset! value-repr
|
||||||
(fn [v]
|
(into []
|
||||||
(dissoc v :new? :key))
|
(filter (fn [r]
|
||||||
@value-repr))))])
|
(not= [:key :new?] (keys r)))
|
||||||
]
|
(assoc-in value
|
||||||
|
(into [i] (get-in template [1 :field]))
|
||||||
|
(let [this-value (if (and e (.. e -target))
|
||||||
|
(.. e -target -value )
|
||||||
|
e)]
|
||||||
|
(if (map? this-value)
|
||||||
|
(update this-value :key (fnil identity (random-uuid)))
|
||||||
|
this-value)) ))))
|
||||||
|
(on-change (mapv
|
||||||
|
(fn [v]
|
||||||
|
(dissoc v :new? :key))
|
||||||
|
@value-repr))))])
|
||||||
|
])
|
||||||
(when-not disable-remove?
|
(when-not disable-remove?
|
||||||
[:div.level-item
|
[:div.level-item
|
||||||
[:a.button.level-item
|
[:a.button.level-item
|
||||||
@@ -537,3 +541,18 @@
|
|||||||
|
|
||||||
(defn account->match-text [x]
|
(defn account->match-text [x]
|
||||||
(str (:numeric-code x) " - " (:name x)))
|
(str (:numeric-code x) " - " (:name x)))
|
||||||
|
|
||||||
|
(defn str->int [x]
|
||||||
|
(cond
|
||||||
|
(nil? x)
|
||||||
|
nil
|
||||||
|
|
||||||
|
(and (string? x)
|
||||||
|
(str/blank? x))
|
||||||
|
nil
|
||||||
|
|
||||||
|
(string? x)
|
||||||
|
(js/parseInt x)
|
||||||
|
|
||||||
|
:else
|
||||||
|
x))
|
||||||
|
|||||||
@@ -17,13 +17,15 @@
|
|||||||
|
|
||||||
(t/deftest yodlee->transaction
|
(t/deftest yodlee->transaction
|
||||||
(t/testing "Should parse dates"
|
(t/testing "Should parse dates"
|
||||||
(t/is (= #inst "2021-01-01T00:00:00-08:00" (:transaction/date (sut/yodlee->transaction (assoc base-transaction :date "2021-01-01")))))
|
(t/is (= #inst "2021-01-01T00:00:00-08:00" (:transaction/date (sut/yodlee->transaction (assoc base-transaction :date "2021-01-01") false))))
|
||||||
(t/is (= #inst "2021-06-01T00:00:00-07:00" (:transaction/date (sut/yodlee->transaction (assoc base-transaction :date "2021-06-01"))))))
|
(t/is (= #inst "2021-06-01T00:00:00-07:00" (:transaction/date (sut/yodlee->transaction (assoc base-transaction :date "2021-06-01") false)))))
|
||||||
|
|
||||||
(t/testing "Should invert amount for debits"
|
(t/testing "Should invert amount for debits"
|
||||||
(t/is (= -12.0 (:transaction/amount (sut/yodlee->transaction (assoc base-transaction
|
(t/is (= -12.0 (:transaction/amount (sut/yodlee->transaction (assoc base-transaction
|
||||||
:amount {:amount 12.0}
|
:amount {:amount 12.0}
|
||||||
:baseType "DEBIT")))))
|
:baseType "DEBIT")
|
||||||
|
false))))
|
||||||
(t/is (= 12.0 (:transaction/amount (sut/yodlee->transaction (assoc base-transaction
|
(t/is (= 12.0 (:transaction/amount (sut/yodlee->transaction (assoc base-transaction
|
||||||
:amount {:amount 12.0}
|
:amount {:amount 12.0}
|
||||||
:baseType "CREDIT")))))))
|
:baseType "CREDIT")
|
||||||
|
false))))))
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
(ns auto-ap.routes.invoices-test
|
(ns auto-ap.routes.invoice-test
|
||||||
(:require
|
(:require
|
||||||
[auto-ap.datomic :refer [uri conn]]
|
[auto-ap.datomic :refer [uri conn]]
|
||||||
[auto-ap.datomic.migrate :as m]
|
[auto-ap.datomic.migrate :as m]
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
(ns auto-ap.routes.invoice-test
|
|
||||||
(:require [clojure.test :as t]))
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user