diff --git a/src/clj/auto_ap/background/mail.clj b/src/clj/auto_ap/background/mail.clj index 59a1232e..4ffaa9a5 100644 --- a/src/clj/auto_ap/background/mail.clj +++ b/src/clj/auto_ap/background/mail.clj @@ -1,25 +1,16 @@ (ns auto-ap.background.mail - (:require [amazonica.aws.sqs :as sqs] - [amazonica.aws.s3 :as s3] - [clojure.data.json :as json] - [clojure-mail.message :as message] - [clojure.string :as str] - [clojure.java.io :as io] - [config.core :refer [env]] - [auto-ap.parse :as parse] - [auto-ap.db.invoices :as invoices] + (:require [amazonica.aws.s3 :as s3] + [amazonica.aws.sqs :as sqs] [auto-ap.db.companies :as companies] - ) - (:import [java.util Properties] - [java.util UUID] - [javax.mail.search FlagTerm] - [java.io FileInputStream File] - [javax.mail.internet MimeMessage] - [javax.mail Session - Folder - Flags - Flags$Flag AuthenticationFailedException] - (com.sun.mail.imap IMAPStore))) + [auto-ap.db.invoices :as invoices] + [auto-ap.parse :as parse] + [clojure-mail.message :as message] + [clojure.data.json :as json] + [clojure.java.io :as io] + [config.core :refer [env]]) + (:import (java.util Properties UUID) + (javax.mail Session) + (javax.mail.internet MimeMessage))) (defn process-sqs [] diff --git a/src/clj/auto_ap/db/companies.clj b/src/clj/auto_ap/db/companies.clj index 53319949..a5b41e88 100644 --- a/src/clj/auto_ap/db/companies.clj +++ b/src/clj/auto_ap/db/companies.clj @@ -1,14 +1,17 @@ (ns auto-ap.db.companies - (:require [clojure.java.jdbc :as j] - [auto-ap.db.utils :refer [clj->db db->clj get-conn]] - [clojure.edn :as edn])) + (:require [auto-ap.db.utils :refer [clj->db db->clj get-conn assign-namespace]] + [auto-ap.entities.companies :as entity] + [clojure.edn :as edn] + [clojure.java.jdbc :as j])) -(defn merge-data [{:keys [data] :as x}] +(defn merge-data [{:keys [::entity/data] :as x}] (merge x (edn/read-string data))) (defn parse [x] (-> x (db->clj) + + (assign-namespace "auto-ap.entities.companies") merge-data )) @@ -20,4 +23,4 @@ (defn upsert [id data] (j/update! (get-conn) :companies (clj->db data) ["id = ?" (Integer/parseInt id)] ) - (merge-data (db->clj (first (j/query (get-conn) ["SELECT * FROM companies WHERE id = ?" (Integer/parseInt id)]))))) + (parse (first (j/query (get-conn) ["SELECT * FROM companies WHERE id = ?" (Integer/parseInt id)])))) diff --git a/src/clj/auto_ap/db/invoices.clj b/src/clj/auto_ap/db/invoices.clj index ddb4793b..eb22c1a2 100644 --- a/src/clj/auto_ap/db/invoices.clj +++ b/src/clj/auto_ap/db/invoices.clj @@ -1,7 +1,8 @@ (ns auto-ap.db.invoices - (:require [clojure.java.jdbc :as j] + (:require [auto-ap.db.utils :refer [clj->db db->clj get-conn]] [auto-ap.parse :as parse] - [auto-ap.db.utils :refer [clj->db db->clj get-conn]])) + [auto-ap.entities.companies :as company] + [clojure.java.jdbc :as j])) (defn insert-multi! [rows] (j/insert-multi! (get-conn) @@ -34,6 +35,6 @@ (do (println row) (assoc row - :company-id (:id (parse/best-match companies customer-identifier)) + :company-id (::company/id (parse/best-match companies customer-identifier)) :imported false :potential-duplicate false))))) diff --git a/src/clj/auto_ap/db/reminders.clj b/src/clj/auto_ap/db/reminders.clj index aa3b8556..fa4262a4 100644 --- a/src/clj/auto_ap/db/reminders.clj +++ b/src/clj/auto_ap/db/reminders.clj @@ -1,9 +1,7 @@ (ns auto-ap.db.reminders - (:require [clojure.java.jdbc :as j] - [auto-ap.parse :as parse] - [auto-ap.db.utils :refer [clj->db db->clj get-conn]] - [clj-time.jdbc] + (:require [auto-ap.db.utils :refer [clj->db db->clj get-conn]] [clj-time.core :as time] + [clojure.java.jdbc :as j] [clojure.string :as str])) (defn insert [row] diff --git a/src/clj/auto_ap/db/users.clj b/src/clj/auto_ap/db/users.clj index 4d84899d..73b7be70 100644 --- a/src/clj/auto_ap/db/users.clj +++ b/src/clj/auto_ap/db/users.clj @@ -1,7 +1,7 @@ (ns auto-ap.db.users - (:require [clojure.java.jdbc :as j] + (:require [auto-ap.db.utils :refer [get-conn]] [clojure.edn :as edn] - [auto-ap.db.utils :refer [clj->db db->clj get-conn]])) + [clojure.java.jdbc :as j])) (defn find-or-insert! [row] (let [user (first (j/find-by-keys (get-conn) diff --git a/src/clj/auto_ap/db/utils.clj b/src/clj/auto_ap/db/utils.clj index 8bb33d13..621be3d4 100644 --- a/src/clj/auto_ap/db/utils.clj +++ b/src/clj/auto_ap/db/utils.clj @@ -1,6 +1,6 @@ (ns auto-ap.db.utils - (:require [config.core :refer [env]]) - (:require [clojure.string :as str])) + (:require [clojure.string :as str] + [config.core :refer [env]])) (defn snake->kebab [s] (str/replace s #"_" "-")) @@ -34,3 +34,14 @@ :user "ap" :password "fifteen-invoices-imported!"})) + +(defn assign-namespace [x n] + (reduce-kv + (fn [x k v] + (assoc x (if (and (keyword? k) + (not (qualified-keyword? k))) + (keyword n (name k)) + k) + v)) + {} + x)) diff --git a/src/clj/auto_ap/db/vendors.clj b/src/clj/auto_ap/db/vendors.clj index cf6e0127..42c19e66 100644 --- a/src/clj/auto_ap/db/vendors.clj +++ b/src/clj/auto_ap/db/vendors.clj @@ -1,23 +1,12 @@ (ns auto-ap.db.vendors - (:require [clojure.java.jdbc :as j] - [auto-ap.db.utils :refer [clj->db db->clj get-conn]] + (:require [auto-ap.db.utils :refer [clj->db db->clj get-conn assign-namespace]] [auto-ap.entities.vendors :as entities] - [clojure.edn :as edn])) + [clojure.edn :as edn] + [clojure.java.jdbc :as j])) (defn merge-data [{:keys [data] :as x}] (merge x (edn/read-string data))) -(defn assign-namespace [x n] - (reduce-kv - (fn [x k v] - (assoc x (if (and (keyword? k) - (not (qualified-keyword? k))) - (keyword n (name k)) - k) - v)) - {} - x)) - (defn parse [x] (-> x (db->clj) diff --git a/src/clj/auto_ap/handler.clj b/src/clj/auto_ap/handler.clj index a797a1b7..455f1c00 100644 --- a/src/clj/auto_ap/handler.clj +++ b/src/clj/auto_ap/handler.clj @@ -1,26 +1,21 @@ (ns auto-ap.handler - (:require [compojure.core :refer :all] - [compojure.route :as route] - [clojure.java.io :as io] - [clojure.string :as str] + (:require [amazonica.core :refer [defcredential]] + [auto-ap.routes.auth :as auth] + [auto-ap.routes.companies :as companies] [auto-ap.routes.invoices :as invoices] - [auto-ap.db.users :as users] + [auto-ap.routes.reminders :as reminders] + [auto-ap.routes.vendors :as vendors] + [buddy.auth.backends.token :refer [jws-backend]] + [buddy.auth.middleware :refer [wrap-authentication + wrap-authorization]] + [compojure.core :refer :all] + [compojure.route :as route] + [config.core :refer [env]] + [ring.middleware.edn :refer [wrap-edn-params]] [ring.middleware.multipart-params :as mp] - [ring.util.response :as response] - [ring.middleware.defaults :refer [wrap-defaults site-defaults]] [ring.middleware.params :refer [wrap-params]] [ring.middleware.reload :refer [wrap-reload]] - [ring.middleware.edn :refer [wrap-edn-params]] - [clojure.java.jdbc :as j] - [config.core :refer [env]] - - [buddy.auth.backends.token :refer [jws-backend]] - [buddy.auth.middleware :refer [wrap-authorization wrap-authentication]] - [auto-ap.routes.companies :as companies] - [auto-ap.routes.vendors :as vendors] - [auto-ap.routes.reminders :as reminders] - [auto-ap.routes.auth :as auth] - [amazonica.core :refer [defcredential]])) + [ring.util.response :as response])) (defcredential (:aws-access-key-id env) (:aws-secret-access-key env) (:aws-region env)) diff --git a/src/clj/auto_ap/parse.clj b/src/clj/auto_ap/parse.clj index d9f0d273..2c445190 100644 --- a/src/clj/auto_ap/parse.clj +++ b/src/clj/auto_ap/parse.clj @@ -1,11 +1,9 @@ (ns auto-ap.parse - (:require [clojure.java.io :as io] - [clojure.string :as str] - [clojure.java.shell :as sh] - [auto-ap.parse.excel :as excel] + (:require [auto-ap.parse.excel :as excel] [auto-ap.parse.templates :as t] [clj-fuzzy.metrics :as m] - )) + [clojure.java.shell :as sh] + [clojure.string :as str])) @@ -54,6 +52,7 @@ (excel/parse-file file filename)) (defn best-match [companies company-identifier] + (println companies) (->> companies (map (fn [company] [company (apply min (map #(m/jaccard (.toLowerCase company-identifier) %) (:matches company)))])) diff --git a/src/clj/auto_ap/parse/excel.clj b/src/clj/auto_ap/parse/excel.clj index 3440d566..0a8d1f80 100644 --- a/src/clj/auto_ap/parse/excel.clj +++ b/src/clj/auto_ap/parse/excel.clj @@ -1,8 +1,8 @@ (ns auto-ap.parse.excel - (:import [org.apache.poi.ss.util CellAddress]) - (:require [dk.ative.docjure.spreadsheet :as d] + (:require [auto-ap.parse.templates :as t] [clojure.string :as str] - [auto-ap.parse.templates :as t])) + [dk.ative.docjure.spreadsheet :as d]) + (:import (org.apache.poi.ss.util CellAddress))) diff --git a/src/clj/auto_ap/routes/auth.clj b/src/clj/auto_ap/routes/auth.clj index 6fb9876d..178f4c2b 100644 --- a/src/clj/auto_ap/routes/auth.clj +++ b/src/clj/auto_ap/routes/auth.clj @@ -1,11 +1,10 @@ (ns auto-ap.routes.auth - (:require - [compojure.core :refer [defroutes GET ]] - [auto-ap.db.users :as users] - [buddy.sign.jwt :as jwt] - [clj-http.client :as http] - [config.core :refer [env]] - [clj-time.core :as time])) + (:require [auto-ap.db.users :as users] + [buddy.sign.jwt :as jwt] + [clj-http.client :as http] + [clj-time.core :as time] + [compojure.core :refer [GET defroutes]] + [config.core :refer [env]])) (def google-client-id "264081895820-0nndcfo3pbtqf30sro82vgq5r27h8736.apps.googleusercontent.com") (def google-client-secret "OC-WemHurPXYpuIw5cT-B90g") diff --git a/src/clj/auto_ap/routes/companies.clj b/src/clj/auto_ap/routes/companies.clj index 6f21860c..0fcf1425 100644 --- a/src/clj/auto_ap/routes/companies.clj +++ b/src/clj/auto_ap/routes/companies.clj @@ -1,7 +1,11 @@ (ns auto-ap.routes.companies - (:require [compojure.core :refer [context GET PUT defroutes wrap-routes]] - [auto-ap.db.companies :as companies] - [auto-ap.routes.utils :refer [wrap-secure]])) + (:require [auto-ap.db.companies :as companies] + [auto-ap.routes.utils :refer [wrap-secure wrap-spec]] + [auto-ap.entities.companies :as entity] + [clojure.spec.alpha :as s] + [compojure.core :refer [GET PUT context defroutes + wrap-routes]])) + (defroutes routes (wrap-routes @@ -10,8 +14,10 @@ {:status 200 :body (pr-str (companies/get-all)) :headers {"Content-Type" "application/edn"}}) - (PUT "/:id" {:keys [edn-params] {:keys [id]} :route-params :as r} - {:status 200 - :body (pr-str (companies/upsert id edn-params)) - :headers {"Content-Type" "application/edn"}})) + (wrap-spec + (PUT "/:id" {:keys [edn-params] {:keys [id]} :route-params :as r} + {:status 200 + :body (pr-str (companies/upsert id edn-params)) + :headers {"Content-Type" "application/edn"}}) + ::entity/company)) wrap-secure)) diff --git a/src/clj/auto_ap/routes/invoices.clj b/src/clj/auto_ap/routes/invoices.clj index 163fbfcd..5827ed56 100644 --- a/src/clj/auto_ap/routes/invoices.clj +++ b/src/clj/auto_ap/routes/invoices.clj @@ -1,9 +1,10 @@ (ns auto-ap.routes.invoices - (:require [compojure.core :refer [context GET PUT POST defroutes wrap-routes]] + (:require [auto-ap.db.companies :as companies] [auto-ap.db.invoices :as invoices] - [auto-ap.db.companies :as companies] [auto-ap.parse :as parse] - [auto-ap.routes.utils :refer [wrap-secure]])) + [auto-ap.routes.utils :refer [wrap-secure]] + [compojure.core :refer [GET POST context defroutes + wrap-routes]])) (defroutes routes (wrap-routes diff --git a/src/clj/auto_ap/routes/reminders.clj b/src/clj/auto_ap/routes/reminders.clj index 17c73bdf..6d8181bc 100644 --- a/src/clj/auto_ap/routes/reminders.clj +++ b/src/clj/auto_ap/routes/reminders.clj @@ -1,20 +1,17 @@ (ns auto-ap.routes.reminders - (:require [compojure.core :refer [context GET POST defroutes wrap-routes]] - [auto-ap.db.vendors :as vendors] + (:require [amazonica.aws.simpleemail :as ses] [auto-ap.db.reminders :as reminders] + [auto-ap.db.vendors :as vendors] [auto-ap.routes.utils :refer [wrap-secure]] - [amazonica.aws.simpleemail :as ses] - [clj-time.core :as time] - [clj-time.coerce :as c] - [clj-time.predicates :as pred] - [clj-time.periodic :as p] - [clj-time.local :as l] - [clj-time.jdbc] [clj-http.client :as http] + [clj-time.coerce :as c] + [clj-time.core :as time] + [clj-time.periodic :as p] + [clj-time.predicates :as pred] [clojure.data.json :as json] - [ring.middleware.json :refer [wrap-json-body]] - ) - (:import [org.joda.time DateTime])) + [compojure.core :refer [GET POST context defroutes + wrap-routes]]) + (:import (org.joda.time DateTime))) (defn next-sunday [] (let [sunday (->> (p/periodic-seq (time/plus (time/today) (time/days 1)) (time/days 1)) diff --git a/src/clj/auto_ap/routes/utils.clj b/src/clj/auto_ap/routes/utils.clj index 31ed48c0..7be97306 100644 --- a/src/clj/auto_ap/routes/utils.clj +++ b/src/clj/auto_ap/routes/utils.clj @@ -1,5 +1,6 @@ (ns auto-ap.routes.utils - (:require [buddy.auth :refer [authenticated?]])) + (:require [clojure.spec.alpha :as s] + [buddy.auth :refer [authenticated?]])) (defn wrap-secure [handler] (fn [request] @@ -7,3 +8,11 @@ (handler request) {:status 401 :body "not authenticated"}))) + +(defn wrap-spec [handler spec] + (fn [request] + (if (not (s/valid? spec (:edn-params request))) + {:status 400 + :body (pr-str (s/explain-data spec (:edn-params request))) + :headers {"Content-Type" "application/edn"}} + (handler request)))) diff --git a/src/clj/auto_ap/routes/vendors.clj b/src/clj/auto_ap/routes/vendors.clj index 90bf1f0e..1451271d 100644 --- a/src/clj/auto_ap/routes/vendors.clj +++ b/src/clj/auto_ap/routes/vendors.clj @@ -1,9 +1,10 @@ (ns auto-ap.routes.vendors - (:require [compojure.core :refer [context GET PUT POST defroutes wrap-routes]] - [auto-ap.db.vendors :as vendors] - [auto-ap.routes.utils :refer [wrap-secure]] + (:require [auto-ap.db.vendors :as vendors] + [auto-ap.routes.utils :refer [wrap-secure wrap-spec]] [auto-ap.entities.vendors :as entity] - [clojure.spec.alpha :as s])) + [clojure.spec.alpha :as s] + [compojure.core :refer [GET POST PUT context defroutes + wrap-routes]])) (defroutes routes (wrap-routes @@ -12,16 +13,17 @@ {:status 200 :body (pr-str (vendors/get-all)) :headers {"Content-Type" "application/edn"}}) - (PUT "/:id" {:keys [edn-params] {:keys [id]} :route-params :as r} - (println edn-params) - (println (s/valid? ::entity/vendor edn-params)) - (println (s/explain ::entity/vendor edn-params)) - {:status 200 - :body (pr-str (vendors/upsert id edn-params)) - :headers {"Content-Type" "application/edn"}}) - (POST "/" {:keys [edn-params] :as r} - (println (s/valid? ::entity/vendor edn-params)) + (wrap-spec + (PUT "/:id" {:keys [edn-params] {:keys [id]} :route-params :as r} {:status 200 - :body (pr-str (vendors/insert edn-params)) - :headers {"Content-Type" "application/edn"}})) + :body (pr-str (vendors/upsert id edn-params)) + :headers {"Content-Type" "application/edn"}}) + ::entity/vendor) + (wrap-spec + (POST "/" {:keys [edn-params] :as r} + + {:status 200 + :body (pr-str (vendors/insert edn-params)) + :headers {"Content-Type" "application/edn"}}) + ::entity/vendor)) wrap-secure)) diff --git a/src/clj/auto_ap/server.clj b/src/clj/auto_ap/server.clj index 7d4e90e5..5e6f5c05 100644 --- a/src/clj/auto_ap/server.clj +++ b/src/clj/auto_ap/server.clj @@ -1,6 +1,6 @@ (ns auto-ap.server - (:require [auto-ap.handler :refer [app]] - [auto-ap.background.mail :refer [always-process-sqs]] + (:require [auto-ap.background.mail :refer [always-process-sqs]] + [auto-ap.handler :refer [app]] [config.core :refer [env]] [ring.adapter.jetty :refer [run-jetty]]) (:gen-class)) diff --git a/src/cljc/auto_ap/entities/companies.cljc b/src/cljc/auto_ap/entities/companies.cljc new file mode 100644 index 00000000..f616ff01 --- /dev/null +++ b/src/cljc/auto_ap/entities/companies.cljc @@ -0,0 +1,22 @@ +(ns auto-ap.entities.companies + (:require [clojure.spec.alpha :as s] + [clojure.string :as str])) + +(def email-regex #"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,63}$") +(s/def ::id int) +(s/def ::identifier (s/nilable string?)) +(s/def ::required-identifier (s/and string? + #(not (str/blank? %)))) + +(s/def ::name ::required-identifier) + +(s/def ::email (s/nilable (s/and string? (s/or :is-email #(re-matches email-regex %) + :is-empty #(= % ""))))) + +(s/def ::company (s/keys :req [::name] + :opt [::email + ::id])) + + +(def company-spec (apply hash-map (drop 1 (s/form ::company)))) +(def all-keys (concat (:req company-spec) (:opt company-spec))) diff --git a/src/cljs/auto_ap/events.cljs b/src/cljs/auto_ap/events.cljs index 24d5e587..4d4c5ac4 100644 --- a/src/cljs/auto_ap/events.cljs +++ b/src/cljs/auto_ap/events.cljs @@ -4,6 +4,7 @@ [auto-ap.subs :as subs] [auto-ap.routes :as routes] [auto-ap.effects :as effects] + [auto-ap.entities.companies :as companies] [bidi.bidi :as bidi])) (re-frame/reg-event-fx @@ -42,14 +43,14 @@ (fn [db [_ companies]] (assoc db :companies (reduce (fn [companies company] - (assoc companies (:id company) company)) + (assoc companies (::companies/id company) company)) {} companies)))) (re-frame/reg-event-db ::swap-company (fn [db [_ company]] - (assoc db :company (:id company)))) + (assoc db :company (::companies/id company)))) (re-frame/reg-event-fx ::set-active-page @@ -70,7 +71,7 @@ {:http {:method :post :token (-> cofx :db :user) :uri (str "/api/invoices/approve" - (when-let [company-id (:id @(re-frame/subscribe [::subs/company]))] + (when-let [company-id (::companies/id @(re-frame/subscribe [::subs/company]))] (str "?company=" company-id))) :on-success [::received-invoices :pending] }})) @@ -82,7 +83,7 @@ :http {:method :get :token (-> cofx :db :user) :uri (str "/api/invoices/pending" - (when-let [company-id (:id @(re-frame/subscribe [::subs/company]))] + (when-let [company-id (::companies/id @(re-frame/subscribe [::subs/company]))] (str "?company=" company-id))) :on-success [::received-invoices :pending]}})) @@ -93,7 +94,7 @@ :http {:method :get :token (-> cofx :db :user) :uri (str "/api/invoices/unpaid" - (when-let [company-id (:id @(re-frame/subscribe [::subs/company]))] + (when-let [company-id (::companies/id @(re-frame/subscribe [::subs/company]))] (str "?company=" company-id))) :on-success [::received-invoices :unpaid]}})) @@ -103,7 +104,7 @@ {:http {:method :post :token (-> cofx :db :user) :uri (str "/api/invoices/reject" - (when-let [company-id (:id @(re-frame/subscribe [::subs/company]))] + (when-let [company-id (::companies/id @(re-frame/subscribe [::subs/company]))] (str "?company=" company-id))) :on-success [::received-invoices :pending] }})) diff --git a/src/cljs/auto_ap/events/admin/companies.cljs b/src/cljs/auto_ap/events/admin/companies.cljs index f8e99682..07751454 100644 --- a/src/cljs/auto_ap/events/admin/companies.cljs +++ b/src/cljs/auto_ap/events/admin/companies.cljs @@ -3,6 +3,7 @@ [auto-ap.db :as db] [auto-ap.routes :as routes] [auto-ap.effects :as effects] + [auto-ap.entities.companies :as entity] [bidi.bidi :as bidi])) (re-frame/reg-event-db @@ -18,10 +19,11 @@ {:db (assoc-in db [:admin :company :saving?] true) :http {:method :put :token (:user db) - :body (pr-str (select-keys edited-company [:name :email :data :invoice-reminder-schedule])) + :body (pr-str edited-company) :headers {"Content-Type" "application/edn"} - :uri (str "/api/companies/" (:id edited-company)) - :on-success [::save-complete]}}))) + :uri (str "/api/companies/" (::entity/id edited-company)) + :on-success [::save-complete] + :on-error [::save-error]}}))) (re-frame/reg-event-db ::save-complete @@ -29,7 +31,14 @@ (-> db (assoc-in [:admin :company] nil) - (assoc-in [:companies (:id company)] company)))) + (assoc-in [:companies (::entity/id company)] company)))) + +(re-frame/reg-event-db + ::save-error + (fn [db [_ company]] + (-> db + (assoc-in [:admin :company :saving?] false) + (assoc-in [:admin :company :error] true)))) (re-frame/reg-event-db ::change diff --git a/src/cljs/auto_ap/views/main.cljs b/src/cljs/auto_ap/views/main.cljs index 65426cbb..5a5debda 100644 --- a/src/cljs/auto_ap/views/main.cljs +++ b/src/cljs/auto_ap/views/main.cljs @@ -6,6 +6,7 @@ [auto-ap.subs :as subs] [auto-ap.events :as events] [auto-ap.views.utils :refer [active-when= login-url]] + [auto-ap.entities.companies :as company] [auto-ap.views.pages :as pages])) (defn page->layout [page] @@ -125,14 +126,14 @@ :on-click (fn [] (re-frame/dispatch [::events/toggle-menu :company]))} [:div.navbar-start [:div { :class (str "navbar-item has-dropdown " (when (get-in @menu [:company :active?]) "is-active"))} - [:a {:class "navbar-link login"} "Company: " (if @company (:name @company) + [:a {:class "navbar-link login"} "Company: " (if @company (::company/name @company) "All")] [:div {:class "navbar-dropdown"} [:a {:class "navbar-item" :on-click (fn [] (re-frame/dispatch [::events/swap-company nil])) } "All"] [:hr {:class "navbar-divider"}] - (for [{:keys [name] :as company} @companies] + (for [{:keys [::company/name] :as company} @companies] ^{:key name } [:a {:class "navbar-item" :on-click (fn [] (re-frame/dispatch [::events/swap-company company])) diff --git a/src/cljs/auto_ap/views/pages/admin/companies.cljs b/src/cljs/auto_ap/views/pages/admin/companies.cljs index 5e4d0ccd..f66219d6 100644 --- a/src/cljs/auto_ap/views/pages/admin/companies.cljs +++ b/src/cljs/auto_ap/views/pages/admin/companies.cljs @@ -4,7 +4,8 @@ [reagent.core :as reagent] [auto-ap.subs :as subs] [auto-ap.events.admin.companies :as events] - [auto-ap.views.utils :refer [login-url dispatch-value-change]] + [auto-ap.entities.companies :as entity] + [auto-ap.views.utils :refer [login-url dispatch-value-change bind-field horizontal-field]] [cljs.reader :as edn] [auto-ap.routes :as routes] [bidi.bidi :as bidi])) @@ -15,15 +16,14 @@ [:thead [:tr [:th "Name"] - [:th "Email"] - [:th "Invoice Reminders"]]] - [:tbody (for [{:keys [id name email data invoice-reminder-schedule] :as c} @companies] + [:th "Email"]]] + [:tbody (for [{:keys [::entity/id ::entity/name ::entity/email] :as c} @companies] ^{:key (str name "-" id )} [:tr {:on-click (fn [] (re-frame/dispatch [::events/edit id])) :style {"cursor" "pointer"}} [:td name] [:td email] - [:td invoice-reminder-schedule]])]])) + ])]])) (defn admin-companies-page [] [:div {:class "inbox-messages"} @@ -49,41 +49,25 @@ (str "Edit " (:name editing-company))] [:button.delete {:on-click (fn [] (re-frame/dispatch [::events/edit nil]))}]] [:section.modal-card-body - [:div.field + + [horizontal-field [:label.label "Name"] [:div.control - [:input.input {:type "text" :value (:name editing-company) - :on-change (dispatch-value-change [::events/change [:name]])}]]] + [bind-field + [:input.input {:type "text" + :field ::entity/name + :event ::events/change + :subscription editing-company}]]]] - [:div.field + [horizontal-field [:label.label "Email"] [:div.control - [:input.input {:type "email" - :value (:email editing-company) - :on-change (dispatch-value-change [::events/change [:email]])}]]] - - [:div.field - [:labal.label "Invoice Reminders"] - [:div.control - [:label.radio - [:input {:type "radio" - :name "schedule" - :value "Weekly" - :checked (if (= "Weekly" (:invoice-reminder-schedule editing-company)) - "checked" - "") - :on-change (dispatch-value-change [::events/change [:invoice-reminder-schedule]])}] - " Send weekly"]] - [:div.control - [:label.radio - [:input {:type "radio" - :name "schedule" - :value "Never" - :checked (if (= "Never" (:invoice-reminder-schedule editing-company)) - "checked" - "") - :on-change (dispatch-value-change [::events/change [:invoice-reminder-schedule]])}] - " Never"]]] + [bind-field + [:input.input {:type "email" + :field ::entity/email + :event ::events/change + :subscription editing-company}]]]] + (when (:saving? editing-company) [:div.is-overlay {:style {"backgroundColor" "rgba(150,150,150, 0.5)"}}])] diff --git a/src/cljs/auto_ap/views/pages/admin/vendors.cljs b/src/cljs/auto_ap/views/pages/admin/vendors.cljs index 1441f45c..f9d153ed 100644 --- a/src/cljs/auto_ap/views/pages/admin/vendors.cljs +++ b/src/cljs/auto_ap/views/pages/admin/vendors.cljs @@ -7,7 +7,7 @@ [auto-ap.events.admin.vendors :as events] [auto-ap.entities.vendors :as entity] [clojure.spec.alpha :as s] - [auto-ap.views.utils :refer [login-url dispatch-value-change dispatch-event]] + [auto-ap.views.utils :refer [login-url dispatch-value-change dispatch-event bind-field horizontal-field]] [cljs.reader :as edn] [auto-ap.routes :as routes] [bidi.bidi :as bidi])) @@ -33,10 +33,6 @@ [:td (::entity/primary-email v)] [:td (::entity/invoice-reminder-schedule v)]])]])) -(defmulti do-bind (fn [_ {:keys [type]}] - - type)) - (defn danger-for [[dom {:keys [field subscription class] :as keys} & rest]] (let [keys (assoc keys :class (str class (when (not (s/valid? field (field subscription))) @@ -45,39 +41,6 @@ (vec (concat [dom keys] rest)))) -(defmethod do-bind "radio" [dom {:keys [field subscription class value] :as keys} & rest] - (let [keys (assoc keys - :on-change (dispatch-value-change [::events/change [field]]) - :checked (= (field subscription) value) - :class (str class - (when (not (s/valid? field (field subscription))) - " is-danger"))) - keys (dissoc keys :field :subscription)] - (vec (concat [dom keys] rest)))) - - -(defmethod do-bind :default [dom {:keys [field subscription class] :as keys} & rest] - (let [keys (assoc keys - :on-change (dispatch-value-change [::events/change [field]]) - :value (field subscription) - :class (str class - (when (not (s/valid? field (field subscription))) - " is-danger"))) - keys (dissoc keys :field :subscription)] - (vec (concat [dom keys] rest)))) - -(defn bind-field [all] - (apply do-bind all)) - -(defn horizontal-field [label & controls] - [:div.field.is-horizontal - [:div.field-label - label - ] - (into - [:div.field-body - ] - (map (fn [c] [:div.field c]) controls))]) (defn edit-dialog [] (let [editing-vendor (:vendor @(re-frame/subscribe [::subs/admin]))] @@ -101,6 +64,7 @@ [bind-field [:input.input {:type "text" :field ::entity/name + :event ::events/change :subscription editing-vendor}]]]] [horizontal-field @@ -110,6 +74,7 @@ [bind-field [:input.input.is-expanded {:type "text" :field ::entity/code + :event ::events/change :subscription editing-vendor}]] [:p.help "The vendor code is used for invoice parsing. Only one vendor at a time can use a code"]]] @@ -122,6 +87,7 @@ [:input.input.is-expanded {:type "text" :placeholder "1700 Pennsylvania Ave" :field ::entity/address1 + :event ::events/change :subscription editing-vendor}]]]] [horizontal-field @@ -131,6 +97,7 @@ [:input.input.is-expanded {:type "text" :placeholder "Suite 400" :field ::entity/address2 + :event ::events/change :subscription editing-vendor}]]]] [horizontal-field @@ -141,6 +108,7 @@ [:input.input.is-expanded {:type "text" :placeholder "Cupertino" :field ::entity/city + :event ::events/change :subscription editing-vendor}]]] [:div.control [:p.help "State"] @@ -148,12 +116,14 @@ [:input.input {:type "text" :placeholder "CA" :field ::entity/state + :event ::events/change :subscription editing-vendor}]]] [:div.control [:p.help "Zip"] [bind-field [:input.input {:type "text" :field ::entity/zip + :event ::events/change :subscription editing-vendor :placeholder "95014"}]]]] @@ -164,6 +134,7 @@ [bind-field [:input.input.is-expanded {:type "text" :field ::entity/primary-contact + :event ::events/change :subscription editing-vendor}]] [:span.icon.is-small.is-left [:i.fa.fa-user]]] @@ -174,12 +145,14 @@ [bind-field [:input.input {:type "email" :field ::entity/primary-email + :event ::events/change :subscription editing-vendor}]]] [:div.control.has-icons-left [bind-field [:input.input {:type "phone" :field ::entity/primary-phone + :event ::events/change :subscription editing-vendor}]] [:span.icon.is-small.is-left [:i.fa.fa-phone]]]] @@ -190,6 +163,7 @@ [bind-field [:input.input.is-expanded {:type "text" :field ::entity/secondary-contact + :event ::events/change :subscription editing-vendor}]] [:span.icon.is-small.is-left [:i.fa.fa-user]]] @@ -199,11 +173,13 @@ [bind-field [:input.input {:type "email" :field ::entity/secondary-email + :event ::events/change :subscription editing-vendor}]]] [:div.control.has-icons-left [bind-field [:input.input {:type "phone" :field ::entity/secondary-phone + :event ::events/change :subscription editing-vendor}]] [:span.icon.is-small.is-left [:i.fa.fa-phone]]]] @@ -217,6 +193,7 @@ :name "schedule" :value "Weekly" :field ::entity/invoice-reminder-schedule + :event ::events/change :subscription editing-vendor}]] " Send weekly"] @@ -226,6 +203,7 @@ :name "schedule" :value "Never" :field ::entity/invoice-reminder-schedule + :event ::events/change :subscription editing-vendor}]] " Never"]]] diff --git a/src/cljs/auto_ap/views/pages/import_invoices.cljs b/src/cljs/auto_ap/views/pages/import_invoices.cljs index 4085861f..e561d357 100644 --- a/src/cljs/auto_ap/views/pages/import_invoices.cljs +++ b/src/cljs/auto_ap/views/pages/import_invoices.cljs @@ -3,6 +3,7 @@ [reagent.core :as reagent] [auto-ap.events :as events] [auto-ap.subs :as subs] + [auto-ap.entities.companies :as company] [cljsjs.dropzone :as dropzone] [cljs.reader :as edn])) (def dropzone @@ -25,7 +26,7 @@ :paramName "file" :headers {"Authorization" (str "Token " @token)} :url (str "/api/invoices/upload" - (when-let [company-name (-> @company :id)] + (when-let [company-name (-> @company ::company/id)] (str "?company=" company-name))) :previewsContainer "#dz-hidden" :previewTemplate "
"})))}))) diff --git a/src/cljs/auto_ap/views/utils.cljs b/src/cljs/auto_ap/views/utils.cljs index 80d9d875..1937aa9f 100644 --- a/src/cljs/auto_ap/views/utils.cljs +++ b/src/cljs/auto_ap/views/utils.cljs @@ -1,5 +1,6 @@ (ns auto-ap.views.utils (:require [re-frame.core :as re-frame] + [clojure.spec.alpha :as s] [cljs-time.format :as format])) (defn active-when= [active-page candidate] @@ -28,3 +29,42 @@ (defn date-time->str [d] (format/unparse pretty-long d)) + + + +(defmulti do-bind (fn [_ {:keys [type]}] + type)) + + +(defmethod do-bind "radio" [dom {:keys [field subscription event class value] :as keys} & rest] + (let [keys (assoc keys + :on-change (dispatch-value-change [event [field]]) + :checked (= (field subscription) value) + :class (str class + (when (not (s/valid? field (field subscription))) + " is-danger"))) + keys (dissoc keys :field :subscription :event)] + (vec (concat [dom keys] rest)))) + + +(defmethod do-bind :default [dom {:keys [field event subscription class] :as keys} & rest] + (let [keys (assoc keys + :on-change (dispatch-value-change [event [field]]) + :value (field subscription) + :class (str class + (when (not (s/valid? field (field subscription))) + " is-danger"))) + keys (dissoc keys :field :subscription :event)] + (vec (concat [dom keys] rest)))) + +(defn bind-field [all] + (apply do-bind all)) + +(defn horizontal-field [label & controls] + [:div.field.is-horizontal + [:div.field-label + label + ] + (into + [:div.field-body] + (map (fn [c] [:div.field c]) controls))])