diff --git a/src/clj/auto_ap/graphql/clients.clj b/src/clj/auto_ap/graphql/clients.clj index 70c68e3f..9b2844b7 100644 --- a/src/clj/auto_ap/graphql/clients.clj +++ b/src/clj/auto_ap/graphql/clients.clj @@ -157,13 +157,14 @@ (when (:square_auth_token edit_client) (square/upsert-locations (-> result :tempids (get id) (or id) d-clients/get-by-id))) (let [updated-client (-> result :tempids (get id) (or id) d-clients/get-by-id)] - (solr/index-documents-raw solr/impl "clients" - [{"id" (:db/id updated-client) - "name" (conj (or (:client/matches updated-client) []) - (:client/name updated-client)) - "code" (:client/code updated-client) - "exact" (map str/upper-case (conj (or (:client/matches updated-client) []) - (:client/name updated-client)))}]) + (when (:client/name updated-client) + (solr/index-documents-raw solr/impl "clients" + [{"id" (:db/id updated-client) + "name" (conj (or (:client/matches updated-client) []) + (:client/name updated-client)) + "code" (:client/code updated-client) + "exact" (map str/upper-case (conj (or (:client/matches updated-client) []) + (:client/name updated-client)))}])) (-> updated-client (update :client/bank-accounts diff --git a/src/clj/auto_ap/graphql/plaid.clj b/src/clj/auto_ap/graphql/plaid.clj index 9d2399aa..a678215d 100644 --- a/src/clj/auto_ap/graphql/plaid.clj +++ b/src/clj/auto_ap/graphql/plaid.clj @@ -1,130 +1,10 @@ (ns auto-ap.graphql.plaid (:require - [auto-ap.datomic - :refer [add-sorter-fields - apply-pagination - apply-sort-3 - conn - merge-query - pull-attr - pull-many-by-id - query2]] + [auto-ap.datomic :refer [conn]] [auto-ap.graphql.utils - :refer [->graphql - <-graphql - assert-admin - assert-can-see-client - assert-present - attach-tracing-resolvers - cleanse-query - limited-clients]] - [auto-ap.plaid.core :as p] - [clj-time.coerce :as coerce] - [clj-time.core :as time] - [clojure.tools.logging :as log] - [datomic.api :as dc] + :refer [assert-admin assert-present attach-tracing-resolvers cleanse-query]] [auto-ap.solr :as solr] - [manifold.deferred :as de] - [manifold.executor :as ex])) - -(defn plaid-link-token [context value _] - (when-not (:client_id value) - (throw (ex-info "Client ID is required" {:validation-error "Client ID is required"}))) - (assert-can-see-client (:id context) (:client_id value)) - (let [client-code (pull-attr (dc/db conn) :client/code (:client_id value))] - {:token (p/get-link-token client-code)})) - -(defn link-plaid [context value _] - (when-not (:client_code value) - (throw (ex-info "Client not provided" {:validation-error "Client not provided."}))) - (when-not (:public_token value) - (throw (ex-info "Public token not provided" {:validation-error "public token not provided"}))) - (log/info (:id context) (pull-attr (dc/db conn) :db/id [:client/code (:client_code value)])) - (assert-can-see-client (:id context) (pull-attr (dc/db conn) :db/id [:client/code (:client_code value)])) - (let [access-token (:access_token (p/exchange-public-token (:public_token value) (:client_code value))) - account-result (p/get-accounts access-token ) - item {:plaid-item/client [:client/code (:client_code value)] - :plaid-item/external-id (-> account-result :item :item_id ) - :plaid-item/access-token access-token - :plaid-item/status (or (some-> account-result :item :error) - "SUCCESS") - :plaid-item/last-updated (coerce/to-date (time/now)) - :db/id "plaid-item"}] - - @(dc/transact conn (->> (:accounts account-result) - (map (fn [a] - (let [balance (some-> a :balances :current (* 0.01))] - (cond-> {:plaid-account/external-id (:account_id a) - :plaid-account/number (:mask a) - :plaid-account/name (str (:name a) " " (:mask a)) - :plaid-item/_accounts "plaid-item"} - balance (assoc :plaid-account/balance balance))))) - (into [item]))) - (log/info "Access token was " access-token) - {:message (str "Plaid linked successfully.")})) - - -(def default-read '[:db/id - :plaid-item/external-id - :plaid-item/last-updated - :plaid-item/status - {:plaid-item/accounts [:db/id - :plaid-account/external-id - :plaid-account/number - :plaid-account/balance - :plaid-account/name]}]) - -(defn raw-graphql-ids [db args] - (let [query (cond-> {:query {:find [] - :in ['$] - :where []} - :args [db]} - - (:sort args) (add-sorter-fields {"external-id" ['[?e :plaid-item/external-id ?sort-external-id]]} - args) - - (limited-clients (:id args)) - (merge-query {:query {:in ['[?xx ...]] - :where ['[?e :plaid-item/client ?xx]]} - :args [ (set (map :db/id (limited-clients (:id args))))]}) - - (:client-id args) - (merge-query {:query {:in '[?client-id] - :where ['[?e :plaid-item/client ?client-id]]} - :args [(:client-id args)]}) - - true - (merge-query {:query {:find ['?e] - :where ['[?e :plaid-item/external-id]]}}))] - - (cond->> (query2 query) - true (apply-sort-3 args) - true (apply-pagination args)))) - -(defn graphql-results [ids db _] - (let [results (pull-many-by-id db default-read ids)] - (->> ids - (map results)))) - -(defn get-graphql [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])) - - - -(defn get-plaid-item-page [context args _] - - (let [args (assoc args :id (:id context)) - [plaid-items cnt] (get-graphql (<-graphql (assoc args :id (:id context))))] - {:plaid_items (->> plaid-items - (map #(update % :plaid-item/last-updated coerce/from-date)) - (map ->graphql)) - :total cnt - :count (count plaid-items) - :start (:start args 0) - :end (+ (:start args 0) (count plaid-items))})) + [datomic.api :as dc])) (defn delete-plaid-item [context args _] (assert-admin (:id context)) @@ -169,28 +49,8 @@ :balance {:type :money} :name {:type 'String} :number {:type 'String}}}} - :queries {:plaid_link_token {:type :plaid_link_result - :args {:client_id {:type :id}} - :resolve :plaid-link-token} - :plaid_item_page {:type :plaid_item_page - :args {:client_id {:type :id} - :sort {:type '(list :sort_item)} - :start {:type 'Int} - :per_page {:type 'Int}} - :resolve :get-plaid-item-page} - :search_plaid_merchants {:type '(list :plaid_merchant) + :queries {:search_plaid_merchants {:type '(list :plaid_merchant) :args {:query {:type 'String}} - :resolve :search-plaid-merchants}} - :mutations {:link_plaid {:type :message - :args {:client_code {:type 'String} - :public_token {:type 'String}} - :resolve :mutation/link-plaid} - :delete_plaid_item {:type :message - :args {:id {:type :id}} - :resolve :mutation/delete-plaid-item}}}) - (attach-tracing-resolvers {:plaid-link-token plaid-link-token - :search-plaid-merchants search-merchants - :get-plaid-item-page get-plaid-item-page - :mutation/link-plaid link-plaid - :mutation/delete-plaid-item delete-plaid-item}))) + :resolve :search-plaid-merchants}}}) + (attach-tracing-resolvers {:search-plaid-merchants search-merchants}))) diff --git a/src/clj/auto_ap/ssr/company/company_1099.clj b/src/clj/auto_ap/ssr/company/company_1099.clj index f254666d..09f8a410 100644 --- a/src/clj/auto_ap/ssr/company/company_1099.clj +++ b/src/clj/auto_ap/ssr/company/company_1099.clj @@ -134,7 +134,7 @@ :title "1099 Vendors" :entity-name "Vendors" :route :company-1099-vendor-table - :action-buttons (fn [user] + :action-buttons (fn [user _] nil) :row-buttons (fn [user e] [(com/icon-button {:hx-get (str (bidi/path-for ssr-routes/only-routes diff --git a/src/clj/auto_ap/ssr/company/plaid.clj b/src/clj/auto_ap/ssr/company/plaid.clj new file mode 100644 index 00000000..c0d8ae3b --- /dev/null +++ b/src/clj/auto_ap/ssr/company/plaid.clj @@ -0,0 +1,176 @@ +(ns auto-ap.ssr.company.plaid + (:require + [auto-ap.datomic + :refer [add-sorter-fields + apply-pagination + apply-sort-3 + conn + merge-query + pull-attr + pull-many-by-id + query2]] + [auto-ap.graphql.utils :refer [assert-can-see-client limited-clients]] + [auto-ap.logging :as alog] + [auto-ap.plaid.core :as p] + [auto-ap.ssr-routes :as ssr-routes] + [auto-ap.ssr.components :as com] + [auto-ap.ssr.grid-page-helper :as helper] + [auto-ap.ssr.svg :as svg] + [auto-ap.time :as atime] + [bidi.bidi :as bidi] + [clj-time.coerce :as coerce] + [clj-time.core :as time] + [datomic.api :as dc] + [hiccup2.core :as hiccup])) + +(def default-read '[:db/id + :plaid-item/external-id + :plaid-item/last-updated + :plaid-item/status + {:plaid-item/accounts [:db/id + :plaid-account/external-id + :plaid-account/number + :plaid-account/balance + :plaid-account/name]}]) + +(defn raw-graphql-ids [db args] + (let [query (cond-> {:query {:find [] + :in ['$] + :where []} + :args [db]} + + (:sort args) (add-sorter-fields {"external-id" ['[?e :plaid-item/external-id ?sort-external-id]] + "status" ['[?e :plaid-item/status ?sort-status]]} + args) + + (limited-clients (:id args)) + (merge-query {:query {:in ['[?xx ...]] + :where ['[?e :plaid-item/client ?xx]]} + :args [ (set (map :db/id (limited-clients (:id args))))]}) + + (:client-id args) + (merge-query {:query {:in '[?client-id] + :where ['[?e :plaid-item/client ?client-id]]} + :args [(:client-id args)]}) + + true + (merge-query {:query {:find ['?e] + :where ['[?e :plaid-item/external-id]]}}))] + + (cond->> (query2 query) + true (apply-sort-3 args) + true (apply-pagination args)))) + + +(defn graphql-results [ids db _] + (let [results (pull-many-by-id db default-read ids)] + (->> ids + (map results)))) + +(defn get-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])) + + + +(defn plaid-link-script [token] + (format "window.plaid = Plaid.create( + { token: \"%s\", + onSuccess: function (x) { htmx.trigger(\"#link-account\", \"linked\", {\"public_token\": x})} + })", token)) + + +(defn link [{{client-code "client_code" public-token "public_token"} :form-params + :keys [identity] + :as request}] + (alog/info ::linking + :request request) + (when-not client-code + (throw (ex-info "Client not provided" {:validation-error "Client not provided."}))) + (when-not public-token + (throw (ex-info "Public token not provided" {:validation-error "public token not provided"}))) + (alog/info ::linking-plaid :id identity :client-code client-code) + (assert-can-see-client identity (pull-attr (dc/db conn) :db/id [:client/code client-code])) + (let [access-token (:access_token (p/exchange-public-token public-token client-code)) + account-result (p/get-accounts access-token ) + item {:plaid-item/client [:client/code client-code] + :plaid-item/external-id (-> account-result :item :item_id ) + :plaid-item/access-token access-token + :plaid-item/status (or (some-> account-result :item :error) + "SUCCESS") + :plaid-item/last-updated (coerce/to-date (time/now)) + :db/id "plaid-item"}] + + @(dc/transact conn (->> (:accounts account-result) + (map (fn [a] + (let [balance (some-> a :balances :current (* 0.01))] + (cond-> {:plaid-account/external-id (:account_id a) + :plaid-account/number (:mask a) + :plaid-account/name (str (:name a) " " (:mask a)) + :plaid-item/_accounts "plaid-item"} + balance (assoc :plaid-account/balance balance))))) + (into [item]))) + (alog/info ::access-token-was :token access-token) + {:headers {"Hx-redirect" (bidi/path-for ssr-routes/only-routes + :company-plaid)}})) + + +(def grid-page {:id "plaid-table" + :nav (com/company-aside-nav) + :id-fn :db/id + :fetch-page (fn [user args] + (get-page (assoc args :id user))) + :breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes + :company)} + "My Company"] + + [:a {:href (bidi/path-for ssr-routes/only-routes + :company-plaid)} + "Plaid"]] + :title "Plaid Accounts" + :entity-name "Plaid accounts" + :route :company-plaid-table + :action-buttons (fn [user params] + (when-let [client-code (:client/code (:client params))] + [[:div {:hx-post (str (bidi/path-for ssr-routes/only-routes + :company-plaid-link + :request-method :post)) + :hx-vals (hiccup/raw (format "js:{client_code: \"%s\", public_token: event.detail.public_token}", client-code)) + :hx-trigger "linked"} + [:script (hiccup/raw (plaid-link-script (p/get-link-token client-code)))] + (com/button {:color :primary + :id "link-account" + :onClick "window.plaid.open()"} + (com/button-icon {} svg/refresh) + "Link new account")]])) + :row-buttons (fn [user e] + [] + #_[(when (is-admin? user) + (com/icon-button {:hx-put (bidi/path-for ssr-routes/only-routes + :company-yodlee-provider-account-refresh) + :hx-target "closest tr"} + svg/refresh))]) + :headers [{:key "plaid-item" + :name "Plaid Item" + :sort-key "id" + :render :db/id} + {:key "status" + :name "Status" + :sort-key "status" + :render #(when-let [status (:plaid-item/status %)] + [:div [:div (com/pill {:color :primary } + status)] + [:div (atime/unparse-local (coerce/to-date-time (:plaid-item/last-updated %)) atime/normal-date)]])} + + {:key "accounts" + :name "Accounts" + :show-starting "md" + :render (fn [e] + [:ul + (for [a (:plaid-item/accounts e)] + [:li (:plaid-account/name a) " - " (:plaid-account/number a)])])}]}) + +(def page (partial helper/page grid-page)) +(def table (partial helper/table grid-page)) diff --git a/src/clj/auto_ap/ssr/company/reports.clj b/src/clj/auto_ap/ssr/company/reports.clj index 1562dfd9..7da733c7 100644 --- a/src/clj/auto_ap/ssr/company/reports.clj +++ b/src/clj/auto_ap/ssr/company/reports.clj @@ -29,7 +29,7 @@ :title "Reports" :entity-name "Reports" :route :company-reports-table - :action-buttons (fn [user] + :action-buttons (fn [user _] nil) :row-buttons (fn [user e] (com/a-icon-button {:href (:report/url e)} diff --git a/src/clj/auto_ap/ssr/company/yodlee.clj b/src/clj/auto_ap/ssr/company/yodlee.clj index 10cfb061..bef5d904 100644 --- a/src/clj/auto_ap/ssr/company/yodlee.clj +++ b/src/clj/auto_ap/ssr/company/yodlee.clj @@ -62,7 +62,7 @@ fastlink.open({fastLinkURL: '%s', :title "Yodlee Accounts" :entity-name "Yodlee accounts" :route :company-yodlee-table - :action-buttons (fn [user] + :action-buttons (fn [user _] [(com/button {:color :primary :on-click "openFastlink()" :hx-get (bidi/path-for ssr-routes/only-routes diff --git a/src/clj/auto_ap/ssr/components/aside.clj b/src/clj/auto_ap/ssr/components/aside.clj index 5a885d99..03b66b65 100644 --- a/src/clj/auto_ap/ssr/components/aside.clj +++ b/src/clj/auto_ap/ssr/components/aside.clj @@ -249,6 +249,11 @@ :href (bidi/path-for ssr-routes/only-routes :company-reports)} "Reports")] + [:li + (menu-button- {:icon svg/bank + :href (bidi/path-for ssr-routes/only-routes + :company-plaid)} + "Plaid Link")] [:li (menu-button- {:icon svg/bank :href (bidi/path-for ssr-routes/only-routes diff --git a/src/clj/auto_ap/ssr/core.clj b/src/clj/auto_ap/ssr/core.clj index bb947cd0..0b483364 100644 --- a/src/clj/auto_ap/ssr/core.clj +++ b/src/clj/auto_ap/ssr/core.clj @@ -7,6 +7,7 @@ [auto-ap.ssr.transaction.insights :as insights] [auto-ap.ssr.company.company-1099 :as company-1099] [auto-ap.ssr.company.yodlee :as company-yodlee] + [auto-ap.ssr.company.plaid :as company-plaid] [auto-ap.ssr.search :as search] [auto-ap.ssr.company-dropdown :as company-dropdown] [auto-ap.ssr.company.reports :as company-reports] @@ -28,6 +29,9 @@ :company-1099-vendor-table (wrap-client-redirect-unauthenticated (wrap-secure company-1099/vendor-table)) :company-1099-vendor-dialog (wrap-client-redirect-unauthenticated (wrap-secure company-1099/vendor-dialog)) :company-1099-vendor-save (wrap-client-redirect-unauthenticated (wrap-secure company-1099/vendor-save)) + :company-plaid (wrap-client-redirect-unauthenticated (wrap-secure company-plaid/page)) + :company-plaid-table (wrap-client-redirect-unauthenticated (wrap-secure company-plaid/table)) + :company-plaid-link (wrap-client-redirect-unauthenticated (wrap-secure company-plaid/link)) :company-yodlee (wrap-client-redirect-unauthenticated (wrap-secure company-yodlee/page)) :company-yodlee-table (wrap-client-redirect-unauthenticated (wrap-secure company-yodlee/table)) :company-yodlee-fastlink-dialog (wrap-client-redirect-unauthenticated (wrap-secure company-yodlee/fastlink-dialog)) diff --git a/src/clj/auto_ap/ssr/grid_page_helper.clj b/src/clj/auto_ap/ssr/grid_page_helper.clj index ff557dcb..8fc6f931 100644 --- a/src/clj/auto_ap/ssr/grid_page_helper.clj +++ b/src/clj/auto_ap/ssr/grid_page_helper.clj @@ -51,7 +51,7 @@ )) "default sort")) -(defn table* [grid-spec user {:keys [start per-page client flash-id sort request]}] +(defn table* [grid-spec user {:keys [start per-page client flash-id sort request] :as params}] (let [start (or start 0) per-page (or per-page 30) [entities total] ((:fetch-page grid-spec) @@ -70,7 +70,7 @@ :subtitle [:div.flex.items-center.gap-2 [:span (format "Total %s: %d, " (:entity-name grid-spec) total)] (sort-by-list sort)] - :action-buttons ((:action-buttons grid-spec) user) + :action-buttons ((:action-buttons grid-spec) user params) :rows (for [entity entities] (row* grid-spec user entity {:flash? (= flash-id ((:id-fn grid-spec) entity))})) :thead-params {:hx-get (bidi/path-for ssr-routes/only-routes diff --git a/src/clj/auto_ap/ssr/ui.clj b/src/clj/auto_ap/ssr/ui.clj index 39974204..c2f72c99 100644 --- a/src/clj/auto_ap/ssr/ui.clj +++ b/src/clj/auto_ap/ssr/ui.clj @@ -26,6 +26,7 @@ [:script {:src "https://unpkg.com/hyperscript.org@0.9.7/dist/_hyperscript.min.js"}] [:script {:src "https://unpkg.com/@popperjs/core@2.11.8/dist/umd/popper.min.js"}] + [:script {:src "https://cdn.plaid.com/link/v2/stable/link-initialize.js"}] #_[:script {:src "https://unpkg.com/htmx.org@1.8.4" :integrity "sha384-wg5Y/JwF7VxGk4zLsJEcAojRtlVp1FKKdGy1qN+OMtdq72WRvX/EdRdqg/LOhYeV" :crossorigin= "anonymous"}] diff --git a/src/cljc/auto_ap/ssr_routes.cljc b/src/cljc/auto_ap/ssr_routes.cljc index a741d8f9..2d1e98b6 100644 --- a/src/cljc/auto_ap/ssr_routes.cljc +++ b/src/cljc/auto_ap/ssr_routes.cljc @@ -28,6 +28,12 @@ "/table" {:get :company-yodlee-table} "/fastlink" {:get :company-yodlee-fastlink-dialog} "/refresh" {:put :company-yodlee-provider-account-refresh}} + + "/plaid" {"" {:get :company-plaid} + "/table" {:get :company-plaid-table} + "/link" {:post :company-plaid-link} + #_#_"/fastlink" {:get :company-yodlee-fastlink-dialog} + #_#_"/refresh" {:put :company-yodlee-provider-account-refresh}} }}) diff --git a/src/cljs/auto_ap/views/main.cljs b/src/cljs/auto_ap/views/main.cljs index 51108ed0..83d65e75 100644 --- a/src/cljs/auto_ap/views/main.cljs +++ b/src/cljs/auto_ap/views/main.cljs @@ -31,7 +31,6 @@ [auto-ap.views.pages.admin.users :refer [admin-users-page]] [auto-ap.views.pages.admin.import-batches :refer [import-batches-page]] [auto-ap.views.pages.company.yodlee2 :as yodlee2] - [auto-ap.views.pages.company.plaid :as plaid] [auto-ap.views.pages.company.other :as company-other])) (defmulti page (fn [active-page] active-page)) @@ -128,9 +127,6 @@ (defmethod page :company-other [_] (company-other/company-other-page)) -(defmethod page :plaid [_] - (plaid/plaid-page)) - (defmethod page :admin-accounts [_] (admin-accounts-page)) diff --git a/src/cljs/auto_ap/views/pages/admin/plaid/table.cljs b/src/cljs/auto_ap/views/pages/admin/plaid/table.cljs deleted file mode 100644 index 9fd53e10..00000000 --- a/src/cljs/auto_ap/views/pages/admin/plaid/table.cljs +++ /dev/null @@ -1,83 +0,0 @@ -(ns auto-ap.views.pages.admin.plaid.table - (:require - [auto-ap.status :as status] - [auto-ap.subs :as subs] - [auto-ap.views.components.buttons :as buttons] - [auto-ap.views.components.grid :as grid] - [auto-ap.views.components.modal :as modal] - [auto-ap.views.pages.data-page :as data-page] - [auto-ap.views.utils - :refer [->$ action-cell-width date->str dispatch-event with-user]] - [re-frame.core :as re-frame])) - -(re-frame/reg-event-fx - ::plaid-item-deleted - (fn [_ _] - {:dispatch [::modal/modal-closed ]})) - -(re-frame/reg-event-fx - ::delete-plaid-item - [with-user ] - (fn [{:keys [user]} [_ id ]] - {:graphql {:token user - :owns-state {:single ::delete-plaid-item} - :query-obj - {:venia/operation {:operation/type :mutation - :operation/name "DeletePlaidItem"} - :venia/queries [{:query/data - [:delete-plaid-item - {:id id} - [:message]]}]} - :on-success [::plaid-item-deleted]}})) - -(re-frame/reg-event-fx - ::delete-requested - [with-user] - (fn [_ [_ id]] - {:dispatch - [::modal/modal-requested {:title "Delete Provider account " - :body [:div "Are you sure you want to delete " id "?"] - :confirm {:value "Delete plaid accounts" - :status-from [::status/single ::delete-plaid-item] - :class "is-danger" - :on-click (dispatch-event [::delete-plaid-item id]) - :close-event [::status/completed ::delete-plaid-item]} - :cancel? true}]})) - -(re-frame/reg-sub - ::params - (fn [db] - (-> db ::params))) - -(defn table [{:keys [data-page]}] - (let [{:keys [data]} @(re-frame/subscribe [::data-page/page data-page]) - is-admin? @(re-frame/subscribe [::subs/is-admin?])] - [grid/grid {:data-page data-page - :column-count 5} - [grid/controls data] - [grid/table {:fullwidth true} - [grid/header - [grid/row {} - [grid/header-cell {:style {:width "18em"}} "Provider Account"] - [grid/header-cell {:style {:width "12em"}} "Status"] - [grid/header-cell {:style {:width "12em"}} "Last Updated"] - [grid/header-cell {} "Accounts"] - [grid/header-cell {:style {:width (action-cell-width 1)}}]]] - [grid/body - (for [{:keys [id name accounts status last-updated] :as c} (:data data)] - ^{:key (str name "-" id)} - [grid/row {:class (:class c) :id id} - [grid/cell {} id] - [grid/cell {} status] - [grid/cell {} (date->str last-updated)] - [grid/cell {} - [:ul - (for [a accounts] - ^{:key (:id a)} - [:li (:name a) [:div.tag (->$ (:balance a))]])]] - [grid/cell {} - [:div.buttons - (when is-admin? - [buttons/fa-icon {:event [::delete-requested (:id c)] - :icon "fa-times"}])]]])]] - [grid/bottom-paginator data]])) diff --git a/src/cljs/auto_ap/views/pages/company/plaid.cljs b/src/cljs/auto_ap/views/pages/company/plaid.cljs deleted file mode 100644 index f400502f..00000000 --- a/src/cljs/auto_ap/views/pages/company/plaid.cljs +++ /dev/null @@ -1,176 +0,0 @@ -(ns auto-ap.views.pages.company.plaid - (:require - [auto-ap.effects.forward :as forward] - [auto-ap.status :as status] - [auto-ap.subs :as subs] - [auto-ap.views.components.layouts :refer [side-bar-layout]] - [auto-ap.views.pages.admin.plaid.table :as table] - [auto-ap.shared-views.company.sidebar :refer [company-side-bar]] - [auto-ap.views.pages.data-page :as data-page] - [auto-ap.views.utils :refer [dispatch-event with-user]] - [clojure.set :as set] - [re-frame.core :as re-frame] - [react-plaid-link :refer [usePlaidLink]] - [reagent.core :as reagent] - [vimsical.re-frame.fx.track :as track])) - -(re-frame/reg-sub - ::link-token - (fn [db] - (-> db ::link-token))) - -(re-frame/reg-sub - ::message - (fn [db] - (-> db ::message))) - -(re-frame/reg-sub - ::params - :<- [::table/params] - (fn [table-params] - table-params)) - -(re-frame/reg-event-fx - ::data-requested - (fn [{:keys [db]} [_ params]] - {:graphql {:token (:user db) - :owns-state {:single ::page} - :query-obj {:venia/queries [{:query/data [:plaid-item-page {:client-id (:id @(re-frame/subscribe [::subs/client])) - :sort (:sort params) - :start (:start params 0)} - [[:plaid-items [:id :last-updated :status - [:client [:id]] - [:accounts [:id :name :number :balance]]]] - :count - :start - :end - :total]] - :query/alias :result}] - } - :on-success (fn [result] - [::data-page/received ::page - (set/rename-keys (:result result) - {:plaid-items :data})])}})) - -(re-frame/reg-event-fx - ::mounted - (fn [{:keys [db]} _] - {:dispatch [::data-requested] - ::forward/register {:id ::plaid-item-deleted - :events #{::table/plaid-item-deleted} - :event-fn (fn [[_ _]] - [::data-requested])} - ::track/register {:id ::params - :subscription [::data-page/params ::page] - :event-fn (fn [params] - [::data-requested params])} - :db (dissoc db - ::link-token - ::message)})) - -(re-frame/reg-event-fx - ::unmounted - (fn [_ _] - {::forward/dispose {:id ::plaid-item-deleted} - ::track/dispose [{:id ::params}]})) - - -(re-frame/reg-event-fx - ::get-link-token - [with-user] - (fn [{:keys [db]} [_ _]] - {:graphql {:token (:user db) - :owns-state {:single ::get-link-token} - :query-obj {:venia/queries [[:plaid-link-token {:client-id (:id @(re-frame/subscribe [::subs/client]))} - [:token]]]} - :on-success [::authenticated]}})) - -(re-frame/reg-event-fx - ::plaid-linked - (fn [{:keys [db]} [_ m]] - {:db (assoc db ::message (:message (:link-plaid m))) - :dispatch [::data-requested]})) - -(re-frame/reg-event-fx - ::exchange-token - [with-user] - (fn [{:keys [db]} [_ client public-token]] - {:graphql {:token (:user db) - :owns-state {:single ::get-link-token} - :query-obj - {:venia/operation {:operation/type :mutation - :operation/name "LinkPlaid"} - :venia/queries [{:query/data - [:link-plaid - {:client-code client - :public-token public-token} - [:message]]}]} - :on-success [::plaid-linked]}})) - -(re-frame/reg-event-db - ::authenticated - (fn [db [_ link-token]] - (-> db - (assoc-in [::link-token] (:token (:plaid-link-token link-token)))))) - - -(defn plaid-item-table [] - [table/table {:data-page ::page - :status @(re-frame/subscribe [::status/single ::page])}]) - -(defn link-button [{:keys [link-token client-code]}] - (let [plaid (usePlaidLink #js {:token link-token - :onSuccess (fn [x] - (re-frame/dispatch [::exchange-token client-code x]))})] - [:div - [:button.button.is-primary {:on-click (.-open plaid)} - [:span [:span.icon [:i.fa.fa-external-link]] " Go to plaid"]]])) - -(defn plaid-link-token-button [] - (let [status @(re-frame/subscribe [::status/single ::get-link-token]) - client @(re-frame/subscribe [::subs/client])] - [:button.button.is-primary {:disabled (status/disabled-for status) - :class (status/class-for status) - :on-click (dispatch-event [::get-link-token (:code client)])} - "Authenticate with Plaid (" (:name client) ")"])) - -(defn link-flow [] - [:div - (let [link-token @(re-frame/subscribe [::link-token]) - client-code (:code @(re-frame/subscribe [::subs/client]))] - (cond - (and link-token client-code) - [:div - "Authentication successful!" - [:f> link-button {:link-token link-token - :client-code client-code}]] - - client-code - [plaid-link-token-button] - - - :else - nil))]) - - -(defn admin-plaid-item-content [] - (let [message @(re-frame/subscribe [::message])] - [:div - [:h1.title "Plaid Accounts"] - (when message - [:div.notification.is-info.is-light - message]) - [plaid-item-table] - [link-flow] - ])) - - -(defn plaid-page [] - (reagent/create-class - {:component-will-unmount #(re-frame/dispatch [::unmounted]) - :component-did-mount #(re-frame/dispatch [::mounted]) - :reagent-render (fn [] - [side-bar-layout {:side-bar [company-side-bar {}] - :main [admin-plaid-item-content]}])})) - - diff --git a/test/clj/auto_ap/integration/graphql/ledger/running_balance.clj b/test/clj/auto_ap/integration/graphql/ledger/running_balance.clj index daa71921..f98b4fdb 100644 --- a/test/clj/auto_ap/integration/graphql/ledger/running_balance.clj +++ b/test/clj/auto_ap/integration/graphql/ledger/running_balance.clj @@ -132,6 +132,6 @@ (:journal-entry/line-items) (map :journal-entry-line/dirty)))) (is (= [true true] - (->> (d/pull (d/db conn) '[{:journal-entry/line-items [:journal-entry-line/dirty]}] journal-entry-3) + (->> (d/pull (d/db conn) '[{:journal-entry/line-items [:journal-entry-line/dirty]}] journal-entry-2) (:journal-entry/line-items) (map :journal-entry-line/dirty))))))))