From 5b578c11e8675bc7f3c9887bd64ef5cfe90ceacd Mon Sep 17 00:00:00 2001 From: Bryce Covert Date: Thu, 7 Dec 2017 11:23:57 -0800 Subject: [PATCH] Now a simple re-frame app --- .gitignore | 2 + Dockerfile | 9 +- project.clj | 68 +++++- resources/public/index.html | 350 +++++++++++++++++++++++++++--- src/{ => clj}/auto_ap/handler.clj | 13 +- src/{ => clj}/auto_ap/parse.clj | 0 src/clj/auto_ap/server.clj | 9 + src/cljs/auto_ap/config.cljs | 4 + src/cljs/auto_ap/core.cljs | 23 ++ src/cljs/auto_ap/db.cljs | 5 + src/cljs/auto_ap/events.cljs | 13 ++ src/cljs/auto_ap/subs.cljs | 13 ++ src/cljs/auto_ap/views.cljs | 149 +++++++++++++ 13 files changed, 618 insertions(+), 40 deletions(-) rename src/{ => clj}/auto_ap/handler.clj (72%) rename src/{ => clj}/auto_ap/parse.clj (100%) create mode 100644 src/clj/auto_ap/server.clj create mode 100644 src/cljs/auto_ap/config.cljs create mode 100644 src/cljs/auto_ap/core.cljs create mode 100644 src/cljs/auto_ap/db.cljs create mode 100644 src/cljs/auto_ap/events.cljs create mode 100644 src/cljs/auto_ap/subs.cljs create mode 100644 src/cljs/auto_ap/views.cljs diff --git a/.gitignore b/.gitignore index 22d6a481..ae81b840 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ pom.xml.asc *.class /.lein-* /.nrepl-port +/resources/public/js/compiled +*.log diff --git a/Dockerfile b/Dockerfile index 59f3cefd..b8e1d7cd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,3 +1,6 @@ -FROM tomcat:9.0-jre8-alpine -RUN 'apk add poppler-utils' -COPY target/auto-ap.war /usr/local/tomcat +FROM openjdk:8-jre-alpine +RUN apk update +RUN apk add poppler +RUN apk add poppler-utils +COPY target/auto-ap-0.1.0-SNAPSHOT-standalone.jar /usr/local/ +CMD java -jar /usr/local/auto-ap-0.1.0-SNAPSHOT-standalone.jar diff --git a/project.clj b/project.clj index 104ad572..3333531b 100644 --- a/project.clj +++ b/project.clj @@ -3,11 +3,71 @@ :url "http://example.com/FIXME" :min-lein-version "2.0.0" :dependencies [[org.clojure/clojure "1.8.0"] + [org.clojure/clojurescript "1.9.908"] + [reagent "0.7.0"] + [re-frame "0.10.2"] [compojure "1.6.0"] + [secretary "1.2.3"] [ring/ring-defaults "0.2.1"] - [ring/ring-json "0.4.0"]] - :plugins [[lein-ring "0.9.7"]] + [ring/ring-json "0.4.0"] + [ring "1.4.0"] + [yogthos/config "0.8"]] + :plugins [[lein-ring "0.9.7"] + [lein-cljsbuild "1.1.5"]] + :clean-targets ^{:protect false} ["resources/public/js/compiled" "target"] :ring {:handler auto-ap.handler/app} + :source-paths ["src/clj"] + + :figwheel {:css-dirs ["resources/public/css"] + :ring-handler auto-ap.handler/app} + + :aliases {"dev" ["do" "clean" + ["pdo" ["figwheel" "dev"]]] + "build" ["do" "clean" + ["cljsbuild" "once" "min"]]} + :profiles - {:dev {:dependencies [[javax.servlet/servlet-api "2.5"] - [ring/ring-mock "0.3.0"]]}}) + {:dev + {:dependencies [[binaryage/devtools "0.9.4"] + [javax.servlet/servlet-api "2.5"] + [figwheel-sidecar "0.5.13"] + [com.cemerick/piggieback "0.2.2"]] + + :plugins [[lein-figwheel "0.5.13"] + [lein-pdo "0.1.1"]]}} + + :cljsbuild + {:builds + [{:id "dev" + :source-paths ["src/cljs"] + :figwheel {:on-jsload "auto-ap.core/mount-root"} + :compiler {:main auto-ap.core + :output-to "resources/public/js/compiled/app.js" + :output-dir "resources/public/js/compiled/out" + :asset-path "js/compiled/out" + :source-map-timestamp true + :preloads [devtools.preload] + :external-config {:devtools/config {:features-to-install :all}} + }} + + {:id "min" + :source-paths ["src/cljs"] + :jar true + :compiler {:main auto-ap.core + :output-to "resources/public/js/compiled/app.js" + :optimizations :advanced + :closure-defines {goog.DEBUG false} + :pretty-print false}} + + + ]} + + :main auto-ap.server + + :aot [auto-ap.server] + + :uberjar-name "auto-ap.jar" + + #_#_:prep-tasks [["cljsbuild" "once" "min"]"compile"]) + +;; diff --git a/resources/public/index.html b/resources/public/index.html index c1b32626..e5211426 100644 --- a/resources/public/index.html +++ b/resources/public/index.html @@ -1,37 +1,331 @@ - - - - - - - - - - -
-

