a lot of progress on typeaheads, etc.
This commit is contained in:
@@ -4,4 +4,4 @@
|
|||||||
4) Permissions per location
|
4) Permissions per location
|
||||||
5) List of payments
|
5) List of payments
|
||||||
6) add payment
|
6) add payment
|
||||||
7) add invoice
|
7) add invoice - in progress
|
||||||
|
|||||||
@@ -92,8 +92,6 @@
|
|||||||
:jar true
|
:jar true
|
||||||
:compiler {:main auto-ap.core
|
:compiler {:main auto-ap.core
|
||||||
:output-to "resources/public/js/compiled/app.js"
|
:output-to "resources/public/js/compiled/app.js"
|
||||||
:npm-deps {:react-autocomplete "1.8.1"}
|
|
||||||
:install-deps true
|
|
||||||
:optimizations :advanced
|
:optimizations :advanced
|
||||||
:closure-defines {goog.DEBUG false}
|
:closure-defines {goog.DEBUG false}
|
||||||
:pretty-print false}}
|
:pretty-print false}}
|
||||||
|
|||||||
18
resources/public/css/font.min.css
vendored
Normal file
18
resources/public/css/font.min.css
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: 'Open Sans';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 300;
|
||||||
|
src: local('Open Sans Light'), local('OpenSans-Light'), url(/ttf/300.ttf) format('truetype');
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Open Sans';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
src: local('Open Sans Regular'), local('OpenSans-Regular'), url(/ttf/400.ttf) format('truetype');
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Open Sans';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 700;
|
||||||
|
src: local('Open Sans Bold'), local('OpenSans-Bold'), url(/ttf/700.ttf) format('truetype');
|
||||||
|
}
|
||||||
4
resources/public/css/fontawesome.min.css
vendored
Normal file
4
resources/public/css/fontawesome.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -4,11 +4,10 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>Auto AP</title>
|
<title>Integreat</title>
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" integrity="sha256-eZrrJcwDc/3uDhsdt61sL2oOBY362qM3lon1gyExkL0=" crossorigin="anonymous" />
|
<link rel="stylesheet" href="/css/fontawesome.min.css" integrity="sha256-eZrrJcwDc/3uDhsdt61sL2oOBY362qM3lon1gyExkL0=" crossorigin="anonymous" />
|
||||||
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,700" rel="stylesheet">
|
<link href="/css/font.min.css" rel="stylesheet">
|
||||||
<!-- Bulma Version 0.6.0 -->
|
<link rel="stylesheet" href="/css/bulma.min.css" integrity="sha256-HEtF7HLJZSC3Le1HcsWbz1hDYFPZCqDhZa9QsCgVUdw=" crossorigin="anonymous" />
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.6.0/css/bulma.min.css" integrity="sha256-HEtF7HLJZSC3Le1HcsWbz1hDYFPZCqDhZa9QsCgVUdw=" crossorigin="anonymous" />
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
form.dz { border: 2px dashed lightgray;}
|
form.dz { border: 2px dashed lightgray;}
|
||||||
|
|||||||
BIN
resources/public/ttf/300.ttf
Normal file
BIN
resources/public/ttf/300.ttf
Normal file
Binary file not shown.
BIN
resources/public/ttf/400.ttf
Normal file
BIN
resources/public/ttf/400.ttf
Normal file
Binary file not shown.
BIN
resources/public/ttf/700.ttf
Normal file
BIN
resources/public/ttf/700.ttf
Normal file
Binary file not shown.
@@ -126,15 +126,17 @@
|
|||||||
(assoc (base-graphql args) :select [:%count.*])))))
|
(assoc (base-graphql args) :select [:%count.*])))))
|
||||||
|
|
||||||
(defn import [parsed-invoices companies vendors]
|
(defn import [parsed-invoices companies vendors]
|
||||||
(insert-multi!
|
(->> (insert-multi!
|
||||||
(for [{:keys [total date invoice-number customer-identifier vendor-code] :as row} parsed-invoices]
|
(for [{:keys [total date invoice-number customer-identifier vendor-code] :as row} parsed-invoices]
|
||||||
(do
|
(do
|
||||||
(dissoc (assoc row
|
(dissoc (assoc row
|
||||||
:company-id (:id (parse/best-match companies customer-identifier))
|
:company-id (:id (parse/best-match companies customer-identifier))
|
||||||
:vendor-id (:id (first (filter #(= (:code %) vendor-code) vendors)))
|
:vendor-id (:id (first (filter #(= (:code %) vendor-code) vendors)))
|
||||||
:imported false
|
:imported false
|
||||||
:potential-duplicate false)
|
:potential-duplicate false)
|
||||||
:vendor-code)))))
|
:vendor-code))))
|
||||||
|
|
||||||
|
(map db->clj)))
|
||||||
|
|
||||||
(defn apply-payment [invoice-id amount]
|
(defn apply-payment [invoice-id amount]
|
||||||
(j/db-do-prepared (get-conn)
|
(j/db-do-prepared (get-conn)
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
[auto-ap.db.checks :as checks]
|
[auto-ap.db.checks :as checks]
|
||||||
[auto-ap.routes.checks :as rchecks]
|
[auto-ap.routes.checks :as rchecks]
|
||||||
[auto-ap.graphql.users :as gq-users]
|
[auto-ap.graphql.users :as gq-users]
|
||||||
|
[auto-ap.graphql.invoices :as gq-invoices]
|
||||||
[auto-ap.db.reminders :as reminders]
|
[auto-ap.db.reminders :as reminders]
|
||||||
[auto-ap.db.invoices-checks :as invoices-checks]
|
[auto-ap.db.invoices-checks :as invoices-checks]
|
||||||
[auto-ap.db.utils :as utils]
|
[auto-ap.db.utils :as utils]
|
||||||
@@ -141,7 +142,14 @@
|
|||||||
:name {:type 'String}
|
:name {:type 'String}
|
||||||
:role {:type 'String}
|
:role {:type 'String}
|
||||||
:companies {:type '(list Int)}}}
|
:companies {:type '(list Int)}}}
|
||||||
}
|
|
||||||
|
:add_invoice
|
||||||
|
{:fields {:id {:type 'Int}
|
||||||
|
:invoice_number {:type 'String}
|
||||||
|
:date {:type 'String}
|
||||||
|
:company_id {:type 'Int}
|
||||||
|
:vendor_id {:type 'Int}
|
||||||
|
:total {:type 'Float}}}}
|
||||||
|
|
||||||
:mutations
|
:mutations
|
||||||
{:print_checks {:type :check_result
|
{:print_checks {:type :check_result
|
||||||
@@ -151,7 +159,11 @@
|
|||||||
:resolve :mutation/print-checks}
|
:resolve :mutation/print-checks}
|
||||||
:edit_user {:type :user
|
:edit_user {:type :user
|
||||||
:args {:edit_user {:type :edit_user}}
|
:args {:edit_user {:type :edit_user}}
|
||||||
:resolve :mutation/edit-user}}})
|
:resolve :mutation/edit-user}
|
||||||
|
|
||||||
|
:add_invoice {:type :invoice
|
||||||
|
:args {:invoice {:type :add_invoice}}
|
||||||
|
:resolve :mutation/add-invoice}}})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -281,10 +293,7 @@
|
|||||||
(:company_id args)
|
(:company_id args)
|
||||||
(:bank_account_id args))))
|
(:bank_account_id args))))
|
||||||
|
|
||||||
(defn edit-user [context args value]
|
|
||||||
(Thread/sleep 1000)
|
|
||||||
(->graphql
|
|
||||||
(gq-users/edit-user (:edit_user args))))
|
|
||||||
|
|
||||||
(def schema
|
(def schema
|
||||||
(-> integreat-schema
|
(-> integreat-schema
|
||||||
@@ -298,7 +307,8 @@
|
|||||||
:get-user get-user
|
:get-user get-user
|
||||||
:get-user-companies get-user-companies
|
:get-user-companies get-user-companies
|
||||||
:mutation/print-checks print-checks
|
:mutation/print-checks print-checks
|
||||||
:mutation/edit-user edit-user
|
:mutation/edit-user gq-users/edit-user
|
||||||
|
:mutation/add-invoice gq-invoices/add-invoice
|
||||||
:get-vendor get-vendor})
|
:get-vendor get-vendor})
|
||||||
schema/compile))
|
schema/compile))
|
||||||
|
|
||||||
|
|||||||
17
src/clj/auto_ap/graphql/invoices.clj
Normal file
17
src/clj/auto_ap/graphql/invoices.clj
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
(ns auto-ap.graphql.invoices
|
||||||
|
(:require [auto-ap.graphql.utils :refer [->graphql]]
|
||||||
|
[auto-ap.db.invoices :as invoices]
|
||||||
|
[auto-ap.time :refer [parse normal-date]]))
|
||||||
|
|
||||||
|
(defn add-invoice [context {{:keys [total invoice_number company_id vendor_id date] :as in} :invoice} value]
|
||||||
|
|
||||||
|
(-> (invoices/insert-multi! [{:invoice-number invoice_number
|
||||||
|
:company-id company_id
|
||||||
|
:vendor-id vendor_id
|
||||||
|
:total total
|
||||||
|
:outstanding-balance total
|
||||||
|
:status "unpaid"
|
||||||
|
:imported true
|
||||||
|
:date (parse date normal-date)}])
|
||||||
|
(first)
|
||||||
|
(->graphql)))
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
(ns auto-ap.graphql.users
|
(ns auto-ap.graphql.users
|
||||||
(:require [auto-ap.db.users :as users]))
|
(:require [auto-ap.db.users :as users]
|
||||||
|
[auto-ap.graphql.utils :refer [->graphql]]))
|
||||||
(defn edit-user [user]
|
|
||||||
(users/update! user)
|
|
||||||
(users/get-by-id (:id user)))
|
|
||||||
|
|
||||||
|
(defn edit-user [context args value]
|
||||||
|
(users/update! (:edit_user args))
|
||||||
|
(->graphql
|
||||||
|
(users/get-by-id (:edit_user args))))
|
||||||
|
|
||||||
|
|||||||
40
src/clj/auto_ap/graphql/utils.clj
Normal file
40
src/clj/auto_ap/graphql/utils.clj
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
(ns auto-ap.graphql.utils
|
||||||
|
(:require [clojure.string :as str]
|
||||||
|
[clojure.walk :as walk]))
|
||||||
|
|
||||||
|
|
||||||
|
(defn snake->kebab [s]
|
||||||
|
(str/replace s #"_" "-"))
|
||||||
|
|
||||||
|
(defn kebab [x]
|
||||||
|
(keyword (snake->kebab (name x))))
|
||||||
|
|
||||||
|
(defn kebab->snake [s]
|
||||||
|
(str/replace s #"-" "_"))
|
||||||
|
|
||||||
|
(defn snake [x]
|
||||||
|
(keyword (kebab->snake (name x))))
|
||||||
|
|
||||||
|
(defn ->graphql [m]
|
||||||
|
(walk/postwalk
|
||||||
|
(fn [node]
|
||||||
|
(cond
|
||||||
|
|
||||||
|
(keyword? node)
|
||||||
|
(snake node)
|
||||||
|
|
||||||
|
:else
|
||||||
|
node))
|
||||||
|
m))
|
||||||
|
|
||||||
|
(defn <-graphql [m]
|
||||||
|
(walk/postwalk
|
||||||
|
(fn [node]
|
||||||
|
(cond
|
||||||
|
|
||||||
|
(keyword? node)
|
||||||
|
(kebab node)
|
||||||
|
|
||||||
|
:else
|
||||||
|
node))
|
||||||
|
m))
|
||||||
@@ -79,11 +79,7 @@
|
|||||||
{:status 200
|
{:status 200
|
||||||
:body (pr-str (invoices/get-pending (query-params "company")))
|
:body (pr-str (invoices/get-pending (query-params "company")))
|
||||||
:headers {"Content-Type" "application/edn"}})
|
:headers {"Content-Type" "application/edn"}})
|
||||||
(POST "/" {:keys [edn-params]}
|
|
||||||
(invoices/insert-multi! (:rows edn-params))
|
|
||||||
{:status 200
|
|
||||||
:body (pr-str (invoices/get-all))
|
|
||||||
:headers {"Content-Type" "application/edn"}})
|
|
||||||
(POST "/approve" {:keys [query-params]}
|
(POST "/approve" {:keys [query-params]}
|
||||||
(invoices/approve)
|
(invoices/approve)
|
||||||
{:status 200
|
{:status 200
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
(ns auto-ap.time
|
(ns auto-ap.time
|
||||||
(:require [clj-time.core :as time]))
|
(:require [clj-time.core :as time]
|
||||||
|
[clj-time.format :as f]))
|
||||||
|
|
||||||
(defn local-now []
|
(defn local-now []
|
||||||
(time/to-time-zone (time/now) (time/time-zone-for-id "America/Los_Angeles")))
|
(time/to-time-zone (time/now) (time/time-zone-for-id "America/Los_Angeles")))
|
||||||
|
|
||||||
|
(def normal-date "MM/dd/yyyy")
|
||||||
|
|
||||||
|
(defn parse [v format]
|
||||||
|
(time/from-time-zone (f/parse (f/formatter format) v)
|
||||||
|
(time/time-zone-for-id "America/Los_Angeles")))
|
||||||
|
|||||||
@@ -28,7 +28,6 @@
|
|||||||
(re-frame/reg-sub
|
(re-frame/reg-sub
|
||||||
::modal-state
|
::modal-state
|
||||||
(fn [db [_ id]]
|
(fn [db [_ id]]
|
||||||
(println "ID" id)
|
|
||||||
(get (:modal-state db) id)))
|
(get (:modal-state db) id)))
|
||||||
|
|
||||||
(re-frame/reg-sub
|
(re-frame/reg-sub
|
||||||
|
|||||||
63
src/cljs/auto_ap/views/components/typeahead.cljs
Normal file
63
src/cljs/auto_ap/views/components/typeahead.cljs
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
(ns auto-ap.views.components.typeahead
|
||||||
|
(:require [reagent.core :as r]
|
||||||
|
[clojure.string :as str]))
|
||||||
|
|
||||||
|
(defn typeahead [{:keys [matches on-change field value]}]
|
||||||
|
(let [text (r/atom (or (second (first (filter #(= (first %) value) matches))) ""))
|
||||||
|
highlighted (r/atom 0)
|
||||||
|
selected (r/atom (first (first (filter #(= (first %) value) matches))))
|
||||||
|
select (fn [[id t]]
|
||||||
|
(reset! selected id)
|
||||||
|
(reset! text t)
|
||||||
|
(println [id t])
|
||||||
|
(when on-change
|
||||||
|
(on-change id)))]
|
||||||
|
(fn [{:keys [matches on-change field value]}]
|
||||||
|
(let [valid-matches (take 5 (for [[[id t :as match] i] (map vector matches (range))
|
||||||
|
:when (str/includes? (.toLowerCase t) (.toLowerCase @text))]
|
||||||
|
match))]
|
||||||
|
[:div.typeahead
|
||||||
|
(if @selected
|
||||||
|
[:div.input
|
||||||
|
[:div.control
|
||||||
|
[:div.tags.has-addons
|
||||||
|
[:span.tag @text]
|
||||||
|
[:a.tag.is-delete {:on-click (fn [] (select [nil ""]))}]]]]
|
||||||
|
|
||||||
|
[:input.input {:type "text"
|
||||||
|
:field [:vendor]
|
||||||
|
:value @text
|
||||||
|
:on-blur (fn [e]
|
||||||
|
(cond @selected
|
||||||
|
nil
|
||||||
|
|
||||||
|
(#{"" nil} @text)
|
||||||
|
nil
|
||||||
|
|
||||||
|
(seq valid-matches)
|
||||||
|
(do (select (first valid-matches))
|
||||||
|
true)
|
||||||
|
|
||||||
|
:else
|
||||||
|
(do (select [nil ""])
|
||||||
|
true)))
|
||||||
|
:on-key-up (fn [e]
|
||||||
|
(if (= 13 (.-keyCode e))
|
||||||
|
(do
|
||||||
|
(select (first valid-matches))
|
||||||
|
false)
|
||||||
|
true))
|
||||||
|
:on-change (fn [e]
|
||||||
|
(reset! highlighted (ffirst valid-matches))
|
||||||
|
(select [nil (.. e -target -value)]))}
|
||||||
|
])
|
||||||
|
(when (and (seq @text)
|
||||||
|
(not @selected)
|
||||||
|
(seq valid-matches))
|
||||||
|
[:div.typeahead-menu
|
||||||
|
[:ul
|
||||||
|
(for [[id t :as match] valid-matches]
|
||||||
|
|
||||||
|
[:li.typeahead-suggestion {:class (if (= id @highlighted)
|
||||||
|
"typeahead-highlighted")
|
||||||
|
:on-mouse-down #(do (println "MATCH" match) (select match))} t])]])]))))
|
||||||
@@ -9,6 +9,7 @@
|
|||||||
[auto-ap.views.pages.check :as check]
|
[auto-ap.views.pages.check :as check]
|
||||||
[auto-ap.views.components.invoice-table :refer [invoice-table] :as invoice-table]
|
[auto-ap.views.components.invoice-table :refer [invoice-table] :as invoice-table]
|
||||||
[auto-ap.views.components.modal :refer [modal action-modal]]
|
[auto-ap.views.components.modal :refer [modal action-modal]]
|
||||||
|
[auto-ap.views.components.typeahead :refer [typeahead]]
|
||||||
[auto-ap.subs :as subs]
|
[auto-ap.subs :as subs]
|
||||||
[auto-ap.events :as events]))
|
[auto-ap.events :as events]))
|
||||||
|
|
||||||
@@ -190,7 +191,29 @@
|
|||||||
(re-frame/reg-event-fx
|
(re-frame/reg-event-fx
|
||||||
::create-invoice
|
::create-invoice
|
||||||
(fn [{:keys [db]} _]
|
(fn [{:keys [db]} _]
|
||||||
{}))
|
{:graphql
|
||||||
|
{:token (-> db :user)
|
||||||
|
:query-obj {:venia/operation {:operation/type :mutation
|
||||||
|
:operation/name "AddInvoice"}
|
||||||
|
|
||||||
|
:venia/queries [{:query/data [:add-invoice
|
||||||
|
{:invoice @(re-frame/subscribe [::new-invoice])}
|
||||||
|
[:id :total :outstanding-balance :date :invoice-number
|
||||||
|
[:company [:id :name]]
|
||||||
|
[:vendor [:id :name]]
|
||||||
|
]]}]}
|
||||||
|
:on-success [::invoice-created]}}))
|
||||||
|
|
||||||
|
(re-frame/reg-event-fx
|
||||||
|
::invoice-created
|
||||||
|
(fn [{:keys [db]} [_ result]]
|
||||||
|
{:dispatch [::events/modal-completed ::new-invoice]
|
||||||
|
:db (-> db
|
||||||
|
(update-in [::invoice-page :invoices]
|
||||||
|
(fn [is]
|
||||||
|
(into [(:add-invoice result)]
|
||||||
|
is)))
|
||||||
|
(dissoc ::new-invoice))}))
|
||||||
|
|
||||||
(defn print-checks-modal []
|
(defn print-checks-modal []
|
||||||
(let [{:keys [checked]} @(re-frame/subscribe [::invoice-page])
|
(let [{:keys [checked]} @(re-frame/subscribe [::invoice-page])
|
||||||
@@ -240,55 +263,7 @@
|
|||||||
:max outstanding-balance
|
:max outstanding-balance
|
||||||
:step "0.01"}]]]]]])]]])))
|
:step "0.01"}]]]]]])]]])))
|
||||||
|
|
||||||
(defn typeahead [{:keys [matches on-change field value]}]
|
|
||||||
(let [text (r/atom (or (second (first (filter #(= (first %) value) matches))) ""))
|
|
||||||
highlighted (r/atom 0)
|
|
||||||
selected (r/atom (first (first (filter #(= (first %) value) matches))))
|
|
||||||
select (fn [[id t]]
|
|
||||||
(reset! selected id)
|
|
||||||
(reset! text t)
|
|
||||||
(println [id t])
|
|
||||||
(when on-change
|
|
||||||
(on-change id)))]
|
|
||||||
(fn [{:keys [matches on-change field value]}]
|
|
||||||
(let [valid-matches (take 5 (for [[[id t :as match] i] (map vector matches (range))
|
|
||||||
:when (str/includes? (.toLowerCase t) (.toLowerCase @text))]
|
|
||||||
match))]
|
|
||||||
[:div.typeahead
|
|
||||||
[:input.input {:type "text"
|
|
||||||
:field [:vendor]
|
|
||||||
:value @text
|
|
||||||
:on-blur (fn [e]
|
|
||||||
(cond @selected
|
|
||||||
nil
|
|
||||||
|
|
||||||
(seq valid-matches)
|
|
||||||
(do (select (first valid-matches))
|
|
||||||
true)
|
|
||||||
|
|
||||||
:else
|
|
||||||
(do (select [nil ""])
|
|
||||||
true))
|
|
||||||
)
|
|
||||||
:on-key-up (fn [e]
|
|
||||||
(if (= 13 (.-keyCode e))
|
|
||||||
(do
|
|
||||||
(select (first valid-matches))
|
|
||||||
false)
|
|
||||||
true))
|
|
||||||
:on-change (fn [e]
|
|
||||||
(reset! highlighted (ffirst valid-matches))
|
|
||||||
(select [nil (.. e -target -value)]))}]
|
|
||||||
(when (and (seq @text)
|
|
||||||
(not @selected)
|
|
||||||
(seq valid-matches))
|
|
||||||
[:div.typeahead-menu
|
|
||||||
[:ul
|
|
||||||
(for [[id t :as match] valid-matches]
|
|
||||||
|
|
||||||
[:li.typeahead-suggestion {:class (if (= id @highlighted)
|
|
||||||
"typeahead-highlighted")
|
|
||||||
:on-mouse-down #(do (println "MATCH" match) (select match))} t])]])]))))
|
|
||||||
|
|
||||||
(defn new-invoice-modal []
|
(defn new-invoice-modal []
|
||||||
(let [data @(re-frame/subscribe [::new-invoice])
|
(let [data @(re-frame/subscribe [::new-invoice])
|
||||||
|
|||||||
Reference in New Issue
Block a user