(cloud) big performance improvements for page load.

This commit is contained in:
2023-04-06 14:38:22 -07:00
parent 2a0f736af7
commit 5561861d3d
10 changed files with 349 additions and 272 deletions

View File

@@ -1,10 +1,46 @@
(ns auto-ap.datomic.clients (ns auto-ap.datomic.clients
(:require (:require
[auto-ap.datomic :refer [conn]] [auto-ap.datomic :refer [add-sorter-fields
apply-pagination
apply-sort-3
conn
merge-query
pull-many]]
[auto-ap.graphql.utils :refer [can-see-client? limited-clients]]
[auto-ap.search :as search] [auto-ap.search :as search]
[clj-time.coerce :as coerce] [clj-time.coerce :as coerce]
[clojure.string :as str] [clojure.string :as str]
[datomic.client.api :as dc])) [datomic.client.api :as dc]
[com.brunobonacci.mulog :as mu]))
(def full-read '[*
{:client/square-integration-status [:integration-status/message
:integration-status/last-attempt
:integration-status/last-updated
:db/id
{:integration-status/state [:db/ident]}]}
{:client/address [*]}
{:client/square-locations [:square-location/square-id
:square-location/name
:square-location/client-location
:db/id]}
{:client/ezcater-locations [{:ezcater-location/caterer [:ezcater-caterer/name :db/id]}
:ezcater-location/location
:db/id]}
{:client/bank-accounts [* {:bank-account/type [*]
:bank-account/yodlee-account [:yodlee-account/name :yodlee-account/id :yodlee-account/number]
:bank-account/plaid-account [:plaid-account/name :db/id :plaid-account/number :plaid-account/balance]
:bank-account/intuit-bank-account [:intuit-bank-account/name :intuit-bank-account/external-id :db/id]
:bank-account/integration-status [:integration-status/message
:db/id
:integration-status/last-attempt
:integration-status/last-updated
{:integration-status/state [:db/ident]}]}
]}
{:yodlee-provider-account/_client [*]}
{:plaid-item/_client [*]}
{:client/emails [:db/id :email-contact/email :email-contact/description]}])
(defn cleanse [e] (defn cleanse [e]
(-> e (-> e
@@ -29,52 +65,25 @@
(update :bank-account/sort-order (fn [so] (or so i))))) (update :bank-account/sort-order (fn [so] (or so i)))))
(range) bas))))) (range) bas)))))
(defn get-all [] (defn get-all []
(->> (dc/q '[:find (pull ?e [* (->> (dc/q '[:find (pull ?e ?r)
{:client/square-integration-status [:integration-status/message :in $ ?r
:integration-status/last-attempt
:integration-status/last-updated
:db/id
{:integration-status/state [:db/ident]}]}
{:client/address [*]}
{:client/square-locations [:square-location/square-id
:square-location/name
:square-location/client-location
:db/id]}
{:client/ezcater-locations [{:ezcater-location/caterer [:ezcater-caterer/name :db/id]}
:ezcater-location/location
:db/id]}
{:client/bank-accounts [* {:bank-account/type [*]
:bank-account/yodlee-account [:yodlee-account/name :yodlee-account/id :yodlee-account/number]
:bank-account/plaid-account [:plaid-account/name :db/id :plaid-account/number :plaid-account/balance]
:bank-account/intuit-bank-account [:intuit-bank-account/name :intuit-bank-account/external-id :db/id]
:bank-account/integration-status [:integration-status/message
:db/id
:integration-status/last-attempt
:integration-status/last-updated
{:integration-status/state [:db/ident]}]}
]}
{:yodlee-provider-account/_client [*]}
{:plaid-item/_client [*]}
{:client/emails [:db/id :email-contact/email :email-contact/description]}])
:where [?e :client/name]] :where [?e :client/name]]
(dc/db conn)) (dc/db conn)
full-read)
(map first)
(map cleanse)))
(defn get-minimal []
(->> (dc/q '[:find (pull ?e [:client/name :client/code :client/locations :db/id {:client/bank-accounts [{:bank-account/type [:db/ident]} :bank-account/name :bank-account/code :db/id]}])
:where [?e :client/name]]
(dc/db conn))
(map first) (map first)
(map cleanse))) (map cleanse)))
(defn get-by-id [id] (defn get-by-id [id]
(->> (->>
(dc/pull (dc/db conn ) (dc/pull (dc/db conn )
'[* {:client/bank-accounts [* {:bank-account/type [*] full-read
:bank-account/yodlee-account [:yodlee-account/name :yodlee-account/id :yodlee-account/number]
:bank-account/intuit-bank-account [:intuit-bank-account/name :intuit-bank-account/external-id :db/id]
:bank-account/plaid-account [:plaid-account/name :db/id :plaid-account/number :plaid-account/balance]}]
:client/emails [:db/id :email-contact/email :email-contact/description]}
{:client/ezcater-locations [{:ezcater-location/caterer [:ezcater-caterer/name :db/id]}
:ezcater-location/location
:db/id]}
{:yodlee-provider-account/_client [*]}
{:plaid-item/_client [*]}]
id) id)
(cleanse))) (cleanse)))
@@ -105,3 +114,54 @@
:text match :text match
:exact-match (str/upper-case match)}) :exact-match (str/upper-case match)})
"client")) "client"))
(defn raw-graphql-ids [db args]
(let [query (cond-> {:query {:find []
:in ['$ ]
:where []}
:args [db]}
(not (str/blank? (:code args)))
(merge-query {:query {:in ['?client-code]
:where ['[?e :client/code ?client-code]]}
:args [(:code args)]})
(not (str/blank? (:name-like args)))
(merge-query {:query {:in ['?name-like]
:where ['[?e :client/name ?name]
'[(clojure.string/includes? ?name ?name-like)]]}
:args [(:name-like args)]})
(limited-clients (:id args))
(merge-query {:query {:in ['[?e ...]]
:where []}
:args [(set (map :db/id (limited-clients (:id args))))]})
(:sort args) (add-sorter-fields {"name" ['[?e :client/name ?sort-name]]}
args)
true
(merge-query {:query {:find ['?sort-default '?e] :where ['[?e :client/name ?sort-default]]}}))]
(mu/log ::q
:query query)
(->> query
(dc/q)
(apply-sort-3 (update args :sort conj {:sort-key "default-2" :asc true}))
(apply-pagination args))))
(defn graphql-results [ids db args]
(let [results (->> (pull-many db full-read
ids)
(map cleanse))]
results))
(defn get-graphql-page [args]
(let [db (dc/db conn)
{ids-to-retrieve :ids matching-count :count} (raw-graphql-ids db args)]
[(->> (graphql-results ids-to-retrieve db args))
matching-count]))

View File

@@ -9,11 +9,10 @@
pull-many pull-many
visible-clients visible-clients
conn]] conn]]
[auto-ap.graphql.utils :refer [limited-clients]] [iol-ion.query]
[clj-time.coerce :as c] [clj-time.coerce :as c]
[clj-time.core :as time] [clj-time.core :as time]
[clojure.set :as set] [clojure.set :as set]
[clojure.tools.logging :as log]
[datomic.client.api :as dc] [datomic.client.api :as dc]
[com.brunobonacci.mulog :as mu] [com.brunobonacci.mulog :as mu]
)) ))

View File

@@ -699,15 +699,14 @@
(- (+ total credit) (- (+ total credit)
debit)) debit))
0.0 0.0
(dc/q {:query {:find '[?debit ?credit] (dc/q {:query {:find '[?debit ?credit]
:in '[$ ?client] :in '[$ ?client]
:where ['[?j :journal-entry/client ?client] :where ['[?client :client/bank-accounts ?ba]
'[?j :journal-entry/line-items ?je] '[?ba :bank-account/type :bank-account-type/check]
'[?je :journal-entry-line/account ?ba] '[?je :journal-entry-line/account ?ba]
'[?ba :bank-account/type :bank-account-type/check] '[(get-else $ ?je :journal-entry-line/debit 0.0) ?debit]
'[(get-else $ ?je :journal-entry-line/debit 0.0) ?debit] '[(get-else $ ?je :journal-entry-line/credit 0.0) ?credit]]}
'[(get-else $ ?je :journal-entry-line/credit 0.0) ?credit]]} :args [(dc/db conn) client_id]}))
:args [(dc/db conn) client_id]}))
bills-due-soon (dc/q {:query {:find '[?due ?outstanding ?invoice-number ?vendor-id ?vendor-name] bills-due-soon (dc/q {:query {:find '[?due ?outstanding ?invoice-number ?vendor-id ?vendor-name]
:in '[$ ?client ?due-before] :in '[$ ?client ?due-before]
:where ['[?i :invoice/client ?client] :where ['[?i :invoice/client ?client]
@@ -734,8 +733,8 @@
recent-fulfillments (dc/q {:query {:find '[?f ?d] recent-fulfillments (dc/q {:query {:find '[?f ?d]
:in '[$ ?client ?min-date] :in '[$ ?client ?min-date]
:where ['[?t :transaction/forecast-match ?f] :where ['[?t :transaction/forecast-match ?f]
'[?t :transaction/date ?d]
'[?t :transaction/client ?client] '[?t :transaction/client ?client]
'[?t :transaction/date ?d]
'[(>= ?d ?min-date)]]} '[(>= ?d ?min-date)]]}
:args [(dc/db conn) client_id (coerce/to-date (t/plus (time/local-now) (t/months -2)))]}) :args [(dc/db conn) client_id (coerce/to-date (t/plus (time/local-now) (t/months -2)))]})
forecasted-transactions (for [{:forecasted-transaction/keys [amount identifier day-of-month] forecasted-transactions (for [{:forecasted-transaction/keys [amount identifier day-of-month]

View File

@@ -3,12 +3,13 @@
[amazonica.aws.s3 :as s3] [amazonica.aws.s3 :as s3]
[auto-ap.datomic :refer [audit-transact conn]] [auto-ap.datomic :refer [audit-transact conn]]
[auto-ap.datomic.clients :as d-clients] [auto-ap.datomic.clients :as d-clients]
[iol-ion.tx :refer [upsert-entity]]
[auto-ap.graphql.utils [auto-ap.graphql.utils
:refer [->graphql :refer [->graphql
assert-admin assert-admin
attach-tracing-resolvers attach-tracing-resolvers
can-see-client? can-see-client?
<-graphql
result->page
is-admin?]] is-admin?]]
[auto-ap.routes.queries :as q] [auto-ap.routes.queries :as q]
[auto-ap.square.core :as square] [auto-ap.square.core :as square]
@@ -18,12 +19,12 @@
[clojure.set :as set] [clojure.set :as set]
[clojure.string :as str] [clojure.string :as str]
[clojure.tools.logging :as log] [clojure.tools.logging :as log]
[com.brunobonacci.mulog :as mu]
[datomic.client.api :as dc] [datomic.client.api :as dc]
[unilog.context :as lc] [iol-ion.tx :refer [random-tempid upsert-entity]]
[mount.core :as mount] [mount.core :as mount]
[yang.scheduler :as scheduler] [unilog.context :as lc]
[iol-ion.tx :refer [random-tempid]] [yang.scheduler :as scheduler])
[com.brunobonacci.mulog :as mu])
(:import (:import
(java.util UUID) (java.util UUID)
(org.apache.commons.codec.binary Base64))) (org.apache.commons.codec.binary Base64)))
@@ -200,23 +201,39 @@
(defn get-client [context _ _] (defn get-client [context _ _]
(->graphql (->graphql
(->> (d-clients/get-all) (->> (d-clients/get-minimal)
(filter #(can-see-client? (:id context) %)) (filter #(can-see-client? (:id context) %)))))
(map (fn [c]
(update c :client/bank-accounts (fn [bas] (defn get-admin-client [context {:keys [id]} _]
(map #(set/rename-keys % {:bank-account/use-date-instead-of-post-date? :use-date-instead-of-post-date}) bas))))) (assert-admin (:id context))
(map (fn [c] (->graphql
(if (is-admin? (:id context)) (-> (d-clients/get-by-id id)
c (update :client/bank-accounts (fn [bas]
(-> c (map #(set/rename-keys % {:bank-account/use-date-instead-of-post-date? :use-date-instead-of-post-date}) bas))))))
(dissoc :client/yodlee-provider-accounts)
(dissoc :client/plaid-items))))) (defn get-client-page [context args _]
(map (fn [c] (assert-admin (:id context))
(update c :client/bank-accounts (let [args (assoc args :id (:id context))
(fn [bank-accounts] [clients clients-count] (d-clients/get-graphql-page (assoc (<-graphql (:filters args))
(mapv (fn [ba] :id (:id context)))
(assoc ba :bank-account/yodlee-balance-old nil)) clients (->> clients
bank-accounts))))))))
(map (fn [c]
(update c :client/bank-accounts (fn [bas]
(map #(set/rename-keys % {:bank-account/use-date-instead-of-post-date? :use-date-instead-of-post-date}) bas)))))
(map (fn [c]
(if (is-admin? (:id context))
c
(-> c
(dissoc :client/yodlee-provider-accounts)
(dissoc :client/plaid-items)))))
(map (fn [c]
(update c :client/bank-accounts
(fn [bank-accounts]
(mapv (fn [ba]
(assoc ba :bank-account/yodlee-balance-old nil))
bank-accounts))))))]
(result->page clients clients-count :clients (:filters args))))
(def sales-summary-query (def sales-summary-query
"[:find ?d4 (sum ?total) (sum ?tax) (sum ?tip) (sum ?service-charge) (sum ?discount) (sum ?returns) "[:find ?d4 (sum ?total) (sum ?tax) (sum ?tip) (sum ?service-charge) (sum ?discount) (sum ?returns)
@@ -437,6 +454,15 @@
:yodlee_provider_accounts {:type '(list :yodlee_provider_account)} :yodlee_provider_accounts {:type '(list :yodlee_provider_account)}
:plaid_items {:type '(list :plaid_item)}}} :plaid_items {:type '(list :plaid_item)}}}
:client_page
{:fields {:clients {:type '(list :client)}
:count {:type 'Int}
:total {:type 'Int}
:start {:type 'Int}
:end {:type 'Int}}}
:bank_account :bank_account
{:fields {:id {:type :id} {:fields {:id {:type :id}
:integration_status {:type :integration_status} :integration_status {:type :integration_status}
@@ -471,7 +497,14 @@
(def queries (def queries
{:client {:type '(list :client) {:client {:type '(list :client)
:resolve :get-client}}) :resolve :get-client}
:admin_client {:type :client
:args {:id {:type :id}}
:resolve :get-admin-client}
:client_page {:type :client_page
:args {:filters {:type :client_filters}}
:resolve :get-client-page}})
(def mutations (def mutations
{:edit_client {:type :client {:edit_client {:type :client
@@ -486,6 +519,13 @@
:match {:type 'String} :match {:type 'String}
:id {:type :id}}} :id {:type :id}}}
:client_filters
{:fields {:code {:type 'String}
:name_like {:type 'String}
:start {:type 'Int}
:per_page {:type 'Int}
:sort {:type '(list :sort_item)}}}
:edit_square_location {:fields {:client_location {:type 'String} :edit_square_location {:fields {:client_location {:type 'String}
:id {:type :id}}} :id {:type :id}}}
@@ -551,6 +591,8 @@
(def resolvers (def resolvers
{:get-client get-client {:get-client get-client
:get-admin-client get-admin-client
:get-client-page get-client-page
:mutation/edit-client edit-client :mutation/edit-client edit-client
:mutation/setup-sales-queries setup-sales-queries}) :mutation/setup-sales-queries setup-sales-queries})

View File

@@ -16,7 +16,13 @@
(js->clj (.parse js/JSON (b64/decodeString (second (str/split token #"\." )))))) (js->clj (.parse js/JSON (b64/decodeString (second (str/split token #"\." ))))))
(defn client-query [token] (defn client-query []
(cond-> [:id :name :code :email :locations :feature-flags
[:emails [:id :email :description]]
[:bank-accounts [:id :code :bank-name :name :type :visible
:locations :include-in-reports :current-balance]]]))
(defn client-detail-query [token]
(cond-> [:id :name :signature-file :code :email :matches :week-a-debits :week-a-credits :week-b-debits :week-b-credits :locations :locked-until :square-auth-token :feature-flags (cond-> [:id :name :signature-file :code :email :matches :week-a-debits :week-a-credits :week-b-debits :week-b-credits :locations :locked-until :square-auth-token :feature-flags
[:square-integration-status [:last-updated :last-attempt :message :state :id]] [:square-integration-status [:last-updated :last-attempt :message :state :id]]
[:square-locations [:square-id :id :name :client-location]] [:square-locations [:square-id :id :name :client-location]]
@@ -68,7 +74,7 @@
:query-params (auto-ap.views.utils/query-params) :query-params (auto-ap.views.utils/query-params)
:user token) :user token)
:graphql {:token token :graphql {:token token
:query-obj {:venia/queries [[:client (client-query token)]]} :query-obj {:venia/queries [[:client (client-query)]]}
:on-success [::received-initial] :on-success [::received-initial]
:on-error [::failed-initial]}})))) :on-error [::failed-initial]}}))))
@@ -100,7 +106,7 @@
(fn [{:keys [db]}] (fn [{:keys [db]}]
(let [token (-> db :user)] (let [token (-> db :user)]
{:graphql {:token token {:graphql {:token token
:query-obj {:venia/queries [[:client (client-query token)]]} :query-obj {:venia/queries [[:client (client-query)]]}
:on-success [::received-refreshed-clients]}}))) :on-success [::received-refreshed-clients]}})))
(re-frame/reg-event-fx (re-frame/reg-event-fx

View File

@@ -4,6 +4,7 @@
[auto-ap.status :as status] [auto-ap.status :as status]
[auto-ap.subs :as subs] [auto-ap.subs :as subs]
[auto-ap.shared-views.admin.side-bar :refer [admin-side-bar]] [auto-ap.shared-views.admin.side-bar :refer [admin-side-bar]]
[auto-ap.views.pages.data-page :as data-page]
[auto-ap.views.components.grid :as grid] [auto-ap.views.components.grid :as grid]
[auto-ap.views.components.layouts :refer [side-bar-layout]] [auto-ap.views.components.layouts :refer [side-bar-layout]]
[auto-ap.views.pages.admin.clients.form :as form] [auto-ap.views.pages.admin.clients.form :as form]
@@ -13,8 +14,12 @@
[auto-ap.views.utils :refer [with-user]] [auto-ap.views.utils :refer [with-user]]
[bidi.bidi :as bidi] [bidi.bidi :as bidi]
[clojure.string :as str] [clojure.string :as str]
[clojure.set :as set]
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[vimsical.re-frame.fx.track :as track])) [vimsical.re-frame.fx.track :as track]
[auto-ap.events :as events]
[auto-ap.forms :as forms]
[auto-ap.db :as db]))
(re-frame/reg-event-db (re-frame/reg-event-db
::received-intuit-bank-accounts ::received-intuit-bank-accounts
@@ -24,10 +29,15 @@
(re-frame/reg-event-fx (re-frame/reg-event-fx
::mounted ::mounted
[with-user] [with-user]
(fn [{:keys [user]} _] (fn [{:keys [user db]} _]
{::track/register {:id ::params {::track/register [{:id ::params
:subscription [::params] :subscription [::data-page/params ::page]
:event-fn (fn [params] [::params-change params])} :event-fn (fn [params] [::params-change params])}
{:id ::active-route
:subscription [::subs/active-route]
:event-fn (fn [params] [::params-change params])}]
:db (-> db
(forms/stop-form [::form/form]))
:graphql {:token user :graphql {:token user
:query-obj {:venia/queries [[:intuit_bank_accounts [:external_id :id :name]]]} :query-obj {:venia/queries [[:intuit_bank_accounts [:external_id :id :name]]]}
:owns-state {:single [::load-intuit-bank-accounts]} :owns-state {:single [::load-intuit-bank-accounts]}
@@ -37,34 +47,31 @@
::unmounted ::unmounted
(fn [{:keys [db]} _] (fn [{:keys [db]} _]
{:db (dissoc db ::table/params ::side-bar/filter-params) {:db (dissoc db ::table/params ::side-bar/filter-params)
::track/dispose {:id ::params}})) ::track/dispose [{:id ::params}
{:id ::active-route}]}))
(re-frame/reg-sub (defn data-params->query-params [params]
::params {:start (:start params 0)
:<- [::table/params] :per-page (:per-page params)
:<- [::side-bar/filter-params] :sort (:sort params)
(fn [[table-params filter-params]] :name-like (:name-like params)
(cond-> {} :code (:code params)})
(seq filter-params) (merge filter-params)
(seq table-params) (merge table-params))))
(re-frame/reg-event-fx (re-frame/reg-event-fx
::params-change ::params-change
(fn [_ [_ params]] [with-user]
{:set-uri-params params})) (fn [{:keys [user]} [_ params]]
{:graphql {:token user
(re-frame/reg-sub :owns-state {:single [::data-page/page ::page]}
::page :query-obj {:venia/queries [[:client-page
:<- [::params] {:filters (data-params->query-params params)}
:<- [::subs/clients] [[:clients (events/client-detail-query user)]
(fn [[params all-clients]] :total
(let [matching-clients (cond->> all-clients :start
(not-empty (:name params)) (filter #(str/includes? (str/lower-case (or (:name %) "")) :end]]]}
(str/lower-case (:name params)))) :on-success (fn [result]
(not-empty (:code params)) (filter #(= (str/lower-case (or (:code %) "")) [::data-page/received ::page (set/rename-keys (:client-page result)
(str/lower-case (:code params)))))] {:clients :data})])}}))
(assoc (grid/virtual-paginate-controls (:start params ) (:per-page params) matching-clients)
:data (grid/virtual-paginate (:start params) (:per-page params) matching-clients)))))
(def admin-clients-content (def admin-clients-content
(with-meta (with-meta
@@ -77,14 +84,14 @@
:content [:<> :content [:<>
[:div.is-pulled-right [:div.is-pulled-right
[:a.button.is-primary.is-outlined {:href (bidi/path-for routes/routes :admin-specific-client :id "new")} "New client"]] [:a.button.is-primary.is-outlined {:href (bidi/path-for routes/routes :admin-specific-client :id "new")} "New client"]]
[table/clients-table {:page @(re-frame/subscribe [::page]) [table/clients-table {:data-page ::page
:status @(re-frame/subscribe [::status/single ::page])}]]} :id :clients}]]}
{:key :admin-specific-client {:key :admin-specific-client
:breadcrumb [:span [:a {:href (bidi/path-for routes/routes :admin-clients)} :breadcrumb [:span [:a {:href (bidi/path-for routes/routes :admin-clients)}
"Clients"] "Clients"]
" / " " / "
(or (:name @(re-frame/subscribe [::form/client])) (or (:name (:data @(re-frame/subscribe [::forms/form ::form/form])))
[:i "New client"])] [:i "New client"])]
:content [form/new-client-form]} :content [form/new-client-form]}
]}]]) ]}]])
@@ -94,6 +101,6 @@
(defn admin-clients-page [] (defn admin-clients-page []
[side-bar-layout {:side-bar [admin-side-bar {} [side-bar-layout {:side-bar [admin-side-bar {}
[side-bar/client-side-bar]] [side-bar/client-side-bar {:data-page ::page}]]
:main [admin-clients-content]}]) :main [admin-clients-content]}])

View File

@@ -14,6 +14,7 @@
:refer [search-backed-typeahead]] :refer [search-backed-typeahead]]
[auto-ap.views.utils [auto-ap.views.utils
:refer [date-picker :refer [date-picker
with-user
dispatch-event]] dispatch-event]]
[bidi.bidi :as bidi] [bidi.bidi :as bidi]
[cljs-time.coerce :as coerce] [cljs-time.coerce :as coerce]
@@ -206,42 +207,46 @@
:bank-code bank-code}) :bank-code bank-code})
(:bank-accounts new-client-data))}))) (:bank-accounts new-client-data))})))
(re-frame/reg-sub
::client
:<- [::subs/route-params]
:<- [::subs/clients-by-id]
(fn [[rp clients-by-id]]
(or (get clients-by-id (:id rp))
{})))
(re-frame/reg-event-fx (re-frame/reg-event-fx
::mounted ::mounted
[(re-frame/inject-cofx ::inject/sub [::client])] [with-user (re-frame/inject-cofx ::inject/sub [::subs/route-params])]
(fn [{:keys [db] ::keys [client]} _] (fn [{:keys [user db] ::subs/keys [route-params]} _]
{:db (-> db {:graphql {:token user
(forms/stop-form ::form) :query-obj {:venia/queries [[:admin-client
(forms/start-form ::form (-> client {:id (js/parseInt (:id route-params))}
(assoc :selected-square-locations (->> (:square-locations client) (events/client-detail-query user)]]}
(filter :client-location ) :on-success (fn [result]
(mapv (fn [sl] [::received (:admin-client result)])}
{:id (:id sl) :db (-> db
:square-location sl (forms/stop-form ::form))}))
:client-location (:client-location sl)}))))
(update :locations #(mapv (fn [l] {:location l
:id (random-uuid)}) %))
(update :feature-flags #(mapv (fn [l] {:feature-flag l (re-frame/reg-event-db
:id (random-uuid)}) %)) ::received
(update :matches #(mapv (fn [l] {:match l (fn [db [_ client]]
:id (random-uuid)}) %)) (-> db
(update :bank-accounts (forms/stop-form ::form)
(fn [bas] (forms/start-form ::form (-> client
(mapv (fn [ba] (assoc :selected-square-locations (->> (:square-locations client)
(update ba :locations (fn [ls] (filter :client-location )
(map (fn [l] {:location l (mapv (fn [sl]
:id (random-uuid)}) {:id (:id sl)
ls)))) :square-location sl
bas))))))})) :client-location (:client-location sl)}))))
(update :locations #(mapv (fn [l] {:location l
:id (random-uuid)}) %))
(update :feature-flags #(mapv (fn [l] {:feature-flag l
:id (random-uuid)}) %))
(update :matches #(mapv (fn [l] {:match l
:id (random-uuid)}) %))
(update :bank-accounts
(fn [bas]
(mapv (fn [ba]
(update ba :locations (fn [ls]
(map (fn [l] {:location l
:id (random-uuid)})
ls))))
bas))))))))
(re-frame/reg-event-fx (re-frame/reg-event-fx
::save-new-client ::save-new-client
@@ -249,7 +254,7 @@
(fn [_ _] (fn [_ _]
(let [new-client-req @(re-frame/subscribe [::new-client-request]) (let [new-client-req @(re-frame/subscribe [::new-client-request])
user @(re-frame/subscribe [::subs/token])] user @(re-frame/subscribe [::subs/token])]
{:graphql {:graphql
{:token user {:token user
@@ -258,7 +263,7 @@
:operation/name "EditClient"} :operation/name "EditClient"}
:venia/queries [{:query/data [:edit-client :venia/queries [{:query/data [:edit-client
{:edit-client new-client-req} {:edit-client new-client-req}
(events/client-query user)]}]} (events/client-detail-query user)]}]}
:on-success [::save-complete] :on-success [::save-complete]
:on-error [::forms/save-error ::form]}}))) :on-error [::forms/save-error ::form]}})))
@@ -727,8 +732,7 @@
(defn form-content [] (defn form-content []
(let [_ @(re-frame/subscribe [::client]) (let [{new-client :data} @(re-frame/subscribe [::forms/form ::form])]
{new-client :data} @(re-frame/subscribe [::forms/form ::form])]
^{:key (or (:id new-client) ^{:key (or (:id new-client)
"new")} "new")}
@@ -750,5 +754,6 @@
(def new-client-form (def new-client-form
(with-meta (with-meta
(fn [] (fn []
[form-content]) (let [_ @(re-frame/subscribe [::subs/route-params])]
[form-content]))
{:component-did-mount #(re-frame/dispatch [::mounted])})) {:component-did-mount #(re-frame/dispatch [::mounted])}))

View File

@@ -2,78 +2,22 @@
(:require (:require
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[auto-ap.views.utils :refer [dispatch-value-change]] [auto-ap.views.utils :refer [dispatch-value-change]]
[auto-ap.subs :as subs])) [auto-ap.views.pages.data-page :as data-page]))
(re-frame/reg-sub (defn client-side-bar [{:keys [data-page]}]
::specific-filters
(fn [db ]
(::filters db nil)))
(re-frame/reg-sub
::filters
:<- [::specific-filters]
:<- [::subs/query-params]
(fn [[specific-filters _ 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 _ ]]
(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]} _]
{: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 [:div
[:p.menu-label "Name"] [:p.menu-label "Name"]
[:div.field [:div.field
[:div.control [:input.input {:placeholder "Harry's Food Products" [:div.control [:input.input {:placeholder "Harry's Food Products"
:value @(re-frame/subscribe [::filter :name]) :value @(re-frame/subscribe [::data-page/filter data-page :name-like])
:on-change (dispatch-value-change [::filter-changed :name])} ]]] :on-change (dispatch-value-change [::data-page/filter-changed data-page :name-like])} ]]]
[:p.menu-label "Code"] [:p.menu-label "Code"]
[:div.field [:div.field
[:div.control [:input.input {:placeholder "CBC" [:div.control [:input.input {:placeholder "CBC"
:value @(re-frame/subscribe [::filter :code]) :value @(re-frame/subscribe [::data-page/filter data-page :code])
:on-change (dispatch-value-change [::filter-changed :code])} ]]]]) :on-change (dispatch-value-change [::data-page/filter-changed data-page :code])} ]]]])

View File

@@ -8,7 +8,8 @@
[auto-ap.views.components.buttons :as buttons] [auto-ap.views.components.buttons :as buttons]
[auto-ap.status :as status] [auto-ap.status :as status]
[bidi.bidi :as bidi] [bidi.bidi :as bidi]
[auto-ap.routes :as routes])) [auto-ap.routes :as routes]
[auto-ap.views.pages.data-page :as data-page]))
(re-frame/reg-sub (re-frame/reg-sub
::specific-params ::specific-params
@@ -76,14 +77,16 @@
nil nil
)) ))
(defn clients-table [{:keys [page status]}] (defn clients-table [{:keys [data-page status]}]
(let [states @(re-frame/subscribe [::status/multi ::setup-sales-queries])] (let [states @(re-frame/subscribe [::status/multi ::setup-sales-queries])
{:keys [data]} @(re-frame/subscribe [::data-page/page data-page])]
[grid/grid {:on-params-change (fn [p] [grid/grid {:on-params-change (fn [p]
(re-frame/dispatch [::params-changed p])) (re-frame/dispatch [::params-changed p]))
:data-page data-page
:status status :status status
:params @(re-frame/subscribe [::params]) :params @(re-frame/subscribe [::params])
:column-count 5} :column-count 5}
[grid/controls page] [grid/controls data]
[grid/table {:fullwidth true} [grid/table {:fullwidth true}
[grid/header [grid/header
[grid/row {} [grid/row {}
@@ -95,7 +98,7 @@
[grid/header-cell {:style {:width (action-cell-width 2)}}]] [grid/header-cell {:style {:width (action-cell-width 2)}}]]
] ]
[grid/body [grid/body
(for [{:keys [id name email square-integration-status locked-until code locations bank-accounts]} (:data page)] (for [{:keys [id name email square-integration-status locked-until code locations bank-accounts]} (:data data)]
^{:key (str name "-" id )} ^{:key (str name "-" id )}
[grid/row {:id id} [grid/row {:id id}
[grid/cell {} name] [grid/cell {} name]

View File

@@ -15,7 +15,9 @@
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[recharts] [recharts]
[reagent.core :as r] [reagent.core :as r]
[react])) [react]
[vimsical.re-frame.cofx.inject :as inject]
[auto-ap.status :as status]))
(def pie-chart (r/adapt-react-class recharts/PieChart)) (def pie-chart (r/adapt-react-class recharts/PieChart))
(def pie (r/adapt-react-class recharts/Pie)) (def pie (r/adapt-react-class recharts/Pie))
@@ -238,23 +240,28 @@
(re-frame/reg-event-fx (re-frame/reg-event-fx
::mounted ::mounted
(fn [{:keys [db]} _] [(re-frame/inject-cofx ::inject/sub [::subs/client])]
{:db (assoc db ::top-expense-categories nil) (fn [{:keys [db] ::subs/keys [client]} _]
:graphql {:token (-> db :user) (cond->
:query-obj {:venia/queries [[:expense_account_stats {:db (assoc db ::top-expense-categories nil
{:client-id (:id @(re-frame/subscribe [::subs/client]))} ::cash-flow nil
[[:account [:id :name]] :total]] ::invoice-stats nil)}
[:invoice_stats client (assoc :graphql {:token (-> db :user)
{:client-id (:id @(re-frame/subscribe [::subs/client]))} :owns-state {:single ::page}
[:name :paid :unpaid]] :query-obj {:venia/queries [[:expense_account_stats
[:cash-flow {:client-id (:id client)}
{:client-id (:id @(re-frame/subscribe [::subs/client]))} [[:account [:id :name]] :total]]
[:beginning-balance [:invoice_stats
:outstanding-payments {:client-id (:id client)}
[:invoices-due-soon [:due :outstanding-balance [:vendor [:id :name]] :invoice-number]] [:name :paid :unpaid]]
[:upcoming-credits [:date :amount :identifier]] [:cash-flow
[:upcoming-debits [:date :amount :identifier]]]]]} {:client-id (:id client)}
:on-success [::received]}})) [:beginning-balance
:outstanding-payments
[:invoices-due-soon [:due :outstanding-balance [:vendor [:id :name]] :invoice-number]]
[:upcoming-credits [:date :amount :identifier]]
[:upcoming-debits [:date :amount :identifier]]]]]}
:on-success [::received]}))))
(defn cash-flow-range-button [{:keys [name value chart-options]}] (defn cash-flow-range-button [{:keys [name value chart-options]}]
[:a.button {:class (when (= value (:cash-flow-range chart-options)) [:a.button {:class (when (= value (:cash-flow-range chart-options))
@@ -298,51 +305,56 @@
(defn home-content [] (defn home-content []
(let [client-id (-> @(re-frame/subscribe [::subs/client]) :id) (let [client-id (-> @(re-frame/subscribe [::subs/client]) :id)
chart-options @(re-frame/subscribe [::chart-options])] chart-options @(re-frame/subscribe [::chart-options])
state @(re-frame/subscribe [::status/single ::page])]
^{:key client-id} ^{:key client-id}
[side-bar-layout {:side-bar [:div [side-bar-layout {:side-bar [:div
] ]
:main [:div [:h1.title "Home"] :main [:div [:h1.title "Home"]
[:h1.title.is-4 "Top expense categories"] (if client-id
(let [expense-categories @(re-frame/subscribe [::top-expense-categories])] (if (= :loading (:state state))
(make-pie-chart {:width 800 :height 500 :data (clj->js [:div.loader.is-loading.big.is-centered]
(map (fn [x] {:name (:name (:account x)) :value (:total x)}) expense-categories))}))
[:h1.title.is-4 "Upcoming Bills"]
(make-bar-chart {:width 800 :height 500 :data (clj->js
@(re-frame/subscribe [::invoice-stats]))})
[:h1.title.is-4 "Cash Flow"] [:<>
[:div.buttons.has-addons [:h1.title.is-4 "Top expense categories"]
[cash-flow-range-button {:name "7 days" (let [expense-categories @(re-frame/subscribe [::top-expense-categories])]
:value :seven-days (make-pie-chart {:width 800 :height 500 :data (clj->js
:chart-options chart-options}] (map (fn [x] {:name (:name (:account x)) :value (:total x)}) expense-categories))}))
[cash-flow-range-button {:name "30 days" [:h1.title.is-4 "Upcoming Bills"]
:value :thirty-days (make-bar-chart {:width 800 :height 500 :data (clj->js
:chart-options chart-options}] @(re-frame/subscribe [::invoice-stats]))})
[cash-flow-range-button {:name "60 days" [:h1.title.is-4 "Cash Flow"]
:value :sixty-days [:div.buttons.has-addons
:chart-options chart-options}] [cash-flow-range-button {:name "7 days"
:value :seven-days
:chart-options chart-options}]
[cash-flow-range-button {:name "30 days"
:value :thirty-days
:chart-options chart-options}]
[cash-flow-range-button {:name "90 days" [cash-flow-range-button {:name "60 days"
:value :ninety-days :value :sixty-days
:chart-options chart-options}] :chart-options chart-options}]
[cash-flow-range-button {:name "120 days"
:value :one-hundred-twenty-days
:chart-options chart-options}]
[cash-flow-range-button {:name "150 days"
:value :one-hundred-fifty-days
:chart-options chart-options}]
[cash-flow-range-button {:name "180 days"
:value :one-hundred-eighty-days
:chart-options chart-options}]]
[cash-flow-range-button {:name "90 days"
:value :ninety-days
:chart-options chart-options}]
[cash-flow-range-button {:name "120 days"
:value :one-hundred-twenty-days
:chart-options chart-options}]
[cash-flow-range-button {:name "150 days"
:value :one-hundred-fifty-days
:chart-options chart-options}]
[cash-flow-range-button {:name "180 days"
:value :one-hundred-eighty-days
:chart-options chart-options}]]
(make-cash-flow-chart {:width 800 :height 500 (make-cash-flow-chart {:width 800 :height 500
:data (clj->js @(re-frame/subscribe [::cash-flow]))}) :data (clj->js @(re-frame/subscribe [::cash-flow]))})
[cash-flow-grid]]}])) [cash-flow-grid]])
[:h2.title.is-6 "Please select a customer to see reports."])]}]))
(defn home-page [] (defn home-page []