Invoice Parsing Demo

-
-

Drop invoice pdfs here

- -
-

Found invoices:

- - + + + + + + + Auto AP + + + + + + + + +
+ +
+
+

+
+
+ + + + + + + + - +
+ + + diff --git a/src/auto_ap/handler.clj b/src/clj/auto_ap/handler.clj similarity index 72% rename from src/auto_ap/handler.clj rename to src/clj/auto_ap/handler.clj index c3779792..3ceff4bb 100644 --- a/src/auto_ap/handler.clj +++ b/src/clj/auto_ap/handler.clj @@ -9,15 +9,18 @@ [ring.middleware.json :refer [wrap-json-response]])) (defroutes app-routes + (GET "/hi" [] "hello") (GET "/" [] (response/resource-response "index.html" {:root "public"})) (POST "/pdf-upload" {{ files "file"} :params :as params} (let [{:keys [filename tempfile]} (second files)] - (io/copy tempfile (io/file "resources" "public" filename)) - (for [{:keys [total date invoice-number customer-identifier]} (parse/parse-file (str "resources/public/" filename))] - (do - (println (str "An invoice #" invoice-number " on " date " for " total)) - (str "An invoice for customer " customer-identifier " #" invoice-number " on " date " for " total ))))) + (println tempfile) + #_(io/copy tempfile (io/file "resources" "public" filename)) + (for [{:keys [total date invoice-number customer-identifier]} (parse/parse-file (.getPath tempfile))] + {"customer-identifier" customer-identifier + "invoice-number" invoice-number + "date" date + "total" total}))) (route/resources "/") (route/not-found "Not Found")) diff --git a/src/auto_ap/parse.clj b/src/clj/auto_ap/parse.clj similarity index 100% rename from src/auto_ap/parse.clj rename to src/clj/auto_ap/parse.clj diff --git a/src/clj/auto_ap/server.clj b/src/clj/auto_ap/server.clj new file mode 100644 index 00000000..eccebb99 --- /dev/null +++ b/src/clj/auto_ap/server.clj @@ -0,0 +1,9 @@ +(ns auto-ap.server + (:require [auto-ap.handler :refer [app]] + [config.core :refer [env]] + [ring.adapter.jetty :refer [run-jetty]]) + (:gen-class)) + + (defn -main [& args] + (let [port (Integer/parseInt (or (env :port) "3000"))] + (run-jetty app {:port port :join? false}))) diff --git a/src/cljs/auto_ap/config.cljs b/src/cljs/auto_ap/config.cljs new file mode 100644 index 00000000..82f3cdf7 --- /dev/null +++ b/src/cljs/auto_ap/config.cljs @@ -0,0 +1,4 @@ +(ns auto-ap.config) + +(def debug? + ^boolean goog.DEBUG) diff --git a/src/cljs/auto_ap/core.cljs b/src/cljs/auto_ap/core.cljs new file mode 100644 index 00000000..cc002ea1 --- /dev/null +++ b/src/cljs/auto_ap/core.cljs @@ -0,0 +1,23 @@ +(ns auto-ap.core + (:require [reagent.core :as reagent] + [re-frame.core :as re-frame] + [auto-ap.events :as events] + [auto-ap.views :as views] + [auto-ap.config :as config])) + + +(defn dev-setup [] + (when config/debug? + (enable-console-print!) + (println "dev mode"))) + +(defn mount-root [] + (re-frame/clear-subscription-cache!) + (reagent/render [views/main-panel] + (.getElementById js/document "app"))) + +(defn ^:export init [] + (re-frame/dispatch-sync [::events/initialize-db]) + (dev-setup) + + (mount-root)) diff --git a/src/cljs/auto_ap/db.cljs b/src/cljs/auto_ap/db.cljs new file mode 100644 index 00000000..364ecb0e --- /dev/null +++ b/src/cljs/auto_ap/db.cljs @@ -0,0 +1,5 @@ +(ns auto-ap.db) + +(def default-db + {:company {:name "Campbell brewery"} + :invoices #{}}) diff --git a/src/cljs/auto_ap/events.cljs b/src/cljs/auto_ap/events.cljs new file mode 100644 index 00000000..10e8a5d1 --- /dev/null +++ b/src/cljs/auto_ap/events.cljs @@ -0,0 +1,13 @@ +(ns auto-ap.events + (:require [re-frame.core :as re-frame] + [auto-ap.db :as db])) + +(re-frame/reg-event-db + ::initialize-db + (fn [_ _] + db/default-db)) + +(re-frame/reg-event-db + ::imported-invoices + (fn [db [_ new-invoices]] + (update-in db [:invoices] into new-invoices))) diff --git a/src/cljs/auto_ap/subs.cljs b/src/cljs/auto_ap/subs.cljs new file mode 100644 index 00000000..b312f925 --- /dev/null +++ b/src/cljs/auto_ap/subs.cljs @@ -0,0 +1,13 @@ +(ns auto-ap.subs + (:require [re-frame.core :as re-frame])) + +(re-frame/reg-sub + ::name + (fn [db] + (:name (:company db)))) + + +(re-frame/reg-sub + ::invoices + (fn [db] + (:invoices db))) diff --git a/src/cljs/auto_ap/views.cljs b/src/cljs/auto_ap/views.cljs new file mode 100644 index 00000000..4585a35d --- /dev/null +++ b/src/cljs/auto_ap/views.cljs @@ -0,0 +1,149 @@ +(ns auto-ap.views + (:require [re-frame.core :as re-frame] + [reagent.core :as reagent] + [auto-ap.subs :as subs] + [auto-ap.events :as events])) + +(def dropzone + (with-meta + (fn [] + [:form {:action "/pdf-upload" :class ".dropzone"} + [:div {:class "card"} + [:div {:class "card-header"} + [:span {:class "card-header-title"} "Upload Invoices"]] + [:div {:class "card-content"} + [:span {:class "icon"} + [:i {:class "fa fa-cloud-download"}]] + "Drop invoice pdfs here" + [:input {:type "file", :name "file", :style {:display "none"}}]]]]) + {:component-did-mount (fn [this] + (-> (js/$ (reagent/dom-node this)) + (.dropzone (clj->js {:init (fn [] + (this-as t + (.on t "success" (fn [_ files] + (re-frame/dispatch [::events/imported-invoices (js->clj (.parse js/JSON files))]) + )))) + :url "/pdf-upload"}))))}) + ) + +(defn main-panel [] + (let [name (re-frame/subscribe [::subs/name]) + invoices (re-frame/subscribe [::subs/invoices])] + (println name) + [:div + [:nav {:class "navbar has-shadow"} + [:div {:class "container"} + [:div {:class "navbar-brand"} + [:a {:class "navbar-item", :href "../"} + [:h1 (str "Auto-ap - " @name)]] + [:div {:class "navbar-burger burger", :data-target "navMenu"} + [:span] + [:span] + [:span]]] + [:div {:id "navMenu", :class "navbar-menu"} + [:div {:class "navbar-end"} + [:div {:class "navbar-item has-dropdown is-active"} + [:a {:class "navbar-link login"} ] + [:div {:class "navbar-dropdown", :style {:display "none"}} + [:a {:class "navbar-item"} ] + [:a {:class "navbar-item"} ] + [:a {:class "navbar-item"} ] + [:hr {:class "navbar-divider"}] + [:div {:class "navbar-item"} ]]]]]]] + [:div {:class "columns", :id "mail-app"} + [:aside {:class "column is-narrow aside hero is-fullheight"} + [:div.left-nav + [:div {:class "compose has-text-centered"} + [:a {:class "button is-danger is-block is-bold"} + [:span {:class "compose"} "New Invoice"]]] + [:div {:class "main"} + [:a {:href "#", :class "item active"} + [:span {:class "icon"} + [:i {:class "fa fa-inbox"}]] + [:span {:class "name"} "Upload\n Invoices"]] + [:a {:href "#", :class "item"} + [:span {:class "icon"} + [:i {:class "fa fa-star"}]] + [:span {:class "name"} "Unpaid Invoices"]] + [:a {:href "#", :class "item"} + [:span {:class "icon"} + [:i {:class "fa fa-envelope-o"}]] + [:span {:class "name"} "Paid Invoices"]]]]] + [:div {:class "column messages hero is-fullheight", :id "message-feed"} + [:div {:class "inbox-messages"} + [dropzone] + + [:div {:class "section"}] + [:div {:class "card found-invoices", :style {:display (if (seq @invoices) "block" "none")}} + [:div {:class "card-header"} + [:span {:class "card-header-title"} "Found Invoices"]] + [:div {:class "card-content"} + [:table {:class "table", :style {:width "100%"}} + [:thead + [:tr + [:th "Customer"] + [:th "Invoice #"] + [:th "Date"] + [:th "Amount"]]] + [:tbody (for [{:strs [customer-identifier invoice-number date total]} @invoices] + ^{:key (str customer-identifier "-" invoice-number)} + [:tr + [:td customer-identifier] + [:td invoice-number] + [:td date] + [:td total]])]]]]]]] + [:footer {:class "footer"} + [:div {:class "container"} + [:div {:class "content has-text-centered"} + [:p + [:strong "Auto-AP"]"by " + [:a {:href "https://github.com/"} "Integreat"]"."] + [:p + [:a {:class "icon", :href "https://github.com/dansup/bulma-templates"} + [:i {:class "fa fa-github"}]]]]]]])) + + + +;; +;; +