From 431c2883e24d9f76b18c4919672942d85b746a1d Mon Sep 17 00:00:00 2001 From: Bryce Covert Date: Fri, 13 Apr 2018 20:28:31 -0700 Subject: [PATCH] can send emails on a whim --- ...882-DOWN-add-email-fields-to-reminders.sql | 1 + ...66882-UP-add-email-fields-to-reminders.sql | 4 +++ src/clj/auto_ap/db/reminders.clj | 22 ++++++++++++++- src/clj/auto_ap/routes/reminders.clj | 22 ++++++--------- src/clj/auto_ap/routes/vendors.clj | 28 +++++++++++++++---- src/cljs/auto_ap/events/admin/vendors.cljs | 23 +++++++++++++++ .../auto_ap/views/pages/admin/reminders.cljs | 8 ++++-- .../auto_ap/views/pages/admin/vendors.cljs | 25 ++++++++++++----- src/cljs/auto_ap/views/utils.cljs | 1 + 9 files changed, 103 insertions(+), 31 deletions(-) create mode 100644 migrator/migrations/1523666882-DOWN-add-email-fields-to-reminders.sql create mode 100644 migrator/migrations/1523666882-UP-add-email-fields-to-reminders.sql diff --git a/migrator/migrations/1523666882-DOWN-add-email-fields-to-reminders.sql b/migrator/migrations/1523666882-DOWN-add-email-fields-to-reminders.sql new file mode 100644 index 00000000..0a4a8dc1 --- /dev/null +++ b/migrator/migrations/1523666882-DOWN-add-email-fields-to-reminders.sql @@ -0,0 +1 @@ +-- 1523666882 DOWN add-email-fields-to-reminders \ No newline at end of file diff --git a/migrator/migrations/1523666882-UP-add-email-fields-to-reminders.sql b/migrator/migrations/1523666882-UP-add-email-fields-to-reminders.sql new file mode 100644 index 00000000..303069da --- /dev/null +++ b/migrator/migrations/1523666882-UP-add-email-fields-to-reminders.sql @@ -0,0 +1,4 @@ +-- 1523666882 UP add-email-fields-to-reminders +ALTER table reminders add column email varchar(255); +ALTER table reminders add column subject varchar(255); +ALTER table reminders add column body text; diff --git a/src/clj/auto_ap/db/reminders.clj b/src/clj/auto_ap/db/reminders.clj index 1b0236c6..a3e2f5d5 100644 --- a/src/clj/auto_ap/db/reminders.clj +++ b/src/clj/auto_ap/db/reminders.clj @@ -1,7 +1,9 @@ (ns auto-ap.db.reminders (:require [auto-ap.db.utils :refer [clj->db db->clj get-conn]] [clj-time.core :as time] + [amazonica.aws.simpleemail :as ses] [clojure.java.jdbc :as j] + [config.core :refer [env]] [clj-time.jdbc :as jdbc] [clojure.string :as str])) @@ -34,9 +36,27 @@ (defn get-ready [] (map db->clj (j/query (get-conn) (str - " SELECT reminders.*, vendors.name as vendor_name, vendors.email " + " SELECT reminders.*, vendors.name as vendor_name " " FROM reminders INNER join vendors on reminders.vendor_id = vendors.id " " WHERE reminders.sent IS NULL AND reminders.scheduled < NOW()")))) (defn finish [id] (j/update! (get-conn) :reminders {:sent (time/now)} ["id = ?" id] )) + +(defn template [] + {:subject "Reminder to send invoices" + :body (str "This is a reminder to please reply with a [format of invoice: pdf/excel]" + "version of last week's invoices for:" + "We would greatly appreciate it if you could send that to us by end of day on" + "Monday.")}) + +(defn send-emails [reminders] + (doseq [{:keys [vendor-name id email subject body]} reminders] + (when email + (println "Sending email to" email) + (ses/send-email :destination {:to-addresses [email]} + :source (:invoice-email env) + :message {:subject subject + :body {:html (str "

Hello " vendor-name ",

" body "

- Integreat.

") + :text (str "Hello " vendor-name ",\r\n" body "\r\n - Integreat.")}})) + (finish id))) diff --git a/src/clj/auto_ap/routes/reminders.clj b/src/clj/auto_ap/routes/reminders.clj index c9cd7a4f..5788b87c 100644 --- a/src/clj/auto_ap/routes/reminders.clj +++ b/src/clj/auto_ap/routes/reminders.clj @@ -1,5 +1,5 @@ (ns auto-ap.routes.reminders - (:require [amazonica.aws.simpleemail :as ses] + (:require [auto-ap.db.reminders :as reminders] [auto-ap.db.vendors :as vendors] [auto-ap.routes.utils :refer [wrap-secure]] @@ -30,24 +30,18 @@ (println "Reminders will happen at" (next-sunday)) (println "Reminders to schedule" vendors-without-scheduled) - (doseq [{:keys [id invoice-reminder-schedule]} vendors-without-scheduled] - (reminders/insert {:vendor-id id - :scheduled (next-sunday)})))) + (doseq [{:keys [id primary-email invoice-reminder-schedule]} vendors-without-scheduled] + (reminders/insert (assoc (reminders/template) + :vendor-id id + :email primary-email + :scheduled (next-sunday)))))) (defn find-ready-reminders [] (let [vendors (vendors/get-all) ready-reminders (reminders/get-ready)] ready-reminders)) -(defn send-emails [reminders] - (doseq [{:keys [vendor-name email id]} reminders] - (println "Sending email to" email) - (ses/send-email :destination {:to-addresses [email]} - :source (:invoice-email env) - :message {:subject "Reminder to send invoices" - :body {:html (str "

Hello " vendor-name ",

This is a reminder to send this week's invoices to us. You can just reply to this email.

- Integreat.

") - :text (str "Hello " vendor-name ",\r\nThis is a reminder to send this week's invoices to us. You can just reply to this email.\r\n - Integreat.")}}) - (reminders/finish id))) + (defn replace-joda [x] (into {} (map (fn [[k v]] @@ -71,7 +65,7 @@ (println "Scheduling") (schedule-reminders) (-> (reminders/get-ready) - (send-emails))))) + (reminders/send-emails))))) {:status 200 :body "{}" diff --git a/src/clj/auto_ap/routes/vendors.clj b/src/clj/auto_ap/routes/vendors.clj index a7f78769..a1b07852 100644 --- a/src/clj/auto_ap/routes/vendors.clj +++ b/src/clj/auto_ap/routes/vendors.clj @@ -2,8 +2,9 @@ (:require [auto-ap.db.vendors :as vendors] [auto-ap.entities.vendors :as entity] [auto-ap.routes.utils :refer [wrap-secure wrap-spec]] - [compojure.core :refer [GET POST PUT context defroutes - wrap-routes]])) + [auto-ap.db.reminders :as reminders] + [clj-time.core :as time] + [compojure.core :refer [GET POST PUT context defroutes wrap-routes]])) (defroutes routes (wrap-routes @@ -12,17 +13,32 @@ {:status 200 :body (pr-str (vendors/get-all)) :headers {"Content-Type" "application/edn"}}) - (wrap-spec + (wrap-routes (PUT "/:id" {:keys [edn-params] {:keys [id]} :route-params :as r} {:status 200 :body (pr-str (vendors/upsert id edn-params)) :headers {"Content-Type" "application/edn"}}) - ::entity/vendor) - (wrap-spec + #(wrap-spec % ::entity/vendor)) + (POST "/:id/remind" {:keys [edn-params] {:keys [id :<< as-int]} :route-params :as r} + (let [id (if (int? id) + id + (Integer/parseInt id)) + vendor (vendors/get-by-id id)] + (reminders/insert (assoc + (reminders/template) + :email (:primary-email vendor) + :vendor-id id + :scheduled (time/now))) + (-> (reminders/get-ready) + (reminders/send-emails)) + {:status 200 + :body "{}" + :headers {"Content-Type" "application/edn"}})) + (wrap-routes (POST "/" {:keys [edn-params] :as r} {:status 200 :body (pr-str (vendors/insert edn-params)) :headers {"Content-Type" "application/edn"}}) - ::entity/vendor)) + #(wrap-spec % ::entity/vendor))) wrap-secure)) diff --git a/src/cljs/auto_ap/events/admin/vendors.cljs b/src/cljs/auto_ap/events/admin/vendors.cljs index 8d7e4f8d..656c317a 100644 --- a/src/cljs/auto_ap/events/admin/vendors.cljs +++ b/src/cljs/auto_ap/events/admin/vendors.cljs @@ -42,6 +42,29 @@ :on-success [::save-complete] :on-error [::save-error]})))))) +(re-frame/reg-event-fx + ::remind + (fn [{:keys [db] :as fx} [_ id]] + { :http {:method :post + :token (:user db) + :headers {"Content-Type" "application/edn"} + :uri (str "/api/vendors/" id "/remind") + :on-success [::reminder-sent] + :on-error [::failed "Failed to send reminder"]} + :db (assoc-in db [:admin :banner] nil)})) + +(re-frame/reg-event-db + ::reminder-sent + (fn [db [_ error]] + (-> db + (assoc-in [:admin :banner] "Reminder sent!")))) + +(re-frame/reg-event-db + ::failed + (fn [db [_ error]] + (-> db + (assoc-in [:admin :banner] error)))) + (re-frame/reg-event-db ::save-complete (fn [db [_ vendor]] diff --git a/src/cljs/auto_ap/views/pages/admin/reminders.cljs b/src/cljs/auto_ap/views/pages/admin/reminders.cljs index c5068a60..713c475b 100644 --- a/src/cljs/auto_ap/views/pages/admin/reminders.cljs +++ b/src/cljs/auto_ap/views/pages/admin/reminders.cljs @@ -18,14 +18,16 @@ [:tr [:th "Vendor"] [:th "Scheduled Date"] - [:th "Status"]]] - [:tbody (for [{:keys [id vendor-name scheduled sent]} reminders] + [:th "Status"] + [:th "Email"]]] + [:tbody (for [{:keys [id vendor-name scheduled sent email ]} reminders] ^{:key id} [:tr [:td vendor-name] [:td (date->str scheduled)] [:td (when sent - [:span [:span.icon [:i.fa.fa-check]] "Sent " (date-time->str sent)]) ]])]])) + [:span [:span.icon [:i.fa.fa-check]] "Sent " (date-time->str sent)]) ] + [:td email]])]])) (defn admin-reminders-page [] diff --git a/src/cljs/auto_ap/views/pages/admin/vendors.cljs b/src/cljs/auto_ap/views/pages/admin/vendors.cljs index 091c7420..2377af47 100644 --- a/src/cljs/auto_ap/views/pages/admin/vendors.cljs +++ b/src/cljs/auto_ap/views/pages/admin/vendors.cljs @@ -24,14 +24,22 @@ [:tr [:th "Name"] [:th "Email"] - [:th "Invoice Reminders"]]] + [:th "Invoice Reminders"] + [:th]]] [:tbody (for [v @vendors] ^{:key (str (:id v))} - [:tr {:on-click (fn [] (re-frame/dispatch [::events/edit (:id v)])) + [:tr {:on-click (dispatch-event [::events/edit (:id v)]) :style {"cursor" "pointer"}} [:td (:name v)] [:td (:primary-email v)] - [:td (:invoice-reminder-schedule v)]])]])) + [:td (:invoice-reminder-schedule v)] + [:td + (when (:primary-email v) + [:button.button.is-primary.is-outlined + + {:on-click (dispatch-event [::events/remind (:id v)]) + :href "#"} + [:span.icon [:i.fa.fa-share-square]] [:span "Send Reminder"]])]])]])) (defn danger-for [[dom {:keys [field subscription class] :as keys} & rest]] (let [keys (assoc keys :class (str class @@ -226,7 +234,7 @@ (when (:saving? editing-vendor) [:div.is-overlay {:style {"backgroundColor" "rgba(150,150,150, 0.5)"}}])] [:footer.modal-card-foot - [:a.button.is-primary {:on-click (fn [] (re-frame/dispatch [::events/save])) + [:button.button.is-primary {:on-click (fn [] (re-frame/dispatch [::events/save])) :disabled (when (not (s/valid? ::entity/vendor editing-vendor )) "disabled")} [:span "Save"] @@ -238,17 +246,20 @@ [(with-meta (fn [] [:div.inbox-messages + (when-let [banner (:banner @(re-frame/subscribe [::subs/admin]))] + [:div.notification banner]) (let [vendors (re-frame/subscribe [::subs/vendors]) editing-vendor (:vendor @(re-frame/subscribe [::subs/admin]))] + [:div [:h1.title "Vendors"] [vendors-table] - [:a.button.is-primary.is-large {:on-click (dispatch-event [::events/new])} "New vendor"] + [:div.is-pulled-right + [:a.button.is-primary.is-large {:on-click (dispatch-event [::events/new])} "New vendor"]] (when editing-vendor - [edit-dialog] - )])]) + [edit-dialog])])]) {:component-did-mount (fn [] (re-frame/dispatch [::events/mounted]))})]) diff --git a/src/cljs/auto_ap/views/utils.cljs b/src/cljs/auto_ap/views/utils.cljs index e91b0a56..46517462 100644 --- a/src/cljs/auto_ap/views/utils.cljs +++ b/src/cljs/auto_ap/views/utils.cljs @@ -18,6 +18,7 @@ (defn dispatch-event [event] (fn [e] + (.stopPropagation e) (.preventDefault e) (re-frame/dispatch event)))