416 lines
16 KiB
Clojure
416 lines
16 KiB
Clojure
(ns auto-ap.views.pages.admin.yodlee
|
|
(:require [re-frame.core :as re-frame]
|
|
[auto-ap.forms :as forms]
|
|
[reagent.core :as reagent]
|
|
[clojure.string :as str]
|
|
[cljs-time.format :as f]
|
|
[cljs-time.core :as time]
|
|
[auto-ap.subs :as subs]
|
|
[auto-ap.events :as events]
|
|
[auto-ap.entities.clients :as entity]
|
|
[auto-ap.views.components.layouts :refer [side-bar-layout]]
|
|
[auto-ap.views.components.admin.side-bar :refer [admin-side-bar]]
|
|
[auto-ap.views.components.address :refer [address-field]]
|
|
[auto-ap.views.utils :refer [login-url dispatch-event dispatch-value-change bind-field horizontal-field str->date date->str with-user]]
|
|
[auto-ap.views.components.modal :as modal]
|
|
[auto-ap.status :as status]
|
|
[cljs.reader :as edn]
|
|
[auto-ap.routes :as routes]
|
|
[bidi.bidi :as bidi]))
|
|
|
|
|
|
|
|
(re-frame/reg-sub
|
|
::authentication
|
|
(fn [db]
|
|
(-> db ::yodlee :authentication)))
|
|
|
|
(re-frame/reg-sub
|
|
::can-submit
|
|
(fn [db]
|
|
true))
|
|
|
|
(re-frame/reg-sub
|
|
::loading?
|
|
(fn [db]
|
|
(-> db ::yodlee :loading?)))
|
|
|
|
(re-frame/reg-sub
|
|
::accounts
|
|
(fn [db]
|
|
(-> db ::yodlee :accounts)))
|
|
|
|
(re-frame/reg-sub
|
|
::accounts-loading?
|
|
(fn [db]
|
|
(-> db ::yodlee :accounts-loading?)))
|
|
|
|
(re-frame/reg-sub
|
|
::provider-accounts-loading?
|
|
(fn [db]
|
|
(-> db ::provider-accounts-loading?)))
|
|
|
|
(re-frame/reg-sub
|
|
::provider-accounts
|
|
(fn [db]
|
|
(-> db ::provider-accounts)))
|
|
|
|
(re-frame/reg-event-fx
|
|
::authenticate-with-yodlee
|
|
(fn [{:keys [db]} _]
|
|
{:db (assoc-in db [::yodlee :loading?] true)
|
|
:http {:token (:user db)
|
|
:method :get
|
|
:headers {"Content-Type" "application/edn"}
|
|
:uri (str "/api/yodlee/fastlink")
|
|
:on-success [::authenticated]
|
|
:on-error [::save-error]}}))
|
|
|
|
(re-frame/reg-event-fx
|
|
::mounted
|
|
(fn [{:keys [db]} _]
|
|
{:db (-> db
|
|
(assoc ::yodlee {:provider-accounts-loading? true})
|
|
(assoc ::save-error nil)
|
|
(assoc ::provider-accounts [])
|
|
(assoc ::provider-accounts-loading? true))
|
|
:http {:token (:user db)
|
|
:method :get
|
|
:headers {"Content-Type" "application/edn"}
|
|
:uri (str "/api/yodlee/provider-accounts")
|
|
:on-success [::got-provider-accounts]
|
|
:on-error [::save-error]}}))
|
|
|
|
(re-frame/reg-event-fx
|
|
::kicked
|
|
(fn [{:keys [db]} [_ id state]]
|
|
{:dispatch [::mounted]}))
|
|
|
|
(re-frame/reg-event-fx
|
|
::kicked
|
|
(fn [{:keys [db]} [_ id state]]
|
|
{:dispatch [::mounted]}))
|
|
|
|
(re-frame/reg-event-fx
|
|
::kick
|
|
(fn [{:keys [db]} [_ id]]
|
|
{:http {:token (:user db)
|
|
:method :post
|
|
:headers {"Content-Type" "application/edn"}
|
|
:uri (str "/api/yodlee/provider-accounts/" id)
|
|
:on-success [::kicked id :kicked]
|
|
:on-error [::kicked id :errored]}}))
|
|
|
|
(re-frame/reg-event-fx
|
|
::got-accounts
|
|
(fn [{:keys [db]} [_ accounts]]
|
|
{:db (-> db
|
|
(assoc-in [::yodlee :accounts] accounts)
|
|
(assoc-in [::yodlee :accounts-loading?] false))}))
|
|
|
|
(re-frame/reg-event-fx
|
|
::got-provider-accounts
|
|
(fn [{:keys [db]} [_ accounts]]
|
|
{:db (-> db
|
|
(assoc-in [::provider-accounts] accounts)
|
|
(assoc-in [::provider-accounts-loading?] false))}))
|
|
|
|
(re-frame/reg-event-fx
|
|
::authenticated
|
|
(fn [{:keys [db]} [_ authentication]]
|
|
{:db (-> db
|
|
(assoc-in [::yodlee :authentication] authentication)
|
|
(assoc-in [::yodlee :loading?] false))}))
|
|
|
|
(re-frame/reg-event-fx
|
|
::authenticated-mfa
|
|
(fn [{:keys [db]} [_ provider-account-id authentication]]
|
|
{:db (-> db
|
|
(assoc-in [::yodlee :authentication] authentication)
|
|
(assoc-in [::yodlee :loading?] false)
|
|
(forms/stop-form [::mfa-form provider-account-id]))}))
|
|
|
|
(re-frame/reg-event-fx
|
|
::save-error
|
|
(fn [{:keys [db]} [_ authentication]]
|
|
{:db (assoc db ::load-error "error")}))
|
|
|
|
(defn yodlee-link-button []
|
|
[:div
|
|
(let [authentication @(re-frame/subscribe [::authentication])
|
|
loading? @(re-frame/subscribe [::loading?])]
|
|
|
|
(if authentication
|
|
[:div
|
|
"Authentication successful!"
|
|
[:form {:action (:url authentication) :method "POST"}
|
|
[:input {:type "hidden"
|
|
:name "rsession"
|
|
:value (:session authentication)}]
|
|
[:input {:type "hidden"
|
|
:name "token"
|
|
:value (:token authentication)}]
|
|
[:input {:type "hidden"
|
|
:name "app"
|
|
:value (:app authentication)}]
|
|
|
|
[:input {:type "hidden"
|
|
:name "redirectReq"
|
|
:value "true"}]
|
|
[:button.button.is-primary [:span [:span.icon [:i.fa.fa-external-link]] " Go to yodlee"]]]]
|
|
|
|
[:button.button.is-primary {:class (if loading? "is-loading" "") :on-click (dispatch-event [::authenticate-with-yodlee])} "Authenticate with Yodlee"]))])
|
|
|
|
(defn yodlee-date->date [d]
|
|
(try
|
|
(some-> d
|
|
(str->date (:date-time-no-ms f/formatters))
|
|
)
|
|
(catch js/Error e
|
|
nil)))
|
|
|
|
(defn yodlee-date->str [d]
|
|
(try
|
|
(or (some-> d
|
|
(str->date (:date-time-no-ms f/formatters))
|
|
date->str)
|
|
"N/A")
|
|
(catch js/Error e
|
|
"N/A")))
|
|
|
|
(defn yodlee-accounts-table [accounts]
|
|
(let [bank-accounts @(re-frame/subscribe [::bank-accounts-by-yodlee-account-id])]
|
|
[:div
|
|
[:table.table
|
|
[:thead
|
|
[:tr
|
|
[:th "Account Name"]
|
|
[:th "Account Number"]
|
|
[:th "Yodlee Account Number"]
|
|
[:th "Balance"]
|
|
[:th "Yodlee Status"]
|
|
[:th "Usage"]]]
|
|
[:tbody
|
|
|
|
(for [account accounts]
|
|
^{:key (:id account)} [:tr
|
|
[:td (:accountName account)]
|
|
[:td (:accountNumber account)]
|
|
[:td (:id account)]
|
|
[:td.has-text-right (:amount (:balance account))]
|
|
[:td (str/join ", " (map :additionalStatus (:dataset account)))]
|
|
[:td
|
|
(when-let [bank-accounts (get bank-accounts (:id account))]
|
|
[:div.tags
|
|
(for [bank-account bank-accounts]
|
|
^{:key (:id bank-account)}
|
|
[:div.tag (:name bank-account) " (" (:code bank-account) ")"])])]
|
|
])]]]))
|
|
|
|
(re-frame/reg-event-fx
|
|
::reauthenticate-mfa
|
|
[with-user ]
|
|
(fn [{:keys [user db]} [_ provider-account-id ]]
|
|
{:db (forms/loading db [::mfa-form provider-account-id])
|
|
:http {:token user
|
|
:method :post
|
|
:headers {"Content-Type" "application/edn"}
|
|
:uri (str "/api/yodlee/reauthenticate/" provider-account-id )
|
|
:body {"loginForm"
|
|
{"row"
|
|
(->> (get-in db [::forms/forms [::mfa-form provider-account-id]])
|
|
:data
|
|
:login
|
|
(sort-by (fn [[k v]] k))
|
|
(map second)
|
|
(map (fn [row]
|
|
{"field"
|
|
(mapv (fn [[k v]]
|
|
{"id" k
|
|
"value" v})
|
|
row)})))}
|
|
"field"
|
|
(mapv (fn [[k v]]
|
|
{"id" k
|
|
"value" v})
|
|
(:mfa (:data (get-in db [::forms/forms [::mfa-form provider-account-id]]))))}
|
|
|
|
:on-success [::authenticated-mfa provider-account-id]
|
|
:on-error [::forms/save-error [::mfa-form provider-account-id] ]}}))
|
|
|
|
(re-frame/reg-event-fx
|
|
::provider-account-refreshed
|
|
(fn [{:keys [db]} [_ i result]]
|
|
|
|
{:db (assoc-in db [::provider-accounts] result)
|
|
:dispatch [::forms/form-closing [::refresh-provider-account i]]}))
|
|
|
|
(re-frame/reg-event-fx
|
|
::refresh-provider-account
|
|
[with-user ]
|
|
(fn [{:keys [user db]} [_ provider-account-id ]]
|
|
{:db (forms/loading db [::refresh-provider-account provider-account-id])
|
|
:http {:token user
|
|
:method :post
|
|
:headers {"Content-Type" "application/edn"}
|
|
:uri (str "/api/yodlee/provider-accounts/refresh/" provider-account-id )
|
|
:body {}
|
|
:on-success [::provider-account-refreshed provider-account-id]
|
|
:on-error [::forms/save-error [::refresh-provider-account provider-account-id] ]}}))
|
|
|
|
(re-frame/reg-event-fx
|
|
::provider-account-deleted
|
|
(fn [{:keys [db]} [_ i result]]
|
|
{:db (assoc-in db [::provider-accounts] result)
|
|
:dispatch-n [[::forms/form-closing [::refresh-provider-account i]]
|
|
[::modal/modal-closed ]]}))
|
|
|
|
(re-frame/reg-event-fx
|
|
::delete-provider-account
|
|
[with-user ]
|
|
(fn [{:keys [user db]} [_ provider-account-id ]]
|
|
{:http {:token user
|
|
:method :post
|
|
:owns-state {:single ::delete-provider-account}
|
|
:headers {"Content-Type" "application/edn"}
|
|
:uri (str "/api/yodlee/provider-accounts/delete/" provider-account-id )
|
|
:body {}
|
|
:on-success [::provider-account-deleted provider-account-id]
|
|
:on-error [::forms/save-error [::delete-provider-account provider-account-id] ]}}))
|
|
|
|
|
|
|
|
(re-frame/reg-event-fx
|
|
::delete-requested
|
|
[with-user]
|
|
(fn [{:keys [user db]} [_ account-id]]
|
|
{:dispatch
|
|
[::modal/modal-requested {:title "Delete Provider account "
|
|
:body [:div "Are you sure you want to delete provider account " account-id "?"]
|
|
:confirm {:value "Delete provider account"
|
|
:status-from [::status/single ::delete-provider-account]
|
|
:class "is-danger"
|
|
:on-click (dispatch-event [::delete-provider-account account-id])
|
|
:close-event [::status/completed ::delete-provider-account]}
|
|
:cancel? true}]}))
|
|
|
|
|
|
(defn delete-button [account-id]
|
|
[:button.button
|
|
{:on-click (dispatch-event [::delete-requested account-id])}
|
|
[:span.icon [:i.fa.fa-times]]])
|
|
|
|
(re-frame/reg-sub
|
|
::bank-accounts-by-yodlee-account-id
|
|
:<- [::subs/bank-accounts]
|
|
(fn [bank-accounts]
|
|
(group-by :yodlee-account-id bank-accounts)))
|
|
|
|
(defn yodlee-provider-accounts-table []
|
|
(let [bank-accounts @(re-frame/subscribe [::bank-accounts-by-yodlee-account-id])]
|
|
|
|
(if @(re-frame/subscribe [::provider-accounts-loading?])
|
|
[:div "Loading..."]
|
|
[:div.columns
|
|
[:div.column.is-half
|
|
(doall
|
|
(for [account @(re-frame/subscribe [::provider-accounts])
|
|
:let [{:keys [error status] :as g} @(re-frame/subscribe [::forms/form [::refresh-provider-account (:id account)]])
|
|
total-usages (mapcat (comp bank-accounts :id) (:accounts account))]]
|
|
|
|
^{:key (:id account)}
|
|
[:div.card {:style {:margin-bottom "1em"}}
|
|
[:div.card-header
|
|
[:div.card-header-title "Provider account " (:id account)]
|
|
[:div.card-header-icon
|
|
(when (seq total-usages)
|
|
[:div.tags
|
|
[:div.tag.is-primary (count total-usages) " usages"]])]
|
|
[:div.card-header-icon
|
|
[delete-button (:id account)]]
|
|
[:div.card-header-icon
|
|
(cond
|
|
(= :loading status) [:button.button.is-disabled.is-loading [:i.fa.fa-refresh]]
|
|
error [:button.button.is-disabled [:span.icon [:i.fa.fa-exclamation-triangle]]]
|
|
:else
|
|
[:button.button
|
|
{:on-click (dispatch-event [::refresh-provider-account (:id account)])}
|
|
[:span.icon [:i.fa.fa-refresh]]])]]
|
|
[:div.card-content
|
|
|
|
(if (> (some-> (-> account :dataset first :lastUpdated)
|
|
(yodlee-date->date )
|
|
(time/interval (time/now))
|
|
(time/in-days ))
|
|
1)
|
|
[:div.notification.is-info.is-light
|
|
[:div.level
|
|
[:div.level-left
|
|
[:div.level-item
|
|
[:p
|
|
"This account was last updated on "
|
|
(yodlee-date->str (-> account :dataset first :lastUpdated))
|
|
", and last attempted "
|
|
(yodlee-date->str (-> account :dataset first :lastUpdateAttempt))
|
|
"."]]]
|
|
[:div.level-right [:button.button.is-success {:on-click (dispatch-event [::kick (:id account)] )} "Sync yodlee with bank" ]]]
|
|
|
|
])
|
|
|
|
|
|
[yodlee-accounts-table (:accounts account)]
|
|
(if (not= (-> account :dataset first :additionalStatus)
|
|
"AVAILABLE_DATA_RETRIEVED")
|
|
[:div
|
|
[:div.notification.is-info.is-warning
|
|
[:div.level
|
|
[:div.level-left
|
|
[:div.level-item
|
|
"This provider account's status is '"
|
|
(-> account :dataset first :additionalStatus)
|
|
"'. If this is in error, it might help to try reauthenticating by filling out the form below."]]]]
|
|
(let [{error :error account-data :data } @(re-frame/subscribe [::forms/form [::mfa-form (:id account)]])
|
|
change-event [::forms/change [::mfa-form (:id account)]]
|
|
{:keys [form-inline field field-holder raw-field error-notification submit-button]} (forms/vertical-form {:can-submit [::can-submit]
|
|
:change-event change-event
|
|
:submit-event [::reauthenticate-mfa (:id account)]
|
|
:id [::mfa-form (:id account)]} )]
|
|
(form-inline {:title "Reauthenticate"}
|
|
[:<>
|
|
(error-notification)
|
|
(doall
|
|
(for [[row i] (map vector (-> account :loginForm last :row) (range))
|
|
f (:field row)
|
|
:let [options (map :optionValue (:option f))]]
|
|
^{:key (:id f)}
|
|
[:div
|
|
(field (:label row)
|
|
[:input.input {:type "text" :field [:login i (:id f)]}])
|
|
(if (seq options)
|
|
[:ul
|
|
(for [o options]
|
|
^{:key o}
|
|
[:li [:pre o]])])]))
|
|
(doall
|
|
(for [f (-> account :field)]
|
|
^{:key (:id f)}
|
|
(field (:label f)
|
|
[:input.input {:type "text" :mfa [:form (:id f)] :value (-> f :field first :value)}])))
|
|
(submit-button "Reauthenticate")]))])]]))]])))
|
|
|
|
|
|
(defn admin-yodlee-content []
|
|
[(with-meta
|
|
(fn []
|
|
[:div
|
|
[:h1.title "Yodlee provider accounts"]
|
|
|
|
[yodlee-provider-accounts-table]
|
|
[yodlee-link-button]])
|
|
{:component-did-mount (fn []
|
|
(re-frame/dispatch [::mounted]))})])
|
|
|
|
#_(defn admin-yodlee-page []
|
|
[side-bar-layout {:side-bar [admin-side-bar {}]
|
|
:main [admin-yodlee-content]}])
|