448 lines
18 KiB
Clojure
448 lines
18 KiB
Clojure
(ns auto-ap.views.pages.admin.clients
|
|
(:require-macros [cljs.core.async.macros :refer [go]]
|
|
[clojure.string :as str])
|
|
(:require [re-frame.core :as re-frame]
|
|
|
|
[reagent.core :as reagent]
|
|
[clojure.spec.alpha :as s]
|
|
[clojure.string :as str]
|
|
[auto-ap.subs :as subs]
|
|
[auto-ap.forms :as forms]
|
|
[auto-ap.events :as events]
|
|
[auto-ap.entities.clients :as entity]
|
|
[auto-ap.views.components.address :refer [address-field]]
|
|
[auto-ap.views.components.layouts :refer [side-bar-layout appearing-side-bar]]
|
|
[auto-ap.views.components.admin.side-bar :refer [admin-side-bar]]
|
|
[auto-ap.views.utils :refer [login-url dispatch-event dispatch-value-change bind-field horizontal-field]]
|
|
[auto-ap.views.components.modal :refer [action-modal]]
|
|
[cljs.reader :as edn]
|
|
[auto-ap.routes :as routes]
|
|
[bidi.bidi :as bidi]))
|
|
|
|
(re-frame/reg-event-db
|
|
::new
|
|
(fn [db [_ client-id]]
|
|
(-> db
|
|
(forms/start-form ::new-client {:bank-accounts []}))))
|
|
|
|
|
|
|
|
(re-frame/reg-event-fx
|
|
::edit-client-clicked
|
|
(fn [{:keys [db]} [_ client-id]]
|
|
{:db (-> db
|
|
(forms/start-form ::new-client (get (:clients db) client-id)))}))
|
|
|
|
(re-frame/reg-sub
|
|
::new-client-request
|
|
:<- [::forms/form ::new-client]
|
|
(fn [{new-client-data :data} _]
|
|
{:id (:id new-client-data),
|
|
: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)
|
|
:address {:street1 (:street1 (:address new-client-data))
|
|
:street2 (:street2 (:address new-client-data)),
|
|
:city (:city (:address new-client-data))
|
|
:state (:state (:address new-client-data))
|
|
:zip (:zip (:address new-client-data))}
|
|
:bank-accounts (map (fn [{:keys [number name check-number type id code bank-name routing bank-code new? sort-order visible yodlee-account-id]}]
|
|
{:number number
|
|
:name name
|
|
:check-number check-number
|
|
:type type
|
|
:id id
|
|
:sort-order sort-order
|
|
:visible visible
|
|
:yodlee-account-id (when yodlee-account-id
|
|
(js/parseInt yodlee-account-id))
|
|
:code (if new?
|
|
(str (:code new-client-data) "-" code)
|
|
code)
|
|
:bank-name bank-name
|
|
:routing routing
|
|
:bank-code bank-code})
|
|
(:bank-accounts new-client-data))}))
|
|
|
|
|
|
(re-frame/reg-event-fx
|
|
::save-new-client
|
|
[(forms/in-form ::new-client)]
|
|
(fn [{{new-client-data :data :as new-client-form} :db} _]
|
|
|
|
(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 [:address [:street1 :street2 :city :state :zip]] [:bank-accounts [:id :number :check-number :name :code :bank-code :bank-name :routing :type :visible :yodlee-account-id :sort-order]]]]}]}
|
|
:on-success [::save-complete]
|
|
:on-error [::forms/save-error ::new-client]}}
|
|
{:db new-client-form}))))
|
|
|
|
|
|
(re-frame/reg-event-db
|
|
::save-complete
|
|
(fn [db [_ client]]
|
|
(-> db
|
|
(forms/stop-form ::new-client)
|
|
|
|
(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 ::new-client) (re-frame/path [:data])]
|
|
(fn [client _]
|
|
(-> client
|
|
(update :locations conj (:location client))
|
|
(dissoc :location))))
|
|
|
|
(re-frame/reg-event-db
|
|
::add-new-bank-account
|
|
[(forms/in-form ::new-client) (re-frame/path [:data])]
|
|
(fn [client [_ type]]
|
|
(update client :bank-accounts conj {:type type :active? true :new? true :visible true :sort-order (count (:bank-accounts client))})))
|
|
|
|
(re-frame/reg-event-db
|
|
::bank-account-activated
|
|
[(forms/in-form ::new-client) (re-frame/path [:data :bank-accounts])]
|
|
(fn [bank-accounts [_ index]]
|
|
(update bank-accounts index assoc :active? true)))
|
|
|
|
(re-frame/reg-event-db
|
|
::bank-account-deactivated
|
|
[(forms/in-form ::new-client) (re-frame/path [:data :bank-accounts])]
|
|
(fn [bank-accounts [_ index]]
|
|
(update bank-accounts index assoc :active? false)))
|
|
|
|
(re-frame/reg-event-db
|
|
::bank-account-removed
|
|
[(forms/in-form ::new-client) (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)))))
|
|
|
|
(re-frame/reg-event-db
|
|
::sort-swapped
|
|
[(forms/in-form ::new-client) (re-frame/path [:data :bank-accounts])]
|
|
(fn [bank-accounts [_ source dest]]
|
|
(->> (-> bank-accounts
|
|
(assoc-in [source :sort-order] (get-in bank-accounts [dest :sort-order]))
|
|
(assoc-in [dest :sort-order] (get-in bank-accounts [source :sort-order]))
|
|
|
|
)
|
|
(sort-by :sort-order)
|
|
vec)))
|
|
|
|
(re-frame/reg-event-db
|
|
::toggle-visible
|
|
[(forms/in-form ::new-client) (re-frame/path [:data :bank-accounts])]
|
|
(fn [bank-accounts [_ account]]
|
|
(-> (->> bank-accounts
|
|
(sort-by :sort-order)
|
|
vec)
|
|
(update-in [account :visible] #(not %)))))
|
|
|
|
(defn clients-table []
|
|
(let [clients (re-frame/subscribe [::subs/clients])
|
|
editing-client (:client @(re-frame/subscribe [::subs/admin]))]
|
|
[:table {:class "table", :style {:width "100%"}}
|
|
[:thead
|
|
[:tr
|
|
[:th {:style {:width "50%"}} "Name"]
|
|
[:th {:style {:width "10%"}} "Name"]
|
|
[:th {:style {:width "20%"}} "Locations"]
|
|
[:th {:style {:width "20%"}} "Email"]]]
|
|
[:tbody (for [{:keys [id name email code locations] :as c} @clients]
|
|
^{:key (str name "-" id )}
|
|
[:tr {:on-click (fn [] (re-frame/dispatch [::edit-client-clicked id]))
|
|
:style {"cursor" "pointer"}}
|
|
[:td name]
|
|
[:td code]
|
|
[:td (str/join ", " locations)]
|
|
[:td email]])]]))
|
|
|
|
|
|
|
|
(defn admin-clients-content []
|
|
[:div
|
|
(let [clients (re-frame/subscribe [::subs/clients])
|
|
editing-client (:client @(re-frame/subscribe [::subs/admin]))]
|
|
[:div
|
|
[:h1.title "Clients"]
|
|
[:div.is-pulled-right
|
|
[:a.button.is-primary.is-large {:on-click (dispatch-event [::new])} "New client"]]
|
|
[clients-table]])])
|
|
|
|
|
|
(defn bank-account-card [new-client {:keys [active? new? type visible code name number check-number id sort-order] :as bank-account} first? last?]
|
|
(let [change-event [::forms/change ::new-client]]
|
|
[:div.card {:style {:margin-bottom "1em"}}
|
|
[:header.card-header
|
|
[:p.card-header-title {:style {:text-overflow "ellipsis"}}
|
|
[:span.icon.inline
|
|
(cond
|
|
(#{:check ":check"} type) [:span.icon-check-payment-sign]
|
|
|
|
(#{:credit ":credit"} type) [:span.icon-credit-card-1]
|
|
|
|
:else [:span.icon-accounting-bill])]
|
|
code ": " name]
|
|
[:p {:style {:padding "0.75em 0.25em"}}
|
|
[:a.button.is-outlined {:on-click (dispatch-event [::toggle-visible sort-order])} [:span.icon (if visible
|
|
[:span.fa.fa-eye]
|
|
[:span.fa.fa-eye-slash]
|
|
)]]]
|
|
(when-not last?
|
|
[:p {:style {:padding "0.75em 0.25em"}}
|
|
[:a.button.is-primary.is-outlined {:on-click (dispatch-event [::sort-swapped sort-order (inc sort-order)])} [:span.icon [:span.fa.fa-sort-down]]]])
|
|
(when-not first?
|
|
[:p {:style {:padding "0.75em 0.25em"}}
|
|
[:a.button.is-primary.is-outlined {:on-click (dispatch-event [::sort-swapped sort-order (dec sort-order)])} [:span.icon [:span.fa.fa-sort-up]]]])
|
|
(if active?
|
|
[:a.card-header-icon
|
|
{:on-click (dispatch-event [::bank-account-deactivated sort-order])}
|
|
[:span.icon
|
|
[:span.fa.fa-angle-up]]]
|
|
[:a.card-header-icon
|
|
{:on-click (dispatch-event [::bank-account-activated sort-order])}
|
|
[:span.icon
|
|
[:span.fa.fa-angle-down]]])]
|
|
(when active?
|
|
[:div.card-content
|
|
[:label.label "General"]
|
|
[horizontal-field
|
|
nil
|
|
[:div.control
|
|
[:p.help "Account Code"]
|
|
(if new?
|
|
[:div.field.has-addons.is-extended
|
|
[:p.control [:a.button.is-static (:code new-client) "-" ]]
|
|
[:p.control
|
|
[bind-field
|
|
[:input.input {:type "code"
|
|
:field [:bank-accounts sort-order :code]
|
|
:spec ::entity/code
|
|
:event change-event
|
|
:subscription new-client}]]]]
|
|
[:div.field [:p.control code]])]
|
|
|
|
[:div.control
|
|
[:p.help "Nickname"]
|
|
[bind-field
|
|
[:input.input {:placeholder "BOA Checking #1"
|
|
:type "text"
|
|
:field [:bank-accounts sort-order :name]
|
|
:event change-event
|
|
:subscription new-client}]]]]
|
|
(when (#{:check ":check"} type )
|
|
[:div
|
|
|
|
[:label.label "Bank"]
|
|
[horizontal-field
|
|
nil
|
|
[:div.control
|
|
[:p.help "Bank Name"]
|
|
[bind-field
|
|
[:input.input {:placeholder "Bank of America"
|
|
:type "text"
|
|
:field [:bank-accounts sort-order :bank-name]
|
|
:event change-event
|
|
:subscription new-client}]]]
|
|
[:div.control
|
|
[:p.help "Routing #"]
|
|
[bind-field
|
|
[:input.input {:placeholder "104819123"
|
|
:type "text"
|
|
:field [:bank-accounts sort-order :routing]
|
|
:event change-event
|
|
:subscription new-client}]]]
|
|
[:div.control
|
|
[:p.help "Bank code"]
|
|
[bind-field
|
|
[:input.input {:placeholder "12/10123"
|
|
:type "text"
|
|
:field [:bank-accounts sort-order :bank-code]
|
|
:event change-event
|
|
:subscription new-client}]]]]
|
|
|
|
[:label.label "Checking account"]
|
|
[horizontal-field
|
|
nil
|
|
[:div.control
|
|
[:p.help "Account #"]
|
|
[bind-field
|
|
[:input.input {:placeholder "123456789"
|
|
:type "text"
|
|
:field [:bank-accounts sort-order :number]
|
|
:event change-event
|
|
:subscription new-client}]]]
|
|
[:div.control
|
|
[:p.help "Check Number"]
|
|
[bind-field
|
|
[:input.input {:placeholder "10000"
|
|
:type "text"
|
|
:field [:bank-accounts sort-order :check-number]
|
|
:event change-event
|
|
:subscription new-client}]]]]
|
|
[:div.field
|
|
[:label.label "Yodlee Account"]
|
|
[:div.control
|
|
[bind-field
|
|
[:input.input {:placeholder "Yodlee Account #"
|
|
:type "text"
|
|
:field [:bank-accounts sort-order :yodlee-account-id]
|
|
:event change-event
|
|
:subscription new-client}]]]]])
|
|
(when (#{:credit ":credit"} type )
|
|
[:div
|
|
|
|
[:label.label "Account"]
|
|
[horizontal-field
|
|
nil
|
|
[:div.control
|
|
[:p.help "Bank Name"]
|
|
[bind-field
|
|
[:input.input {:placeholder "Bank of America"
|
|
:type "text"
|
|
:field [:bank-accounts sort-order :bank-name]
|
|
:event change-event
|
|
:subscription new-client}]]]]
|
|
|
|
[horizontal-field
|
|
nil
|
|
[:div.control
|
|
[:p.help "Account #"]
|
|
[bind-field
|
|
[:input.input {:placeholder "123456789"
|
|
:type "text"
|
|
:field [:bank-accounts sort-order :number]
|
|
:event change-event
|
|
:subscription new-client}]]]]
|
|
[:div.field
|
|
[:p.help "Yodlee Account"]
|
|
[:div.control
|
|
[bind-field
|
|
[:input.input {:placeholder "Yodlee Account #"
|
|
:type "text"
|
|
:field [:bank-accounts sort-order :yodlee-account-id]
|
|
:event change-event
|
|
:subscription new-client}]]]]])])
|
|
|
|
(when active?
|
|
[:footer.card-footer
|
|
[:a.card-footer-item {:href "#" :on-click (dispatch-event [::bank-account-deactivated sort-order])} "Done"]
|
|
(when new?
|
|
[:a.card-footer-item.is-warning {:href "#" :on-click (dispatch-event [::bank-account-removed sort-order])} "Remove"])])])
|
|
)
|
|
|
|
(defn new-client-form []
|
|
(let [{error :error new-client :data } @(re-frame/subscribe [::forms/form ::new-client])
|
|
change-event [::forms/change ::new-client]]
|
|
|
|
[forms/side-bar-form {:form ::new-client}
|
|
[:form
|
|
[:h1.title.is-2 "Add client"]
|
|
[:div.field
|
|
[:p.help "Name"]
|
|
[:div.control
|
|
[bind-field
|
|
[:input.input {:type "text"
|
|
:field :name
|
|
:spec ::entity/name
|
|
:event change-event
|
|
:subscription new-client}]]]]
|
|
|
|
[:div.field
|
|
[:p.help "Client code"]
|
|
(if (:id new-client)
|
|
[:div.control
|
|
(:code new-client)
|
|
]
|
|
[:div.control
|
|
[bind-field
|
|
[:input.input {:type "code"
|
|
:field :code
|
|
:spec ::entity/code
|
|
:event change-event
|
|
:subscription new-client}]]])]
|
|
|
|
[:div.field
|
|
[:p.help "Email"]
|
|
[:div.control
|
|
[bind-field
|
|
[:input.input {:type "email"
|
|
:field :email
|
|
:spec ::entity/email
|
|
:event change-event
|
|
:subscription new-client}]]]]
|
|
|
|
[:div.field
|
|
[:p.help "Locations"]
|
|
[:div.control
|
|
[:div.field.has-addons
|
|
[:p.control
|
|
[bind-field
|
|
[:input.input {:type "text"
|
|
:field :location
|
|
:event change-event
|
|
:subscription new-client}]]]
|
|
[:p.control [:button.button.is-primary {:on-click (dispatch-event [::add-new-location])} "Add"]]]
|
|
[:ul
|
|
(for [location (:locations new-client)]
|
|
^{:key location} [:li location ])]]]
|
|
|
|
[:div {:style {:padding-bottom "0.75em" :padding-top "0.75em"}}
|
|
[:h2.subtitle "Address"]
|
|
[address-field {:field [:address]
|
|
:event change-event
|
|
: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))))])
|
|
|
|
[: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 "Add bank account"]
|
|
#_(when (:saving? new-client) [:div.is-overlay {:style {"backgroundColor" "rgba(150,150,150, 0.5)"}}])
|
|
#_(println (s/explain-data ::entity/client new-client))
|
|
|
|
(when error
|
|
[:div.notification.is-warning.animated.fadeInUp
|
|
error])
|
|
|
|
|
|
[:submit.button.is-large.is-primary {:disabled (if (s/valid? ::entity/client @(re-frame/subscribe [::new-client-request]))
|
|
""
|
|
"disabled")
|
|
:on-click (dispatch-event [::save-new-client])
|
|
:class (str @(re-frame/subscribe [::forms/loading-class ::new-client]) (when error " animated shake"))} "Save"]]]))
|
|
|
|
(defn admin-clients-page []
|
|
(let [{:keys [active?]} @(re-frame/subscribe [::forms/form ::new-client])]
|
|
[side-bar-layout {:side-bar [admin-side-bar {}]
|
|
:main [admin-clients-content]
|
|
:right-side-bar [appearing-side-bar {:visible? active?} [new-client-form]] }]))
|
|
|