From 74e34e3592815214e231a09ad366b3dd17d3351a Mon Sep 17 00:00:00 2001 From: Bryce Covert Date: Sat, 5 Sep 2020 08:18:29 -0700 Subject: [PATCH] One modal to rule them all. --- resources/public/css/main.css | 5 + src/clj/auto_ap/graphql/transaction_rules.clj | 1 - src/clj/auto_ap/yodlee/core.clj | 2 +- src/cljs/auto_ap/events.cljs | 22 --- src/cljs/auto_ap/views/components/modal.cljs | 38 +---- src/cljs/auto_ap/views/pages/admin/rules.cljs | 3 +- .../auto_ap/views/pages/admin/rules/form.cljs | 12 +- .../pages/admin/rules/results_modal.cljs | 133 ++++++++---------- .../views/pages/admin/rules/table.cljs | 1 - .../auto_ap/views/pages/admin/yodlee.cljs | 50 +++---- 10 files changed, 106 insertions(+), 161 deletions(-) diff --git a/resources/public/css/main.css b/resources/public/css/main.css index 554ac242..e797a2c9 100644 --- a/resources/public/css/main.css +++ b/resources/public/css/main.css @@ -454,3 +454,8 @@ table.balance-sheet th.total { .table.grid.wrappable td {text-overflow: ""; overflow: visible; white-space: normal; } .table.grid.wrappable td.expandable {text-overflow: ""; overflow: visible; white-space: normal; } .table.grid.wrappable th.expandable {text-overflow: ""; overflow: visible; white-space: normal; } + +/* This is to make is so flexbox modal footers go to new lines between error message */ +.modal-card-foot { + flex-wrap: wrap; +} diff --git a/src/clj/auto_ap/graphql/transaction_rules.clj b/src/clj/auto_ap/graphql/transaction_rules.clj index 06c968bd..790110e3 100644 --- a/src/clj/auto_ap/graphql/transaction_rules.clj +++ b/src/clj/auto_ap/graphql/transaction_rules.clj @@ -192,7 +192,6 @@ (defn test-transaction-rule [{:keys [id]} {{:keys [description note client_id bank_account_id amount_lte amount_gte dom_lte dom_gte yodlee_merchant_id]} :transaction_rule :as z} value] (assert-admin id) - (log/info "HI") (-test-transaction-rule id #:transaction-rule {:description description :client (when client_id {:db/id client_id}) :bank-account (when bank_account_id {:db/id bank_account_id}) diff --git a/src/clj/auto_ap/yodlee/core.clj b/src/clj/auto_ap/yodlee/core.clj index 46477d9a..58898025 100644 --- a/src/clj/auto_ap/yodlee/core.clj +++ b/src/clj/auto_ap/yodlee/core.clj @@ -148,7 +148,7 @@ (-> (str (:yodlee-base-url env) "/providerAccounts/" id ) (client/get {:headers (merge base-headers {"Authorization" (auth-header cob-session user-session)}) - :query-params {"include" "credentials,questions,preferences"} + :query-params {"include" "credentials,preferences"} :as :json}) :body :providerAccount diff --git a/src/cljs/auto_ap/events.cljs b/src/cljs/auto_ap/events.cljs index 864ea1c4..6fcd2681 100644 --- a/src/cljs/auto_ap/events.cljs +++ b/src/cljs/auto_ap/events.cljs @@ -115,28 +115,6 @@ (assoc-in db (into location field) value) (update-in db (into location (butlast field)) dissoc (last field))))) -(re-frame/reg-event-db - ::modal-status - (fn [db [_ id state]] - (println "changing modal status" id "to") - (-> db - (update-in [:modal-state id] #(merge % state)) - #_(dissoc :auto-ap.forms/forms) - ))) - -(re-frame/reg-event-db - ::modal-failed - (fn [db [_ id message]] - (-> db - (assoc-in [:modal-state id :saving?] false) - (assoc-in [:modal-state id :error-message] message)))) - -(re-frame/reg-event-db - ::modal-completed - (fn [db [_ id state]] - (-> db - (update-in [:modal-state] #(dissoc % id)) - (update :auto-ap.forms/forms dissoc id)))) (re-frame/reg-event-fx ::set-active-page diff --git a/src/cljs/auto_ap/views/components/modal.cljs b/src/cljs/auto_ap/views/components/modal.cljs index a9139016..c7b85ec3 100644 --- a/src/cljs/auto_ap/views/components/modal.cljs +++ b/src/cljs/auto_ap/views/components/modal.cljs @@ -6,35 +6,6 @@ [auto-ap.status :as status] [auto-ap.views.utils :refer [with-keys appearing dispatch-event]])) -(defn modal [{:keys [title foot hide-event class]} & body] - [:div.modal.is-active (cond-> {} - class (assoc :class class)) - [:div.modal-background {:on-click (fn [] (re-frame/dispatch hide-event ))}] - - [:div.modal-card - [:header.modal-card-head - [:p.modal-card-title - title] - [:button.delete {:on-click (fn [e] (.preventDefault e) (re-frame/dispatch hide-event))}]] - (into [:section.modal-card-body] - (r/children (r/current-component))) - - (when foot - [:footer.modal-card-foot - foot])]]) - - -(defn simple-modal [{:keys [title foot class warning action-text id save-event can-submit? status-from] :or {can-submit? true}} & rest] - (let [{:keys [visible? saving? error-message]} @(re-frame/subscribe [::subs/modal-state id status-from])] - (when visible? - (-> [modal {:title [:span title] - :class class - :foot foot - :id id - :hide-event [::events/modal-status id {:visible? false :error-message nil}]}] - (into (r/children (r/current-component))) - (into [(when saving? [:div.is-overlay {:style {"backgroundColor" "rgba(150,150,150, 0.5)"}}])]))))) - (re-frame/reg-sub ::modal-state (fn [db] @@ -57,7 +28,7 @@ (let [state (re-frame/subscribe [::modal-state])] (fn [] (if (:visible? @state) - (let [{:keys [title body foot class cancel? confirm]} @state] + (let [{:keys [title body status-from foot class cancel? confirm]} @state] [:div.modal.is-active (cond-> {} class (assoc :class class)) [:div.modal-background {:on-click (dispatch-event [::modal-closed])}] @@ -69,7 +40,8 @@ [:button.delete {:on-click (dispatch-event [::modal-closed])}]] [:section.modal-card-body body] - (let [status (some-> confirm :status-from re-frame/subscribe deref ) + (let [status (or (some-> confirm :status-from re-frame/subscribe deref ) + (some-> status-from re-frame/subscribe deref )) can-submit (if-let [can-submit (-> confirm :can-submit)] @(re-frame/subscribe can-submit) true)] @@ -79,7 +51,7 @@ :timeout 200 :enter-class "appear" :exit-class "disappear"} - [:div.notification.is-warning (:message (first (:error status)))]] + [:div.has-text-danger {:style {:flex-basis "100%"}} (:message (first (:error status)))] ] foot] [:footer.modal-card-foot [:div @@ -87,7 +59,7 @@ :timeout 200 :enter-class "appear" :exit-class "disappear"} - [:div.has-text-danger (:message (first (:error status)))]] + [:div.has-text-danger {:style {:flex-basis "100%"}} (:message (first (:error status)))]] [:div.buttons (when confirm diff --git a/src/cljs/auto_ap/views/pages/admin/rules.cljs b/src/cljs/auto_ap/views/pages/admin/rules.cljs index c7ae163d..8d81cac5 100644 --- a/src/cljs/auto_ap/views/pages/admin/rules.cljs +++ b/src/cljs/auto_ap/views/pages/admin/rules.cljs @@ -103,5 +103,4 @@ [side-bar/rule-side-bar {:data-page ::page}]] :main [rules-content] :right-side-bar [appearing-side-bar {:visible? active?} - [form/form {:data-page ::page}]] - :bottom [results-modal/test-results-modal]}])) + [form/form {:data-page ::page}]]}])) diff --git a/src/cljs/auto_ap/views/pages/admin/rules/form.cljs b/src/cljs/auto_ap/views/pages/admin/rules/form.cljs index ed28a16f..c4e6f823 100644 --- a/src/cljs/auto_ap/views/pages/admin/rules/form.cljs +++ b/src/cljs/auto_ap/views/pages/admin/rules/form.cljs @@ -3,7 +3,6 @@ [auto-ap.events :as events] [auto-ap.forms :as forms] [auto-ap.subs :as subs] - [auto-ap.views.components.modal :refer [modal]] [auto-ap.views.components.button-radio :refer [button-radio]] [auto-ap.utils :refer [dollars=]] [auto-ap.views.components.dropdown :refer [drop-down]] @@ -17,7 +16,7 @@ [clojure.spec.alpha :as s] [clojure.string :as str] [re-frame.core :as re-frame] - [auto-ap.views.pages.admin.rules.results-modal :as results-modal])) + [auto-ap.status :as status])) ;; SUBS @@ -47,7 +46,6 @@ ::can-submit :<- [::forms/form ::form] (fn [{:keys [data status]} _] - (println (s/explain ::entity/transaction-rule data)) (s/valid? ::entity/transaction-rule data))) (re-frame/reg-sub @@ -204,6 +202,7 @@ (fn [{:keys [user] {:keys [data]} :db} [_ params]] {:graphql {:token user + :owns-state {:single ::test} :query-obj @(re-frame/subscribe [::test-query]) :on-success [::succeeded-test] :on-error [::forms/save-error ::form]}})) @@ -238,6 +237,7 @@ (let [{:keys [data active? error id]} @(re-frame/subscribe [::forms/form ::form]) {:keys [form-inline field raw-field error-notification submit-button ]} rule-form default-note @(re-frame/subscribe [::default-note]) + test-state @(re-frame/subscribe [::status/single ::test]) exists? (:id data)] ^{:key id} (form-inline (assoc params :title "New Transaction Rule") @@ -347,9 +347,11 @@ [:div.is-divider] (error-notification) - [:div.columns [:div.column - [:a.button.is-medium.is-fullwidth.is-outlined {:on-click (dispatch-event [::test-clicked])} "Test Rule"]] + [:a.button.is-medium.is-fullwidth.is-outlined {:on-click (dispatch-event [::test-clicked]) + :disabled (status/disabled-for test-state) + :class (status/class-for test-state)} + "Test Rule"]] [:div.column (submit-button "Save")]]]))]) diff --git a/src/cljs/auto_ap/views/pages/admin/rules/results_modal.cljs b/src/cljs/auto_ap/views/pages/admin/rules/results_modal.cljs index c8d90522..f65961d7 100644 --- a/src/cljs/auto_ap/views/pages/admin/rules/results_modal.cljs +++ b/src/cljs/auto_ap/views/pages/admin/rules/results_modal.cljs @@ -1,10 +1,11 @@ (ns auto-ap.views.pages.admin.rules.results-modal (:require [auto-ap.events :as events] [auto-ap.subs :as subs] - [auto-ap.views.components.modal :refer [modal]] [auto-ap.views.utils :refer [date->str dispatch-event with-user]] [auto-ap.views.pages.transactions.common :refer [transaction-read]] - [re-frame.core :as re-frame])) + [re-frame.core :as re-frame] + [auto-ap.views.components.modal :as modal] + [auto-ap.status :as status])) @@ -36,6 +37,58 @@ (count checked) 0))) +(defn results-body [params] + (let [runnable? @(re-frame/subscribe [::runnable?]) + checked @(re-frame/subscribe [::checked]) + all-checked @(re-frame/subscribe [::all-checked]) + checked-count @(re-frame/subscribe [::checked-count])] + [:table.table.is-fullwidth.compact + [:tr + (when runnable? + [:th {:style {:width "2em"}} + [:input.checkbox {:type "checkbox" + :checked all-checked + :on-change (dispatch-event [::toggle-all])}]]) + [:th "Client"] + [:th "Bank Account"] + [:th "Description"] + [:th "Date"] + [:th "Amount"]] + (for [{:keys [id client bank-account description-original date amount]} @(re-frame/subscribe [::test-results])] + + ^{:key id} + [:tr + (when runnable? + [:td + [:input.checkbox {:type "checkbox" + :checked (boolean (checked id)) + :on-change (dispatch-event [::toggle id])}]]) + [:td (:name client)] + [:td (:name bank-account)] + [:td description-original] + [:td (when date (date->str date))] + [:td amount]])])) + +(defn foot [params] + (let [runnable? @(re-frame/subscribe [::runnable?]) + checked @(re-frame/subscribe [::checked]) + all-checked @(re-frame/subscribe [::all-checked]) + checked-count @(re-frame/subscribe [::checked-count]) + status @(re-frame/subscribe [::status/single ::apply])] + (when runnable? + [:div + [:button.button.is-primary {:disabled (or (boolean (= checked-count 0)) + (status/disabled-for status)) + :class (status/class-for status) + :on-click (dispatch-event [::apply-rule params])} + (str "Apply to " checked-count " transactions")] + + [:button.button.is-warning {:disabled (or (boolean (not all-checked)) + (status/disabled-for status)) + :class (status/class-for status) + :on-click (dispatch-event [::apply-rule (assoc params :all? true)])} + (str "Apply to all transactions")]]))) + (re-frame/reg-event-fx ::opening (fn [{:keys [db]} [_ results transaction-rule-id runnable?]] @@ -45,7 +98,11 @@ (assoc ::transaction-rule-id transaction-rule-id) (assoc ::checked #{}) (assoc ::runnable? runnable?)) - :dispatch [::events/modal-status ::test-results {:visible? true :error-message nil}]})) + :dispatch [::modal/modal-requested {:title "Rule results" + :class "wide" + :body [results-body] + :status-from [::status/single ::apply] + :foot [foot]}]})) (re-frame/reg-event-db ::toggle @@ -66,28 +123,13 @@ #{} (into #{} (map :id test-results))))))) -(re-frame/reg-event-fx - ::apply-failed - [with-user] - (fn [{:keys [db user]} [_ params]] - {:graphql - {:token user - :query-obj {:venia/operation {:operation/type :mutation - :operation/name "MatchTransactionRules"} - :venia/queries [{:query/data [:match-transaction-rules - {:transaction-ids (seq @(re-frame/subscribe [::checked])) - :all (boolean (:all? params)) - :transaction-rule-id (::transaction-rule-id db)} - transaction-read]}]} - :on-success [::events/modal-status ::test-results {:visible? false}] - :on-error [:events/modal-failed ::test-results]}})) - (re-frame/reg-event-fx ::apply-rule [with-user] (fn [{:keys [db user]} [_ params]] {:graphql {:token user + :owns-state {:single ::apply} :query-obj {:venia/operation {:operation/type :mutation :operation/name "MatchTransactionRules"} :venia/queries [{:query/data [:match-transaction-rules @@ -95,55 +137,4 @@ :all (boolean (:all? params)) :transaction-rule-id (::transaction-rule-id db)} transaction-read]}]} - :on-success [::events/modal-status ::test-results {:visible? false}] - :on-error [::events/modal-failed ::test-results]}})) - - -(defn test-results-modal [params] - (let [runnable? @(re-frame/subscribe [::runnable?]) - checked @(re-frame/subscribe [::checked]) - all-checked @(re-frame/subscribe [::all-checked]) - checked-count @(re-frame/subscribe [::checked-count]) - modal-state @(re-frame/subscribe [::subs/modal-state ::test-results])] - (when (:visible? modal-state) - [modal {:title "Rule results" - :class "wide" - :hide-event [::events/modal-status ::test-results {:visible? false}] - :foot (when runnable? - [:div - (when (:error-message modal-state) - [:div.notification.is-warning - (:error-message modal-state)]) - [:button.button.is-primary {:disabled (boolean (= checked-count 0)) - :on-click (dispatch-event [::apply-rule params])} - (str "Apply to " checked-count " transactions")] - - [:button.button.is-warning {:disabled (not all-checked) - :on-click (dispatch-event [::apply-rule (assoc params :all? true)])} - (str "Apply to all transactions")]])} - [:table.table.is-fullwidth.compact - [:tr - (when runnable? - [:th {:style {:width "2em"}} - [:input.checkbox {:type "checkbox" - :checked all-checked - :on-change (dispatch-event [::toggle-all])}]]) - [:th "Client"] - [:th "Bank Account"] - [:th "Description"] - [:th "Date"] - [:th "Amount"]] - (for [{:keys [id client bank-account description-original date amount]} @(re-frame/subscribe [::test-results])] - - ^{:key id} - [:tr - (when runnable? - [:td - [:input.checkbox {:type "checkbox" - :checked (boolean (checked id)) - :on-change (dispatch-event [::toggle id])}]]) - [:td (:name client)] - [:td (:name bank-account)] - [:td description-original] - [:td (when date (date->str date))] - [:td amount]])]]))) + :on-success [::modal/modal-closed]}})) diff --git a/src/cljs/auto_ap/views/pages/admin/rules/table.cljs b/src/cljs/auto_ap/views/pages/admin/rules/table.cljs index 1505cb6c..f3265b44 100644 --- a/src/cljs/auto_ap/views/pages/admin/rules/table.cljs +++ b/src/cljs/auto_ap/views/pages/admin/rules/table.cljs @@ -8,7 +8,6 @@ [auto-ap.views.components.sorter :refer [sorted-column toggle-sort-by sort-icon]] [auto-ap.views.components.buttons :as buttons] [auto-ap.views.components.grid :as grid] - [auto-ap.views.components.modal :refer [simple-modal]] [auto-ap.events :as events] [auto-ap.status :as status] [re-frame.core :as re-frame] diff --git a/src/cljs/auto_ap/views/pages/admin/yodlee.cljs b/src/cljs/auto_ap/views/pages/admin/yodlee.cljs index fcb40e5f..28b4225e 100644 --- a/src/cljs/auto_ap/views/pages/admin/yodlee.cljs +++ b/src/cljs/auto_ap/views/pages/admin/yodlee.cljs @@ -1,6 +1,4 @@ (ns auto-ap.views.pages.admin.yodlee - (:require-macros [cljs.core.async.macros :refer [go]] - ) (:require [re-frame.core :as re-frame] [auto-ap.forms :as forms] [reagent.core :as reagent] @@ -14,7 +12,8 @@ [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 :refer [simple-modal modal]] + [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])) @@ -248,18 +247,17 @@ (re-frame/reg-event-fx ::provider-account-deleted (fn [{:keys [db]} [_ i result]] - {:db (assoc-in db [::provider-accounts] result) - :dispatch [::forms/form-closing [::refresh-provider-account i]]})) + :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 ]] - {:db (forms/loading db [::delete-provider-account provider-account-id]) - :dispatch (dispatch-event [::events/modal-status [::delete provider-account-id] {:visible? false}] ) - :http {:token user + {: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 {} @@ -267,24 +265,26 @@ :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] - (let [confirmed? (reagent/atom false)] - (fn [] - (let [delete-form @(re-frame/subscribe [::forms/form [::delete-provider-account account-id]])] - [:div - (cond - (= :loading (:status delete-form)) [:button.button.is-disabled.is-loading [:i.fa.fa-times]] - (:error delete-form) [:button.button.is-disabled [:span.icon [:i.fa.fa-exclamation-triangle]]] - :else - [:button.button - {:on-click (dispatch-event [::events/modal-status [::delete account-id] {:visible? true}])} - [:span.icon [:i.fa.fa-times]]]) - [simple-modal {:id [::delete account-id] - :title "Confirmation" - :foot [:div - [:button.button.is-danger {:on-click (dispatch-event [::delete-provider-account account-id] )} "Delete provider account"] - [:button.button {:on-click (dispatch-event [::events/modal-status [::delete account-id] {:visible? false}] )}"Cancel"]]} - "Are you sure you want to delete provider account " account-id "?"]])))) + [:button.button + {:on-click (dispatch-event [::delete-requested account-id])} + [:span.icon [:i.fa.fa-times]]]) (defn yodlee-provider-accounts-table []