More standardization
This commit is contained in:
@@ -197,6 +197,11 @@ nav.navbar .navbar-item.is-active {
|
||||
background-color: #F9F9F9;
|
||||
border-right: 1px solid #DEDEDE;
|
||||
}
|
||||
|
||||
.aside .subtitle {
|
||||
padding-top: 2rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
.messages {
|
||||
display:block;
|
||||
background-color: #fff;
|
||||
|
||||
@@ -1,36 +1,40 @@
|
||||
(ns auto-ap.datomic.clients
|
||||
(:require [datomic.api :as d]
|
||||
[auto-ap.datomic :refer [uri]]))
|
||||
[auto-ap.datomic :refer [uri]]
|
||||
[clojure.tools.logging :as log]))
|
||||
|
||||
(defn cleanse [e]
|
||||
(-> e
|
||||
(update :client/location-matches
|
||||
(fn [lms]
|
||||
(map #(assoc % :location-match/match (first (:location-match/matches %))) lms)))
|
||||
(update :client/bank-accounts
|
||||
(fn [bas]
|
||||
(map (fn [i ba]
|
||||
(-> ba
|
||||
(update :bank-account/type :db/ident )
|
||||
(update :bank-account/sort-order (fn [so] (or so i)))))
|
||||
(range) bas)))))
|
||||
(defn get-all []
|
||||
|
||||
(->> (d/q '[:find (pull ?e [* {:client/address [*]}])
|
||||
(->> (d/q '[:find (pull ?e [*
|
||||
{:client/address [*]}
|
||||
{:client/bank-accounts [* {:bank-account/type [*]}]}])
|
||||
:where [?e :client/name]]
|
||||
(d/db (d/connect uri)))
|
||||
(map first)
|
||||
(map (fn [c]
|
||||
(update c :client/location-matches
|
||||
(fn [lms]
|
||||
|
||||
(map #(assoc % :location-match/match (first (:location-match/matches %))) lms)))))
|
||||
(map (fn [c]
|
||||
(update c :client/bank-accounts
|
||||
(fn [bas]
|
||||
(map (fn [i ba]
|
||||
(-> ba
|
||||
(update :bank-account/type :db/ident )
|
||||
(update :bank-account/sort-order (fn [so] (or so i)))))
|
||||
(range) bas)))))
|
||||
(map cleanse)
|
||||
|
||||
))
|
||||
(defn get-by-id [id]
|
||||
(->>
|
||||
(d/query (-> {:query {:find ['(pull ?e [*])]
|
||||
(d/query (-> {:query {:find ['(pull ?e [* {:client/bank-accounts [* {:bank-account/type [*]}]}])]
|
||||
:in ['$ '?e]
|
||||
:where [['?e]]}
|
||||
:args [(d/db (d/connect uri)) id]}
|
||||
))
|
||||
(first)
|
||||
(first)
|
||||
(cleanse)
|
||||
#_(map first)
|
||||
|
||||
#_(first)))
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
new-id-set (set (map :db/id vs))
|
||||
retract-ids (filter (complement new-id-set) ids)]
|
||||
(cond-> []
|
||||
true (into (map (fn [i] [:db/retractEntity i ]) retract-ids))
|
||||
true (into (map (fn [i] [:db/retract e a i ]) retract-ids))
|
||||
(seq vs) (conj {:db/id e
|
||||
a vs})))})}]])
|
||||
|
||||
@@ -308,7 +308,8 @@
|
||||
{:db/ident :invoice/automatically-paid-when-due
|
||||
:db/doc "Whether this invoice should be marked as paid when it's due"
|
||||
:db/valueType :db.type/boolean
|
||||
:db/cardinality :db.cardinality/one}]]}}
|
||||
:db/cardinality :db.cardinality/one}]]}
|
||||
:auto-ap/fix-reset-rels {:txes-fn `reset-function}}
|
||||
]
|
||||
(println "Conforming database...")
|
||||
(c/ensure-conforms conn norms-map)
|
||||
|
||||
@@ -65,7 +65,8 @@
|
||||
{
|
||||
:location_match
|
||||
{:fields {:location {:type 'String}
|
||||
:match {:type 'String}}}
|
||||
:match {:type 'String}
|
||||
:id {:type :id}}}
|
||||
|
||||
:client
|
||||
{:fields {:id {:type :id}
|
||||
@@ -526,7 +527,8 @@
|
||||
:invoice_payment_amount {:fields {:invoice_id {:type :id}
|
||||
:amount {:type 'Float}}}
|
||||
:edit_location_match {:fields {:location {:type 'String}
|
||||
:match {:type 'String}}}
|
||||
:match {:type 'String}
|
||||
:id {:type :id}}}
|
||||
|
||||
:edit_forecasted_transaction {:fields {:identifier {:type 'String}
|
||||
:id {:type :id}
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
[datomic.api :as d]
|
||||
[auto-ap.datomic :refer [uri remove-nils]]
|
||||
[auto-ap.graphql.utils :refer [->graphql assert-admin can-see-client?]]
|
||||
[clojure.string :as str]))
|
||||
[clojure.string :as str]
|
||||
[clojure.tools.logging :as log]))
|
||||
|
||||
(defn assert-client-code-is-unique [code]
|
||||
(when (seq (d/query {:query {:find '[?id]
|
||||
@@ -76,6 +77,7 @@
|
||||
:forecasted-transaction/amount (:amount %)}
|
||||
)
|
||||
(:forecasted_transactions edit_client))]]
|
||||
_ (log/info "upserting client" transactions)
|
||||
result @(d/transact (d/connect uri) transactions)]
|
||||
(-> result :tempids (get id) (or id) d-clients/get-by-id
|
||||
(update :client/location-matches
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
:graphql {:token token
|
||||
:query-obj {:venia/queries [[:client
|
||||
|
||||
[:id :name :code :email :matches :week-a-debits :week-a-credits :week-b-debits :week-b-credits :locations [:location-matches [:location :match]] [:bank-accounts [:id :code :number :bank-name :bank-code :check-number :name :routing :type :sort-order :visible :yodlee-account-id :locations :include-in-reports] ]
|
||||
[:id :name :code :email :matches :week-a-debits :week-a-credits :week-b-debits :week-b-credits :locations [:location-matches [:id :location :match]] [:bank-accounts [:id :code :number :bank-name :bank-code :check-number :name :routing :type :sort-order :visible :yodlee-account-id :locations :include-in-reports] ]
|
||||
[:address [:street1 :street2 :city :state :zip]]
|
||||
[:forecasted-transactions [:id :amount :identifier :day-of-month]]]]
|
||||
[:vendor
|
||||
@@ -75,7 +75,7 @@
|
||||
(fn [{:keys [db]} [_ token user]]
|
||||
{:graphql {:token token
|
||||
:query-obj {:venia/queries [[:client
|
||||
[:id :name :code :matches :locations :week-a-debits :week-a-credits :week-b-debits :week-b-credits [:location-matches [:location :match]]
|
||||
[:id :name :code :matches :locations :week-a-debits :week-a-credits :week-b-debits :week-b-credits [:location-matches [:id :location :match]]
|
||||
[:address [:street1 :street2 :city :state :zip]]
|
||||
[:bank-accounts [:id :code :number :bank-name :bank-code :check-number :name :routing :type :sort-order :visible :yodlee-account-id :locations :include-in-reports] ]
|
||||
[:forecasted-transactions [:id :amount :identifier :day-of-month]]]]
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
[auto-ap.subs :as subs]
|
||||
[auto-ap.views.components.address :refer [address-field]]
|
||||
[auto-ap.views.components.layouts :refer [side-bar]]
|
||||
[auto-ap.views.utils :refer [dispatch-event horizontal-field nf]]
|
||||
[auto-ap.views.utils :refer [dispatch-event horizontal-field nf multi-field]]
|
||||
[cljs-time.coerce :as coerce]
|
||||
[cljs-time.core :as t]
|
||||
[clojure.spec.alpha :as s]
|
||||
@@ -12,9 +12,11 @@
|
||||
|
||||
(re-frame/reg-sub
|
||||
::can-submit
|
||||
:<- [::forms/form ::form]
|
||||
(fn [{:keys [data status]} _]
|
||||
(s/valid? ::entity/client data)))
|
||||
:<- [::new-client-request]
|
||||
(fn [r _]
|
||||
true
|
||||
|
||||
#_(s/valid? ::entity/client r)))
|
||||
|
||||
(re-frame/reg-sub
|
||||
::new-client-request
|
||||
@@ -24,8 +26,8 @@
|
||||
:name (:name new-client-data)
|
||||
:code (:code new-client-data) ;; TODO add validation can't change
|
||||
:email (:email new-client-data)
|
||||
:locations (:locations new-client-data)
|
||||
:matches (vec (:matches new-client-data))
|
||||
:locations (mapv :location (:locations new-client-data))
|
||||
:matches (mapv :match (:matches new-client-data))
|
||||
:location-matches (:location-matches new-client-data)
|
||||
:week-a-credits (:week-a-credits new-client-data)
|
||||
:week-a-debits (:week-a-debits new-client-data)
|
||||
@@ -51,7 +53,7 @@
|
||||
:id id
|
||||
:sort-order sort-order
|
||||
:visible visible
|
||||
:locations (vec locations)
|
||||
:locations (mapv :location locations)
|
||||
:yodlee-account-id (when yodlee-account-id
|
||||
(js/parseInt yodlee-account-id))
|
||||
:code (if new?
|
||||
@@ -65,9 +67,19 @@
|
||||
(re-frame/reg-event-fx
|
||||
::editing
|
||||
(fn [{:keys [db]} [_ client-id]]
|
||||
(println (get (:clients db) client-id))
|
||||
{:db (-> db
|
||||
(forms/stop-form ::form)
|
||||
(forms/start-form ::form (get (:clients db) client-id)))}))
|
||||
(forms/start-form ::form (-> (get (:clients db) client-id)
|
||||
(update :locations #(mapv (fn [l] {:location l}) %))
|
||||
(update :matches #(mapv (fn [l] {:match l}) %))
|
||||
(update :bank-accounts
|
||||
(fn [bas]
|
||||
(mapv (fn [ba]
|
||||
(update ba :locations (fn [ls]
|
||||
(map (fn [l] {:location l})
|
||||
ls))))
|
||||
bas))))))}))
|
||||
|
||||
(re-frame/reg-event-fx
|
||||
::save-new-client
|
||||
@@ -77,25 +89,22 @@
|
||||
(let [new-client-req @(re-frame/subscribe [::new-client-request])
|
||||
user @(re-frame/subscribe [::subs/token])]
|
||||
|
||||
(if (s/valid? ::entity/client new-client-req)
|
||||
|
||||
{:db (-> new-client-form
|
||||
(assoc :status :loading)
|
||||
(assoc :error nil))
|
||||
:graphql
|
||||
{:token user
|
||||
:query-obj {:venia/operation {:operation/type :mutation
|
||||
:operation/name "EditClient"}
|
||||
:venia/queries [{:query/data [:edit-client
|
||||
{:edit-client new-client-req}
|
||||
[:id :name :code :email :locations :matches :week-a-debits :week-a-credits :week-b-debits :week-b-credits
|
||||
[:location-matches [:location :match]]
|
||||
[:address [:street1 :street2 :city :state :zip]]
|
||||
[:forecasted-transactions [:id :amount :identifier :day-of-month]]
|
||||
[:bank-accounts [:id :number :check-number :name :code :bank-code :bank-name :routing :type :visible :yodlee-account-id :sort-order :locations]]]]}]}
|
||||
:on-success [::save-complete]
|
||||
:on-error [::forms/save-error ::new-client]}}
|
||||
{:db new-client-form}))))
|
||||
{:db (-> new-client-form
|
||||
(assoc :status :loading)
|
||||
(assoc :error nil))
|
||||
:graphql
|
||||
{:token user
|
||||
:query-obj {:venia/operation {:operation/type :mutation
|
||||
:operation/name "EditClient"}
|
||||
:venia/queries [{:query/data [:edit-client
|
||||
{:edit-client new-client-req}
|
||||
[:id :name :code :email :locations :matches :week-a-debits :week-a-credits :week-b-debits :week-b-credits
|
||||
[:location-matches [:location :match :id]]
|
||||
[:address [:street1 :street2 :city :state :zip]]
|
||||
[:forecasted-transactions [:id :amount :identifier :day-of-month]]
|
||||
[:bank-accounts [:id :number :check-number :name :code :bank-code :bank-name :routing :type :visible :yodlee-account-id :sort-order :locations]]]]}]}
|
||||
:on-success [::save-complete]
|
||||
:on-error [::forms/save-error ::form]}})))
|
||||
(re-frame/reg-event-db
|
||||
::save-complete
|
||||
(fn [db [_ client]]
|
||||
@@ -104,48 +113,6 @@
|
||||
|
||||
(assoc-in [:clients (:id (:edit-client client))] (update (:edit-client client) :bank-accounts (fn [bas] (->> bas (sort-by :sort-order) vec)))))))
|
||||
|
||||
(re-frame/reg-event-db
|
||||
::add-new-location
|
||||
[(forms/in-form ::form) (re-frame/path [:data])]
|
||||
(fn [client _]
|
||||
(-> client
|
||||
(update :locations conj (:location client))
|
||||
(dissoc :location))))
|
||||
|
||||
(re-frame/reg-event-db
|
||||
::add-location-to-bank-account
|
||||
[(forms/in-form ::form) (re-frame/path [:data])]
|
||||
(fn [client [_ which-account]]
|
||||
(println client which-account)
|
||||
(-> client
|
||||
(update-in [:bank-accounts which-account :locations] #(conj (or % #{}) (get-in client [:bank-accounts which-account :location-select])))
|
||||
(update-in [:bank-accounts which-account] dissoc :location-select))))
|
||||
|
||||
(re-frame/reg-event-db
|
||||
::add-new-match
|
||||
[(forms/in-form ::form) (re-frame/path [:data])]
|
||||
(fn [client _]
|
||||
(-> client
|
||||
(update :matches conj (:match client))
|
||||
(update :matches set)
|
||||
(dissoc :match))))
|
||||
|
||||
(re-frame/reg-event-db
|
||||
::remove-match
|
||||
[(forms/in-form ::form) (re-frame/path [:data])]
|
||||
(fn [client [_ which]]
|
||||
(-> client
|
||||
(update :matches set)
|
||||
(update :matches disj which))))
|
||||
|
||||
(re-frame/reg-event-db
|
||||
::add-new-location-match
|
||||
[(forms/in-form ::form) (re-frame/path [:data])]
|
||||
(fn [client _]
|
||||
(-> client
|
||||
(update :location-matches conj (:location-match client))
|
||||
(dissoc :location-match))))
|
||||
|
||||
(re-frame/reg-event-db
|
||||
::add-forecasted-transaction
|
||||
[(forms/in-form ::form) (re-frame/path [:data])]
|
||||
@@ -168,17 +135,6 @@
|
||||
[]
|
||||
%)))))
|
||||
|
||||
(re-frame/reg-event-db
|
||||
::remove-location-match
|
||||
[(forms/in-form ::form) (re-frame/path [:data])]
|
||||
(fn [client [_ i]]
|
||||
(-> client
|
||||
(update :location-matches (fn [lm]
|
||||
(->> lm
|
||||
(map vector (range))
|
||||
(filter (fn [[index item]]
|
||||
(not= index i)))
|
||||
(map second)))))))
|
||||
|
||||
(re-frame/reg-event-db
|
||||
::add-new-bank-account
|
||||
@@ -203,10 +159,7 @@
|
||||
[(forms/in-form ::form) (re-frame/path [:data :bank-accounts])]
|
||||
(fn [bank-accounts [_ index]]
|
||||
(vec (concat (take index bank-accounts)
|
||||
(drop (inc index) bank-accounts)))
|
||||
#_(update db :bank-accounts
|
||||
(fn [bas]
|
||||
(filter #(not= (:code %) code) bas)))))
|
||||
(drop (inc index) bank-accounts)))))
|
||||
|
||||
(re-frame/reg-event-db
|
||||
::sort-swapped
|
||||
@@ -362,23 +315,19 @@
|
||||
[:label.label "Locations"]
|
||||
[:div.control
|
||||
[:p.help "If this account is location-specific, add the valid locations"]
|
||||
[:div.field.has-addons
|
||||
[:p.control
|
||||
[:div.select
|
||||
[raw-field
|
||||
[:select {:type "select"
|
||||
:style {:width "7em"}
|
||||
:field [:bank-accounts sort-order :location-select]
|
||||
:allow-nil? true
|
||||
:spec (set (get-in new-client [:locations]))}
|
||||
(map (fn [l] ^{:key l} [:option {:value l} l]) (get-in new-client [:locations]))]]]]
|
||||
[:p.control {:on-click (dispatch-event [::add-location-to-bank-account sort-order]) } [:a.button "Add"]]]]
|
||||
|
||||
(if-let [locations (seq (get-in new-client [:bank-accounts sort-order :locations]))]
|
||||
[:ul
|
||||
(for [location locations]
|
||||
^{:key location} [:li location ])]
|
||||
[:i "This account applies to all locations"])]
|
||||
[raw-field
|
||||
[multi-field {:type "multi-field"
|
||||
:field [:bank-accounts sort-order :locations]
|
||||
:template [[:select.select {:type "select"
|
||||
:style {:width "7em"}
|
||||
:allow-nil? true
|
||||
:field [:location]
|
||||
:spec (set (map :location (get-in new-client [:locations])))}
|
||||
[:<>
|
||||
[:option ""]
|
||||
[:<> (map (fn [l] ^{:key (:location l)}
|
||||
[:option {:value (:location l)} (:location l)])
|
||||
(get-in new-client [:locations]))]]]]}]]]]
|
||||
[:div.field
|
||||
[:label.checkbox
|
||||
[raw-field
|
||||
@@ -394,168 +343,138 @@
|
||||
|
||||
(defn new-client-form []
|
||||
(let [{new-client :data } @(re-frame/subscribe [::forms/form ::form])
|
||||
{:keys [form field raw-field error-notification submit-button ]} client-form
|
||||
{:keys [form-inline field raw-field error-notification submit-button ]} client-form
|
||||
next-week-a (if (is-week-a? (t/now))
|
||||
"This week"
|
||||
"Next week")
|
||||
next-week-b (if (is-week-a? (t/now))
|
||||
"Next week"
|
||||
"This week")]
|
||||
(println "ID" (:id new-client))
|
||||
|
||||
[side-bar {:on-close (dispatch-event [::forms/form-closing ::form ])}
|
||||
[form {:title "Add client"}
|
||||
[field "Name"
|
||||
[:input.input {:type "text"
|
||||
:field [:name]
|
||||
:spec ::entity/name
|
||||
}]]
|
||||
^{:key (:id new-client)}
|
||||
[:div ;; div is important for actually unmounting for now
|
||||
(form-inline {:title "Add client"}
|
||||
[:<>
|
||||
(field "Name"
|
||||
[:input.input {:type "text"
|
||||
:field [:name]
|
||||
:spec ::entity/name
|
||||
}])
|
||||
|
||||
[:div.field
|
||||
[:p.help "Client code"
|
||||
]
|
||||
(if (:id new-client)
|
||||
[:div.control
|
||||
(:code new-client)]
|
||||
[:div.control
|
||||
[raw-field
|
||||
[:input.input {:type "code"
|
||||
:field :code
|
||||
:spec ::entity/code}]]])]
|
||||
|
||||
[:div.field
|
||||
[:p.help "Client code"
|
||||
]
|
||||
(if (:id new-client)
|
||||
[:div.control
|
||||
(:code new-client)]
|
||||
[:div.control
|
||||
(raw-field
|
||||
[:input.input {:type "code"
|
||||
:field :code
|
||||
:spec ::entity/code}])])]
|
||||
|
||||
|
||||
[field "Email"
|
||||
[:input.input {:type "email"
|
||||
:field :email
|
||||
:spec ::entity/email}]]
|
||||
(field "Email"
|
||||
[:input.input {:type "email"
|
||||
:field :email
|
||||
:spec ::entity/email}])
|
||||
|
||||
[:div.field
|
||||
[:p.help "Matches"]
|
||||
[:div.control
|
||||
[:div.field.has-addons
|
||||
[:p.control
|
||||
[raw-field
|
||||
[:input.input {:type "text"
|
||||
:field :match}]]]
|
||||
[:p.control [:button.button.is-primary {:on-click (dispatch-event [::add-new-match])} "Add"]]]]
|
||||
[:ul
|
||||
(for [match (:matches new-client)]
|
||||
^{:key match} [:li match [:a {:on-click (dispatch-event [::remove-match match])} [:span.icon [:span.fa.fa-times]]]])]]
|
||||
|
||||
|
||||
|
||||
[:h2.subtitle.is-5 "Name matches"]
|
||||
[:div.control
|
||||
(raw-field
|
||||
[multi-field {:type "multi-field"
|
||||
:field :matches
|
||||
:template [[:input.input {:field [:match]
|
||||
:placeholder "Harry's burger joint"
|
||||
:style { :width "15em"}}]]}])]
|
||||
|
||||
[:div.field
|
||||
[:p.help "Locations"]
|
||||
[:div.control
|
||||
[:div.field.has-addons
|
||||
[:p.control
|
||||
[raw-field
|
||||
[:input.input {:type "text"
|
||||
:field :location}]]]
|
||||
[:p.control [:button.button.is-primary {:on-click (dispatch-event [::add-new-location])} "Add"]]]
|
||||
[:ul
|
||||
(for [location (:locations new-client)]
|
||||
^{:key location} [:li location ])]]]
|
||||
|
||||
[:h2.subtitle "Locations"]
|
||||
[:div.control
|
||||
(raw-field
|
||||
[multi-field {:type "multi-field"
|
||||
:field :locations
|
||||
:allow-change? false
|
||||
:template [[:input.input {:field [:location]
|
||||
:maxlength 2
|
||||
:style { :width "4em"}}]]}])]
|
||||
|
||||
[:div.field
|
||||
[:p.help "Location matches"]
|
||||
[:div.control
|
||||
[:div.field.has-addons
|
||||
|
||||
[:p.control
|
||||
|
||||
[raw-field
|
||||
[:input.input {:type "text"
|
||||
:placeholder "San Jose"
|
||||
:field [:location-match :match]}]]]
|
||||
[:p.control
|
||||
[raw-field
|
||||
[:input.input {:type "text"
|
||||
:placeholder "DT"
|
||||
:field [:location-match :location]}]]]
|
||||
[:p.control [:button.button.is-primary {:on-click (dispatch-event [::add-new-location-match])} "Add"]]]
|
||||
|
||||
[:ul
|
||||
(for [[index {:keys [location match]}] (map vector (range) (:location-matches new-client))]
|
||||
^{:key index} [:li match "->" location [:a {:on-click (dispatch-event [::remove-location-match index])} [:span.icon
|
||||
[:span.fa.fa-times]]]])]]]
|
||||
[:h2.subtitle "Location Matches"]
|
||||
[:div.control
|
||||
(raw-field
|
||||
[multi-field {:type "multi-field"
|
||||
:field :location-matches
|
||||
:template [[:input.input {:field [:match]
|
||||
:placeholder "Downtown"
|
||||
:style { :width "15em"}}]
|
||||
[:input.input {:field [:location]
|
||||
:placeholder "DT"
|
||||
:maxlength 2
|
||||
:style { :width "4em"}}]]}])]
|
||||
|
||||
[:div {:style {:padding-bottom "0.75em" :padding-top "0.75em"}}
|
||||
[:h2.subtitle "Address"]
|
||||
[address-field {:field [:address]
|
||||
:event [::forms/change ::form]
|
||||
:subscription new-client}]]
|
||||
[:div {:style {:padding-bottom "0.75em" :padding-top "0.75em"}}
|
||||
[:h2.subtitle "Address"]
|
||||
[address-field {:field [:address]
|
||||
:event [::forms/change ::form]
|
||||
:subscription new-client}]]
|
||||
|
||||
[:h2.subtitle "Bank accounts"]
|
||||
(for [bank-account (sort-by :sort-order (:bank-accounts new-client))]
|
||||
^{:key (:sort-order bank-account)}
|
||||
[bank-account-card new-client bank-account (= 0 (:sort-order bank-account)) (= (:sort-order bank-account) (dec (count (:bank-accounts new-client))))])
|
||||
[:h2.subtitle "Bank accounts"]
|
||||
(for [bank-account (sort-by :sort-order (:bank-accounts new-client))]
|
||||
^{:key (:sort-order bank-account)}
|
||||
[bank-account-card new-client bank-account (= 0 (:sort-order bank-account)) (= (:sort-order bank-account) (dec (count (:bank-accounts new-client))))])
|
||||
|
||||
[:div.columns
|
||||
[:div.column.is-third
|
||||
[:a.button.is-primary.is-outlined.is-fullwidth {:on-click (dispatch-event [::add-new-bank-account :credit])} "Add Credit Account"]]
|
||||
[:div.column.is-third
|
||||
[:a.button.is-primary.is-outlined.is-fullwidth {:on-click (dispatch-event [::add-new-bank-account :check])} "Add Checking Account"]]
|
||||
[:div.column.is-third
|
||||
[:a.button.is-primary.is-outlined.is-fullwidth {:on-click (dispatch-event [::add-new-bank-account :cash])} "Add Cash Account"]]]
|
||||
[:div.columns
|
||||
[:div.column.is-third
|
||||
[:a.button.is-primary.is-outlined.is-fullwidth {:on-click (dispatch-event [::add-new-bank-account :credit])} "Add Credit Account"]]
|
||||
[:div.column.is-third
|
||||
[:a.button.is-primary.is-outlined.is-fullwidth {:on-click (dispatch-event [::add-new-bank-account :check])} "Add Checking Account"]]
|
||||
[:div.column.is-third
|
||||
[:a.button.is-primary.is-outlined.is-fullwidth {:on-click (dispatch-event [::add-new-bank-account :cash])} "Add Cash Account"]]]
|
||||
|
||||
[:h2.subtitle "Cash flow"]
|
||||
[:label.label (str "Week A (" next-week-a ")")]
|
||||
[field "Regular Credits"
|
||||
[:input.input {:type "number"
|
||||
:placeholder "500.00"
|
||||
:field [:week-a-credits]
|
||||
:step "0.01"}]]
|
||||
[field "Regular Debits"
|
||||
[:input.input {:type "number"
|
||||
:placeholder "150.00"
|
||||
:field [:week-a-debits]
|
||||
:step "0.01"}]]
|
||||
[:label.label (str "Week B (" next-week-b ")")]
|
||||
[field "Regular Credits"
|
||||
[:input.input {:type "number"
|
||||
:placeholder "1000.00"
|
||||
:field [:week-b-credits]
|
||||
:step "0.01"}]]
|
||||
[field "Regular Debits"
|
||||
[:input.input {:type "number"
|
||||
:placeholder "250.00"
|
||||
:field [:week-b-debits]
|
||||
:step "0.01"}]]
|
||||
[:div.field
|
||||
[:label.label "Forecasted transactions"]
|
||||
[:div.control
|
||||
[horizontal-field
|
||||
nil
|
||||
[:h2.subtitle "Cash flow"]
|
||||
[:label.label (str "Week A (" next-week-a ")")]
|
||||
[field "Regular Credits"
|
||||
[:input.input {:type "number"
|
||||
:placeholder "500.00"
|
||||
:field [:week-a-credits]
|
||||
:step "0.01"}]]
|
||||
[field "Regular Debits"
|
||||
[:input.input {:type "number"
|
||||
:placeholder "150.00"
|
||||
:field [:week-a-debits]
|
||||
:step "0.01"}]]
|
||||
[:label.label (str "Week B (" next-week-b ")")]
|
||||
[field "Regular Credits"
|
||||
[:input.input {:type "number"
|
||||
:placeholder "1000.00"
|
||||
:field [:week-b-credits]
|
||||
:step "0.01"}]]
|
||||
[field "Regular Debits"
|
||||
[:input.input {:type "number"
|
||||
:placeholder "250.00"
|
||||
:field [:week-b-debits]
|
||||
:step "0.01"}]]
|
||||
[:div.field
|
||||
[:label.label "Forecasted transactions"]
|
||||
|
||||
[:p.control
|
||||
[:p.help "Identifier"]
|
||||
[raw-field
|
||||
[:input.input {:type "text"
|
||||
:placeholder "Identifier"
|
||||
:field [:new-forecasted-transaction :identifier]}]]]
|
||||
[:p.control
|
||||
[:p.help "Day of month"]
|
||||
[raw-field
|
||||
[:input.input {:type "number"
|
||||
:placeholder "Day of month"
|
||||
:step "1"
|
||||
:field [:new-forecasted-transaction :day-of-month]}]]]
|
||||
[:p.control
|
||||
[:p.help "Amount"]
|
||||
[raw-field
|
||||
[:input.input {:type "number"
|
||||
:placeholder "250.00"
|
||||
:field [:new-forecasted-transaction :amount]
|
||||
:step "0.01"}]]]
|
||||
[:a.button {:on-click (dispatch-event [::add-forecasted-transaction])} "Add"]]]
|
||||
|
||||
[:ul
|
||||
(for [forecasted-transaction (:forecasted-transactions new-client)]
|
||||
^{:key (or (:id forecasted-transaction)
|
||||
(:temp-id forecasted-transaction))}
|
||||
[:li (:identifier forecasted-transaction) ": " (nf (:amount forecasted-transaction)) " on day " (:day-of-month forecasted-transaction) " of the month"
|
||||
[:a {:on-click (dispatch-event [::remove-forecasted-transaction (or (:id forecasted-transaction)
|
||||
(:temp-id forecasted-transaction))])} [:span.icon [:span.fa.fa-times]]]])]]
|
||||
|
||||
[error-notification]
|
||||
[submit-button "Save"]]]))
|
||||
[:div.control
|
||||
(raw-field
|
||||
[multi-field {:type "multi-field"
|
||||
:field :forecasted-transactions
|
||||
:template [[:input.input {:type "text"
|
||||
:placeholder "Identifier"
|
||||
:field [ :identifier]}]
|
||||
[:input.input {:type "number"
|
||||
:placeholder "Day of month"
|
||||
:step "1"
|
||||
:field [:day-of-month]}]
|
||||
[:input.input {:type "number"
|
||||
:placeholder "250.00"
|
||||
:field [:amount]
|
||||
:step "0.01"}]]}])]]
|
||||
(error-notification)
|
||||
(submit-button "Save")])]]))
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
[goog.i18n.NumberFormat.Format]
|
||||
[cljs-time.core :as t]
|
||||
[clojure.string :as str]
|
||||
[goog.crypt.base64 :as base64])
|
||||
[goog.crypt.base64 :as base64]
|
||||
[reagent.core :as r])
|
||||
(:import
|
||||
(goog.i18n NumberFormat)
|
||||
(goog.i18n.NumberFormat Format)))
|
||||
@@ -100,6 +101,85 @@
|
||||
(defn with-keys [children]
|
||||
(map-indexed (fn [i c] ^{:key i} c) children))
|
||||
|
||||
|
||||
(def css-transition-group
|
||||
(reagent/adapt-react-class js/ReactTransitionGroup.CSSTransition))
|
||||
|
||||
|
||||
(defn appearing [{:keys [visible? enter-class exit-class timeout]} & children ]
|
||||
(let [final-state (reagent/atom visible?)]
|
||||
(fn [{:keys [visible?]} & children]
|
||||
[css-transition-group {:in visible? :class-names {:exit exit-class :enter enter-class} :timeout timeout :onEnter (fn [] (reset! final-state true )) :onExited (fn [] (reset! final-state false))}
|
||||
(if (or @final-state visible?)
|
||||
(first children)
|
||||
[:span])])))
|
||||
|
||||
|
||||
(defn multi-field [{:keys [override-key override-value-key change-event default-key data value template on-change allow-change?]} ]
|
||||
(let [value-repr (r/atom (mapv
|
||||
(fn [x]
|
||||
(assoc x :key (random-uuid) :new? false))
|
||||
value))]
|
||||
(fn [{:keys [override-key override-value-key change-event default-key data value template on-change allow-change?]} ]
|
||||
(let [value @value-repr
|
||||
already-has-new-row? (= [:key :new?] (keys (last value)))
|
||||
value (if already-has-new-row?
|
||||
value
|
||||
(conj value {:key (random-uuid)
|
||||
:new? true}))]
|
||||
[:div
|
||||
(for [[i override] (map vector (range) value)
|
||||
:let [is-disabled? (if (= false allow-change?)
|
||||
(not (boolean (:new? override)))
|
||||
nil)]]
|
||||
^{:key (:key override)}
|
||||
[:div.level
|
||||
[:div.level-left
|
||||
[:div.level-item
|
||||
(if (:new? override)
|
||||
|
||||
[:div.icon.is-medium {:class (if (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
|
||||
[update template 1 assoc
|
||||
:value (get-in override (get-in template [1 :field]))
|
||||
:disabled is-disabled?
|
||||
:on-change (fn [e]
|
||||
|
||||
(reset! value-repr
|
||||
(into []
|
||||
(filter (fn [r]
|
||||
(not= [:key :new?] (keys r)))
|
||||
(assoc-in value (into [i] (get-in template [1 :field])) (.. e -target -value ) ))))
|
||||
(on-change (mapv
|
||||
(fn [v]
|
||||
(dissoc v :new? :key))
|
||||
@value-repr)))]])
|
||||
]
|
||||
[:div.level-item
|
||||
[:a.button
|
||||
|
||||
{:disabled is-disabled?
|
||||
:on-click (fn []
|
||||
|
||||
(when-not is-disabled?
|
||||
(reset! value-repr (into []
|
||||
(filter (fn [{:keys [key ] :as v}]
|
||||
(not= key (:key override)))
|
||||
value)))
|
||||
|
||||
(on-change (mapv
|
||||
(fn [v]
|
||||
(dissoc v :new? :key))
|
||||
@value-repr))))}
|
||||
[:span.icon [:span.icon-remove]]]]
|
||||
]])]))))
|
||||
|
||||
(defmethod do-bind "select" [dom {:keys [field allow-nil? subscription event class value spec] :as keys} & rest]
|
||||
(let [field (if (keyword? field) [field] field)
|
||||
event (if (keyword? event) [event] event)
|
||||
@@ -158,6 +238,20 @@
|
||||
keys (dissoc keys :field :subscription :event :spec)]
|
||||
(into [dom keys] (with-keys rest))))
|
||||
|
||||
(defmethod do-bind "multi-field" [dom {:keys [field event subscription class spec] :as keys} & rest]
|
||||
(let [field (if (keyword? field) [field] field)
|
||||
event (if (keyword? event) [event] event)
|
||||
keys (assoc keys
|
||||
:on-change (fn [value]
|
||||
(println value)
|
||||
(re-frame/dispatch (conj (conj event field) value)))
|
||||
:value (get-in subscription field)
|
||||
:class (str class
|
||||
(when (and spec (not (s/valid? spec (get-in subscription field))))
|
||||
" is-danger")))
|
||||
keys (dissoc keys :field :subscription :event :spec)]
|
||||
(into [dom keys] (with-keys rest))))
|
||||
|
||||
|
||||
(defmethod do-bind "typeahead-entity" [dom {:keys [field event text-event subscription class spec match->text] :as keys} & rest]
|
||||
(let [field (if (keyword? field) [field] field)
|
||||
@@ -309,24 +403,11 @@
|
||||
[:div.field-body]
|
||||
(with-keys (map (fn [x] [:div.field x]) controls)))])
|
||||
|
||||
|
||||
|
||||
(def css-transition-group
|
||||
(reagent/adapt-react-class js/ReactTransitionGroup.CSSTransition))
|
||||
|
||||
(def date-picker
|
||||
(do
|
||||
(reagent/adapt-react-class (aget js/DatePicker "default"))))
|
||||
|
||||
|
||||
(defn appearing [{:keys [visible? enter-class exit-class timeout]} & children ]
|
||||
|
||||
(let [final-state (reagent/atom visible?)]
|
||||
(fn [{:keys [visible?]} & children]
|
||||
[css-transition-group {:in visible? :class-names {:exit exit-class :enter enter-class} :timeout timeout :onEnter (fn [] (reset! final-state true )) :onExited (fn [] (reset! final-state false))}
|
||||
(if (or @final-state visible?)
|
||||
(first children)
|
||||
[:span])])))
|
||||
|
||||
(defn local-now []
|
||||
(t/to-default-time-zone (t/now)))
|
||||
|
||||
Reference in New Issue
Block a user