Files
integreat/src/cljs/auto_ap/views/pages/admin/clients.cljs
2019-04-17 19:30:01 -07:00

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]] }]))