From e1ffeda8513efe2beccfa2b416cb843b4db4ceae Mon Sep 17 00:00:00 2001 From: Bryce Covert Date: Tue, 4 Aug 2020 11:55:20 -0700 Subject: [PATCH] standardized clients. --- src/clj/auto_ap/routes/graphql.clj | 2 +- src/cljs/auto_ap/events/admin/clients.cljs | 1 - .../auto_ap/views/pages/admin/clients.cljs | 647 ++---------------- .../views/pages/admin/clients/form.cljs | 561 +++++++++++++++ .../views/pages/admin/clients/side_bar.cljs | 79 +++ .../views/pages/admin/clients/table.cljs | 53 ++ .../views/pages/admin/rules/table.cljs | 89 ++- src/cljs/auto_ap/views/pages/admin/users.cljs | 3 +- 8 files changed, 799 insertions(+), 636 deletions(-) create mode 100644 src/cljs/auto_ap/views/pages/admin/clients/form.cljs create mode 100644 src/cljs/auto_ap/views/pages/admin/clients/side_bar.cljs create mode 100644 src/cljs/auto_ap/views/pages/admin/clients/table.cljs diff --git a/src/clj/auto_ap/routes/graphql.clj b/src/clj/auto_ap/routes/graphql.clj index 900f8bca..cf81aa0f 100644 --- a/src/clj/auto_ap/routes/graphql.clj +++ b/src/clj/auto_ap/routes/graphql.clj @@ -34,7 +34,7 @@ :headers {"Content-Type" "application/edn"}}) (do (log/error "GraphQL error" e) {:status 500 - :body (pr-str {:errors [(merge {:message (.getMessage e)} (ex-data e))]}) + :body (pr-str {:errors [(merge {:message (str "Unhandled error:" (str e))} (ex-data e))]}) :headers {"Content-Type" "application/edn"}})))))) diff --git a/src/cljs/auto_ap/events/admin/clients.cljs b/src/cljs/auto_ap/events/admin/clients.cljs index 461b518f..2ef74159 100644 --- a/src/cljs/auto_ap/events/admin/clients.cljs +++ b/src/cljs/auto_ap/events/admin/clients.cljs @@ -10,7 +10,6 @@ (re-frame/reg-event-fx ::edit (fn [{:keys [db]} [_ client-id]] - (println "EDITING") {:dispatch [::events/modal-status :auto-ap.views.pages.admin.clients/edit {:visible? true}] :db (assoc-in db [:admin :client] (get (:clients db) client-id))})) diff --git a/src/cljs/auto_ap/views/pages/admin/clients.cljs b/src/cljs/auto_ap/views/pages/admin/clients.cljs index bd74be11..4039b39d 100644 --- a/src/cljs/auto_ap/views/pages/admin/clients.cljs +++ b/src/cljs/auto_ap/views/pages/admin/clients.cljs @@ -1,8 +1,5 @@ (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] @@ -17,605 +14,81 @@ [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 nf]] [auto-ap.views.components.modal :refer [action-modal]] + [auto-ap.views.pages.admin.clients.table :as table] + [auto-ap.views.pages.admin.clients.form :as form] [cljs.reader :as edn] [auto-ap.routes :as routes] - [bidi.bidi :as bidi])) + [bidi.bidi :as bidi] + [auto-ap.status :as status] + [auto-ap.views.components.grid :as grid] + [auto-ap.views.pages.admin.clients.side-bar :as side-bar] + [vimsical.re-frame.fx.track :as track])) + +(re-frame/reg-event-fx + ::mounted + (fn [{:keys [db]} _] + {::track/register {:id ::params + :subscription [::params] + :event-fn (fn [params] [::params-change params])}})) + +(re-frame/reg-event-fx + ::unmounted + (fn [{:keys [db]} _] + {:db (dissoc db ::table/params ::side-bar/filter-params) + ::track/dispose {:id ::params}})) + +(re-frame/reg-sub + ::params + :<- [::table/params] + :<- [::side-bar/filter-params] + (fn [[table-params filter-params]] + (cond-> {} + (seq filter-params) (merge filter-params) + (seq table-params) (merge table-params)))) (re-frame/reg-event-db ::new (fn [db [_ client-id]] (-> db - (forms/start-form ::form {:bank-accounts []})))) - - + (forms/start-form ::form/form {:bank-accounts []})))) (re-frame/reg-event-fx - ::edit-client-clicked - (fn [{:keys [db]} [_ client-id]] - {:db (-> db - (forms/stop-form ::form) - (forms/start-form ::form (get (:clients db) client-id)))})) + ::params-change + (fn [_ [_ params]] + {:set-uri-params params})) (re-frame/reg-sub - ::can-submit - :<- [::forms/form ::form] - (fn [{:keys [data status]} _] - (println (s/explain ::entity/client data)) - (s/valid? ::entity/client data))) + ::page + :<- [::params] + :<- [::subs/clients] + (fn [[params all-clients]] + (let [matching-clients (cond->> all-clients + (not-empty (:name params)) (filter #(str/includes? (str/lower-case (or (:name %) "")) + (str/lower-case (:name params)))) + (not-empty (:code params)) (filter #(= (str/lower-case (or (:code %) "")) + (str/lower-case (:code params)))))] + (assoc (grid/virtual-paginate-controls (:start params ) matching-clients) + :data (grid/virtual-paginate (:start params) matching-clients))))) -(re-frame/reg-sub - ::new-client-request - :<- [::forms/form ::form] - (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) - :matches (vec (: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) - :week-b-credits (:week-b-credits new-client-data) - :week-b-debits (:week-b-debits 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))} - :forecasted-transactions (map (fn [{:keys [id day-of-month identifier amount]}] - {:id id - :day-of-month day-of-month - :identifier identifier - :amount amount}) - (:forecasted-transactions new-client-data)) - :bank-accounts (map (fn [{:keys [number name check-number include-in-reports type id code bank-name routing bank-code new? sort-order visible yodlee-account-id locations]}] - {:number number - :name name - :check-number check-number - :include-in-reports include-in-reports - :type type - :id id - :sort-order sort-order - :visible visible - :locations (vec locations) - :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))})) +(def admin-clients-content + (with-meta + (fn [] + [:div + [:div + [:h1.title "Clients"] + [:div.is-pulled-right + [:a.button.is-primary.is-outlined {:on-click (dispatch-event [::new])} "New client"]] + [table/clients-table {:page @(re-frame/subscribe [::page]) + :status @(re-frame/subscribe [::status/single ::page])}]]]) + {:component-did-mount #(re-frame/dispatch [::mounted]) + :component-will-unmount #(re-frame/dispatch-sync [::unmounted])})) -(re-frame/reg-event-fx - ::save-new-client - [(forms/in-form ::form)] - (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 :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})))) - - -(re-frame/reg-event-db - ::save-complete - (fn [db [_ client]] - (-> db - (forms/stop-form ::form) - - (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])] - (fn [client _] - (-> client - (update :forecasted-transactions conj (assoc (:new-forecasted-transaction client) :temp-id (random-uuid))) - (dissoc :new-forecasted-transaction)))) - -(re-frame/reg-event-db - ::remove-forecasted-transaction - [(forms/in-form ::form) (re-frame/path [:data])] - (fn [client [_ which]] - - (-> client - (update :forecasted-transactions #(transduce - (filter (fn [x] (and (not= (:temp-id x) which) - (not= (:id x) which)))) - - conj - [] - %))))) - -(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 - [(forms/in-form ::form) (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 ::form) (re-frame/path [:data :bank-accounts])] - (fn [bank-accounts [_ index]] - (update (vec (sort-by :sort-order bank-accounts)) index assoc :active? true))) - -(re-frame/reg-event-db - ::bank-account-deactivated - [(forms/in-form ::form) (re-frame/path [:data :bank-accounts])] - (fn [bank-accounts [_ index]] - (update (vec (sort-by :sort-order bank-accounts)) index assoc :active? false))) - -(re-frame/reg-event-db - ::bank-account-removed - [(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))))) - -(re-frame/reg-event-db - ::sort-swapped - [(forms/in-form ::form) (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 ::form) (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]])]) - -(def client-form - (forms/vertical-form {:can-submit [::can-submit] - :change-event [::forms/change ::form] - :submit-event [::save-new-client ] - :id ::form})) - -(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 [{:keys [form field raw-field error-notification submit-button ]} client-form] - [: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 - [raw-field - [:input.input {:type "code" - :field [:bank-accounts sort-order :code] - :spec ::entity/code}]]]] - [:div.field [:p.control code]])] - - [field "Nickname" - [:input.input {:placeholder "BOA Checking #1" - :type "text" - :field [:bank-accounts sort-order :name]}]]] - (when (#{:check ":check"} type ) - [:div - - [:label.label "Bank"] - [horizontal-field - nil - [field "Bank Name" - [:input.input {:placeholder "Bank of America" - :type "text" - :field [:bank-accounts sort-order :bank-name]}]] - [field "Routing #" - [:input.input {:placeholder "104819123" - :type "text" - :field [:bank-accounts sort-order :routing]}]] - [field "Bank code" - [:input.input {:placeholder "12/10123" - :type "text" - :field [:bank-accounts sort-order :bank-code]}]]] - - [:label.label "Checking account"] - [horizontal-field - nil - [field "Account #" - [:input.input {:placeholder "123456789" - :type "text" - :field [:bank-accounts sort-order :number]}]] - - [field "Check Number" - [:input.input {:placeholder "10000" - :type "text" - :field [:bank-accounts sort-order :check-number]}]]] - [field "Yodlee Account" - [:input.input {:placeholder "Yodlee Account #" - :type "text" - :field [:bank-accounts sort-order :yodlee-account-id]}]] - - ]) - (when (#{:credit ":credit"} type ) - [:div - - - [:label.label "Account"] - [horizontal-field - nil - [field "Bank Name" - [:input.input {:placeholder "Bank of America" - :type "text" - :field [:bank-accounts sort-order :bank-name]}]]] - - [horizontal-field - nil - [field "Account #" - [:input.input {:placeholder "123456789" - :type "text" - :field [:bank-accounts sort-order :number]}]]] - - [field "Yodlee Account" - [:input.input {:placeholder "Yodlee Account #" - :type "text" - :field [:bank-accounts sort-order :yodlee-account-id]}]]]) - [:div.field - [: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"])] - [:div.field - [:label.checkbox - [raw-field - [:input {:type "checkbox" - :field [:bank-accounts sort-order :include-in-reports]}]] - " Include in reports"]]]) - - (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"])])]) - ) - - -(def first-week-a (coerce/to-date-time #inst "1999-12-27T00:00:00.000-07:00")) - -(defn is-week-a? [d] - (= 0 (mod (t/in-weeks (t/interval first-week-a d)) 2))) - -(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 - 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")] - - [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 - }]] - - [: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}]] - - [: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]]]])]] - - - - [: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 ])]]] - - [: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]]]])]]] - - [: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))))]) - - [: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 - - [: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"]]])) - (defn admin-clients-page [] - (let [{:keys [active?]} @(re-frame/subscribe [::forms/form ::form])] - [side-bar-layout {:side-bar [admin-side-bar {}] + (let [{:keys [active?]} @(re-frame/subscribe [::forms/form ::form/form])] + [side-bar-layout {:side-bar [admin-side-bar {} + [side-bar/client-side-bar]] :main [admin-clients-content] - :right-side-bar [appearing-side-bar {:visible? active?} [new-client-form]] }])) + :right-side-bar [appearing-side-bar {:visible? active?} [form/new-client-form]] }])) diff --git a/src/cljs/auto_ap/views/pages/admin/clients/form.cljs b/src/cljs/auto_ap/views/pages/admin/clients/form.cljs new file mode 100644 index 00000000..8c2b9bab --- /dev/null +++ b/src/cljs/auto_ap/views/pages/admin/clients/form.cljs @@ -0,0 +1,561 @@ +(ns auto-ap.views.pages.admin.clients.form + (:require [auto-ap.entities.clients :as entity] + [auto-ap.forms :as forms] + [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]] + [cljs-time.coerce :as coerce] + [cljs-time.core :as t] + [clojure.spec.alpha :as s] + [re-frame.core :as re-frame])) + +(re-frame/reg-sub + ::can-submit + :<- [::forms/form ::form] + (fn [{:keys [data status]} _] + (s/valid? ::entity/client data))) + +(re-frame/reg-sub + ::new-client-request + :<- [::forms/form ::form] + (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) + :matches (vec (: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) + :week-b-credits (:week-b-credits new-client-data) + :week-b-debits (:week-b-debits 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))} + :forecasted-transactions (map (fn [{:keys [id day-of-month identifier amount]}] + {:id id + :day-of-month day-of-month + :identifier identifier + :amount amount}) + (:forecasted-transactions new-client-data)) + :bank-accounts (map (fn [{:keys [number name check-number include-in-reports type id code bank-name routing bank-code new? sort-order visible yodlee-account-id locations]}] + {:number number + :name name + :check-number check-number + :include-in-reports include-in-reports + :type type + :id id + :sort-order sort-order + :visible visible + :locations (vec locations) + :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 + ::editing + (fn [{:keys [db]} [_ client-id]] + {:db (-> db + (forms/stop-form ::form) + (forms/start-form ::form (get (:clients db) client-id)))})) + +(re-frame/reg-event-fx + ::save-new-client + [(forms/in-form ::form)] + (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 :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})))) +(re-frame/reg-event-db + ::save-complete + (fn [db [_ client]] + (-> db + (forms/stop-form ::form) + + (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])] + (fn [client _] + (-> client + (update :forecasted-transactions conj (assoc (:new-forecasted-transaction client) :temp-id (random-uuid))) + (dissoc :new-forecasted-transaction)))) + +(re-frame/reg-event-db + ::remove-forecasted-transaction + [(forms/in-form ::form) (re-frame/path [:data])] + (fn [client [_ which]] + + (-> client + (update :forecasted-transactions #(transduce + (filter (fn [x] (and (not= (:temp-id x) which) + (not= (:id x) which)))) + + conj + [] + %))))) + +(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 + [(forms/in-form ::form) (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 ::form) (re-frame/path [:data :bank-accounts])] + (fn [bank-accounts [_ index]] + (update (vec (sort-by :sort-order bank-accounts)) index assoc :active? true))) + +(re-frame/reg-event-db + ::bank-account-deactivated + [(forms/in-form ::form) (re-frame/path [:data :bank-accounts])] + (fn [bank-accounts [_ index]] + (update (vec (sort-by :sort-order bank-accounts)) index assoc :active? false))) + +(re-frame/reg-event-db + ::bank-account-removed + [(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))))) + +(re-frame/reg-event-db + ::sort-swapped + [(forms/in-form ::form) (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 ::form) (re-frame/path [:data :bank-accounts])] + (fn [bank-accounts [_ account]] + (-> (->> bank-accounts + (sort-by :sort-order) + vec) + (update-in [account :visible] #(not %))))) + + + + +(def client-form + (forms/vertical-form {:can-submit [::can-submit] + :change-event [::forms/change ::form] + :submit-event [::save-new-client ] + :id ::form})) + +(def first-week-a (coerce/to-date-time #inst "1999-12-27T00:00:00.000-07:00")) + +(defn is-week-a? [d] + (= 0 (mod (t/in-weeks (t/interval first-week-a d)) 2))) + + +(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 [{:keys [form field raw-field error-notification submit-button ]} client-form] + [: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 + [raw-field + [:input.input {:type "code" + :field [:bank-accounts sort-order :code] + :spec ::entity/code}]]]] + [:div.field [:p.control code]])] + + [field "Nickname" + [:input.input {:placeholder "BOA Checking #1" + :type "text" + :field [:bank-accounts sort-order :name]}]]] + (when (#{:check ":check"} type ) + [:div + + [:label.label "Bank"] + [horizontal-field + nil + [field "Bank Name" + [:input.input {:placeholder "Bank of America" + :type "text" + :field [:bank-accounts sort-order :bank-name]}]] + [field "Routing #" + [:input.input {:placeholder "104819123" + :type "text" + :field [:bank-accounts sort-order :routing]}]] + [field "Bank code" + [:input.input {:placeholder "12/10123" + :type "text" + :field [:bank-accounts sort-order :bank-code]}]]] + + [:label.label "Checking account"] + [horizontal-field + nil + [field "Account #" + [:input.input {:placeholder "123456789" + :type "text" + :field [:bank-accounts sort-order :number]}]] + + [field "Check Number" + [:input.input {:placeholder "10000" + :type "text" + :field [:bank-accounts sort-order :check-number]}]]] + [field "Yodlee Account" + [:input.input {:placeholder "Yodlee Account #" + :type "text" + :field [:bank-accounts sort-order :yodlee-account-id]}]] + + ]) + (when (#{:credit ":credit"} type ) + [:div + + + [:label.label "Account"] + [horizontal-field + nil + [field "Bank Name" + [:input.input {:placeholder "Bank of America" + :type "text" + :field [:bank-accounts sort-order :bank-name]}]]] + + [horizontal-field + nil + [field "Account #" + [:input.input {:placeholder "123456789" + :type "text" + :field [:bank-accounts sort-order :number]}]]] + + [field "Yodlee Account" + [:input.input {:placeholder "Yodlee Account #" + :type "text" + :field [:bank-accounts sort-order :yodlee-account-id]}]]]) + [:div.field + [: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"])] + [:div.field + [:label.checkbox + [raw-field + [:input {:type "checkbox" + :field [:bank-accounts sort-order :include-in-reports]}]] + " Include in reports"]]]) + + (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 [{new-client :data } @(re-frame/subscribe [::forms/form ::form]) + {:keys [form 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")] + + [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 + }]] + + [: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}]] + + [: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]]]])]] + + + + [: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 ])]]] + + [: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]]]])]]] + + [: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))))]) + + [: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 + + [: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"]]])) diff --git a/src/cljs/auto_ap/views/pages/admin/clients/side_bar.cljs b/src/cljs/auto_ap/views/pages/admin/clients/side_bar.cljs new file mode 100644 index 00000000..c95a2713 --- /dev/null +++ b/src/cljs/auto_ap/views/pages/admin/clients/side_bar.cljs @@ -0,0 +1,79 @@ +(ns auto-ap.views.pages.admin.clients.side-bar + (:require + [re-frame.core :as re-frame] + [auto-ap.views.utils :refer [dispatch-value-change]] + [auto-ap.subs :as subs])) + +(re-frame/reg-sub + ::specific-filters + (fn [db ] + (::filters db nil))) + +(re-frame/reg-sub + ::filters + :<- [::specific-filters] + :<- [::subs/query-params] + (fn [[specific-filters vendors-by-id query-params] ] + (let [url-filters (-> query-params + (select-keys #{:name + :code})) + url-filters {:name (str (:name url-filters)) + :code (str (:code url-filters))}] + (merge url-filters specific-filters )))) + +(re-frame/reg-sub + ::filter + :<- [::filters] + (fn [filters [_ which]] + (get filters which))) + +(re-frame/reg-sub + ::filter-params + :<- [::settled-filters] + :<- [::filters] + :<- [::subs/active-page] + (fn [[settled-filters filters ap ]] + (let [filters (or settled-filters filters)] + {:name (:name filters) + :code (:code filters)}))) + +(re-frame/reg-sub + ::settled-filters + (fn [db ] + (::settled-filters db))) + +(re-frame/reg-event-fx + ::filters-settled + (fn [{:keys [db]} [_ & params]] + {:db (assoc db ::settled-filters @(re-frame/subscribe [::filters]))})) + +(re-frame/reg-event-fx + ::filter-changed + (fn [{:keys [db]} [_ & params]] + (let [[a b c] params + [which val] (if (= 3 (count params)) + [(into [a] b) c] + [[a] b])] + {:db (assoc-in db (into [::filters] which) val) + :dispatch-debounce {:event [::filters-settled] + :time 800 + :key ::filters}}))) + + +(defn client-side-bar [] + [:div + [:p.menu-label "Name"] + + [:div.field + [:div.control [:input.input {:placeholder "Harry's Food Products" + :value @(re-frame/subscribe [::filter :name]) + :on-change (dispatch-value-change [::filter-changed :name])} ]]] + + [:p.menu-label "Code"] + + [:div.field + [:div.control [:input.input {:placeholder "CBC" + :value @(re-frame/subscribe [::filter :code]) + :on-change (dispatch-value-change [::filter-changed :code])} ]]]]) + + diff --git a/src/cljs/auto_ap/views/pages/admin/clients/table.cljs b/src/cljs/auto_ap/views/pages/admin/clients/table.cljs new file mode 100644 index 00000000..5d2ed5e3 --- /dev/null +++ b/src/cljs/auto_ap/views/pages/admin/clients/table.cljs @@ -0,0 +1,53 @@ +(ns auto-ap.views.pages.admin.clients.table + (:require [auto-ap.subs :as subs] + [clojure.string :as str] + [re-frame.core :as re-frame] + [auto-ap.views.pages.admin.clients.form :as form] + [auto-ap.views.utils :refer [action-cell-width]] + [auto-ap.views.components.grid :as grid] + [auto-ap.views.components.buttons :as buttons])) + +(re-frame/reg-sub + ::specific-params + (fn [db] + (::params db))) + +(re-frame/reg-event-fx + ::params-changed + (fn [{:keys [db]} [_ p]] + {:db (assoc db ::params p)})) + +(re-frame/reg-sub + ::params + :<- [::specific-params] + :<- [::subs/query-params] + (fn [[specific-params query-params]] + (merge (select-keys query-params #{:start :sort}) specific-params ))) + + +(defn clients-table [{:keys [page status]}] + [grid/grid {:on-params-change (fn [p] + (re-frame/dispatch [::params-changed p])) + :status status + :params @(re-frame/subscribe [::params]) + :column-count 5} + [grid/controls page] + [grid/table {:fullwidth true} + [grid/header + [grid/row {} + [grid/header-cell {} "Name"] + [grid/header-cell {:style {:width "20em"}} "Code"] + [grid/header-cell {} "Locations"] + [grid/header-cell {} "Email"] + [grid/header-cell {:style {:width (action-cell-width 1)}}]] + ] + [grid/body + (for [{:keys [id name email code locations] :as c} (:data page)] + ^{:key (str name "-" id )} + [grid/row + [grid/cell {} name] + [grid/cell {} code] + [grid/cell {} (str/join ", " locations)] + [grid/cell {} email] + [grid/cell {} [buttons/fa-icon {:event [::form/editing id] + :icon :fa-pencil}]]])]]]) diff --git a/src/cljs/auto_ap/views/pages/admin/rules/table.cljs b/src/cljs/auto_ap/views/pages/admin/rules/table.cljs index bc26366b..7c7f0812 100644 --- a/src/cljs/auto_ap/views/pages/admin/rules/table.cljs +++ b/src/cljs/auto_ap/views/pages/admin/rules/table.cljs @@ -106,59 +106,58 @@ opc (fn [p] (re-frame/dispatch [::params-changed p])) states @(re-frame/subscribe [::status/multi ::run])] - [:div - [grid/grid {:on-params-change opc - :params @(re-frame/subscribe [::table-params]) - :status status - :column-count 6} - [grid/controls {:start start :end end :count count :total total}] - [grid/table {:fullwidth true } - [grid/header - [grid/row {} - [grid/sortable-header-cell {:sort-key "client" - :sort-name "Client"} - "Client"] + [grid/grid {:on-params-change opc + :params @(re-frame/subscribe [::table-params]) + :status status + :column-count 6} + [grid/controls {:start start :end end :count count :total total}] + [grid/table {:fullwidth true } + [grid/header + [grid/row {} + [grid/sortable-header-cell {:sort-key "client" + :sort-name "Client"} + "Client"] - [grid/sortable-header-cell {:sort-key "bank-account" - :sort-name "Bank Account"} - "Bank Account"] + [grid/sortable-header-cell {:sort-key "bank-account" + :sort-name "Bank Account"} + "Bank Account"] - [grid/sortable-header-cell {:sort-key "description" - :sort-name "Description"} - "Description"] + [grid/sortable-header-cell {:sort-key "description" + :sort-name "Description"} + "Description"] - [grid/header-cell {:style {:width "12em"}} "Amount"] + [grid/header-cell {:style {:width "12em"}} "Amount"] - [grid/sortable-header-cell {:sort-key "note" - :sort-name "Note"} - "Note"] + [grid/sortable-header-cell {:sort-key "note" + :sort-name "Note"} + "Note"] - [grid/header-cell {:style {:width (action-cell-width 3)}}]]] - [grid/body - (for [{:keys [client bank-account description amount-lte amount-gte note id] :as r} transaction-rules] - ^{:key id} - [grid/row {:class (:class r)} - [grid/cell {} (:name client)] - [grid/cell {} (:name bank-account)] - [grid/cell {} description] - [grid/cell {:class "has-text-right"} - (cond (and amount-gte amount-lte) - (str (->$ amount-gte) " - " (->$ amount-lte)) + [grid/header-cell {:style {:width (action-cell-width 3)}}]]] + [grid/body + (for [{:keys [client bank-account description amount-lte amount-gte note id] :as r} transaction-rules] + ^{:key id} + [grid/row {:class (:class r)} + [grid/cell {} (:name client)] + [grid/cell {} (:name bank-account)] + [grid/cell {} description] + [grid/cell {:class "has-text-right"} + (cond (and amount-gte amount-lte) + (str (->$ amount-gte) " - " (->$ amount-lte)) - amount-gte - (str ">=" (->$ amount-gte)) + amount-gte + (str ">=" (->$ amount-gte)) - amount-lte - (str "<=" (->$ amount-lte)) + amount-lte + (str "<=" (->$ amount-lte)) - :else - "")] - [grid/cell {} note] - [grid/cell {} - [:div.buttons - [buttons/fa-icon {:event [::run-clicked r] :icon :fa-play :class (status/class-for (get states (:id r)))}] - [buttons/sl-icon {:event [::request-delete r] :icon :icon-bin-2}] - [buttons/fa-icon {:event [::form/editing r] :icon :fa-pencil}]]]])]]]])) + :else + "")] + [grid/cell {} note] + [grid/cell {} + [:div.buttons + [buttons/fa-icon {:event [::run-clicked r] :icon :fa-play :class (status/class-for (get states (:id r)))}] + [buttons/sl-icon {:event [::request-delete r] :icon :icon-bin-2}] + [buttons/fa-icon {:event [::form/editing r] :icon :fa-pencil}]]]])]]])) (defn table [params] (r/create-class {:component-will-unmount (dispatch-event [::unmounted]) diff --git a/src/cljs/auto_ap/views/pages/admin/users.cljs b/src/cljs/auto_ap/views/pages/admin/users.cljs index ef1713c8..afc92419 100644 --- a/src/cljs/auto_ap/views/pages/admin/users.cljs +++ b/src/cljs/auto_ap/views/pages/admin/users.cljs @@ -89,8 +89,7 @@ (defn admin-users-content [] [:div [:h1.title "Users"] - [users-table] - [form/form]]) + [users-table]]) (defn admin-users-page []