diff --git a/Dockerfile b/Dockerfile index b8e1d7cd..810a6689 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,5 +2,5 @@ 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 +COPY target/auto-ap.jar /usr/local/ +CMD java -jar /usr/local/auto-ap.jar diff --git a/project.clj b/project.clj index 3333531b..a074ae06 100644 --- a/project.clj +++ b/project.clj @@ -7,7 +7,8 @@ [reagent "0.7.0"] [re-frame "0.10.2"] [compojure "1.6.0"] - [secretary "1.2.3"] + [kibu/pushy "0.3.8"] + [bidi "2.1.2"] [ring/ring-defaults "0.2.1"] [ring/ring-json "0.4.0"] [ring "1.4.0"] @@ -24,7 +25,8 @@ :aliases {"dev" ["do" "clean" ["pdo" ["figwheel" "dev"]]] "build" ["do" "clean" - ["cljsbuild" "once" "min"]]} + ["cljsbuild" "once" "min"] + ["uberjar"]]} :profiles {:dev @@ -44,7 +46,7 @@ :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" + :asset-path "/js/compiled/out" :source-map-timestamp true :preloads [devtools.preload] :external-config {:devtools/config {:features-to-install :all}} @@ -55,7 +57,7 @@ :jar true :compiler {:main auto-ap.core :output-to "resources/public/js/compiled/app.js" - :optimizations :advanced + :optimizations :whitespace :closure-defines {goog.DEBUG false} :pretty-print false}} @@ -68,6 +70,5 @@ :uberjar-name "auto-ap.jar" - #_#_:prep-tasks [["cljsbuild" "once" "min"]"compile"]) + :prep-tasks [["cljsbuild" "once" "min"] "compile"]) -;; diff --git a/resources/public/index.html b/resources/public/index.html index e5211426..8f4cfa1f 100644 --- a/resources/public/index.html +++ b/resources/public/index.html @@ -325,7 +325,7 @@ */ - + diff --git a/src/clj/auto_ap/handler.clj b/src/clj/auto_ap/handler.clj index 3ceff4bb..201686fe 100644 --- a/src/clj/auto_ap/handler.clj +++ b/src/clj/auto_ap/handler.clj @@ -9,7 +9,6 @@ [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} @@ -22,6 +21,9 @@ "date" date "total" total}))) (route/resources "/") + (routes (ANY "*" [] (response/resource-response "index.html" {:root "public"}))) + + (route/not-found "Not Found")) #_(defroutes routes diff --git a/src/cljs/auto_ap/core.cljs b/src/cljs/auto_ap/core.cljs index cc002ea1..e8e960de 100644 --- a/src/cljs/auto_ap/core.cljs +++ b/src/cljs/auto_ap/core.cljs @@ -3,11 +3,20 @@ [re-frame.core :as re-frame] [auto-ap.events :as events] [auto-ap.views :as views] - [auto-ap.config :as config])) + [auto-ap.config :as config] + [auto-ap.routes :as routes] + [pushy.core :as pushy] + [bidi.bidi :as bidi])) +(defn- parse-url [url] + (.log js/console "test" (bidi/match-route routes/routes url)) + (bidi/match-route routes/routes url)) + +(defn- dispatch-route [matched-route] + (re-frame/dispatch [::events/set-active-page (:handler matched-route)])) (defn dev-setup [] - (when config/debug? + (when true (enable-console-print!) (println "dev mode"))) @@ -17,7 +26,9 @@ (.getElementById js/document "app"))) (defn ^:export init [] - (re-frame/dispatch-sync [::events/initialize-db]) (dev-setup) - + (pushy/start! (pushy/pushy dispatch-route parse-url)) + (re-frame/dispatch-sync [::events/initialize-db]) (mount-root)) + + diff --git a/src/cljs/auto_ap/db.cljs b/src/cljs/auto_ap/db.cljs index 364ecb0e..7d6c7404 100644 --- a/src/cljs/auto_ap/db.cljs +++ b/src/cljs/auto_ap/db.cljs @@ -2,4 +2,5 @@ (def default-db {:company {:name "Campbell brewery"} - :invoices #{}}) + :invoices #{} + }) diff --git a/src/cljs/auto_ap/events.cljs b/src/cljs/auto_ap/events.cljs index 10e8a5d1..9d813885 100644 --- a/src/cljs/auto_ap/events.cljs +++ b/src/cljs/auto_ap/events.cljs @@ -1,11 +1,19 @@ (ns auto-ap.events (:require [re-frame.core :as re-frame] - [auto-ap.db :as db])) + [auto-ap.db :as db] + [auto-ap.routes :as routes] + [bidi.bidi :as bidi])) (re-frame/reg-event-db ::initialize-db (fn [_ _] - db/default-db)) + (assoc db/default-db + :active-page (:handler (bidi/match-route routes/routes (.. js/window -location -pathname)))))) + +(re-frame/reg-event-db + ::set-active-page + (fn [db [_ active-page]] + (assoc db :active-page active-page))) (re-frame/reg-event-db ::imported-invoices diff --git a/src/cljs/auto_ap/routes.cljs b/src/cljs/auto_ap/routes.cljs new file mode 100644 index 00000000..f73363f6 --- /dev/null +++ b/src/cljs/auto_ap/routes.cljs @@ -0,0 +1,8 @@ +(ns auto-ap.routes + (:require [bidi.bidi :as bidi])) + +(def routes ["/" {"" :index + "invoices/" {"" :invoices + "import" :import-invoices + "unpaid" :unpaid-invoices + "paid" :paid-invoices}}]) diff --git a/src/cljs/auto_ap/subs.cljs b/src/cljs/auto_ap/subs.cljs index b312f925..8c545008 100644 --- a/src/cljs/auto_ap/subs.cljs +++ b/src/cljs/auto_ap/subs.cljs @@ -6,6 +6,10 @@ (fn [db] (:name (:company db)))) +(re-frame/reg-sub + ::active-page + (fn [db] + (:active-page db))) (re-frame/reg-sub ::invoices diff --git a/src/cljs/auto_ap/views.cljs b/src/cljs/auto_ap/views.cljs index 4585a35d..24c006ad 100644 --- a/src/cljs/auto_ap/views.cljs +++ b/src/cljs/auto_ap/views.cljs @@ -2,7 +2,12 @@ (:require [re-frame.core :as re-frame] [reagent.core :as reagent] [auto-ap.subs :as subs] - [auto-ap.events :as events])) + [auto-ap.events :as events] + [auto-ap.routes :as routes] + [bidi.bidi :as bidi])) + +(defn active-when= [active-page candidate] + (when (= active-page candidate) " active")) (def dropzone (with-meta @@ -17,90 +22,132 @@ "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"}))))}) - ) + (js/Dropzone. (reagent/dom-node this) + (clj->js {:init (fn [] + (.on (js-this) "success" (fn [_ files] + (re-frame/dispatch [::events/imported-invoices (js->clj (.parse js/JSON files))]) + ))) + :url "/pdf-upload"})))})) + +(defmulti active-page identity) + +(defmethod active-page :index [] + [:div {:class "inbox-messages"} + [:div.hero + [:div.hero-body + [:div.container + [:h1.title "Dashboard"] + [:h2.subtitle "To get started, " + [:a {:href (bidi/path-for routes/routes :import-invoices)} "Import some invoices"]]]]]]) + +(defmethod active-page :unpaid-invoices [] + [:div {:class "inbox-messages"} + [:h1.title "Unpaid invoices"]]) + +(defmethod active-page :paid-invoices [] + [:div {:class "inbox-messages"} + [:h1.title "Paid invoices"]]) + +(defmethod active-page :invoices [] + [:div {:class "inbox-messages"} + [:h1.title "All invoices"]]) + +(defmethod active-page :import-invoices [] + (let [invoices (re-frame/subscribe [::subs/invoices])] + [:div {:class "inbox-messages"} + [:h1.title "Upload invoices"] + [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]])]]]]])) (defn main-panel [] (let [name (re-frame/subscribe [::subs/name]) - invoices (re-frame/subscribe [::subs/invoices])] - (println name) + ap (re-frame/subscribe [::subs/active-page])] [: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"}]]]]]]])) + [: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 menu hero is-fullheight"} + [:div.main + [:p.menu-label "General"] + [:p.menu-item + [:a {:href (bidi/path-for routes/routes :index) , :class (str "item" (active-when= @ap :index))} + [:span {:class "icon"} + [:i {:class "fa fa-tachometer"}]] + [:span {:class "name"} "Dashboard"]]] + + [:p.menu-label "Accounts Payable"] + [:ul.menu-list + [:li.menu-item + [:a {:href (bidi/path-for routes/routes :import-invoices) , :class (str "item" (active-when= @ap :import-invoices))} + [:span {:class "icon"} + [:i {:class "fa fa-star-o"}]] + + [:span {:class "name"} "Upload Invoices"]]] + [:li.menu-item + [:a {:href (bidi/path-for routes/routes :unpaid-invoices), :class (str "item" (active-when= @ap :unpaid-invoices))} + [:span {:class "icon"} + [:i {:class "fa fa-envelope-o"}]] + [:span {:class "name"} "Unpaid Invoices"]] + ] + [:li.menu-item + [:a {:href (bidi/path-for routes/routes :paid-invoices), :class (str "item" (active-when= @ap :paid-invoices))} + [:span {:class "icon"} + [:i {:class "fa fa-envelope-o"}]] + [:span {:class "name"} "Paid Invoices"]] + ] + [:ul ]]] + + [: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 "column messages hero is-fullheight", :id "message-feed"} + [active-page @ap]]] + [: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"}]]]]]]]))