Files
integreat/src/cljs/auto_ap/events.cljs

308 lines
14 KiB
Clojure

(ns auto-ap.events
(:require [auto-ap.client-selection :refer [client-selection-schema
client-selection-transformer]]
[auto-ap.db :as db]
[auto-ap.routes :as routes]
[auto-ap.ssr-routes :as ssr-routes]
[auto-ap.utils :refer [by]]
[auto-ap.views.pages.data-page :as data-page]
[auto-ap.views.utils :refer [gunzip parse-jwt with-user]]
[bidi.bidi :as bidi]
[cemerick.url :as url]
[clojure.string :as str]
[goog.crypt.base64 :as b64]
[malli.core :as mc]
[pako]
[re-frame.core :as re-frame]))
(defn jwt->data [token]
(let [raw (js->clj (.parse js/JSON (b64/decodeString (second (str/split token #"\.")))))
gz-clients (or (:gz-clients raw)
(get raw "gz-clients"))]
(cond-> raw
gz-clients (assoc "user/clients" (gunzip gz-clients)))))
(defn client-query []
(cond-> [:id :name :code :email :locations :feature-flags :groups
[:emails [:id :email :description]]
[:bank-accounts [:id :code :bank-name :name :type :visible
:locations :include-in-reports :current-balance
:sort-order]]]))
(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
[:square-integration-status [:last-updated :last-attempt :message :state :id]]
[:square-locations [:square-id :id :name :client-location]]
[:ezcater-locations [:id [:caterer [:name :id]] :location]]
[:emails [:id :email :description]]
[:location-matches [:id :location :match]]
[:bank-accounts [:id :start-date :numeric-code :code :number :bank-name :bank-code :check-number :name :routing :type :sort-order :visible :yodlee-account-id
[:integration-status [:last-updated :last-attempt :message :state :id]]
[:yodlee-account [:name :id :number]]
[:plaid-account [:name :id :number]]
[:intuit-bank-account [:name :id :external-id]]
:use-date-instead-of-post-date
:locations :include-in-reports :current-balance :yodlee-balance-old]]
[:address [:id :street1 :street2 :city :state :zip]]
[:forecasted-transactions [:id :amount :identifier :day-of-month]]]
(= "admin" (or (get (jwt->data token) "role") (get (jwt->data token) "user/role"))) (into [[:yodlee-provider-accounts [:id [:accounts [:id :name :number :available-balance]]]]
[:plaid-items [:id [:accounts [:id :name :number :balance]]]]])))
(re-frame/reg-event-fx
::initialize-db
(fn [{:keys [_]} [_ token]]
(let [handler (:handler (bidi/match-route routes/routes (.. js/window -location -pathname)))
last-client-id (.getItem js/localStorage "last-client-id")
last-selected-clients (try (some->> "last-selected-clients"
(.getItem js/localStorage)
not-empty
(.parse js/JSON)
js->clj
( #(mc/decode client-selection-schema % client-selection-transformer)))
(catch js/Error e
:all))
jwt-data (some-> token jwt->data)
selected-client-assignment (cond (and token
(= "admin" (get jwt-data "user/role"))
(not last-selected-clients))
:mine
(and token
last-client-id
(not last-selected-clients))
{:selected [(js/parseInt last-client-id)]}
:else
nil)]
(cond
(= :login handler)
{:db (cond-> (assoc db/default-db
:active-route :login
:last-client-id last-client-id
:selected-clients last-selected-clients
:user nil))}
(and (not= :login handler) (not token))
{:redirect "/login"
:db (assoc db/default-db
:active-route :login
:last-client-id last-client-id
:selected-clients last-selected-clients
:user token)}
(and token (= "none" (or (get jwt-data "role") (get jwt-data "user/role"))))
{:redirect "/needs-activation"
:db (assoc db/default-db
:active-route :needs-activation
:last-client-id last-client-id
:selected-clients last-selected-clients
:user token)}
:else
(cond-> {:db (cond-> (assoc db/default-db
:active-route handler
:is-initial-loading? true
:last-client-id last-client-id
:selected-clients last-selected-clients
:query-params (auto-ap.views.utils/query-params)
:user token)
selected-client-assignment
(assoc :selected-clients selected-client-assignment))
:graphql {:token token
:query-obj {:venia/queries [[:client (client-query)]]}
:on-success [::received-initial]
:on-error [::failed-initial]}}
selected-client-assignment
(assoc :set-local-storage ["last-selected-clients" (.stringify js/JSON selected-client-assignment)]))))))
(re-frame/reg-event-db
::toggle-menu
(fn [db [_ which]]
(update-in db [:menu which :active?] #(not %))))
(re-frame/reg-event-fx
::received-initial
(fn [{:keys [db]} [_ {clients :client}]]
(let [only-one-client (when (= 1 (count clients))
(->> clients first :id js/parseInt))]
(when only-one-client
(.setItem js/localStorage "last-client-id" only-one-client)
(.setItem js/localStorage "last-selected-clients"
(.stringify js/JSON (clj->js {:selected [only-one-client]}))))
{:db (cond-> (-> db
(assoc :clients (by :id clients))
(assoc :is-initial-loading? false)
(assoc :client (or only-one-client
(->> clients
(map :id)
(filter #(= % (:last-client-id db)))
first))))
only-one-client (assoc :last-client-id only-one-client
:selected-clients {:selected [only-one-client]}))
:interval {:action :start
:id :refresh-clients
:frequency 600000
:event [::refresh-clients]}})))
(re-frame/reg-event-fx
::refresh-clients
(fn [{:keys [db]}]
(let [token (-> db :user)]
{:graphql {:token token
:query-obj {:venia/queries [[:client (client-query)]]}
:on-success [::received-refreshed-clients]}})))
(re-frame/reg-event-fx
::received-refreshed-clients
(fn [{:keys [db]} [_ {clients :client}]]
{:db (assoc db :clients (by :id clients))}))
(re-frame/reg-event-db
::failed-initial
(fn [db [_ e]]
(assoc db :initial-error e
:is-initial-loading? false
:active-route :initial-error)))
(re-frame/reg-event-fx
::swapped-client
(fn [{:keys [db]} [_ client client-identifier]]
(when (:id client)
(.setItem js/localStorage "last-client-id" (:id client)))
(.setItem js/localStorage "last-selected-clients"
(.stringify js/JSON
(clj->js (condp = client-identifier
:all
:all
:mine
:mine
{:selected [(js/parseInt (:id client))]}))))
{:db (assoc db :client (:id client)
:selected-clients
(condp = client-identifier
:all
:all
:mine
:mine
{:selected [(js/parseInt (:id client))]}))}))
(re-frame/reg-event-fx
::swap-client
[with-user]
(fn [{:keys [db user]} [_ client]]
(let [client-identifier (or (:id client) client)]
{:http {:token user
:method :put
:uri (str (bidi/path-for ssr-routes/only-routes
:active-client
:request-method :put)
"?"
(url/map->query {:search-client client-identifier}))
:headers {"x-clients"
(.stringify js/JSON
(clj->js (cond (= :all client-identifier)
"all"
(= :mine client-identifier)
"mine"
:else
{:selected [client-identifier]})))}
:on-success [::swapped-client client client-identifier]}})))
(re-frame/reg-event-fx
::set-active-route
(fn [{:keys [db]} [_ handler params route-params]]
(cond
(and (not= :login handler) (not (:user db)))
{:redirect (bidi/path-for routes/routes :login)
:db (assoc db :active-route :login
:active-page :login
:menu nil
:page-failure nil)}
(and (not= "admin" (:user/role (parse-jwt (:user db))))
(str/includes? (name handler) "admin"))
{:redirect (bidi/path-for routes/routes :index)
:db (assoc db :active-route :index
:active-page :index
:menu nil
:page-failure nil)}
:else
{:db (-> db
(assoc :active-route handler
:auto-ap.views.components.modal/state nil
:page-failure nil
:menu nil
:query-params params
:route-params route-params)
(data-page/dispose-all))})))
(re-frame/reg-event-fx
::logout
(fn [{:keys [db]} [_ logout-reason]]
{:db (assoc db :user nil :menu {:client {:active? false}
:account {:active? false}}
:logout-reason logout-reason)
:redirect (bidi/path-for routes/routes :login)
:set-local-storage ["jwt" nil]}))
(re-frame/reg-event-db
::yodlee-merchants-received
(fn [db [_ data]]
(assoc db :yodlee-merchants (:yodlee-merchants data))))
(re-frame/reg-event-fx
::yodlee-merchants-needed
(fn [{:keys [db]} _]
{:graphql {:token (:user db)
:query-obj {:venia/queries [[:yodlee-merchants
[:name :yodlee-id :id]]]}
:on-success [::yodlee-merchants-received]}}))
(re-frame/reg-event-fx
::vendor-preferences-requested
[with-user]
(fn [{:keys [user]} [_ {:keys [client-id vendor-id on-success on-failure owns-state]}]]
{:graphql {:token user
:query-obj {:venia/queries [[:vendor-by-id
{:id vendor-id}
[[:automatically-paid-when-due [:id]]
[:schedule-payment-dom [[:client [:id]] :dom]]
[:default-account [:id]]]]
[:account-for-vendor
{:vendor-id vendor-id
:client-id client-id}
[:name :id :numeric-code :location]]]}
:owns-state owns-state
:on-success (fn [r]
(let [schedule-payment-dom (->> r
:vendor-by-id
:schedule-payment-dom
(filter (fn [spd]
(= (-> spd :client :id)
client-id)))
first
:dom)
automatically-paid-when-due (boolean ((->> r :vendor-by-id :automatically-paid-when-due (map :id) set) client-id))]
(conj on-success {:default-account (:account-for-vendor r)
:schedule-payment-dom schedule-payment-dom
:automatically-paid-when-due automatically-paid-when-due
:vendor-autopay? (or automatically-paid-when-due (boolean schedule-payment-dom))})))
:on-failure on-failure}}))