(ns auto-ap.events (:require [auto-ap.db :as db] [auto-ap.routes :as routes] [auto-ap.utils :refer [by]] [auto-ap.views.pages.data-page :as data-page] [auto-ap.views.utils :refer [parse-jwt with-user gunzip]] [bidi.bidi :as bidi] [clojure.string :as str] [clojure.edn :as edn] [goog.crypt.base64 :as b64] [re-frame.core :as re-frame] [auto-ap.ssr-routes :as ssr-routes] [cemerick.url :as url] [auto-ap.subs :as subs] [pako])) (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 [: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 (edn/read-string (.getItem js/localStorage "last-selected-clients")) 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)) [(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" 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 ))] (when only-one-client (.setItem js/localStorage "last-client-id" only-one-client) (.setItem js/localStorage "last-selected-clients" (pr-str [(js/parseInt 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 [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" (condp = client-identifier :all :all :mine :mine (pr-str [(js/parseInt (:id client))]))) {:db (assoc db :client (:id client) :selected-clients (condp = client-identifier :all :all :mine :mine [(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})) :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}}))