Builds client SSR approach, sunsets old cljs.
This commit is contained in:
@@ -143,3 +143,10 @@
|
||||
(defn update! [cursor v]
|
||||
"Replaces value supplied by cursor with value v."
|
||||
(-transact! cursor (constantly v)))
|
||||
|
||||
(defn ensure-path! [cursor p default]
|
||||
(let [next-to-last (get-in cursor (butlast p))
|
||||
next-to-last-v @next-to-last]
|
||||
(when (not (get next-to-last-v (last p)))
|
||||
(transact! next-to-last #(assoc % (last p) default))))
|
||||
cursor)
|
||||
|
||||
@@ -1,182 +1,12 @@
|
||||
(ns auto-ap.graphql.clients
|
||||
(:require
|
||||
[amazonica.aws.s3 :as s3]
|
||||
[auto-ap.datomic :refer [audit-transact conn]]
|
||||
[auto-ap.datomic.clients :as d-clients]
|
||||
[auto-ap.graphql.utils
|
||||
:refer [->graphql
|
||||
assert-admin
|
||||
attach-tracing-resolvers
|
||||
can-see-client?
|
||||
<-graphql
|
||||
result->page
|
||||
is-admin?]]
|
||||
[auto-ap.routes.queries :as q]
|
||||
[auto-ap.square.core3 :as square]
|
||||
[auto-ap.utils :refer [heartbeat]]
|
||||
[clj-time.coerce :as coerce]
|
||||
[clojure.java.io :as io]
|
||||
[clojure.set :as set]
|
||||
[clojure.string :as str]
|
||||
[com.brunobonacci.mulog :as mu]
|
||||
[datomic.api :as dc]
|
||||
[iol-ion.tx :refer [random-tempid]]
|
||||
[mount.core :as mount]
|
||||
[yang.scheduler :as scheduler]
|
||||
[auto-ap.solr :as solr])
|
||||
(:import
|
||||
(java.util UUID)
|
||||
(org.apache.commons.codec.binary Base64)))
|
||||
|
||||
(defn assert-client-code-is-unique [code]
|
||||
(when (seq (dc/q {:find '[?id]
|
||||
:in ['$ '?code]
|
||||
:where ['[?id :client/code ?code]]}
|
||||
(dc/db conn) code))
|
||||
(throw (ex-info "Client is not unique" {:validation-error (str "Client code '" code "' is not unique.")}))))
|
||||
|
||||
(defn upload-signature-data [signature-data]
|
||||
(let [prefix "data:image/jpeg;base64,"]
|
||||
(when signature-data
|
||||
(when-not (str/starts-with? signature-data prefix)
|
||||
(throw (ex-info "Invalid signature image" {:validation-error (str "Invalid signature image.")})))
|
||||
(let [signature-id (str (UUID/randomUUID))
|
||||
raw-bytes (Base64/decodeBase64 (subs signature-data (count prefix)))]
|
||||
(s3/put-object :bucket-name "integreat-signature-images" #_(:data-bucket env)
|
||||
:key (str signature-id ".jpg")
|
||||
:input-stream (io/make-input-stream raw-bytes {})
|
||||
:metadata {:content-type "image/jpeg"
|
||||
:content-length (count raw-bytes)}
|
||||
:canned-acl "public-read")
|
||||
(str "https://integreat-signature-images.s3.amazonaws.com/" signature-id ".jpg")))))
|
||||
|
||||
(defn assert-no-shared-transaction-sources [client-code txes]
|
||||
(let [new-db (:db-after (dc/with (dc/db conn)
|
||||
txes))]
|
||||
(when (seq (->> (dc/q '[:find ?src (count ?ba)
|
||||
:in $ ?c
|
||||
:where [?c :client/bank-accounts ?ba]
|
||||
(or
|
||||
[?ba :bank-account/intuit-bank-account ?src]
|
||||
[?ba :bank-account/plaid-account ?src]
|
||||
[?ba :bank-account/yodlee-account-id ?src])]
|
||||
new-db [:client/code client-code])
|
||||
(filter (fn [[_ cnt]]
|
||||
(> cnt 1)))))
|
||||
(throw (ex-info "Cannot reuse yodlee/plaid/intuit account" {:validation-error (str "Cannot reuse yodlee/plaid/intuit account")})))))
|
||||
|
||||
(defn edit-client [context {:keys [edit_client]} _]
|
||||
(assert-admin (:id context))
|
||||
(when-not (:id edit_client)
|
||||
(assert-client-code-is-unique (:code edit_client)))
|
||||
|
||||
(let [client (when (:id edit_client) (d-clients/get-by-id (:id edit_client)))
|
||||
id (or (:db/id client) "new-client")
|
||||
signature-file (upload-signature-data (:signature_data edit_client))
|
||||
client-code (if (str/blank? (:client/code client))
|
||||
(:code edit_client)
|
||||
(:client/code client))
|
||||
updated-entity (cond-> {:db/id id
|
||||
:client/code client-code
|
||||
:client/name (:name edit_client)
|
||||
:client/matches (:matches edit_client)
|
||||
:client/email (:email edit_client)
|
||||
:client/locked-until (some-> (:locked_until edit_client) (coerce/to-date))
|
||||
:client/locations (filter identity (:locations edit_client))
|
||||
:client/week-a-debits (:week_a_debits edit_client)
|
||||
:client/week-a-credits (:week_a_credits edit_client)
|
||||
:client/week-b-debits (:week_b_debits edit_client)
|
||||
:client/square-auth-token (:square_auth_token edit_client)
|
||||
:client/square-locations (map
|
||||
(fn [sl]
|
||||
{:db/id (or (:id sl) (random-tempid))
|
||||
:square-location/client-location (:client_location sl)})
|
||||
(:square_locations edit_client))
|
||||
|
||||
:client/emails (map (fn [e]
|
||||
{:db/id (or (:id e)
|
||||
(random-tempid))
|
||||
:email-contact/email (:email e)
|
||||
:email-contact/description (:description e)})
|
||||
(:emails edit_client))
|
||||
|
||||
:client/feature-flags (:feature_flags edit_client)
|
||||
:client/ezcater-locations (map
|
||||
(fn [el]
|
||||
{:db/id (or (:id el) (random-tempid))
|
||||
:ezcater-location/location (:location el)
|
||||
:ezcater-location/caterer (:caterer el)})
|
||||
(:ezcater_locations edit_client))
|
||||
:client/week-b-credits (:week_b_credits edit_client)
|
||||
:client/location-matches (->> (:location_matches edit_client)
|
||||
(filter (fn [lm] (and (:location lm) (:match lm))))
|
||||
(map (fn [lm] {:db/id (or (:id lm) (random-tempid))
|
||||
:location-match/location (:location lm)
|
||||
:location-match/matches [(:match lm)]})))
|
||||
:client/address (when (seq (filter identity (vals (:address edit_client))))
|
||||
{:db/id (or (:id (:address edit_client)) (random-tempid))
|
||||
:address/street1 (:street1 (:address edit_client))
|
||||
:address/street2 (:street2 (:address edit_client))
|
||||
:address/city (:city (:address edit_client))
|
||||
:address/state (:state (:address edit_client))
|
||||
:address/zip (:zip (:address edit_client))})
|
||||
:client/bank-accounts (map (fn [ba]
|
||||
{:db/id (or (:id ba) (random-tempid))
|
||||
:bank-account/code (:code ba)
|
||||
:bank-account/bank-name (:bank_name ba)
|
||||
:bank-account/bank-code (:bank_code ba)
|
||||
:bank-account/start-date (-> (:start_date ba) (coerce/to-date))
|
||||
:bank-account/routing (:routing ba)
|
||||
:bank-account/include-in-reports (:include_in_reports ba)
|
||||
|
||||
:bank-account/name (:name ba)
|
||||
:bank-account/visible (:visible ba)
|
||||
:bank-account/number (:number ba)
|
||||
:bank-account/check-number (:check_number ba)
|
||||
:bank-account/numeric-code (:numeric_code ba)
|
||||
:bank-account/sort-order (:sort_order ba)
|
||||
:bank-account/locations (:locations ba)
|
||||
:bank-account/use-date-instead-of-post-date? (boolean (:use_date_instead_of_post_date ba))
|
||||
|
||||
:bank-account/yodlee-account-id (:yodlee_account_id ba)
|
||||
:bank-account/type (keyword "bank-account-type" (name (:type ba)))
|
||||
:bank-account/yodlee-account (when (:yodlee_account ba)
|
||||
[:yodlee-account/id (:yodlee_account ba)])
|
||||
:bank-account/plaid-account (:plaid_account ba)
|
||||
:bank-account/intuit-bank-account (:intuit_bank_account ba)})
|
||||
(:bank_accounts edit_client))}
|
||||
signature-file (assoc :client/signature-file signature-file))
|
||||
|
||||
_ (mu/log ::upserting :up updated-entity)
|
||||
_ (assert-no-shared-transaction-sources client-code [[:upsert-entity updated-entity]])
|
||||
|
||||
result (audit-transact [[:upsert-entity updated-entity]] (:id context))]
|
||||
(when (:square_auth_token edit_client)
|
||||
@(square/upsert-locations (-> result :tempids (get id) (or id) d-clients/get-by-id)))
|
||||
(let [updated-client (-> result :tempids (get id) (or id) d-clients/get-by-id)]
|
||||
(when (:client/name updated-client)
|
||||
(solr/index-documents-raw solr/impl "clients"
|
||||
[{"id" (:db/id updated-client)
|
||||
"name" (conj (or (:client/matches updated-client) [])
|
||||
(:client/name updated-client))
|
||||
"code" (:client/code updated-client)
|
||||
"exact" (map str/upper-case (conj (or (:client/matches updated-client) [])
|
||||
(:client/name updated-client)))}]))
|
||||
(-> updated-client
|
||||
|
||||
(update :client/bank-accounts
|
||||
(fn [bas]
|
||||
(map #(set/rename-keys % {:bank-account/use-date-instead-of-post-date? :use-date-instead-of-post-date}) bas)))
|
||||
(update :client/location-matches
|
||||
(fn [lms]
|
||||
(mapcat (fn [lm]
|
||||
(map (fn [m]
|
||||
{:location-match/match m
|
||||
:location-match/location (:location-match/location lm)})
|
||||
(:location-match/matches lm)))
|
||||
lms)))
|
||||
->graphql))))
|
||||
|
||||
(:require [auto-ap.datomic :refer [conn]]
|
||||
[auto-ap.datomic.clients :as d-clients]
|
||||
[auto-ap.graphql.utils
|
||||
:refer [->graphql <-graphql assert-admin attach-tracing-resolvers
|
||||
can-see-client? is-admin? result->page]]
|
||||
[clojure.set :as set]
|
||||
[com.brunobonacci.mulog :as mu]
|
||||
[datomic.api :as dc]))
|
||||
|
||||
(defn refresh-all-current-balance []
|
||||
(mu/with-context {:source "current-balance-refresh"}
|
||||
@@ -238,201 +68,11 @@
|
||||
bank-accounts))))))]
|
||||
(result->page clients clients-count :clients (:filters args))))
|
||||
|
||||
(def sales-summary-query
|
||||
"[:find ?d4 (sum ?total) (sum ?tax) (sum ?tip) (sum ?service-charge) (sum ?discount) (sum ?returns)
|
||||
:with ?s
|
||||
:in $
|
||||
:where
|
||||
[(ground (iol-ion.query/recent-date 120)) ?min-d]
|
||||
[(ground #inst \"2040-01-01\") ?max-d]
|
||||
[?c :client/code \"%s\"]
|
||||
[(iol-ion.query/sales-orders-in-range $ ?c ?min-d ?max-d) [?s ...]]
|
||||
[?s :sales-order/date ?d]
|
||||
[?s :sales-order/total ?total]
|
||||
[?s :sales-order/tax ?tax]
|
||||
[?s :sales-order/tip ?tip]
|
||||
[?s :sales-order/service-charge ?service-charge]
|
||||
[?s :sales-order/returns ?returns]
|
||||
[?s :sales-order/discount ?discount]
|
||||
[(iol-ion.query/excel-date ?d) ?d4]
|
||||
]")
|
||||
|
||||
(def sales-category-query
|
||||
"[:find ?d4 ?n ?n2 (sum ?total) (sum ?tax) (sum ?discount)
|
||||
:with ?s ?li
|
||||
:in $
|
||||
:where
|
||||
[(ground (iol-ion.query/recent-date 120)) ?min-d]
|
||||
[(ground #inst \"2040-01-01\") ?max-d]
|
||||
[?c :client/code \"%s\"]
|
||||
[(iol-ion.query/sales-orders-in-range $ ?c ?min-d ?max-d) [?s ...]]
|
||||
[?s :sales-order/date ?d]
|
||||
[?s :sales-order/line-items ?li]
|
||||
[?li :order-line-item/category ?n]
|
||||
[(get-else $ ?li :order-line-item/item-name \"\") ?n2]
|
||||
[?li :order-line-item/total ?total]
|
||||
[?li :order-line-item/tax ?tax]
|
||||
[?li :order-line-item/discount ?discount]
|
||||
[(iol-ion.query/excel-date ?d) ?d4]]")
|
||||
|
||||
(def expected-deposits-query
|
||||
"[:find ?d4 ?t ?f
|
||||
:in $
|
||||
:where
|
||||
[(ground (iol-ion.query/recent-date 120)) ?min-d]
|
||||
[?c :client/code \"%s\"]
|
||||
[?s :expected-deposit/client ?c]
|
||||
[?s :expected-deposit/sales-date ?date]
|
||||
[(>= ?date ?min-d)]
|
||||
[?s :expected-deposit/total ?t]
|
||||
[?s :expected-deposit/fee ?f]
|
||||
[(iol-ion.query/excel-date ?date) ?d4]
|
||||
]")
|
||||
|
||||
(def tenders-query
|
||||
"[:find ?d4 ?type ?p2 (sum ?total) (sum ?tip)
|
||||
:with ?charge
|
||||
:in $
|
||||
:where
|
||||
[(ground (iol-ion.query/recent-date 120)) ?min-d]
|
||||
[?c :client/code \"%s\"]
|
||||
[?s :sales-order/client ?c]
|
||||
[?s :sales-order/date ?date]
|
||||
[(>= ?date ?min-d)]
|
||||
[?s :sales-order/charges ?charge]
|
||||
[?charge :charge/type-name ?type]
|
||||
[?charge :charge/total ?total]
|
||||
[?charge :charge/tip ?tip]
|
||||
[(get-else $ ?charge :charge/processor :na) ?ccp]
|
||||
[(get-else $ ?ccp :db/ident :na) ?p]
|
||||
[(name ?p) ?p2]
|
||||
[(iol-ion.query/excel-date ?date) ?d4]
|
||||
]")
|
||||
|
||||
(def tenders2-query
|
||||
"[:find ?d4 ?type ?p2 (sum ?total) (sum ?tip)
|
||||
:with ?charge
|
||||
:in $
|
||||
:where
|
||||
[(ground (iol-ion.query/recent-date 120)) ?min-d]
|
||||
[?charge :charge/date ?date]
|
||||
[(>= ?date ?min-d)]
|
||||
[?charge :charge/client ?c]
|
||||
[?c :client/code \"%s\"]
|
||||
[?charge :charge/type-name ?type]
|
||||
[?charge :charge/total ?total]
|
||||
[?charge :charge/tip ?tip]
|
||||
(or
|
||||
|
||||
(and [_ :expected-deposit/charges ?charge ]
|
||||
[(ground :settlement) ?ccp]
|
||||
[(ground :settlement) ?p])
|
||||
(and
|
||||
(not [_ :expected-deposit/charges ?charge])
|
||||
[(get-else $ ?charge :charge/processor :na) ?ccp]
|
||||
[(get-else $ ?ccp :db/ident :na) ?p]
|
||||
))
|
||||
[(name ?p) ?p2]
|
||||
[(iol-ion.query/excel-date ?date) ?d4]]
|
||||
" )
|
||||
|
||||
(def refunds-query
|
||||
"[:find ?d4 ?t (sum ?total) (sum ?fee)
|
||||
:with ?r
|
||||
:in $
|
||||
:where
|
||||
[(ground (iol-ion.query/recent-date 120)) ?min-d]
|
||||
[?r :sales-refund/client [:client/code \"%s\"]]
|
||||
[?r :sales-refund/date ?date]
|
||||
[(>= ?date ?min-d)]
|
||||
[?r :sales-refund/total ?total]
|
||||
[?r :sales-refund/fee ?fee]
|
||||
[?r :sales-refund/type ?t]
|
||||
[(iol-ion.query/excel-date ?date) ?d4]
|
||||
]")
|
||||
|
||||
(def cash-drawer-shift-query
|
||||
"[:find ?d4 (sum ?paid-in) (sum ?paid-out) (sum ?expected-cash) (sum ?opened-cash)
|
||||
:with ?cds
|
||||
:in $
|
||||
:where
|
||||
[?cds :cash-drawer-shift/date ?date]
|
||||
[(ground (iol-ion.query/recent-date 120)) ?min-d]
|
||||
[(>= ?date ?min-d)]
|
||||
[?cds :cash-drawer-shift/client [:client/code \"%s\"]]
|
||||
[?cds :cash-drawer-shift/paid-in ?paid-in]
|
||||
[?cds :cash-drawer-shift/paid-out ?paid-out]
|
||||
[?cds :cash-drawer-shift/expected-cash ?expected-cash]
|
||||
[?cds :cash-drawer-shift/opened-cash ?opened-cash]
|
||||
[(iol-ion.query/excel-date ?date) ?d4]]")
|
||||
|
||||
|
||||
|
||||
|
||||
(defn setup-sales-queries-impl [client-id]
|
||||
(let [{client-code :client/code feature-flags :client/feature-flags} (dc/pull (dc/db conn) '[:client/code :client/feature-flags] client-id)
|
||||
is-new-square? ((set feature-flags) "new-square")]
|
||||
(q/put-query (str (UUID/randomUUID))
|
||||
(format sales-summary-query client-code)
|
||||
(str "sales query for " client-code)
|
||||
(str client-code "-sales-summary")
|
||||
[:client/code client-code]
|
||||
)
|
||||
(q/put-query (str (UUID/randomUUID))
|
||||
(format sales-category-query client-code)
|
||||
(str "sales category query for " client-code)
|
||||
(str client-code "-sales-category")
|
||||
[:client/code client-code]
|
||||
)
|
||||
(q/put-query (str (UUID/randomUUID))
|
||||
(format expected-deposits-query client-code)
|
||||
(str "expected deposit query for " client-code)
|
||||
(str client-code "-expected-deposit")
|
||||
[:client/code client-code]
|
||||
)
|
||||
(q/put-query (str (UUID/randomUUID))
|
||||
(format (if is-new-square? tenders2-query tenders-query) client-code)
|
||||
(str "tender query for " client-code)
|
||||
(str client-code "-tender")
|
||||
[:client/code client-code]
|
||||
)
|
||||
|
||||
(q/put-query (str (UUID/randomUUID))
|
||||
(format refunds-query client-code)
|
||||
(str "refunds query for " client-code)
|
||||
(str client-code "-refund")
|
||||
[:client/code client-code])
|
||||
|
||||
(q/put-query (str (UUID/randomUUID))
|
||||
(format cash-drawer-shift-query client-code)
|
||||
(str "cash drawer shift query for " client-code)
|
||||
(str client-code "-cash-drawer-shift")
|
||||
[:client/code client-code])
|
||||
(let [sales-summary-id (:saved-query/guid (dc/pull (dc/db conn) [:saved-query/guid] [:saved-query/lookup-key (str client-code "-sales-summary")]))
|
||||
sales-category-id (:saved-query/guid (dc/pull (dc/db conn) [:saved-query/guid] [:saved-query/lookup-key (str client-code "-sales-category")]))
|
||||
expected-deposit-id (:saved-query/guid (dc/pull (dc/db conn) [:saved-query/guid] [:saved-query/lookup-key (str client-code "-expected-deposit")]))
|
||||
tender-id (:saved-query/guid (dc/pull (dc/db conn) [:saved-query/guid] [:saved-query/lookup-key (str client-code "-tender")]))
|
||||
refund-id (:saved-query/guid (dc/pull (dc/db conn) [:saved-query/guid] [:saved-query/lookup-key (str client-code "-refund")]))
|
||||
cash-drawer-shift-id (:saved-query/guid (dc/pull (dc/db conn) [:saved-query/guid] [:saved-query/lookup-key (str client-code "-cash-drawer-shift")]))]
|
||||
{:message (str/join "\n"
|
||||
[
|
||||
(str "For " client-code ":")
|
||||
(str "Sales: " "https://app.integreatconsult.com/api/queries/" sales-summary-id "/results/json")
|
||||
(str "Sales Category: " "https://app.integreatconsult.com/api/queries/" sales-category-id "/results/json")
|
||||
(str "Expected Deposits: " "https://app.integreatconsult.com/api/queries/" expected-deposit-id "/results/json")
|
||||
(str "Tenders: " "https://app.integreatconsult.com/api/queries/" tender-id "/results/json")
|
||||
(str "Refund: " "https://app.integreatconsult.com/api/queries/" refund-id "/results/json")
|
||||
(str "Cash Drawer Shift: " "https://app.integreatconsult.com/api/queries/" cash-drawer-shift-id "/results/json")])})))
|
||||
|
||||
|
||||
(defn setup-sales-queries [context args _]
|
||||
(assert-admin (:id context))
|
||||
(setup-sales-queries-impl (:client_id args)))
|
||||
|
||||
|
||||
(defn reset-all-queries []
|
||||
(doseq [[c] (dc/q '[:find ?c :where [?c :client/code]] (dc/db conn))]
|
||||
(setup-sales-queries-impl c)))
|
||||
|
||||
|
||||
(def objects
|
||||
@@ -535,82 +175,15 @@
|
||||
:resolve :get-client-page}})
|
||||
|
||||
(def mutations
|
||||
{:edit_client {:type :client
|
||||
:args {:edit_client {:type :edit_client}}
|
||||
:resolve :mutation/edit-client}
|
||||
:setup_sales_queries {:type :message
|
||||
:args {:client_id {:type :id}}
|
||||
:resolve :mutation/setup-sales-queries}})
|
||||
{})
|
||||
|
||||
(def input-objects
|
||||
{:edit_location_match {:fields {:location {:type 'String}
|
||||
:match {:type 'String}
|
||||
:id {:type :id}}}
|
||||
|
||||
:client_filters
|
||||
{ :client_filters
|
||||
{:fields {:code {:type 'String}
|
||||
:name_like {:type 'String}
|
||||
:start {:type 'Int}
|
||||
:per_page {:type 'Int}
|
||||
:sort {:type '(list :sort_item)}}}
|
||||
|
||||
:edit_square_location {:fields {:client_location {:type 'String}
|
||||
:id {:type :id}}}
|
||||
|
||||
:edit_ezcater_location {:fields {:location {:type 'String}
|
||||
:caterer {:type :id}
|
||||
:id {:type :id}}}
|
||||
|
||||
:edit_forecasted_transaction {:fields {:identifier {:type 'String}
|
||||
:id {:type :id}
|
||||
:day_of_month {:type 'Int}
|
||||
:amount {:type :money}}}
|
||||
:edit_email_contact {:fields {:id {:type :id}
|
||||
:email {:type 'String}
|
||||
:description {:type 'String}}}
|
||||
:edit_client {:fields {:id {:type :id}
|
||||
:name {:type 'String}
|
||||
:locked_until {:type :iso_date}
|
||||
:code {:type 'String}
|
||||
:square_auth_token {:type 'String}
|
||||
:feature_flags {:type '(list String)}
|
||||
:signature_data {:type 'String}
|
||||
:email {:type 'String}
|
||||
:emails {:type '(list :edit_email_contact)}
|
||||
:week_a_credits {:type :money}
|
||||
:week_a_debits {:type :money}
|
||||
:week_b_credits {:type :money}
|
||||
:week_b_debits {:type :money}
|
||||
:address {:type :add_address}
|
||||
:locations {:type '(list String)}
|
||||
:matches {:type '(list String)}
|
||||
:location_matches {:type '(list :edit_location_match)}
|
||||
:square_locations {:type '(list :edit_square_location)}
|
||||
:ezcater_locations {:type '(list :edit_ezcater_location)}
|
||||
:bank_accounts {:type '(list :edit_bank_account)}
|
||||
:forecasted_transactions {:type '(list :edit_forecasted_transaction)}}}
|
||||
|
||||
:edit_bank_account
|
||||
{:fields {:id {:type :id}
|
||||
:code {:type 'String}
|
||||
:type {:type :bank_account_type}
|
||||
:start_date {:type :iso_date}
|
||||
:number {:type 'String}
|
||||
:check_number {:type 'Int}
|
||||
:numeric_code {:type 'Int}
|
||||
:visible {:type 'Boolean}
|
||||
:include_in_reports {:type 'Boolean}
|
||||
:sort_order {:type 'Int}
|
||||
:name {:type 'String}
|
||||
:bank_code {:type 'String}
|
||||
:routing {:type 'String}
|
||||
:bank_name {:type 'String}
|
||||
:locations {:type '(list String)}
|
||||
:yodlee_account_id {:type 'Int}
|
||||
:use_date_instead_of_post_date {:type 'Boolean}
|
||||
:intuit_bank_account {:type :id}
|
||||
:plaid_account {:type :id}
|
||||
:yodlee_account {:type 'Int}}}})
|
||||
:sort {:type '(list :sort_item)}}} })
|
||||
|
||||
(def enums
|
||||
{:bank_account_type {:values [{:enum-value :check}
|
||||
@@ -620,9 +193,7 @@
|
||||
(def resolvers
|
||||
{:get-client get-client
|
||||
:get-admin-client get-admin-client
|
||||
:get-client-page get-client-page
|
||||
:mutation/edit-client edit-client
|
||||
:mutation/setup-sales-queries setup-sales-queries})
|
||||
:get-client-page get-client-page })
|
||||
|
||||
|
||||
(defn attach [schema]
|
||||
|
||||
@@ -217,7 +217,7 @@
|
||||
(pull-many (dc/db conn)
|
||||
d-clients/full-read))]
|
||||
|
||||
(mu/with-context {:clients (map :client/code clients)}
|
||||
(mu/with-context {:clients (take 10 (map :client/code clients))}
|
||||
(handler (assoc request
|
||||
:clients clients
|
||||
:client (when (= 1 (count clients))
|
||||
@@ -273,7 +273,7 @@
|
||||
(handler request)))
|
||||
|
||||
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
|
||||
(def app
|
||||
(defonce app
|
||||
(-> route-handler
|
||||
(wrap-hx-current-url-params)
|
||||
(wrap-guess-route)
|
||||
@@ -293,7 +293,7 @@
|
||||
(byte-array
|
||||
[42, 52, -31, 105, -126, -33, -118, -69, -82, -59, -15, -69, -38, 103, -102, -1])} )})
|
||||
|
||||
(wrap-reload)
|
||||
#_(wrap-reload)
|
||||
(wrap-params)
|
||||
(mp/wrap-multipart-params)
|
||||
(wrap-edn-params)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
(ns auto-ap.jobs.bulk-journal-import
|
||||
(:gen-class)
|
||||
#_(:gen-class)
|
||||
(:require
|
||||
[amazonica.aws.s3 :as s3]
|
||||
[auto-ap.graphql.ledger :refer [import-ledger]]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
(ns auto-ap.jobs.close-auto-invoices
|
||||
(:gen-class)
|
||||
#_(:gen-class)
|
||||
(:require
|
||||
[auto-ap.datomic :refer [conn]]
|
||||
[auto-ap.jobs.core :refer [execute]]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
(ns auto-ap.jobs.current-balance-cache
|
||||
(:gen-class)
|
||||
#_(:gen-class)
|
||||
(:require
|
||||
[auto-ap.graphql.clients :as clients]
|
||||
[auto-ap.jobs.core :refer [execute]]))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
(ns auto-ap.jobs.ezcater-upsert
|
||||
(:gen-class)
|
||||
#_(:gen-class)
|
||||
(:require
|
||||
[auto-ap.jobs.core :refer [execute]]
|
||||
[auto-ap.ezcater.core :as ezcater]))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
(ns auto-ap.jobs.intuit
|
||||
(:gen-class)
|
||||
#_(:gen-class)
|
||||
(:require
|
||||
[auto-ap.import.intuit :as intuit]
|
||||
[auto-ap.jobs.core :refer [execute]]))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
(ns auto-ap.jobs.ledger-reconcile
|
||||
(:gen-class)
|
||||
#_(:gen-class)
|
||||
(:require
|
||||
[auto-ap.jobs.core :refer [execute]]
|
||||
[auto-ap.ledger :as ledger]))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
(ns auto-ap.jobs.load-historical-sales
|
||||
(:gen-class)
|
||||
#_(:gen-class)
|
||||
(:require
|
||||
[auto-ap.datomic :refer [conn]]
|
||||
[auto-ap.jobs.core :refer [execute]]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
(ns auto-ap.jobs.plaid
|
||||
(:gen-class)
|
||||
#_(:gen-class)
|
||||
(:require
|
||||
[auto-ap.import.plaid :as plaid]
|
||||
[auto-ap.jobs.core :refer [execute]]))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
(ns auto-ap.jobs.register-invoice-import
|
||||
(:gen-class)
|
||||
#_(:gen-class)
|
||||
(:require
|
||||
[amazonica.aws.s3 :as s3]
|
||||
[auto-ap.datomic :refer [audit-transact conn pull-attr]]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
(ns auto-ap.jobs.square
|
||||
(:gen-class)
|
||||
#_(:gen-class)
|
||||
(:require
|
||||
[auto-ap.jobs.core :refer [execute]]
|
||||
[auto-ap.square.core3 :as square3]))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
(ns auto-ap.jobs.vendor-usages
|
||||
(:gen-class)
|
||||
#_(:gen-class)
|
||||
(:require
|
||||
[auto-ap.datomic :refer [conn]]
|
||||
[auto-ap.jobs.core :refer [execute]]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
(ns auto-ap.jobs.yodlee2
|
||||
(:gen-class)
|
||||
#_(:gen-class)
|
||||
(:require
|
||||
[auto-ap.import.yodlee2 :as yodlee2]
|
||||
[auto-ap.jobs.core :refer [execute]]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
(ns auto-ap.server
|
||||
(:gen-class)
|
||||
#_(:gen-class)
|
||||
(:require
|
||||
[auto-ap.handler :refer [app]]
|
||||
[auto-ap.jobs.restore-from-backup :as job-restore-from-backup]
|
||||
|
||||
@@ -36,14 +36,20 @@
|
||||
(sort-by :created-at)
|
||||
reverse))
|
||||
|
||||
(defn is-background-job? [task]
|
||||
|
||||
(defn is-background-job?
|
||||
"This function checks whether a given task is a background job.
|
||||
It does this by checking the environment of the task's container definitions for an environment variable
|
||||
with the name 'INTEGREAT_JOB'. If such a variable exists, the function returns true, otherwise, it returns false.
|
||||
Parameters: task - a map representing the task to be checked.
|
||||
Returns: true if the task is a background job, false otherwise."
|
||||
[task]
|
||||
(->> task
|
||||
:task-definition
|
||||
:container-definitions
|
||||
(mapcat :environment)
|
||||
(filter (comp #{"INTEGREAT_JOB"} :name))
|
||||
seq))
|
||||
|
||||
(defn task-definition->job-name [task-definition]
|
||||
(->> (:container-definitions task-definition)
|
||||
(mapcat :environment)
|
||||
|
||||
1721
src/clj/auto_ap/ssr/admin/clients.clj
Normal file
1721
src/clj/auto_ap/ssr/admin/clients.clj
Normal file
File diff suppressed because it is too large
Load Diff
0
src/clj/auto_ap/ssr/admin/sales_powerqueries.clj
Normal file
0
src/clj/auto_ap/ssr/admin/sales_powerqueries.clj
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
32
src/clj/auto_ap/ssr/common_handlers.clj
Normal file
32
src/clj/auto_ap/ssr/common_handlers.clj
Normal file
@@ -0,0 +1,32 @@
|
||||
(ns auto-ap.ssr.common-handlers
|
||||
(:require [auto-ap.ssr.form-cursor :as fc]
|
||||
[auto-ap.ssr.utils :refer [html-response wrap-schema-enforce]]))
|
||||
|
||||
|
||||
(defn add-new-entity-handler
|
||||
([path render-fn] (add-new-entity-handler path
|
||||
render-fn
|
||||
(fn default-data [base _]
|
||||
base)))
|
||||
([path render-fn build-data]
|
||||
(-> (fn new-entity [{{:keys [index]} :query-params :as request}]
|
||||
(html-response
|
||||
(fc/start-form-with-prefix (conj path (or index 0))
|
||||
(build-data {:db/id (str (java.util.UUID/randomUUID))
|
||||
:new? true} request)
|
||||
[]
|
||||
(render-fn fc/*current* request))))
|
||||
(wrap-schema-enforce :query-schema [:map
|
||||
[:index {:optional true
|
||||
:default 0} [nat-int? {:default 0}]]]))))
|
||||
|
||||
(defn add-new-primitive-handler [path default-value render-fn]
|
||||
(-> (fn new-location-match [{{:keys [index]} :query-params}]
|
||||
(html-response
|
||||
(fc/start-form-with-prefix (conj path (or index 0))
|
||||
default-value
|
||||
[]
|
||||
(render-fn fc/*current*))))
|
||||
(wrap-schema-enforce :query-schema [:map
|
||||
[:index {:optional true
|
||||
:default 0} [nat-int? {:default 0}]]])))
|
||||
@@ -1,87 +1,162 @@
|
||||
(ns auto-ap.ssr.company
|
||||
(:require
|
||||
[auto-ap.datomic :refer [conn pull-attr]]
|
||||
[auto-ap.datomic.clients :refer [full-read]]
|
||||
[auto-ap.solr :as solr]
|
||||
[auto-ap.ssr-routes :as ssr-routes]
|
||||
[auto-ap.ssr.components :as com]
|
||||
[auto-ap.ssr.svg :as svg]
|
||||
[auto-ap.ssr.ui :refer [base-page]]
|
||||
[auto-ap.ssr.utils :refer [html-response]]
|
||||
[bidi.bidi :as bidi]
|
||||
[cemerick.url :as url]
|
||||
[clojure.string :as str]
|
||||
[config.core :refer [env]]
|
||||
[datomic.api :as dc]
|
||||
[ring.middleware.json :refer [wrap-json-response]]))
|
||||
(:require [amazonica.aws.s3 :as s3]
|
||||
[auto-ap.datomic :refer [conn pull-attr]]
|
||||
[auto-ap.datomic.clients :refer [full-read]]
|
||||
[auto-ap.permissions :as permissions]
|
||||
[auto-ap.solr :as solr]
|
||||
[auto-ap.ssr-routes :as ssr-routes]
|
||||
[auto-ap.ssr.components :as com]
|
||||
[auto-ap.ssr.hx :as hx]
|
||||
[auto-ap.ssr.svg :as svg]
|
||||
[auto-ap.ssr.ui :refer [base-page]]
|
||||
[auto-ap.ssr.utils :refer [html-response]]
|
||||
[bidi.bidi :as bidi]
|
||||
[cemerick.url :as url]
|
||||
[clojure.java.io :as io]
|
||||
[clojure.string :as str]
|
||||
[config.core :refer [env]]
|
||||
[datomic.api :as dc]
|
||||
[ring.middleware.json :refer [wrap-json-response]])
|
||||
(:import [java.util UUID]
|
||||
(org.apache.commons.codec.binary Base64)))
|
||||
|
||||
(defn please-select-client-screen* []
|
||||
[:div.grid.grid-cols-3
|
||||
(com/content-card {}
|
||||
[:div.col-span-1.p-4 {:class "p-4 sm:p-6"}
|
||||
[:h3 {:class "mb-4 text-xl font-semibold dark:text-white"}
|
||||
"Please select a company"]
|
||||
])])
|
||||
"Please select a company"]])])
|
||||
|
||||
(defn main-content* [{:keys [client]}]
|
||||
(defn signature [request]
|
||||
(let [signature-file (pull-attr (dc/db conn) :client/signature-file (:db/id (:client request)))]
|
||||
(com/content-card {:class " w-[748px]"
|
||||
:hx-target "this"
|
||||
:hx-swap "outerHTML"}
|
||||
[:div.col-span-1.p-4 {:class "p-4 sm:p-6 space-y-4 overflow-visible "
|
||||
:x-data (hx/json {"signature" nil
|
||||
"editing" false
|
||||
"existing" (boolean signature-file)})
|
||||
:hx-put (bidi/path-for ssr-routes/only-routes
|
||||
:company-update-signature)
|
||||
:hx-trigger "accepted"
|
||||
:hx-vals "js:{signatureData: event.detail.signatureData}"}
|
||||
[:h3 {:class "mb-4 text-xl font-semibold dark:text-white"}
|
||||
"Signature"]
|
||||
[:div.htmx-indicator
|
||||
[:div.bg-gray-100.flex.items-center.text-green-500.justify-center.rounded.rounded-lg.border.border-gray-400 {:style {:width "696px" :height "261px"}}
|
||||
(svg/spinner {:class "w-4 h-4 text-primary-300"})
|
||||
[:div.ml-3 "Loading..."]]]
|
||||
|
||||
[:div.htmx-indicator-hidden
|
||||
(when signature-file
|
||||
[:img.rounded.rounded-lg.border.border-gray-300.bg-gray-50 {:src signature-file
|
||||
:width 696
|
||||
:height 261
|
||||
:x-show "existing && !editing"}])
|
||||
[:canvas.rounded.rounded-lg.border.border-gray-300
|
||||
|
||||
|
||||
{:style {:width 696
|
||||
:height 261}
|
||||
:x-init "signature= new SignaturePad($el); signature.off()"
|
||||
":class" "editing ? 'bg-white' : 'bg-gray-50' "
|
||||
:width 696
|
||||
:height 261
|
||||
:x-show "existing ? editing: true"}]]
|
||||
|
||||
[:div.flex.gap-2.justify-end
|
||||
(com/button {:color :primary
|
||||
:x-show "!editing"
|
||||
"@click" "signature.clear(); signature.on(); editing=true;"}
|
||||
"New signature")
|
||||
(com/button {:color :primary
|
||||
:x-show "editing"
|
||||
"@click" "signature.clear();"}
|
||||
"Clear")
|
||||
|
||||
(com/button {:color :primary
|
||||
"@click" "$data.signatureData=signature.toDataURL('image/png'); signature.off(); editing=false; $dispatch('accepted', {signatureData: $data.signatureData}) "
|
||||
:x-show "editing"}
|
||||
"Accept")]])))
|
||||
|
||||
(defn upload-signature-data [{{:strs [signatureData]} :form-params client :client :as request}]
|
||||
(let [prefix "data:image/png;base64,"]
|
||||
(when signatureData
|
||||
(when-not (str/starts-with? signatureData prefix)
|
||||
(throw (ex-info "Invalid signature image" {:validation-error (str "Invalid signature image.")})))
|
||||
(let [signature-id (str (UUID/randomUUID))
|
||||
raw-bytes (Base64/decodeBase64 (subs signatureData (count prefix)))]
|
||||
(s3/put-object :bucket-name "integreat-signature-images" #_(:data-bucket env)
|
||||
:key (str signature-id ".png")
|
||||
:input-stream (io/make-input-stream raw-bytes {})
|
||||
:metadata {:content-type "image/png"
|
||||
:content-length (count raw-bytes)}
|
||||
:canned-acl "public-read")
|
||||
@(dc/transact conn [{:db/id (:db/id client)
|
||||
:client/signature-file (str "https://integreat-signature-images.s3.amazonaws.com/" signature-id ".png")}])
|
||||
(html-response
|
||||
(signature request))))))
|
||||
|
||||
(defn main-content* [{:keys [client identity] :as request}]
|
||||
(if-not client
|
||||
(please-select-client-screen*)
|
||||
(let [client (dc/pull (dc/db conn) full-read (:db/id client))]
|
||||
[:div.grid.grid-cols-3.gap-4
|
||||
(com/content-card {}
|
||||
[:div.col-span-1.p-4 {:class "p-4 sm:p-6"}
|
||||
[:h3 {:class "mb-4 text-xl font-semibold dark:text-white"}
|
||||
(:client/name client)]
|
||||
(when-let [address (-> client :client/address)]
|
||||
[:div.flex.flex-col.gap-1.text-lg.dark:text-white.text-gray-700
|
||||
[:p (-> address :address/street1)]
|
||||
[:p (-> address :address/street2)]
|
||||
[:p (-> address :address/city) " "
|
||||
(-> address :address/state) ", "
|
||||
(-> address :address/zip)]])]
|
||||
)
|
||||
(com/content-card {}
|
||||
[:div.col-span-1.p-4 {:class "p-4 sm:p-6"}
|
||||
[:h3 {:class "mb-4 text-xl font-semibold dark:text-white"}
|
||||
"Downloads"]
|
||||
[:a {:href (str (assoc (url/url (str (:base-url env) "/api/vendors/company/export"))
|
||||
:query {"client" (:client/code client)}))}
|
||||
(com/button {:color :primary}
|
||||
"Download vendor list"
|
||||
(com/button-icon {} svg/download))]])])))
|
||||
[:div
|
||||
[:div.grid.grid-cols-3.gap-4
|
||||
(com/content-card {}
|
||||
[:div.col-span-1.p-4 {:class "p-4 sm:p-6"}
|
||||
[:h3 {:class "mb-4 text-xl font-semibold dark:text-white"}
|
||||
(:client/name client)]
|
||||
(when-let [address (-> client :client/address)]
|
||||
[:div.flex.flex-col.gap-1.text-lg.dark:text-white.text-gray-700
|
||||
[:p (-> address :address/street1)]
|
||||
[:p (-> address :address/street2)]
|
||||
[:p (-> address :address/city) " "
|
||||
(-> address :address/state) ", "
|
||||
(-> address :address/zip)]])])
|
||||
(com/content-card {}
|
||||
[:div.col-span-1.p-4 {:class "p-4 sm:p-6"}
|
||||
[:h3 {:class "mb-4 text-xl font-semibold dark:text-white"}
|
||||
"Downloads"]
|
||||
[:a {:href (str (assoc (url/url (str (:base-url env) "/api/vendors/company/export"))
|
||||
:query {"client" (:client/code client)}))}
|
||||
(com/button {:color :primary}
|
||||
"Download vendor list"
|
||||
(com/button-icon {} svg/download))]])
|
||||
[:div]]
|
||||
(when (permissions/can? identity {:client client :subject :signature :activity :edit})
|
||||
(signature request))])))
|
||||
|
||||
(defn page [{:keys [identity matched-route] :as request}]
|
||||
(base-page
|
||||
request
|
||||
(com/page {:nav (com/company-aside-nav)
|
||||
:client-selection (:client-selection (:session request))
|
||||
:client (:client request)
|
||||
:identity (:identity request)
|
||||
:app-params {
|
||||
:hx-get (bidi/path-for ssr-routes/only-routes
|
||||
:company)
|
||||
:hx-trigger "clientSelected from:body"
|
||||
:hx-select "#app-contents"
|
||||
:hx-swap "outerHTML swap:300ms"}}
|
||||
(com/breadcrumbs {}
|
||||
[:a {:href (bidi/path-for ssr-routes/only-routes
|
||||
:company)}
|
||||
"My Company"])
|
||||
(main-content* {:client (:client request)}))
|
||||
"My Company"))
|
||||
request
|
||||
(com/page {:nav (com/company-aside-nav)
|
||||
:client-selection (:client-selection (:session request))
|
||||
:client (:client request)
|
||||
:identity (:identity request)
|
||||
:app-params {:hx-get (bidi/path-for ssr-routes/only-routes
|
||||
:company)
|
||||
:hx-trigger "clientSelected from:body"
|
||||
:hx-select "#app-contents"
|
||||
:hx-swap "outerHTML swap:300ms"}}
|
||||
(com/breadcrumbs {}
|
||||
[:a {:href (bidi/path-for ssr-routes/only-routes
|
||||
:company)}
|
||||
"My Company"])
|
||||
(main-content* request))
|
||||
"My Company"))
|
||||
|
||||
(defn search [{:keys [clients query-params]}]
|
||||
(let [valid-client-ids (set (map :db/id clients))
|
||||
name-like-ids (when (not-empty (get query-params "q"))
|
||||
(set (map (comp #(Long/parseLong %) :id)
|
||||
(solr/query solr/impl "clients"
|
||||
{"query" (format "_text_:(%s*)" (str/upper-case (solr/escape (get query-params "q"))))
|
||||
"fields" "id"
|
||||
"limit" 300}))))
|
||||
(set (map (comp #(Long/parseLong %) :id)
|
||||
(solr/query solr/impl "clients"
|
||||
{"query" (format "_text_:(%s*)" (str/upper-case (solr/escape (get query-params "q"))))
|
||||
"fields" "id"
|
||||
"limit" 300}))))
|
||||
valid-clients (for [n name-like-ids
|
||||
:when (valid-client-ids n)]
|
||||
{"value" n "label" (pull-attr (dc/db conn) :client/name n)}
|
||||
)]
|
||||
{"value" n "label" (pull-attr (dc/db conn) :client/name n)})]
|
||||
{:body (take 10 valid-clients)}))
|
||||
|
||||
(def search (wrap-json-response search))
|
||||
@@ -109,13 +184,13 @@
|
||||
(defn bank-account-typeahead* [{:keys [client-id name value]}]
|
||||
(if client-id
|
||||
(com/typeahead {:name name
|
||||
:class "w-96"
|
||||
:placeholder "Search..."
|
||||
:url (bidi/path-for ssr-routes/only-routes :bank-account-search
|
||||
:db/id client-id)
|
||||
:value value
|
||||
:value-fn (some-fn :db/id identity)
|
||||
:content-fn (some-fn :bank-account/name #(pull-attr (dc/db conn) :bank-account/name %))})
|
||||
:class "w-96"
|
||||
:placeholder "Search..."
|
||||
:url (bidi/path-for ssr-routes/only-routes :bank-account-search
|
||||
:db/id client-id)
|
||||
:value value
|
||||
:value-fn (some-fn :db/id identity)
|
||||
:content-fn (some-fn :bank-account/name #(pull-attr (dc/db conn) :bank-account/name %))})
|
||||
[:span.text-xs.text-gray-500 "Please select a client before selecting a bank account."
|
||||
[:input {:type "hidden"
|
||||
:name name}]]))
|
||||
|
||||
@@ -11,8 +11,7 @@
|
||||
[auto-ap.ssr.components.tags :as tags]
|
||||
[auto-ap.ssr.components.paginator :as paginator]
|
||||
[auto-ap.ssr.components.radio :as radio]))
|
||||
|
||||
|
||||
;; potemkin can be used here
|
||||
(def breadcrumbs breadcrumbs/breadcrumbs-)
|
||||
(def button buttons/button-)
|
||||
(def validated-save-button buttons/validated-save-button-)
|
||||
@@ -24,8 +23,7 @@
|
||||
(def button-group-button buttons/group-button-)
|
||||
(def modal dialog/modal-)
|
||||
(def modal-card dialog/modal-card-)
|
||||
(def stacked-modal-card dialog/stacked-modal-card-)
|
||||
(def stacked-modal-card-2 dialog/stacked-modal-card-2-)
|
||||
(def modal-card-advanced dialog/modal-card-advanced-)
|
||||
(def modal-header dialog/modal-header-)
|
||||
(def modal-header-attachment dialog/modal-header-attachment-)
|
||||
(def modal-body dialog/modal-body-)
|
||||
@@ -70,12 +68,9 @@
|
||||
(def data-grid-new-row data-grid/new-row-)
|
||||
|
||||
(defn link [params & children]
|
||||
(into [:a (update params :class str " font-medium text-blue-600 dark:text-blue-500 hover:underline ")]
|
||||
(into [:a (update params :class str " font-medium text-blue-600 dark:text-blue-500 hover:underline cursor-pointer")]
|
||||
children))
|
||||
|
||||
|
||||
|
||||
|
||||
(def paginator paginator/paginator-)
|
||||
(def data-grid-card data-grid/data-grid-card-)
|
||||
|
||||
|
||||
@@ -8,8 +8,10 @@
|
||||
[auto-ap.routes.admin.transaction-rules :as transaction-rules]
|
||||
[auto-ap.ssr.hiccup-helper :as hh]
|
||||
[auto-ap.routes.admin.import-batch :as ib-routes]
|
||||
[auto-ap.routes.admin.clients :as ac-routes]
|
||||
[auto-ap.routes.admin.excel-invoices :as ei-routes]
|
||||
[auto-ap.routes.admin.vendors :as v-routes]))
|
||||
[auto-ap.routes.admin.vendors :as v-routes]
|
||||
[auto-ap.graphql.clients :as clients]))
|
||||
|
||||
(defn menu-button- [params & children]
|
||||
[:div
|
||||
@@ -199,7 +201,7 @@
|
||||
|
||||
[:li
|
||||
(menu-button- {:icon svg/restaurant
|
||||
:href (bidi/path-for client-routes/routes :admin-clients)
|
||||
:href (bidi/path-for ssr-routes/only-routes ::ac-routes/page)
|
||||
:target "_new"}
|
||||
"Clients")]
|
||||
[:li
|
||||
|
||||
@@ -176,7 +176,7 @@
|
||||
|
||||
|
||||
(defn validated-save-button- [{:keys [errors class] :as params} & children]
|
||||
(button- (-> {:color (or (:color params) :primary)
|
||||
(button- (-> {:color (or (:color params) :primary)
|
||||
:type "submit" :class (cond-> (or class "")
|
||||
true (hh/add-class "w-32")
|
||||
(seq errors) (hh/add-class "animate-shake"))}
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
(ns auto-ap.ssr.components.card
|
||||
(:require [auto-ap.ssr.hiccup-helper :as hh]
|
||||
[auto-ap.ssr.hx :as hx]))
|
||||
[auto-ap.ssr.hx :as hx]
|
||||
[clojure.string :as str]))
|
||||
|
||||
(defn card- [params & children]
|
||||
(into [:div (update params :class str " shadow-md dark:bg-gray-800 sm:rounded-lg border-2 border-gray-200 dark:border-gray-900 bg-white overflow-hidden")]
|
||||
(into [:div (update params :class
|
||||
#(cond-> (or % "")
|
||||
(not (str/includes? % "bg-")) (hh/add-class "dark:bg-gray-800 bg-white ")
|
||||
true (hh/add-class "shadow-md sm:rounded-lg border-2 border-gray-200 dark:border-gray-900 overflow-hidden")))]
|
||||
children))
|
||||
|
||||
(defn content-card- [params & children]
|
||||
[:section {:class (hh/add-class " py-3 sm:py-5" (:class params))}
|
||||
[:section (merge params {:class (hh/add-class " py-3 sm:py-5" (:class params))})
|
||||
[:div {:class "max-w-screen-2xl"}
|
||||
(into
|
||||
[:div {:class "relative overflow-hidden shadow-md dark:bg-gray-800 sm:rounded-lg border-2 border-gray-200 dark:border-gray-900 bg-white"}]
|
||||
|
||||
@@ -3,36 +3,19 @@
|
||||
[auto-ap.ssr.hiccup-helper :as hh]
|
||||
[auto-ap.ssr.hx :as hx]))
|
||||
|
||||
(defn modal- [params & children]
|
||||
|
||||
(defn modal-
|
||||
"This modal function is used to create a modal window with a stack that allows for transitioning between modals.
|
||||
|
||||
:params should include the following keys:
|
||||
- :handle-unexpected-error? (default: true) - A boolean indicating whether to handle unexpected errors.
|
||||
- :class (optional) - A string representing additional CSS classes to add to the modal.
|
||||
|
||||
&children should include the child components to be rendered within the modal."
|
||||
[{:as params} & children]
|
||||
[:div (-> params
|
||||
(assoc "@click.outside" "open=false"
|
||||
:x-data (hx/json {:index 0 :hidingIndex -1 :unexpectedError false :transitioning false})
|
||||
"x-on:htmx:response-error" "unexpectedError=true"
|
||||
"x-on:htmx:before-request" "unexpectedError=false"
|
||||
:x-ref "modalStack"
|
||||
"@modalnext.window"
|
||||
" $refs.modalStack.children[index].setAttribute('x-transition:leave-end', '-translate-x-full scale-0 opacity-0' );
|
||||
$refs.modalStack.children[index + 1].setAttribute('x-transition:enter-start', 'translate-x-full scale-0 opacity-0' );
|
||||
hidingIndex = index;
|
||||
setTimeout(() => {index ++; transitioning=true; hidingIndex = -1; }, 150);
|
||||
setTimeout(() => transitioning=false, 320)"
|
||||
|
||||
"@modalprevious.window"
|
||||
" $refs.modalStack.children[index].setAttribute('x-transition:leave-end', 'translate-x-full scale-0 opacity-0' );
|
||||
$refs.modalStack.children[index - 1].setAttribute('x-transition:enter-start', '-translate-x-full scale-0 opacity-0' );
|
||||
hidingIndex = index;
|
||||
setTimeout(() => { index --; hidingIndex = -1; transitioning=true; }, 150);
|
||||
setTimeout(() => transitioning=false, 320)"
|
||||
|
||||
"@modalpop.window"
|
||||
" $refs.modalStack.children[index].setAttribute('x-transition:leave-end', 'translate-x-full scale-0 opacity-0' );
|
||||
$refs.modalStack.children[index - 1].setAttribute('x-transition:enter-start', '-translate-x-full scale-0 opacity-0' );
|
||||
hidingIndex = index;
|
||||
setTimeout(() => {index --; transitioning=true;}, 150);
|
||||
setTimeout(() => { $refs.modalStack.removeChild($refs.modalStack.children[index+1]); hidingIndex=-1; }, 300);
|
||||
setTimeout(() => transitioning=false, 320)
|
||||
"
|
||||
)
|
||||
(assoc "@click.outside" "open=false")
|
||||
(dissoc :handle-unexpected-error?)
|
||||
(update :class (fnil hh/add-class "") "w-full h-full modal-stack"))
|
||||
children])
|
||||
|
||||
@@ -40,11 +23,10 @@
|
||||
[:div (update params
|
||||
:class (fn [c] (-> c
|
||||
(or "")
|
||||
(hh/add-class "w-full p-4 h-full modal-card")
|
||||
)))
|
||||
(hh/add-class "w-full p-4 h-full modal-card"))))
|
||||
[:div {:class "bg-white rounded-lg shadow dark:bg-gray-700 dark:text-white modal-content w-full flex flex-col h-full"}
|
||||
[:div {:class "flex items-start justify-between p-4 border-b rounded-t dark:border-gray-600 shrink-0"} header]
|
||||
[:div {:class "px-6 space-y-6 overflow-y-scroll w-full shrink"}
|
||||
[:div {:class "px-6 py-2 space-y-6 overflow-y-scroll w-full shrink"}
|
||||
#_[:div.bg-green-300.w-full.h-64
|
||||
"hello"]
|
||||
content]
|
||||
@@ -55,28 +37,6 @@
|
||||
[:div {:class "shrink-0"}
|
||||
footer]])]])
|
||||
|
||||
|
||||
(defn stacked-modal-card- [index params header content footer]
|
||||
[:div (merge params
|
||||
{:class (hh/add-class "bg-white rounded-lg shadow dark:bg-gray-700 dark:text-white modal-content flex flex-col h-full" (:class params ""))
|
||||
:x-data (hx/json {:i index})
|
||||
:x-show "index == i && hidingIndex != i"
|
||||
"x-trap" "index == i && hidingIndex == -1 && !transitioning"
|
||||
"x-transition:enter" "transition duration-150",
|
||||
"x-transition:enter-end" "translate-x-0 scale-100 opacity-100",
|
||||
"x-transition:leave" "transition duration-150",
|
||||
"x-transition:leave-start" "translate-x-0 scale-100 opacity-100",
|
||||
})
|
||||
[:div {:class "flex items-start justify-between p-4 border-b rounded-t dark:border-gray-600 shrink-0"} header] ;; todo componentize these
|
||||
[:div {:class "px-6 space-y-6 overflow-y-scroll w-full shrink"}
|
||||
|
||||
content] ;; TODO componentize
|
||||
(when footer [:div {:class "p-4"}
|
||||
[:span.items-center.bg-red-100.text-red-800.text-xs.font-medium.mb-2.p-1.rounded-full.inline-flex (hx/alpine-appear {:x-show "unexpectedError" :class "dark:bg-red-900 dark:text-red-300"})
|
||||
[:span {:class "w-2 h-2 bg-red-500 rounded-full"}]
|
||||
[:span.px-2.py-0.5 "An unexpected error has occured. Integreat staff have been notified."]]
|
||||
[:div {:class "shrink-0"}]footer])])
|
||||
|
||||
(defn modal-header- [params & children]
|
||||
[:div {:class "flex items-start justify-between p-4 border-b rounded-t dark:border-gray-600 shrink-0"}
|
||||
children])
|
||||
@@ -92,21 +52,14 @@
|
||||
|
||||
(defn modal-footer- [params & children]
|
||||
[:div {:class "p-4"}
|
||||
[:span.items-center.bg-red-100.text-red-800.text-xs.font-medium.mb-2.p-1.rounded-full.inline-flex (hx/alpine-appear {:x-show "unexpectedError" :class "dark:bg-red-900 dark:text-red-300"})
|
||||
[:span.items-center.bg-red-100.text-red-800.text-xs.font-medium.mb-2.p-1.rounded-full.inline-flex
|
||||
(hx/alpine-appear {:x-show "unexpectedError" :class "dark:bg-red-900 dark:text-red-300"})
|
||||
[:span {:class "w-2 h-2 bg-red-500 rounded-full"}]
|
||||
[:span.px-2.py-0.5 "An unexpected error has occured. Integreat staff have been notified."]]
|
||||
[:div {:class "shrink-0"}]
|
||||
children])
|
||||
|
||||
(defn stacked-modal-card-2- [index params & children]
|
||||
(defn modal-card-advanced- [params & children]
|
||||
[:div (merge params
|
||||
{:class (hh/add-class "bg-white rounded-lg shadow dark:bg-gray-700 dark:text-white modal-content flex flex-col h-full" (:class params ""))
|
||||
:x-data (hx/json {:i index})
|
||||
:x-show "index == i && hidingIndex != i"
|
||||
"x-trap" "index == i && hidingIndex == -1 && !transitioning"
|
||||
"x-transition:enter" "transition duration-150",
|
||||
"x-transition:enter-end" "translate-x-0 scale-100 opacity-100",
|
||||
"x-transition:leave" "transition duration-150",
|
||||
"x-transition:leave-start" "translate-x-0 scale-100 opacity-100",
|
||||
})
|
||||
{:class (hh/add-class "bg-white rounded-lg shadow dark:bg-gray-700 dark:text-white modal-content flex flex-col h-full" (:class params "")) })
|
||||
children])
|
||||
|
||||
349
src/clj/auto_ap/ssr/components/multi_modal.clj
Normal file
349
src/clj/auto_ap/ssr/components/multi_modal.clj
Normal file
@@ -0,0 +1,349 @@
|
||||
(ns auto-ap.ssr.components.multi-modal
|
||||
(:require [auto-ap.ssr.components :as com]
|
||||
[auto-ap.ssr.form-cursor :as fc]
|
||||
|
||||
[auto-ap.ssr.nested-form-params :refer [wrap-nested-form-params]]
|
||||
[auto-ap.ssr.utils
|
||||
:refer [ html-response
|
||||
assert-schema
|
||||
main-transformer
|
||||
modal-response
|
||||
wrap-form-4xx-2
|
||||
wrap-schema-enforce]]
|
||||
[auto-ap.ssr.components.timeline :as timeline]
|
||||
[bidi.bidi :as bidi]
|
||||
[hiccup.util :as hu]
|
||||
[auto-ap.ssr-routes :as ssr-routes]
|
||||
[auto-ap.ssr.svg :as svg]
|
||||
[auto-ap.ssr.hx :as hx]
|
||||
[malli.core :as mc]
|
||||
[hiccup2.core :as hiccup2]
|
||||
[hiccup2.core :as hiccup]
|
||||
[auto-ap.cursor :as cursor]
|
||||
[malli.core :as m]
|
||||
[auto-ap.logging :as alog])
|
||||
(:import [auto_ap.cursor VecCursor]))
|
||||
|
||||
|
||||
(def default-form-props {:hx-ext "response-targets"
|
||||
:hx-swap "outerHTML"
|
||||
:hx-target-400 "#form-errors .error-content"
|
||||
:hx-trigger "submit"
|
||||
:hx-target "this"
|
||||
"x-trap" "true"
|
||||
:class "h-full w-full" })
|
||||
|
||||
(defprotocol ModalWizardStep
|
||||
(step-key [this])
|
||||
(edit-path [this request])
|
||||
(render-step [this request])
|
||||
(step-schema [this])
|
||||
(step-name [this]))
|
||||
|
||||
(defprotocol Initializable
|
||||
(init-step-params [this request]))
|
||||
|
||||
(defprotocol Discardable
|
||||
(can-discard? [this step-params])
|
||||
(discard-changes [this request]))
|
||||
|
||||
|
||||
(defn- init-step-params- [step request]
|
||||
(if (satisfies? Initializable step)
|
||||
(init-step-params step request)
|
||||
{}))
|
||||
|
||||
(defprotocol LinearModalWizard
|
||||
(hydrate-from-request [this request])
|
||||
(get-current-step [this])
|
||||
(navigate [this step-key])
|
||||
|
||||
(form-schema [this])
|
||||
(steps [this])
|
||||
(get-step [this step-key])
|
||||
(render-wizard [this request])
|
||||
(submit [this request]))
|
||||
|
||||
(defrecord MultiStepFormState [snapshot edit-path step-params])
|
||||
(defn select-state [multi-form-state edit-path default]
|
||||
(->MultiStepFormState (:snapshot multi-form-state)
|
||||
edit-path
|
||||
(or (get-in (:snapshot multi-form-state) edit-path)
|
||||
default)))
|
||||
|
||||
|
||||
(defn merge-multi-form-state [{:keys [snapshot edit-path step-params] :as multi-form-state}]
|
||||
(let [cursor (cursor/cursor (or snapshot {}))
|
||||
;; this hack makes sure that, in the event of a missing vector entry, will make sure to add it first
|
||||
edit-cursor (cond-> cursor
|
||||
(seq edit-path) (cursor/ensure-path! edit-path {})
|
||||
(seq edit-path) (get-in edit-path {}))
|
||||
|
||||
_ (cursor/transact! edit-cursor (fn [spot]
|
||||
(merge spot step-params)))]
|
||||
(assoc multi-form-state
|
||||
:snapshot @cursor
|
||||
:edit-path []
|
||||
:step-params @cursor)))
|
||||
|
||||
(def step-key-schema (mc/schema [:orn {:decode/arbitrary clojure.edn/read-string
|
||||
:encode/arbitrary pr-str}
|
||||
[:sub-step [:cat :keyword [:or :int :string]]]
|
||||
[:step :keyword]]))
|
||||
|
||||
(def encode-step-key
|
||||
(m/-instrument {:schema [:=> [:cat step-key-schema] :any]}
|
||||
(fn encode-step-key [sk]
|
||||
(mc/encode step-key-schema sk main-transformer))))
|
||||
|
||||
|
||||
|
||||
(defn render-timeline [linear-wizard current-step validation-route]
|
||||
(let [step-names (map #(step-name (get-step linear-wizard %)) (steps linear-wizard))
|
||||
active-index (.indexOf step-names (step-name current-step))]
|
||||
(timeline/vertical-timeline
|
||||
{}
|
||||
(for [[n i] (map vector (steps linear-wizard) (range))]
|
||||
(timeline/vertical-timeline-step (cond-> {}
|
||||
(= i active-index) (assoc :active? true)
|
||||
(< i active-index) (assoc :visited? true)
|
||||
(= i (dec (count step-names))) (assoc :last? true))
|
||||
[:a.cursor-pointer.whitespace-nowrap {:x-data (hx/json {:timelineIndex i})
|
||||
:hx-put (hu/url (bidi/path-for ssr-routes/only-routes validation-route)
|
||||
{:from (encode-step-key (step-key current-step))
|
||||
:to (encode-step-key (step-key (get-step linear-wizard n)))})}
|
||||
(step-name (get-step linear-wizard n))])))))
|
||||
(defn back-button [linear-wizard step validation-route]
|
||||
[:a.cursor-pointer.whitespace-nowrap {:hx-put (hu/url (bidi/path-for ssr-routes/only-routes validation-route)
|
||||
{:from (encode-step-key (step-key step))
|
||||
:to (encode-step-key (->> (partition-all 2 1 (steps linear-wizard))
|
||||
(filter (fn [[from to]]
|
||||
(= to (step-key step))))
|
||||
ffirst))})}
|
||||
"Back"])
|
||||
|
||||
(defn default-next-button [linear-wizard step validation-route]
|
||||
(let [steps (steps linear-wizard)
|
||||
last? (= (step-key step) (last steps))
|
||||
next-step (when-not last? (->> steps
|
||||
(drop-while #(not= (step-key step)
|
||||
%))
|
||||
(drop 1)
|
||||
first
|
||||
(get-step linear-wizard)))]
|
||||
(com/validated-save-button (cond-> {:errors (seq fc/*form-errors*)
|
||||
;;:x-data (hx/json {})
|
||||
:x-ref "next"
|
||||
:class "w-48"}
|
||||
(not last?) (assoc :hx-put (hu/url (bidi/path-for ssr-routes/only-routes validation-route)
|
||||
{:from (encode-step-key (step-key step))
|
||||
:to (encode-step-key (step-key next-step))})))
|
||||
|
||||
(if next-step
|
||||
(step-name next-step)
|
||||
"Save")
|
||||
(when-not last?
|
||||
[:div.w-5.h-5 svg/arrow-right]))))
|
||||
|
||||
(defn default-step-body [params & children]
|
||||
[:div.space-y-1 {:class "w-[600px] h-[700px]"}
|
||||
children])
|
||||
|
||||
(defn default-step-footer [linear-wizard step & {:keys [validation-route
|
||||
discard-button
|
||||
next-button]}]
|
||||
[:div.flex.justify-end
|
||||
[:div.flex.items-baseline.gap-x-4
|
||||
(com/form-errors {:errors (:errors (:step-params fc/*form-errors*))})
|
||||
(when (not= (first (steps linear-wizard))
|
||||
(step-key step))
|
||||
(when validation-route
|
||||
(back-button linear-wizard step validation-route)))
|
||||
(when (and (satisfies? Discardable step) (can-discard? step @fc/*current*))
|
||||
discard-button)
|
||||
(cond next-button
|
||||
next-button
|
||||
|
||||
validation-route
|
||||
(default-next-button linear-wizard step validation-route)
|
||||
|
||||
:else
|
||||
[:div "No action possible."])]])
|
||||
|
||||
(defn default-render-step [linear-wizard step & {:keys [head body footer validation-route discard-route]}]
|
||||
(let [is-last? (= (step-key step) (last (steps linear-wizard)))]
|
||||
(com/modal-card-advanced
|
||||
{"@keydown.enter.prevent.stop" "$refs.next.click()"
|
||||
:class (str (when is-last? "last-modal-step")
|
||||
" transition duration-300 ease-in-out
|
||||
")
|
||||
":class" (hiccup/raw "{
|
||||
\"htmx-swapping:-translate-x-2/3 htmx-swapping:opacity-0 htmx-swapping:scale-0 htmx-added:translate-x-2/3 htmx-added:opacity-0 htmx-added:scale-0 scale-100 translate-x-0 opacity-100\": $data.transitionType=='forward',
|
||||
\"htmx-swapping:translate-x-2/3 htmx-swapping:opacity-0 htmx-swapping:scale-0 htmx-added:-translate-x-2/3 htmx-added:opacity-0 htmx-added:scale-0 scale-100 translate-x-0 opacity-100\": $data.transitionType=='backward'
|
||||
}
|
||||
")
|
||||
"x-data" ""}
|
||||
(com/modal-header {}
|
||||
head)
|
||||
#_(com/modal-header-attachment {})
|
||||
[:div.flex.shrink
|
||||
[:div.grow-0.pr-6.pt-2.bg-gray-100.self-stretch #_{:style "margin-left:-20px"} (render-timeline linear-wizard step validation-route)]
|
||||
(com/modal-body {}
|
||||
body)]
|
||||
|
||||
(com/modal-footer {}
|
||||
footer))))
|
||||
|
||||
(defn wrap-ensure-step [handler]
|
||||
(->
|
||||
(fn [{:keys [wizard multi-form-state] :as request}]
|
||||
(assert-schema (step-schema (get-current-step wizard)) (:step-params multi-form-state))
|
||||
(handler request))
|
||||
(wrap-form-4xx-2 (fn [{:keys [wizard] :as request}] ;; THIS MAY BE BETTER TO JUST MAKE THE LINEAR WIZARD POPULATE FROM THE REQUEST
|
||||
(html-response
|
||||
(render-wizard wizard request)
|
||||
:headers {"x-transition-type" "none"
|
||||
"HX-reswap" "outerHTML"})))))
|
||||
|
||||
(defn get-transition-type [wizard from-step-key to-step-key]
|
||||
(let [to-step-index (.indexOf (steps wizard) to-step-key)
|
||||
|
||||
from-step-index (.indexOf (steps wizard)
|
||||
from-step-key)]
|
||||
(cond (= -1 to-step-index)
|
||||
nil
|
||||
(= -1 from-step-index)
|
||||
nil
|
||||
(= from-step-index to-step-index)
|
||||
nil
|
||||
(> from-step-index to-step-index)
|
||||
"backward"
|
||||
:else
|
||||
"forward")))
|
||||
|
||||
(def next-handler
|
||||
(-> (fn [{:keys [wizard] :as request}]
|
||||
(let [current-step (get-current-step wizard)
|
||||
to-step (:to (:query-params request))
|
||||
wizard (navigate wizard to-step)
|
||||
new-step (get-current-step wizard)
|
||||
transition-type (get-transition-type wizard (step-key current-step) to-step)]
|
||||
(html-response
|
||||
(render-wizard wizard
|
||||
(-> request
|
||||
(assoc :multi-form-state (-> (:multi-form-state request)
|
||||
(merge-multi-form-state)
|
||||
(select-state
|
||||
(edit-path new-step request)
|
||||
(init-step-params- new-step request))))))
|
||||
:headers {"HX-reswap" (when transition-type "outerHTML swap:0.15s")
|
||||
"x-transition-type" (or transition-type "none")})))
|
||||
(wrap-ensure-step)
|
||||
(wrap-schema-enforce :query-schema
|
||||
[:map
|
||||
[:to step-key-schema]])))
|
||||
|
||||
(def discard-handler
|
||||
(->
|
||||
(fn [{:keys [wizard multi-form-state] :as request}]
|
||||
(let [current-step (get-current-step wizard)
|
||||
to-step (:to (:query-params request))
|
||||
wizard (navigate wizard to-step)
|
||||
transition-type (get-transition-type wizard (step-key current-step) to-step)]
|
||||
(html-response
|
||||
(render-wizard wizard
|
||||
(-> request
|
||||
(assoc :multi-form-state (discard-changes current-step multi-form-state))))
|
||||
:headers {"HX-reswap" (when transition-type "outerHTML swap:0.15s")
|
||||
"x-transition-type" (or transition-type "none")})))
|
||||
(wrap-schema-enforce :query-schema
|
||||
[:map
|
||||
[:to step-key-schema]])))
|
||||
|
||||
(def submit-handler
|
||||
(-> (fn [{:keys [wizard multi-form-state] :as request}]
|
||||
(submit wizard (-> request
|
||||
(assoc :multi-form-state (merge-multi-form-state multi-form-state)))))
|
||||
(wrap-ensure-step)))
|
||||
|
||||
(defn default-render-wizard [linear-wizard {:keys [multi-form-state form-errors snapshot current-step] :as request} & {:keys [form-params]}]
|
||||
(let [current-step (get-current-step linear-wizard)
|
||||
edit-path (edit-path current-step request)]
|
||||
[:form#wizard-form form-params
|
||||
(fc/start-form multi-form-state (when form-errors {:step-params form-errors})
|
||||
(list
|
||||
(fc/with-field :snapshot
|
||||
(com/hidden {:name (fc/field-name)
|
||||
:value (pr-str (fc/field-value))}))
|
||||
(fc/with-field :edit-path
|
||||
(com/hidden {:name (fc/field-name)
|
||||
:value (pr-str (or edit-path []))}))
|
||||
(com/hidden {:name "current-step"
|
||||
:value (pr-str (step-key current-step))})
|
||||
|
||||
(fc/with-field :step-params
|
||||
(com/modal
|
||||
{:id "wizardmodal"}
|
||||
|
||||
(render-step current-step request)))))]))
|
||||
|
||||
(defn wrap-wizard [handler linear-wizard]
|
||||
(fn [request]
|
||||
(let [current-step-key (if-let [current-step (get (:form-params request) "current-step")]
|
||||
(mc/decode step-key-schema current-step main-transformer)
|
||||
(first (steps linear-wizard)))
|
||||
current-step (get-step linear-wizard current-step-key)
|
||||
multi-form-state (-> (:multi-form-state request)
|
||||
(update :snapshot (fn [snapshot]
|
||||
(mc/decode (form-schema linear-wizard)
|
||||
snapshot
|
||||
main-transformer)))
|
||||
(update :step-params (fn [step-params]
|
||||
(or
|
||||
(mc/decode (step-schema current-step)
|
||||
step-params
|
||||
main-transformer)
|
||||
{} ;; Todo add a defaultable
|
||||
))))
|
||||
request (-> request
|
||||
(assoc :multi-form-state multi-form-state))
|
||||
linear-wizard (navigate linear-wizard current-step-key)]
|
||||
(handler
|
||||
(assoc request :wizard (hydrate-from-request linear-wizard request))))))
|
||||
|
||||
(defn open-wizard-handler [{:keys [wizard current-step] :as request}]
|
||||
(modal-response
|
||||
[:div {:x-data (hx/json {"transitionType" "none"
|
||||
|
||||
}
|
||||
)
|
||||
"@htmx:after-request" "if(event.detail.xhr.getResponseHeader('x-transition-type')) { $data.transitionType = event.detail.xhr.getResponseHeader('x-transition-type');}"
|
||||
}
|
||||
(render-wizard wizard request)]))
|
||||
|
||||
|
||||
|
||||
(defn wrap-init-multi-form-state [handler get-multi-form-state]
|
||||
(->
|
||||
(fn init-multi-form [request]
|
||||
(handler (assoc request :multi-form-state (get-multi-form-state request))))
|
||||
(wrap-nested-form-params)))
|
||||
|
||||
(defn wrap-decode-multi-form-state [handler]
|
||||
(wrap-init-multi-form-state
|
||||
handler
|
||||
(fn parse-multi-form-state [request]
|
||||
(map->MultiStepFormState (mc/decode [:map
|
||||
[:snapshot {:optional true
|
||||
:decode/arbitrary
|
||||
#(clojure.edn/read-string {:readers clj-time.coerce/data-readers
|
||||
:eof nil}
|
||||
%)}
|
||||
[:maybe :any]]
|
||||
[:edit-path {:optional true :decode/arbitrary (fn [z]
|
||||
(clojure.edn/read-string z))} [:maybe [:sequential {:min 0} any?]]]
|
||||
[:step-params {:optional true}
|
||||
[:maybe
|
||||
:any]]]
|
||||
(:form-params request)
|
||||
main-transformer)))))
|
||||
@@ -2,9 +2,9 @@
|
||||
(:require [auto-ap.ssr.hiccup-helper :as hh]))
|
||||
|
||||
(defn timeline-step [{:keys [active? visited? last?]} & children]
|
||||
(if active?
|
||||
(if active?
|
||||
[:li {:class "flex items-center text-primary-600 font-medium dark:text-primary-500"}
|
||||
[:span {:class "flex items-center justify-center w-5 h-5 mr-2 text-xs border-2 border-primary-600 rounded-full shrink-0 dark:border-primary-500"} ]
|
||||
[:span {:class "flex items-center justify-center w-5 h-5 mr-2 text-xs border-2 border-primary-600 rounded-full shrink-0 dark:border-primary-500"}]
|
||||
children
|
||||
(when-not last?
|
||||
[:svg {:class "w-3 h-3 ml-2 sm:ml-4", :aria-hidden "true", :xmlns "http://www.w3.org/2000/svg", :fill "none", :viewbox "0 0 12 10"}
|
||||
@@ -24,5 +24,24 @@
|
||||
[:ol {:class "flex items-center w-full space-x-2 text-xs text-center text-gray-500 bg-white dark:text-gray-400 sm:text-base dark:bg-gray-800 sm:space-x-4 px-2"}
|
||||
children
|
||||
#_[:li {:class "flex items-center"}
|
||||
[:span {:class "flex items-center justify-center w-5 h-5 mr-2 text-xs border border-gray-500 rounded-full shrink-0 dark:border-gray-400"} ]]])
|
||||
[:span {:class "flex items-center justify-center w-5 h-5 mr-2 text-xs border border-gray-500 rounded-full shrink-0 dark:border-gray-400"}]]])
|
||||
|
||||
(defn vertical-timeline-step [{:keys [active? visited? last?]} & children]
|
||||
(if active?
|
||||
[:li {:class "flex items-center text-primary-600 font-medium dark:text-primary-500"}
|
||||
[:span {:class "flex items-center justify-center w-5 h-5 mr-2 text-xs border-2 border-primary-600 rounded-full shrink-0 dark:border-primary-500"}]
|
||||
children ]
|
||||
[:li {:class (cond-> "flex items-center"
|
||||
(not visited?) (hh/add-class "text-gray-400"))}
|
||||
[:span {:class "flex items-center justify-center w-5 h-5 mr-2 text-xs border border-gray-500 rounded-full shrink-0 dark:border-gray-400"}
|
||||
(when visited?
|
||||
[:svg {:class "w-3 h-3 text-primary-600 dark:text-primary-500", :aria-hidden "true", :xmlns "http://www.w3.org/2000/svg", :fill "none", :viewbox "0 0 16 12"}
|
||||
[:path {:stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "2", :d "M1 5.917 5.724 10.5 15 1.5"}]])]
|
||||
children ]))
|
||||
|
||||
(defn vertical-timeline [params & children]
|
||||
[:ol {:class "flex flex-col items-start space-y-2 text-xs text-center text-gray-500 bg-gray-100 dark:text-gray-400 sm:text-base dark:bg-gray-800 sm:space-y-4 px-2"}
|
||||
children
|
||||
#_[:li {:class "flex items-center"}
|
||||
[:span {:class "flex items-center justify-center w-5 h-5 mr-2 text-xs border border-gray-500 rounded-full shrink-0 dark:border-gray-400"}]]])
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
[auto-ap.ssr.admin.import-batch :as import-batch]
|
||||
[auto-ap.ssr.admin.transaction-rules :as admin-rules]
|
||||
[auto-ap.ssr.admin.vendors :as admin-vendors]
|
||||
[auto-ap.ssr.admin.clients :as admin-clients]
|
||||
[auto-ap.ssr.auth :as auth]
|
||||
[auto-ap.ssr.company :as company]
|
||||
[auto-ap.ssr.company-dropdown :as company-dropdown]
|
||||
@@ -54,6 +55,7 @@
|
||||
:company-plaid-table (wrap-client-redirect-unauthenticated (wrap-secure company-plaid/table))
|
||||
:company-plaid-link (wrap-client-redirect-unauthenticated (wrap-secure company-plaid/link))
|
||||
:company-plaid-relink (wrap-client-redirect-unauthenticated (wrap-secure company-plaid/relink))
|
||||
:company-update-signature (wrap-client-redirect-unauthenticated (wrap-secure company/upload-signature-data))
|
||||
:company-yodlee (wrap-client-redirect-unauthenticated (wrap-secure company-yodlee/page))
|
||||
:company-yodlee-table (wrap-client-redirect-unauthenticated (wrap-secure company-yodlee/table))
|
||||
:company-yodlee-fastlink-dialog (wrap-client-redirect-unauthenticated (wrap-secure company-yodlee/fastlink-dialog))
|
||||
@@ -89,5 +91,6 @@
|
||||
(into admin/key->handler)
|
||||
(into admin-jobs/key->handler)
|
||||
(into admin-vendors/key->handler)
|
||||
(into admin-clients/key->handler)
|
||||
(into admin-rules/key->handler)))
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
(:require [auto-ap.ssr.utils :refer [path->name2]]
|
||||
[auto-ap.cursor :as cursor]))
|
||||
|
||||
(def ^:dynamic *prefix* [])
|
||||
(def ^:dynamic *prefix* [])
|
||||
(def ^:dynamic *form-data*)
|
||||
(def ^:dynamic *form-errors*)
|
||||
(def ^:dynamic *prev-cursor* nil)
|
||||
@@ -22,18 +22,30 @@
|
||||
`(binding [*prefix* ~prefix]
|
||||
(start-form ~form-data ~errors ~@rest)))
|
||||
|
||||
(defmacro with-prefix [prefix & rest]
|
||||
`(binding [*prefix* (into (or *prefix* []) ~prefix)]
|
||||
~@rest))
|
||||
|
||||
(defmacro with-cursor [cursor & rest]
|
||||
`(binding [*current* ~cursor]
|
||||
~@rest))
|
||||
~@rest))
|
||||
|
||||
(defmacro with-field [field & rest]
|
||||
`(with-cursor (get *current* ~field )
|
||||
`(with-cursor (get *current* ~field)
|
||||
~@rest))
|
||||
|
||||
(defmacro with-field-default [field default & rest]
|
||||
`(with-cursor (get *current* ~field ~default)
|
||||
~@rest))
|
||||
|
||||
`(let [new-cursor# (get *current* ~field ~default)
|
||||
new-cursor2# (if (not (deref new-cursor#))
|
||||
(do
|
||||
(cursor/transact! *current*
|
||||
(fn [c#]
|
||||
(assoc c# ~field ~default)))
|
||||
(get *current* ~field ~default))
|
||||
|
||||
new-cursor#)]
|
||||
(with-cursor new-cursor2#
|
||||
~@rest)))
|
||||
|
||||
(defn field-name
|
||||
([] (field-name *current*))
|
||||
@@ -62,10 +74,10 @@
|
||||
(defn cursor-map
|
||||
([f] (cursor-map *current* f))
|
||||
([cursor f]
|
||||
(when (field-value)
|
||||
(when (seq (field-value))
|
||||
(doall
|
||||
(for [n cursor]
|
||||
(with-cursor n
|
||||
(f n)))))))
|
||||
(for [n cursor]
|
||||
(with-cursor n
|
||||
(f n)))))))
|
||||
|
||||
|
||||
|
||||
@@ -34,15 +34,14 @@
|
||||
(remove-wildcard [this wildcard]
|
||||
(if (sequential? wildcard)
|
||||
(reduce
|
||||
remove-wildcard
|
||||
this
|
||||
wildcard)
|
||||
remove-wildcard
|
||||
this
|
||||
wildcard)
|
||||
(reduce
|
||||
remove-class
|
||||
this
|
||||
(filter (fn [c]
|
||||
(str/starts-with? c wildcard)
|
||||
) @class-set)))
|
||||
remove-class
|
||||
this
|
||||
(filter (fn [c]
|
||||
(str/starts-with? c wildcard)) @class-set)))
|
||||
this)
|
||||
(replace-wildcard [this wildcard add]
|
||||
(remove-wildcard this wildcard)
|
||||
@@ -51,7 +50,7 @@
|
||||
(replace-tw [this add]
|
||||
;; TODO
|
||||
)
|
||||
Object
|
||||
Object
|
||||
(toString [this]
|
||||
(str/join " " @class-set)))))
|
||||
|
||||
@@ -60,8 +59,7 @@
|
||||
(add-class [this add]
|
||||
(add-class (string->class-list this) add))
|
||||
(remove-class [this remove]
|
||||
(remove-class (string->class-list this) remove)
|
||||
)
|
||||
(remove-class (string->class-list this) remove))
|
||||
(replace-class [this remove add]
|
||||
(replace-class (string->class-list this) remove add))
|
||||
(remove-wildcard [this wildcard]
|
||||
@@ -70,29 +68,11 @@
|
||||
(replace-wildcard (string->class-list this) wildcard add))
|
||||
(add-tw [this tw]
|
||||
(replace-tw (string->class-list this)
|
||||
tw)
|
||||
))
|
||||
tw)))
|
||||
|
||||
|
||||
(str (hiccup/html [:div {:class (-> "hello bryce hello-1 hello-2"
|
||||
(replace-wildcard ["hello-" "b"] ["hi" "there"]))}]))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
(str (hiccup/html [:div {:class (-> "p-1.5 "
|
||||
(add-class "bg-blue-500")
|
||||
#_(replace-wildcard ["hello-" "b"] ["hi" "there"]))}]))
|
||||
|
||||
@@ -84,51 +84,51 @@
|
||||
matching-count]))
|
||||
|
||||
(def grid-page
|
||||
(helper/build
|
||||
{:id "cash-drawer-shift-table"
|
||||
:nav (com/main-aside-nav)
|
||||
:page-specific-nav filters
|
||||
:fetch-page fetch-page
|
||||
:oob-render
|
||||
(fn [request]
|
||||
[(assoc-in (date-range-field* request) [1 :hx-swap-oob] true)])
|
||||
:breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes
|
||||
:company)}
|
||||
"POS"]
|
||||
{}
|
||||
#_(helper/build
|
||||
{:id "cash-drawer-shift-table"
|
||||
:nav (com/main-aside-nav)
|
||||
:page-specific-nav filters
|
||||
:fetch-page fetch-page
|
||||
:oob-render
|
||||
(fn [request]
|
||||
[(assoc-in (date-range-field* request) [1 :hx-swap-oob] true)])
|
||||
:breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes
|
||||
:company)}
|
||||
"POS"]
|
||||
|
||||
[:a {:href (bidi/path-for ssr-routes/only-routes
|
||||
:pos-cash-drawer-shifts)}
|
||||
"Cash Drawer Shifts"]]
|
||||
:title "Cash drawer shifts"
|
||||
:entity-name "Cash drawer shift"
|
||||
:route :pos-cash-drawer-shift-table
|
||||
:headers [{:key "client"
|
||||
:name "Client"
|
||||
:sort-key "client"
|
||||
:hide? (fn [args]
|
||||
(= (count (:clients args)) 1))
|
||||
:render #(-> % :cash-drawer-shift/client :client/code)}
|
||||
{:key "date"
|
||||
:name "Date"
|
||||
:sort-key "date"
|
||||
:render #(atime/unparse-local (:cash-drawer-shift/date %) atime/standard-time)}
|
||||
{:key "paid-in"
|
||||
:name "Paid in"
|
||||
:sort-key "paid-in"
|
||||
:render #(some->> % :cash-drawer-shift/paid-in (format "$%.2f"))}
|
||||
{:key "paid-out"
|
||||
:name "Paid out"
|
||||
:sort-key "paid-out"
|
||||
:render #(some->> % :cash-drawer-shift/paid-out (format "$%.2f"))}
|
||||
{:key "expected-cash"
|
||||
:name "Expected cash"
|
||||
:sort-key "expected-cash"
|
||||
:render #(some->> % :cash-drawer-shift/expected-cash (format "$%.2f"))}
|
||||
{:key "opened-cash"
|
||||
:name "Opened cash"
|
||||
:sort-key "opened-cash"
|
||||
:render #(some->> % :cash-drawer-shift/opened-cash (format "$%.2f"))}
|
||||
]}))
|
||||
[:a {:href (bidi/path-for ssr-routes/only-routes
|
||||
:pos-cash-drawer-shifts)}
|
||||
"Cash Drawer Shifts"]]
|
||||
:title "Cash drawer shifts"
|
||||
:entity-name "Cash drawer shift"
|
||||
:route :pos-cash-drawer-shift-table
|
||||
:headers [{:key "client"
|
||||
:name "Client"
|
||||
:sort-key "client"
|
||||
:hide? (fn [args]
|
||||
(= (count (:clients args)) 1))
|
||||
:render #(-> % :cash-drawer-shift/client :client/code)}
|
||||
{:key "date"
|
||||
:name "Date"
|
||||
:sort-key "date"
|
||||
:render #(atime/unparse-local (:cash-drawer-shift/date %) atime/standard-time)}
|
||||
{:key "paid-in"
|
||||
:name "Paid in"
|
||||
:sort-key "paid-in"
|
||||
:render #(some->> % :cash-drawer-shift/paid-in (format "$%.2f"))}
|
||||
{:key "paid-out"
|
||||
:name "Paid out"
|
||||
:sort-key "paid-out"
|
||||
:render #(some->> % :cash-drawer-shift/paid-out (format "$%.2f"))}
|
||||
{:key "expected-cash"
|
||||
:name "Expected cash"
|
||||
:sort-key "expected-cash"
|
||||
:render #(some->> % :cash-drawer-shift/expected-cash (format "$%.2f"))}
|
||||
{:key "opened-cash"
|
||||
:name "Opened cash"
|
||||
:sort-key "opened-cash"
|
||||
:render #(some->> % :cash-drawer-shift/opened-cash (format "$%.2f"))}]}))
|
||||
|
||||
|
||||
(def row* (partial helper/row* grid-page))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
(ns auto-ap.ssr.svg)
|
||||
|
||||
(def pie
|
||||
(def pie
|
||||
[:svg {:xmlns "http://www.w3.org/2000/svg", :viewbox "0 0 24 24"}
|
||||
[:defs]
|
||||
[:title "analytics-pie-2"]
|
||||
@@ -11,28 +11,28 @@
|
||||
|
||||
(def accounting-invoice-mail
|
||||
[:svg {:xmlns "http://www.w3.org/2000/svg", :viewbox "0 0 24 24"}
|
||||
[:defs]
|
||||
[:title "accounting-document"]
|
||||
[:path {:d "M21.75,21.75a1.5,1.5,0,0,1-1.5,1.5H3.75a1.5,1.5,0,0,1-1.5-1.5V2.25A1.5,1.5,0,0,1,3.75.75H14.379a1.5,1.5,0,0,1,1.06.439l5.872,5.872a1.5,1.5,0,0,1,.439,1.06Z", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1.5px"}]
|
||||
[:path {:d "M21.75,8.25h-6a1.5,1.5,0,0,1-1.5-1.5v-6", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1.5px"}]
|
||||
[:path {:d "M6.2,11.594a2.224,2.224,0,0,0,1.858.875c1.139,0,2.063-.693,2.063-1.547S9.2,9.376,8.062,9.376,6,8.683,6,7.828s.924-1.547,2.062-1.547a2.221,2.221,0,0,1,1.858.875", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1.5px"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "12.469", :stroke-linecap "round", :stroke-width "1.5px", :x1 "8.062", :y2 "13.5", :x2 "8.062"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "5.25", :stroke-linecap "round", :stroke-width "1.5px", :x1 "8.062", :y2 "6.281", :x2 "8.062"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "15", :stroke-linecap "round", :stroke-width "1.5px", :x1 "12", :y2 "15", :x2 "18"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "19.5", :stroke-linecap "round", :stroke-width "1.5px", :x1 "6.75", :y2 "19.5", :x2 "18"}]])
|
||||
[:defs]
|
||||
[:title "accounting-document"]
|
||||
[:path {:d "M21.75,21.75a1.5,1.5,0,0,1-1.5,1.5H3.75a1.5,1.5,0,0,1-1.5-1.5V2.25A1.5,1.5,0,0,1,3.75.75H14.379a1.5,1.5,0,0,1,1.06.439l5.872,5.872a1.5,1.5,0,0,1,.439,1.06Z", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1.5px"}]
|
||||
[:path {:d "M21.75,8.25h-6a1.5,1.5,0,0,1-1.5-1.5v-6", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1.5px"}]
|
||||
[:path {:d "M6.2,11.594a2.224,2.224,0,0,0,1.858.875c1.139,0,2.063-.693,2.063-1.547S9.2,9.376,8.062,9.376,6,8.683,6,7.828s.924-1.547,2.062-1.547a2.221,2.221,0,0,1,1.858.875", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1.5px"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "12.469", :stroke-linecap "round", :stroke-width "1.5px", :x1 "8.062", :y2 "13.5", :x2 "8.062"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "5.25", :stroke-linecap "round", :stroke-width "1.5px", :x1 "8.062", :y2 "6.281", :x2 "8.062"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "15", :stroke-linecap "round", :stroke-width "1.5px", :x1 "12", :y2 "15", :x2 "18"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "19.5", :stroke-linecap "round", :stroke-width "1.5px", :x1 "6.75", :y2 "19.5", :x2 "18"}]])
|
||||
|
||||
(def receipt-register-1
|
||||
[:svg {:xmlns "http://www.w3.org/2000/svg", :viewbox "0 0 24 24"}
|
||||
[:g
|
||||
[:path {:d "M17.63,18h3a1.5,1.5,0,0,1,1.5,1.5v2.25a1.5,1.5,0,0,1-1.5,1.5H3.38a1.5,1.5,0,0,1-1.5-1.5V18H7.13", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1.5px"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "17.03", :stroke-linecap "round", :stroke-width "1.5px", :x1 "11.45", :y2 "14.35", :x2 "8.71"}]
|
||||
[:circle {:cx "12.48", :cy "18.11", :r "1.5", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1.5px"}]
|
||||
[:path {:d "M1.88,4.5H8.63L9.81,3.31A1.5,1.5,0,0,0,8.75.75H3.38a1.5,1.5,0,0,0-1.5,1.5V18", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1.5px"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "6.1", :stroke-linecap "round", :stroke-width "1.5px", :x1 "13.22", :y2 "6.59", :x2 "14.2"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "8.54", :stroke-linecap "round", :stroke-width "1.5px", :x1 "17.13", :y2 "9.25", :x2 "17.97"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "12.09", :stroke-linecap "round", :stroke-width "1.5px", :x1 "19.9", :y2 "13.03", :x2 "20.46"}]
|
||||
[:path {:d "M8.63,4.5V7.74A12.22,12.22,0,0,1,18.92,18", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1.5px"}]
|
||||
[:circle {:cx "7.13", :cy "12.75", :r "2.25", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1.5px"}]]])
|
||||
[:g
|
||||
[:path {:d "M17.63,18h3a1.5,1.5,0,0,1,1.5,1.5v2.25a1.5,1.5,0,0,1-1.5,1.5H3.38a1.5,1.5,0,0,1-1.5-1.5V18H7.13", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1.5px"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "17.03", :stroke-linecap "round", :stroke-width "1.5px", :x1 "11.45", :y2 "14.35", :x2 "8.71"}]
|
||||
[:circle {:cx "12.48", :cy "18.11", :r "1.5", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1.5px"}]
|
||||
[:path {:d "M1.88,4.5H8.63L9.81,3.31A1.5,1.5,0,0,0,8.75.75H3.38a1.5,1.5,0,0,0-1.5,1.5V18", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1.5px"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "6.1", :stroke-linecap "round", :stroke-width "1.5px", :x1 "13.22", :y2 "6.59", :x2 "14.2"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "8.54", :stroke-linecap "round", :stroke-width "1.5px", :x1 "17.13", :y2 "9.25", :x2 "17.97"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "12.09", :stroke-linecap "round", :stroke-width "1.5px", :x1 "19.9", :y2 "13.03", :x2 "20.46"}]
|
||||
[:path {:d "M8.63,4.5V7.74A12.22,12.22,0,0,1,18.92,18", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1.5px"}]
|
||||
[:circle {:cx "7.13", :cy "12.75", :r "2.25", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1.5px"}]]])
|
||||
|
||||
(def payments
|
||||
[:svg {:xmlns "http://www.w3.org/2000/svg", :viewbox "0 0 24 24"}
|
||||
@@ -45,38 +45,38 @@
|
||||
|
||||
(def bank
|
||||
[:svg {:xmlns "http://www.w3.org/2000/svg", :viewbox "0 0 24 24"}
|
||||
[:g
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "23.25", :stroke-linecap "round", :stroke-width "1.5px", :x1 "0.75", :y2 "23.25", :x2 "23.25"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "19.25", :stroke-linecap "round", :stroke-width "1.5px", :x1 "0.75", :y2 "19.25", :x2 "23.25"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "10.25", :stroke-linecap "round", :stroke-width "1.5px", :x1 "2", :y2 "16.25", :x2 "2"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "10.25", :stroke-linecap "round", :stroke-width "1.5px", :x1 "5.5", :y2 "16.25", :x2 "5.5"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "10.25", :stroke-linecap "round", :stroke-width "1.5px", :x1 "10.25", :y2 "16.25", :x2 "10.25"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "10.25", :stroke-linecap "round", :stroke-width "1.5px", :x1 "13.75", :y2 "16.25", :x2 "13.75"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "10.25", :stroke-linecap "round", :stroke-width "1.5px", :x1 "18.5", :y2 "16.25", :x2 "18.5"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "10.25", :stroke-linecap "round", :stroke-width "1.5px", :x1 "22", :y2 "16.25", :x2 "22"}]
|
||||
[:path {:d "M23.25,7.25H.75L11.19,1a1.49,1.49,0,0,1,1.62,0Z", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1.5px"}]]])
|
||||
[:g
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "23.25", :stroke-linecap "round", :stroke-width "1.5px", :x1 "0.75", :y2 "23.25", :x2 "23.25"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "19.25", :stroke-linecap "round", :stroke-width "1.5px", :x1 "0.75", :y2 "19.25", :x2 "23.25"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "10.25", :stroke-linecap "round", :stroke-width "1.5px", :x1 "2", :y2 "16.25", :x2 "2"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "10.25", :stroke-linecap "round", :stroke-width "1.5px", :x1 "5.5", :y2 "16.25", :x2 "5.5"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "10.25", :stroke-linecap "round", :stroke-width "1.5px", :x1 "10.25", :y2 "16.25", :x2 "10.25"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "10.25", :stroke-linecap "round", :stroke-width "1.5px", :x1 "13.75", :y2 "16.25", :x2 "13.75"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "10.25", :stroke-linecap "round", :stroke-width "1.5px", :x1 "18.5", :y2 "16.25", :x2 "18.5"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "10.25", :stroke-linecap "round", :stroke-width "1.5px", :x1 "22", :y2 "16.25", :x2 "22"}]
|
||||
[:path {:d "M23.25,7.25H.75L11.19,1a1.49,1.49,0,0,1,1.62,0Z", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1.5px"}]]])
|
||||
|
||||
(def receipt
|
||||
[:svg {:xmlns "http://www.w3.org/2000/svg", :viewbox "0 0 24 24"}
|
||||
[:g
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "21.75", :stroke-linecap "round", :stroke-width "1.5px", :x1 "17.25", :y2 "21.75", :x2 "11.25"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "13.25", :stroke-linecap "round", :stroke-width "1.5px", :x1 "8.75", :y2 "13.25", :x2 "15.25"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "9.75", :stroke-linecap "round", :stroke-width "1.5px", :x1 "8.75", :y2 "9.75", :x2 "15.25"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "6.25", :stroke-linecap "round", :stroke-width "1.5px", :x1 "8.75", :y2 "6.25", :x2 "15.25"}]
|
||||
[:path {:d "M20.25.75h-1.5v5.5h4.5V3.75A3,3,0,0,0,20.25.75Z", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1.5px"}]
|
||||
[:path {:d "M13.5,17.25H3.75a3,3,0,0,0-3,3v3h10.5V19.5a2.25,2.25,0,0,1,4.5,0v.75a1.5,1.5,0,0,0,1.5,1.5h0a1.5,1.5,0,0,0,1.5-1.5V.75H8.25a3,3,0,0,0-3,3v13.5", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1.5px"}]]])
|
||||
[:g
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "21.75", :stroke-linecap "round", :stroke-width "1.5px", :x1 "17.25", :y2 "21.75", :x2 "11.25"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "13.25", :stroke-linecap "round", :stroke-width "1.5px", :x1 "8.75", :y2 "13.25", :x2 "15.25"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "9.75", :stroke-linecap "round", :stroke-width "1.5px", :x1 "8.75", :y2 "9.75", :x2 "15.25"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "6.25", :stroke-linecap "round", :stroke-width "1.5px", :x1 "8.75", :y2 "6.25", :x2 "15.25"}]
|
||||
[:path {:d "M20.25.75h-1.5v5.5h4.5V3.75A3,3,0,0,0,20.25.75Z", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1.5px"}]
|
||||
[:path {:d "M13.5,17.25H3.75a3,3,0,0,0-3,3v3h10.5V19.5a2.25,2.25,0,0,1,4.5,0v.75a1.5,1.5,0,0,0,1.5,1.5h0a1.5,1.5,0,0,0,1.5-1.5V.75H8.25a3,3,0,0,0-3,3v13.5", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1.5px"}]]])
|
||||
|
||||
(defn spinner [{:keys [class]}]
|
||||
[:svg {:aria-hidden "true", :role "status", :class (str "animate-spin " class) :viewbox "0 0 100 101", :fill "none", :xmlns "http://www.w3.org/2000/svg"}
|
||||
[:path {:d "M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z", :fill "#E5E7EB"}]
|
||||
[:path {:d "M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z", :fill "currentColor"}]])
|
||||
[:path {:d "M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z", :fill "#E5E7EB"}]
|
||||
[:path {:d "M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z", :fill "currentColor"}]])
|
||||
|
||||
(defn spinner-primary [{:keys [class]}]
|
||||
[:svg {:aria-hidden "true", :role "status", :class (str "animate-spin " class) :viewbox "0 0 100 101", :fill "none", :xmlns "http://www.w3.org/2000/svg"}
|
||||
[:path {:d "M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z", :fill "#79b52e"}]
|
||||
[:path {:d "M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z", :fill "currentColor"}]])
|
||||
|
||||
(def search
|
||||
(def search
|
||||
[:svg {:xmlns "http://www.w3.org/2000/svg", :viewbox "0 0 24 24"}
|
||||
[:defs]
|
||||
[:title "search"]
|
||||
@@ -93,11 +93,11 @@
|
||||
|
||||
|
||||
(def home
|
||||
[:svg { :fill "currentColor", :viewbox "0 0 20 20", :xmlns "http://www.w3.org/2000/svg"}
|
||||
[:svg {:fill "currentColor", :viewbox "0 0 20 20", :xmlns "http://www.w3.org/2000/svg"}
|
||||
[:path {:d "M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z"}]])
|
||||
|
||||
(def breadcrumb-component
|
||||
[:svg { :fill "currentColor", :viewbox "0 0 20 20", :xmlns "http://www.w3.org/2000/svg"}
|
||||
[:svg {:fill "currentColor", :viewbox "0 0 20 20", :xmlns "http://www.w3.org/2000/svg"}
|
||||
[:path {:fill-rule "evenodd", :d "M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z", :clip-rule "evenodd"}]])
|
||||
|
||||
(def refresh
|
||||
@@ -106,7 +106,7 @@
|
||||
|
||||
|
||||
(def upload
|
||||
[:svg { :xmlns "http://www.w3.org/2000/svg", :fill "none", :viewbox "0 0 24 24", :stroke-width "2", :stroke "currentColor", :aria-hidden "true"}
|
||||
[:svg {:xmlns "http://www.w3.org/2000/svg", :fill "none", :viewbox "0 0 24 24", :stroke-width "2", :stroke "currentColor", :aria-hidden "true"}
|
||||
[:path {:stroke-linecap "round", :stroke-linejoin "round", :d "M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5m-13.5-9L12 3m0 0l4.5 4.5M12 3v13.5"}]])
|
||||
|
||||
(def vendors
|
||||
@@ -122,29 +122,29 @@
|
||||
|
||||
(def report
|
||||
[:svg {:xmlns "http://www.w3.org/2000/svg", :viewbox "0 0 24 24"}
|
||||
[:defs]
|
||||
[:title "app-window-pie-chart"]
|
||||
[:rect {:y "2.253", :rx "1.5", :stroke "currentColor", :fill "none", :stroke-linejoin "round", :width "21", :stroke-linecap "round", :stroke-width "1.5px", :x "1.51", :ry "1.5", :height "19.5"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "6.753", :stroke-linecap "round", :stroke-width "1.5px", :x1 "1.51", :y2 "6.753", :x2 "22.51"}]
|
||||
[:circle {:cx "9.01", :cy "14.253", :r "4.5", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1.5px"}]
|
||||
[:polyline {:points "9.01 9.753 9.01 14.253 12.192 17.435", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1.5px"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "11.253", :stroke-linecap "round", :stroke-width "1.5px", :x1 "16.51", :y2 "11.253", :x2 "19.51"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "14.253", :stroke-linecap "round", :stroke-width "1.5px", :x1 "16.51", :y2 "14.253", :x2 "19.51"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "17.253", :stroke-linecap "round", :stroke-width "1.5px", :x1 "16.51", :y2 "17.253", :x2 "19.51"}]])
|
||||
[:defs]
|
||||
[:title "app-window-pie-chart"]
|
||||
[:rect {:y "2.253", :rx "1.5", :stroke "currentColor", :fill "none", :stroke-linejoin "round", :width "21", :stroke-linecap "round", :stroke-width "1.5px", :x "1.51", :ry "1.5", :height "19.5"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "6.753", :stroke-linecap "round", :stroke-width "1.5px", :x1 "1.51", :y2 "6.753", :x2 "22.51"}]
|
||||
[:circle {:cx "9.01", :cy "14.253", :r "4.5", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1.5px"}]
|
||||
[:polyline {:points "9.01 9.753 9.01 14.253 12.192 17.435", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1.5px"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "11.253", :stroke-linecap "round", :stroke-width "1.5px", :x1 "16.51", :y2 "11.253", :x2 "19.51"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "14.253", :stroke-linecap "round", :stroke-width "1.5px", :x1 "16.51", :y2 "14.253", :x2 "19.51"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "17.253", :stroke-linecap "round", :stroke-width "1.5px", :x1 "16.51", :y2 "17.253", :x2 "19.51"}]])
|
||||
|
||||
(def government-building
|
||||
[:svg {:xmlns "http://www.w3.org/2000/svg", :viewbox "0 0 24 24"}
|
||||
[:g
|
||||
[:rect {:y "14.25", :stroke "currentColor", :fill "none", :stroke-linejoin "round", :width "3", :stroke-linecap "round", :stroke-width "1.5px", :x "3.5", :height "6"}]
|
||||
[:rect {:y "14.25", :stroke "currentColor", :fill "none", :stroke-linejoin "round", :width "3", :stroke-linecap "round", :stroke-width "1.5px", :x "10.5", :height "6"}]
|
||||
[:rect {:y "14.25", :stroke "currentColor", :fill "none", :stroke-linejoin "round", :width "3", :stroke-linecap "round", :stroke-width "1.5px", :x "17.5", :height "6"}]
|
||||
[:path {:d "M21.75,13.39a.87.87,0,0,1-.86.86H3.11a.86.86,0,0,1-.25-1.69l8.85-2.72a1,1,0,0,1,.58,0l8.85,2.72A.87.87,0,0,1,21.75,13.39Z", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1.5px"}]
|
||||
[:polyline {:points "15.5 8.25 18 8.25 18 11.6", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1.5px"}]
|
||||
[:polyline {:points "6 11.6 6 8.25 8.5 8.25", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1.5px"}]
|
||||
[:path {:d "M6,8.25a6,6,0,0,1,12,0", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1.5px"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "0.75", :stroke-linecap "round", :stroke-width "1.5px", :x1 "12", :y2 "2.25", :x2 "12"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "23.25", :stroke-linecap "round", :stroke-width "1.5px", :x1 "0.75", :y2 "23.25", :x2 "23.25"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "20.25", :stroke-linecap "round", :stroke-width "1.5px", :x1 "2", :y2 "20.25", :x2 "22"}]]])
|
||||
[:g
|
||||
[:rect {:y "14.25", :stroke "currentColor", :fill "none", :stroke-linejoin "round", :width "3", :stroke-linecap "round", :stroke-width "1.5px", :x "3.5", :height "6"}]
|
||||
[:rect {:y "14.25", :stroke "currentColor", :fill "none", :stroke-linejoin "round", :width "3", :stroke-linecap "round", :stroke-width "1.5px", :x "10.5", :height "6"}]
|
||||
[:rect {:y "14.25", :stroke "currentColor", :fill "none", :stroke-linejoin "round", :width "3", :stroke-linecap "round", :stroke-width "1.5px", :x "17.5", :height "6"}]
|
||||
[:path {:d "M21.75,13.39a.87.87,0,0,1-.86.86H3.11a.86.86,0,0,1-.25-1.69l8.85-2.72a1,1,0,0,1,.58,0l8.85,2.72A.87.87,0,0,1,21.75,13.39Z", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1.5px"}]
|
||||
[:polyline {:points "15.5 8.25 18 8.25 18 11.6", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1.5px"}]
|
||||
[:polyline {:points "6 11.6 6 8.25 8.5 8.25", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1.5px"}]
|
||||
[:path {:d "M6,8.25a6,6,0,0,1,12,0", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1.5px"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "0.75", :stroke-linecap "round", :stroke-width "1.5px", :x1 "12", :y2 "2.25", :x2 "12"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "23.25", :stroke-linecap "round", :stroke-width "1.5px", :x1 "0.75", :y2 "23.25", :x2 "23.25"}]
|
||||
[:line {:stroke "currentColor", :fill "none", :stroke-linejoin "round", :y1 "20.25", :stroke-linecap "round", :stroke-width "1.5px", :x1 "2", :y2 "20.25", :x2 "22"}]]])
|
||||
|
||||
(def external-link
|
||||
[:svg
|
||||
@@ -168,7 +168,7 @@
|
||||
"M14.387,13v5.5a1,1,0,0,1-1,1h-12a1,1,0,0,1-1-1V6.5a1,1,0,0,1,1-1h12a1,1,0,0,1,1,1V7",
|
||||
:fill "none",
|
||||
:stroke "currentColor",
|
||||
:stroke-linecap "round",
|
||||
:stroke-linecap "round",
|
||||
:stroke-linejoin "round"}]])
|
||||
(def play
|
||||
[:svg {:xmlns "http://www.w3.org/2000/svg", :viewbox "-0.5 -0.5 24 24"}
|
||||
@@ -201,9 +201,19 @@
|
||||
{:d "M21.914,6.328,17.672,2.086l.707-.707a3,3,0,0,1,4.242,4.242Z",
|
||||
:fill "none",
|
||||
:stroke "currentColor",
|
||||
:stroke-linecap "round",
|
||||
:stroke-linecap "round",
|
||||
:stroke-linejoin "round"}]])
|
||||
|
||||
(def dollar-tag
|
||||
[:svg {:xmlns "http://www.w3.org/2000/svg", :viewbox "0 0 24 24"}
|
||||
[:defs]
|
||||
[:title "tag-dollar"]
|
||||
[:path {:d "M17.5 5a1.5 1.5 0 1 0 3 0 1.5 1.5 0 1 0 -3 0", :fill "none", :stroke "currentcolor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1"}]
|
||||
[:path {:d "m14.257 12.571 -1.985 -1.985a1.107 1.107 0 0 0 -1.686 0c-0.925 0.924 1.2 4.46 0.272 5.384a1.171 1.171 0 0 1 -1.687 0l-1.985 -1.985", :fill "none", :stroke "currentcolor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1"}]
|
||||
[:path {:d "m12.843 11.157 1.061 -1.061", :fill "none", :stroke "currentcolor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1"}]
|
||||
[:path {:d "m7.54 16.46 1.061 -1.061", :fill "none", :stroke "currentcolor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1"}]
|
||||
[:path {:d "M0.854 12.646a1.207 1.207 0 0 0 0 1.708l8.792 8.792a1.207 1.207 0 0 0 1.708 0l11.439 -11.439A2.414 2.414 0 0 0 23.5 10V1.5a1 1 0 0 0 -1 -1H14a2.414 2.414 0 0 0 -1.707 0.707Z", :fill "none", :stroke "currentcolor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1"}]])
|
||||
|
||||
(def drop-down
|
||||
[:svg {:aria-hidden "true", :fill "none", :stroke "currentColor", :viewbox "0 0 24 24", :xmlns "http://www.w3.org/2000/svg"}
|
||||
[:path {:stroke-linecap "round", :stroke-linejoin "round", :stroke-width "2", :d "M19 9l-7 7-7-7"}]])
|
||||
@@ -224,63 +234,63 @@
|
||||
{:d "M23.5,18.5v4a1,1,0,0,1-1,1H1.5a1,1,0,0,1-1-1v-4",
|
||||
:fill "none",
|
||||
:stroke "currentColor",
|
||||
:stroke-linecap "round",
|
||||
:stroke-linecap "round",
|
||||
:stroke-linejoin "round"}]])
|
||||
|
||||
(def trash
|
||||
[:svg
|
||||
{:xmlns "http://www.w3.org/2000/svg", :viewBox "0 0 24 24"}
|
||||
[:defs]
|
||||
[:title "bin-1"]
|
||||
[:path
|
||||
{:d
|
||||
"M21,4.5,19.188,21.709A2,2,0,0,1,17.2,23.5H6.8a2,2,0,0,1-1.989-1.791L3,4.5",
|
||||
:fill "none",
|
||||
:stroke "currentColor",
|
||||
:stroke-linecap "round",
|
||||
:stroke-linejoin "round"}]
|
||||
[:line
|
||||
{:x1 "0.5",
|
||||
:y1 "4.5",
|
||||
:x2 "23.5",
|
||||
:y2 "4.5",
|
||||
:fill "none",
|
||||
:stroke "currentColor",
|
||||
:stroke-linecap "round",
|
||||
:stroke-linejoin "round"}]
|
||||
[:path
|
||||
{:d "M7.5,4.5v-3a1,1,0,0,1,1-1h7a1,1,0,0,1,1,1v3",
|
||||
:fill "none",
|
||||
:stroke "currentColor",
|
||||
:stroke-linecap "round",
|
||||
:stroke-linejoin "round"}]
|
||||
[:line
|
||||
{:x1 "12",
|
||||
:y1 "9",
|
||||
:x2 "12",
|
||||
:y2 "19.5",
|
||||
:fill "none",
|
||||
:stroke "currentColor",
|
||||
:stroke-linecap "round",
|
||||
:stroke-linejoin "round"}]
|
||||
[:line
|
||||
{:x1 "16.5",
|
||||
:y1 "9",
|
||||
:x2 "16",
|
||||
:y2 "19.5",
|
||||
:fill "none",
|
||||
:stroke "currentColor",
|
||||
:stroke-linecap "round",
|
||||
:stroke-linejoin "round"}]
|
||||
[:line
|
||||
{:x1 "7.5",
|
||||
:y1 "9",
|
||||
:x2 "8",
|
||||
:y2 "19.5",
|
||||
:fill "none",
|
||||
:stroke "currentColor",
|
||||
:stroke-linecap "round",
|
||||
:stroke-linejoin "round"}]])
|
||||
{:xmlns "http://www.w3.org/2000/svg", :viewBox "0 0 24 24"}
|
||||
[:defs]
|
||||
[:title "bin-1"]
|
||||
[:path
|
||||
{:d
|
||||
"M21,4.5,19.188,21.709A2,2,0,0,1,17.2,23.5H6.8a2,2,0,0,1-1.989-1.791L3,4.5",
|
||||
:fill "none",
|
||||
:stroke "currentColor",
|
||||
:stroke-linecap "round",
|
||||
:stroke-linejoin "round"}]
|
||||
[:line
|
||||
{:x1 "0.5",
|
||||
:y1 "4.5",
|
||||
:x2 "23.5",
|
||||
:y2 "4.5",
|
||||
:fill "none",
|
||||
:stroke "currentColor",
|
||||
:stroke-linecap "round",
|
||||
:stroke-linejoin "round"}]
|
||||
[:path
|
||||
{:d "M7.5,4.5v-3a1,1,0,0,1,1-1h7a1,1,0,0,1,1,1v3",
|
||||
:fill "none",
|
||||
:stroke "currentColor",
|
||||
:stroke-linecap "round",
|
||||
:stroke-linejoin "round"}]
|
||||
[:line
|
||||
{:x1 "12",
|
||||
:y1 "9",
|
||||
:x2 "12",
|
||||
:y2 "19.5",
|
||||
:fill "none",
|
||||
:stroke "currentColor",
|
||||
:stroke-linecap "round",
|
||||
:stroke-linejoin "round"}]
|
||||
[:line
|
||||
{:x1 "16.5",
|
||||
:y1 "9",
|
||||
:x2 "16",
|
||||
:y2 "19.5",
|
||||
:fill "none",
|
||||
:stroke "currentColor",
|
||||
:stroke-linecap "round",
|
||||
:stroke-linejoin "round"}]
|
||||
[:line
|
||||
{:x1 "7.5",
|
||||
:y1 "9",
|
||||
:x2 "8",
|
||||
:y2 "19.5",
|
||||
:fill "none",
|
||||
:stroke "currentColor",
|
||||
:stroke-linecap "round",
|
||||
:stroke-linejoin "round"}]])
|
||||
|
||||
(def alert
|
||||
[:svg {:xmlns "http://www.w3.org/2000/svg", :viewbox "0 0 24 24"}
|
||||
@@ -442,3 +452,39 @@
|
||||
[:defs]
|
||||
[:title "dislike"]
|
||||
[:path {:d "M4.5,8h0a1.5,1.5,0,0,1,0-3h1a1.5,1.5,0,0,1,0-3H12c4,0,3,1.87,11,1.87V13H20a7.811,7.811,0,0,0-7.5,7.856c0,1.582-3,1.813-3-1.187A29.774,29.774,0,0,1,10.5,14h-8a1.5,1.5,0,0,1,0-3h1a1.5,1.5,0,0,1,0-3h1", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]])
|
||||
|
||||
(def dollar
|
||||
[:svg {:xmlns "http://www.w3.org/2000/svg", :fill "none", :viewbox "0 0 24 24"}
|
||||
[:path {:stroke "currentcolor", :stroke-linecap "round", :stroke-linejoin "round", :d "M21 7H3a0.5 0.5 0 0 0 -0.5 0.5v9a0.5 0.5 0 0 0 0.5 0.5h18a0.5 0.5 0 0 0 0.5 -0.5v-9A0.5 0.5 0 0 0 21 7Z", :stroke-width "1"}]
|
||||
[:path {:stroke "currentcolor", :stroke-linecap "round", :stroke-linejoin "round", :d "M22.5 5h-21a1 1 0 0 0 -1 1v12a1 1 0 0 0 1 1h21a1 1 0 0 0 1 -1V6a1 1 0 0 0 -1 -1Z", :stroke-width "1"}]
|
||||
[:path {:stroke "currentcolor", :stroke-linecap "round", :stroke-linejoin "round", :d "M12 15a3 3 0 1 0 0 -6 3 3 0 0 0 0 6Z", :stroke-width "1"}]
|
||||
[:path {:stroke "currentcolor", :d "M4.996 9.75a0.25 0.25 0 0 1 0 -0.5", :stroke-width "1"}]
|
||||
[:path {:stroke "currentcolor", :d "M4.996 9.75a0.25 0.25 0 0 0 0 -0.5", :stroke-width "1"}]
|
||||
[:g
|
||||
[:path {:stroke "currentcolor", :d "M18.996 14.75a0.25 0.25 0 1 1 0 -0.5", :stroke-width "1"}]
|
||||
[:path {:stroke "currentcolor", :d "M18.996 14.75a0.25 0.25 0 1 0 0 -0.5", :stroke-width "1"}]]])
|
||||
|
||||
(def credit-card
|
||||
[:svg {:xmlns "http://www.w3.org/2000/svg", :viewbox "0 0 24 24"}
|
||||
[:defs]
|
||||
[:title "credit-card-1"]
|
||||
[:path {:d "M2.504 4h19s2 0 2 2v12s0 2 -2 2h-19s-2 0 -2 -2V6s0 -2 2 -2", :fill "none", :stroke "currentcolor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1"}]
|
||||
[:path {:d "m0.504 8 23 0", :fill "none", :stroke "currentcolor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1"}]
|
||||
[:path {:d "m20.504 12 -3 0", :fill "none", :stroke "currentcolor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1"}]
|
||||
[:path {:d "m11.504 12 -8 0", :fill "none", :stroke "currentcolor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1"}]
|
||||
[:path {:d "m6.504 15 -3 0", :fill "none", :stroke "currentcolor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1"}]])
|
||||
|
||||
(def check
|
||||
[:svg {:xmlns "http://www.w3.org/2000/svg", :viewbox "0 0 24 24"}
|
||||
[:defs]
|
||||
[:title "check-payment-sign"]
|
||||
[:path {:d "m2.5 20.5 7 0", :fill "none", :stroke "currentcolor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1"}]
|
||||
[:path {:d "m14 19.616 0.751 -0.751a1 1 0 0 1 1.677 0.465l0.072 0.286 0.818 -0.545a1 1 0 0 1 1.262 0.125l0.42 0.42h2", :fill "none", :stroke "currentcolor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1"}]
|
||||
[:path {:d "m2.504 17.616 5 0", :fill "none", :stroke "currentcolor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1"}]
|
||||
[:path {:d "m14.075 5.505 2.122 2.122", :fill "none", :stroke "currentcolor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1"}]
|
||||
[:path {:d "m18.813 2.465 0.425 0.424a1.2 1.2 0 0 1 0 1.697l-8.698 8.697 0 0 -2.121 -2.121 0 0 8.697 -8.697a1.2 1.2 0 0 1 1.697 0Z", :fill "none", :stroke "currentcolor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1"}]
|
||||
[:path {:d "m8.418 11.162 -1.414 3.536 3.536 -1.414", :fill "none", :stroke "currentcolor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1"}]
|
||||
[:path {:d "m19.025 2.677 1.293 -1.293", :fill "none", :stroke "currentcolor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1"}]
|
||||
[:path {:d "M6.5 9.616H1a0.5 0.5 0 0 0 -0.5 0.5v12a0.5 0.5 0 0 0 0.5 0.5h22a0.5 0.5 0 0 0 0.5 -0.5v-12a0.5 0.5 0 0 0 -0.5 -0.5h-5", :fill "none", :stroke "currentcolor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1"}]
|
||||
[:path {:d "M17.004 12.616h3s0.5 0 0.5 0.5v2s0 0.5 -0.5 0.5h-3s-0.5 0 -0.5 -0.5v-2s0 -0.5 0.5 -0.5", :fill "none", :stroke "currentcolor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1"}]
|
||||
[:path {:d "m16.757 2.823 -0.8 -0.8a1 1 0 0 0 -1.414 0L12.5 4.07", :fill "none", :stroke "currentcolor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1"}]])
|
||||
@@ -15,8 +15,6 @@
|
||||
hiccup))})
|
||||
|
||||
|
||||
|
||||
|
||||
(defn base-page [request contents page-name]
|
||||
(html-page
|
||||
[:html.has-navbar-fixed-top
|
||||
@@ -38,10 +36,11 @@
|
||||
[:script {:src "https://unpkg.com/htmx.org@1.9.6/dist/htmx.js"
|
||||
:crossorigin= "anonymous"}]
|
||||
[:script {:src "https://unpkg.com/htmx.org@1.9.6/dist/htmx.min.js"
|
||||
:crossorigin= "anonymous"}])
|
||||
:crossorigin= "anonymous"}])
|
||||
|
||||
[:script {:src "https://unpkg.com/htmx.org@1.9.6/dist/ext/class-tools.js" :crossorigin= "anonymous"}]
|
||||
|
||||
[:script {:src "https://cdn.jsdelivr.net/npm/sortablejs@1.15.2/Sortable.min.js"}]
|
||||
[:script {:src "https://unpkg.com/htmx.org/dist/ext/debug.js"}]
|
||||
[:script {:src "/js/htmx-disable.js"}]
|
||||
[:script {:type "text/javascript", :src "https://cdn.yodlee.com/fastlink/v4/initialize.js", :async "async"}]]
|
||||
@@ -57,7 +56,8 @@
|
||||
[:link {:rel "stylesheet" :href "https://unpkg.com/dropzone@5/dist/min/dropzone.min.css" :type "text/css"}]
|
||||
[:script {:defer true :src "https://cdn.jsdelivr.net/npm/@alpinejs/focus@3.x.x/dist/cdn.min.js"}]
|
||||
[:script {:defer true :src "https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"}]
|
||||
|
||||
[:script {:src "https://cdn.jsdelivr.net/npm/signature_pad@4.1.7/dist/signature_pad.umd.min.js"}]
|
||||
|
||||
[:style
|
||||
"
|
||||
input::-webkit-outer-spin-button,
|
||||
@@ -74,14 +74,17 @@ input[type=number] {
|
||||
[:body {:hx-ext "disable-submit, class-tools"}
|
||||
contents
|
||||
[:script {:src "/js/flowbite.min.js"}]
|
||||
|
||||
|
||||
|
||||
[:div#modal-holder
|
||||
{ :class "fixed top-0 left-0 z-[99] flex items-center justify-center w-screen h-screen"
|
||||
{:class "fixed top-0 left-0 z-[99] flex items-center justify-center w-screen h-screen"
|
||||
"x-show" "open"
|
||||
":aria-hidden" "!open"
|
||||
"x-data" (hx/json {"open" false})
|
||||
"@modalopen.document" "open=true"
|
||||
"x-data" (hx/json {"open" false
|
||||
"unexpectedError" false})
|
||||
"x-on:htmx:response-error" "unexpectedError=true;"
|
||||
"x-on:htmx:before-request" "unexpectedError=false"
|
||||
"@modalopen.document" "open=true; unexpectedError=null"
|
||||
"@modalclose.document" "open=false"}
|
||||
|
||||
[:div {:class "bg-gray-900 bg-opacity-50 dark:bg-opacity-80 fixed inset-0 z-40 md:p-12"
|
||||
@@ -94,21 +97,18 @@ input[type=number] {
|
||||
"x-transition:leave-start" "!bg-opacity-50"
|
||||
"x-transition:leave-end" "!bg-opacity-0"}
|
||||
|
||||
[:div {
|
||||
:class "flex h-full w-full justify-stretch md:justify-center items-stretch md:items-center "
|
||||
"x-trap.inert.noscroll" "open"
|
||||
"x-trap.inert" "open"
|
||||
"x-show" "open"
|
||||
"x-transition:enter" "ease-out duration-300"
|
||||
"x-transition:enter-start" "!bg-opacity-0 !translate-y-32"
|
||||
"x-transition:enter-end" "!bg-opacity-100 !translate-y-0"
|
||||
"x-transition:leave" "duration-300"
|
||||
"x-transition:leave-start" "!opacity-100 !translate-y-0"
|
||||
"x-transition:leave-end" "!opacity-0 !translate-y-32"}
|
||||
[:div {:class "flex h-full w-full justify-stretch md:justify-center items-stretch md:items-center "
|
||||
"x-trap.inert.noscroll" "open"
|
||||
"x-trap.inert" "open"
|
||||
"x-show" "open"
|
||||
"x-transition:enter" "ease-out duration-300"
|
||||
"x-transition:enter-start" "!bg-opacity-0 !translate-y-32"
|
||||
"x-transition:enter-end" "!bg-opacity-100 !translate-y-0"
|
||||
"x-transition:leave" "duration-300"
|
||||
"x-transition:leave-start" "!opacity-100 !translate-y-0"
|
||||
"x-transition:leave-end" "!opacity-0 !translate-y-32"}
|
||||
|
||||
[:div.flex.items-center.justify-center.max-w-6xl {:class "min-w-[700px] max-h-full "}
|
||||
|
||||
|
||||
[:div#modal-content.flex.flex-col.self-stretch {:class "min-w-[700px] md:p-12"} ;;.overflow-scroll
|
||||
|
||||
]
|
||||
]]]]]]))
|
||||
]]]]]]]))
|
||||
|
||||
@@ -16,51 +16,51 @@
|
||||
:headers (into {"Content-Type" "text/html"}
|
||||
headers)
|
||||
:body (str
|
||||
(hiccup/html
|
||||
{}
|
||||
hiccup)
|
||||
"\n"
|
||||
(str/join "\n"
|
||||
(map (fn [o]
|
||||
(hiccup/html
|
||||
{}
|
||||
o))
|
||||
oob)))})
|
||||
(hiccup/html
|
||||
{}
|
||||
hiccup)
|
||||
"\n"
|
||||
(str/join "\n"
|
||||
(map (fn [o]
|
||||
(hiccup/html
|
||||
{}
|
||||
o))
|
||||
oob)))})
|
||||
|
||||
(defn modal-response [hiccup & {:as opts}]
|
||||
(apply html-response
|
||||
(into
|
||||
[hiccup]
|
||||
(mapcat identity
|
||||
(-> opts
|
||||
(assoc-in [:headers "hx-trigger"] "modalopen")
|
||||
(assoc-in [:headers "hx-retarget"] "#modal-content")
|
||||
(assoc-in [:headers "hx-reswap"] "innerHTML"))))))
|
||||
(into
|
||||
[hiccup]
|
||||
(mapcat identity
|
||||
(-> opts
|
||||
(assoc-in [:headers "hx-trigger"] "modalopen")
|
||||
(assoc-in [:headers "hx-retarget"] "#modal-content")
|
||||
(assoc-in [:headers "hx-reswap"] "innerHTML"))))))
|
||||
|
||||
(defn next-step-modal-response [hiccup & {:as opts}]
|
||||
(apply html-response
|
||||
(into
|
||||
[hiccup]
|
||||
(mapcat identity
|
||||
(-> opts
|
||||
(assoc-in [:headers "hx-retarget"] "#modal-content")
|
||||
(assoc-in [:headers "hx-reswap"] "innerHTML"))))))
|
||||
(into
|
||||
[hiccup]
|
||||
(mapcat identity
|
||||
(-> opts
|
||||
(assoc-in [:headers "hx-retarget"] "#modal-content")
|
||||
(assoc-in [:headers "hx-reswap"] "innerHTML"))))))
|
||||
|
||||
|
||||
|
||||
(defn form-data->map [form-data]
|
||||
(reduce-kv
|
||||
(fn [acc k v]
|
||||
(cond (and (string? v)
|
||||
(empty? v))
|
||||
acc
|
||||
(fn [acc k v]
|
||||
(cond (and (string? v)
|
||||
(empty? v))
|
||||
acc
|
||||
|
||||
:else
|
||||
(assoc-in acc (->> (str/split k #"_")
|
||||
(mapv #(apply keyword (str/split % #"/"))))
|
||||
v)))
|
||||
{}
|
||||
form-data))
|
||||
:else
|
||||
(assoc-in acc (->> (str/split k #"_")
|
||||
(mapv #(apply keyword (str/split % #"/"))))
|
||||
v)))
|
||||
{}
|
||||
form-data))
|
||||
|
||||
(defn path->name [k]
|
||||
(cond (keyword? k)
|
||||
@@ -75,29 +75,32 @@
|
||||
[:vector {:decode/json {:enter (fn [x]
|
||||
(if (sequential? x)
|
||||
x
|
||||
[x])
|
||||
)}}
|
||||
[x]))}}
|
||||
x])
|
||||
|
||||
(defn empty->nil [v]
|
||||
(if (and (string? v) (clojure.string/blank? v))
|
||||
nil
|
||||
v))
|
||||
(if (and (string? v) (clojure.string/blank? v))
|
||||
nil
|
||||
v))
|
||||
|
||||
(defn parse-empty-as-nil []
|
||||
(mt2/transformer
|
||||
{:decoders
|
||||
{:string empty->nil
|
||||
:double empty->nil
|
||||
:int empty->nil
|
||||
:long empty->nil
|
||||
'nat-int? empty->nil}}))
|
||||
(defn parse-empty-as-nil []
|
||||
(mt2/transformer
|
||||
{:decoders
|
||||
{:map (fn [m]
|
||||
(if (not (seq (filter identity (vals m))))
|
||||
nil
|
||||
m))
|
||||
:string empty->nil
|
||||
:double empty->nil
|
||||
:int empty->nil
|
||||
:long empty->nil
|
||||
'nat-int? empty->nil}}))
|
||||
|
||||
(def entity-id (mc/schema [nat-int? {:error/message "required"
|
||||
:decode/arbitrary (fn [e]
|
||||
(if (and (map? e) (:db/id e))
|
||||
(:db/id e)
|
||||
e))} ]))
|
||||
:decode/arbitrary (fn [e]
|
||||
(if (and (map? e) (:db/id e))
|
||||
(:db/id e)
|
||||
e))}]))
|
||||
|
||||
(def temp-id (mc/schema [:string {:min 1}]))
|
||||
(def money (mc/schema [:double]))
|
||||
@@ -110,12 +113,12 @@
|
||||
|
||||
(def regex (mc/schema [:fn {:error/message "not a regex"}
|
||||
(fn check-regx [x]
|
||||
(try
|
||||
(and (string? x)
|
||||
(. java.util.regex.Pattern (compile x java.util.regex.Pattern/CASE_INSENSITIVE)))
|
||||
true
|
||||
(catch Exception _
|
||||
false)))]))
|
||||
(try
|
||||
(and (string? x)
|
||||
(. java.util.regex.Pattern (compile x java.util.regex.Pattern/CASE_INSENSITIVE)))
|
||||
true
|
||||
(catch Exception _
|
||||
false)))]))
|
||||
|
||||
(def map->db-id-decoder
|
||||
{:enter (fn [x]
|
||||
@@ -127,12 +130,12 @@
|
||||
|
||||
(defn many-entity [params & keys]
|
||||
(mc/schema
|
||||
[:vector (merge params {:decode/json map->db-id-decoder
|
||||
:decode/arbitrary (fn [x]
|
||||
(if (sequential? x)
|
||||
x
|
||||
[x]))})
|
||||
(into [:map] keys)]))
|
||||
[:vector (merge params {:decode/json map->db-id-decoder
|
||||
:decode/arbitrary (fn [x]
|
||||
(if (sequential? x)
|
||||
x
|
||||
[x]))})
|
||||
(into [:map] keys)]))
|
||||
|
||||
(defn str->keyword [s]
|
||||
(if (string? s)
|
||||
@@ -156,23 +159,38 @@
|
||||
:form-validation-errors [m]}))))
|
||||
|
||||
(def main-transformer
|
||||
(mt2/transformer
|
||||
parse-empty-as-nil
|
||||
(mt2/key-transformer {:encode keyword->str :decode str->keyword})
|
||||
mt2/string-transformer
|
||||
mt2/json-transformer
|
||||
(mt2/transformer {:name :arbitrary})
|
||||
mt2/default-value-transformer))
|
||||
(mt2/transformer
|
||||
parse-empty-as-nil
|
||||
(mt2/key-transformer {:encode keyword->str :decode str->keyword})
|
||||
mt2/string-transformer
|
||||
mt2/json-transformer
|
||||
(mt2/transformer {:name :arbitrary})
|
||||
mt2/default-value-transformer))
|
||||
|
||||
(defn strip [s]
|
||||
(cond (and (string? s) (str/blank? s))
|
||||
nil
|
||||
nil
|
||||
|
||||
(string? s)
|
||||
(str/trim s)
|
||||
(string? s)
|
||||
(str/trim s)
|
||||
|
||||
:else
|
||||
s))
|
||||
(defn assert-schema [schema entity]
|
||||
(when-not (mc/validate schema entity)
|
||||
(throw (ex-info #_(->> (-> (mc/explain schema entity)
|
||||
(me/humanize {:errors (assoc me/default-errors
|
||||
::mc/missing-key {:error/message {:en "required"}})}))
|
||||
(map (fn [[k v]]
|
||||
(str (if (keyword? k)
|
||||
(name k)
|
||||
k) ": " (str/join ", " v))))
|
||||
(str/join ", "))
|
||||
"validation failed"
|
||||
{:type :schema-validation
|
||||
:decoded entity
|
||||
:error {:explain (mc/explain schema entity)}}))))
|
||||
|
||||
:else
|
||||
s))
|
||||
|
||||
(defn schema-enforce-request [{:keys [form-params query-params params] :as request} & {:keys [form-schema query-schema route-schema params-schema]}]
|
||||
(let [request (try
|
||||
@@ -180,35 +198,35 @@
|
||||
(and (:params request) params-schema)
|
||||
(assoc :params
|
||||
(mc/coerce
|
||||
params-schema
|
||||
(:params request)
|
||||
main-transformer))
|
||||
params-schema
|
||||
(:params request)
|
||||
main-transformer))
|
||||
|
||||
(and (:route-params request) route-schema)
|
||||
(assoc :route-params
|
||||
(mc/coerce
|
||||
route-schema
|
||||
(:route-params request)
|
||||
main-transformer))
|
||||
route-schema
|
||||
(:route-params request)
|
||||
main-transformer))
|
||||
|
||||
(and form-schema form-params)
|
||||
(assoc :form-params
|
||||
(mc/coerce
|
||||
form-schema
|
||||
form-params
|
||||
main-transformer))
|
||||
form-schema
|
||||
form-params
|
||||
main-transformer))
|
||||
|
||||
(and query-schema query-params)
|
||||
(assoc :query-params
|
||||
(mc/coerce
|
||||
query-schema
|
||||
query-params
|
||||
main-transformer)))
|
||||
|
||||
query-schema
|
||||
query-params
|
||||
main-transformer)))
|
||||
|
||||
(catch Exception e
|
||||
(alog/warn ::validation-error :error e)
|
||||
(throw (ex-info (->> (-> e
|
||||
(ex-data )
|
||||
(ex-data)
|
||||
:data
|
||||
:explain
|
||||
(me/humanize {:errors (assoc me/default-errors
|
||||
@@ -237,30 +255,30 @@
|
||||
(and (:params request) params-schema)
|
||||
(assoc :params
|
||||
(mc/decode
|
||||
params-schema
|
||||
(:params request)
|
||||
main-transformer))
|
||||
params-schema
|
||||
(:params request)
|
||||
main-transformer))
|
||||
|
||||
(and (:route-params request) route-schema)
|
||||
(assoc :route-params
|
||||
(mc/decode
|
||||
route-schema
|
||||
(:route-params request)
|
||||
main-transformer))
|
||||
route-schema
|
||||
(:route-params request)
|
||||
main-transformer))
|
||||
|
||||
(and form-schema form-params)
|
||||
(assoc :form-params
|
||||
(mc/decode
|
||||
form-schema
|
||||
form-params
|
||||
main-transformer))
|
||||
form-schema
|
||||
form-params
|
||||
main-transformer))
|
||||
|
||||
(and query-schema query-params)
|
||||
(assoc :query-params
|
||||
(mc/decode
|
||||
query-schema
|
||||
query-params
|
||||
main-transformer)))]
|
||||
query-schema
|
||||
query-params
|
||||
main-transformer)))]
|
||||
request))
|
||||
|
||||
(defn wrap-schema-decode [handler & {:keys [form-schema query-schema route-schema params-schema]}]
|
||||
@@ -275,8 +293,7 @@
|
||||
(into [:enum {:decode/string #(if (keyword? %)
|
||||
%
|
||||
(when (not-empty %)
|
||||
(keyword n %))
|
||||
)}]
|
||||
(keyword n %)))}]
|
||||
(for [{:db/keys [ident]} (all-schema)
|
||||
:when (= n (namespace ident))]
|
||||
ident)))
|
||||
@@ -301,41 +318,39 @@
|
||||
(defn wrap-form-4xx-2 [handler form-handler]
|
||||
(fn [request]
|
||||
(try+
|
||||
(handler request)
|
||||
(catch [:type :schema-validation] e
|
||||
|
||||
(let [humanized (-> e :error :explain (me/humanize {:errors (assoc me/default-errors
|
||||
::mc/missing-key {:error/message {:en "required"}})}))
|
||||
errors (map
|
||||
(fn [e]
|
||||
{:path (:in e)
|
||||
:message (get-in humanized (:in e))})
|
||||
(:errors (:explain (:error e))))]
|
||||
(alog/warn ::form-4xx :errors errors)
|
||||
(form-handler (assoc request
|
||||
:form-params (:decoded e)
|
||||
:field-validation-errors errors
|
||||
:form-errors humanized)))
|
||||
#_(html-response [:span.error-content.text-red-500 (:message &throw-context)]
|
||||
:status 400))
|
||||
(catch [:type :field-validation] e
|
||||
(form-handler (assoc request
|
||||
:form-params (:form e)
|
||||
:form-errors (:form-errors e))))
|
||||
(catch [:type :form-validation] e
|
||||
(form-handler (assoc request
|
||||
:form-params (:form e)
|
||||
:form-validation-errors (:form-validation-errors e)
|
||||
:form-errors {:errors (:form-validation-errors e)}))))))
|
||||
(handler request)
|
||||
(catch [:type :schema-validation] e
|
||||
(let [humanized (-> e :error :explain (me/humanize {:errors (assoc me/default-errors
|
||||
::mc/missing-key {:error/message {:en "required"}})}))
|
||||
errors (map
|
||||
(fn [e]
|
||||
{:path (:in e)
|
||||
:message (get-in humanized (:in e))})
|
||||
(:errors (:explain (:error e))))]
|
||||
(alog/warn ::form-4xx :errors errors)
|
||||
(form-handler (assoc request
|
||||
:form-params (:decoded e)
|
||||
:field-validation-errors errors
|
||||
:form-errors humanized)))
|
||||
#_(html-response [:span.error-content.text-red-500 (:message &throw-context)]
|
||||
:status 400))
|
||||
(catch [:type :field-validation] e
|
||||
(form-handler (assoc request
|
||||
:form-params (:form e)
|
||||
:form-errors (:form-errors e))))
|
||||
(catch [:type :form-validation] e
|
||||
(form-handler (assoc request
|
||||
:form-params (:form e)
|
||||
:form-validation-errors (:form-validation-errors e)
|
||||
:form-errors {:errors (:form-validation-errors e)}))))))
|
||||
|
||||
|
||||
(defn apply-middleware-to-all-handlers [key->handler f]
|
||||
(->> key->handler
|
||||
(reduce
|
||||
(fn [key-handler [k v]]
|
||||
(assoc key-handler k (f v)))
|
||||
key->handler)
|
||||
))
|
||||
(fn [key-handler [k v]]
|
||||
(assoc key-handler k (f v)))
|
||||
key->handler)))
|
||||
|
||||
(defn path->name2 [k & rest]
|
||||
(let [k->n (fn [k]
|
||||
@@ -354,10 +369,10 @@
|
||||
(defn wrap-entity [handler path read]
|
||||
(fn wrap-entity-request [request]
|
||||
(let [entity (some->>
|
||||
(get-in request path)
|
||||
(#(if (string? %) (Long/parseLong %) %))
|
||||
(dc/pull (dc/db conn) read ))]
|
||||
(get-in request path)
|
||||
(#(if (string? %) (Long/parseLong %) %))
|
||||
(dc/pull (dc/db conn) read))]
|
||||
(handler (if entity
|
||||
(assoc request
|
||||
:entity entity)
|
||||
request)))))
|
||||
request)))))
|
||||
344
src/clj/user.clj
344
src/clj/user.clj
@@ -1,10 +1,13 @@
|
||||
(ns user
|
||||
(ns user
|
||||
(:require
|
||||
[amazonica.aws.s3 :as s3]
|
||||
[clojure.tools.namespace.repl :refer [set-refresh-dirs refresh]]
|
||||
[auto-ap.datomic :refer [conn pull-attr random-tempid]]
|
||||
[auto-ap.ledger :as l ]
|
||||
[auto-ap.ledger :as l]
|
||||
[clj-http.core :as http]
|
||||
[clj-http.client :as client]
|
||||
[figwheel.main.api]
|
||||
[hawk.core]
|
||||
[auto-ap.server]
|
||||
[auto-ap.time :as atime]
|
||||
[auto-ap.utils :refer [by]]
|
||||
@@ -29,6 +32,7 @@
|
||||
(:import
|
||||
(org.apache.commons.io.input BOMInputStream)))
|
||||
|
||||
|
||||
(defn println-event [item]
|
||||
(printf "%s: %s - %s:%s by %s\n"
|
||||
(str (c/to-date-time (:mulog/timestamp item)))
|
||||
@@ -36,23 +40,22 @@
|
||||
(if (:mulog/duration item)
|
||||
(str " " (int (/ (:mulog/duration item) 1000000)) "ms")
|
||||
"")
|
||||
(:user-name item)
|
||||
)
|
||||
(:user-name item))
|
||||
(println (reduce
|
||||
(fn [acc [k v]]
|
||||
(assoc acc k v))
|
||||
{}
|
||||
(dissoc
|
||||
item
|
||||
:user)))
|
||||
#_(puget/cprint (reduce
|
||||
(fn [acc [k v]]
|
||||
(assoc acc k v))
|
||||
{}
|
||||
(dissoc
|
||||
item
|
||||
:user))
|
||||
{:seq-limit 10})
|
||||
(dissoc
|
||||
item
|
||||
:user)))
|
||||
#_(puget/cprint (reduce
|
||||
(fn [acc [k v]]
|
||||
(assoc acc k v))
|
||||
{}
|
||||
(dissoc
|
||||
item
|
||||
:user))
|
||||
{:seq-limit 10})
|
||||
(println))
|
||||
|
||||
|
||||
@@ -70,8 +73,7 @@
|
||||
(publish [_ buffer]
|
||||
;; items are pairs [offset <item>]
|
||||
(doseq [item (transform (map second (rb/items buffer)))]
|
||||
(println-event item)
|
||||
)
|
||||
(println-event item))
|
||||
(flush)
|
||||
(rb/clear buffer)))
|
||||
|
||||
@@ -91,23 +93,23 @@
|
||||
(defn load-accounts [conn]
|
||||
(let [[header & rows] (-> "master-account-list.csv" (io/resource) io/input-stream (BOMInputStream.) (io/reader) csv/read-csv)
|
||||
code->existing-account (by :account/numeric-code (map first (dc/q {:find ['(pull ?e [:account/numeric-code
|
||||
:db/id])]
|
||||
:db/id])]
|
||||
:in ['$]
|
||||
:where ['[?e :account/name]]}
|
||||
(dc/db conn))))
|
||||
(dc/db conn))))
|
||||
|
||||
also-merge-txes (fn [also-merge old-account-id]
|
||||
(if old-account-id
|
||||
(if old-account-id
|
||||
(let [[sunset-account]
|
||||
(first (dc/q {:find ['?a ]
|
||||
:in ['$ '?ac ]
|
||||
(first (dc/q {:find ['?a]
|
||||
:in ['$ '?ac]
|
||||
:where ['[?a :account/numeric-code ?ac]]}
|
||||
(dc/db conn) also-merge))]
|
||||
(into (mapv
|
||||
(fn [[entity id _]]
|
||||
[:db/add entity id old-account-id])
|
||||
(dc/q {:find ['?e '?id '?a ]
|
||||
:in ['$ '?ac ]
|
||||
(dc/q {:find ['?e '?id '?a]
|
||||
:in ['$ '?ac]
|
||||
:where ['[?a :account/numeric-code ?ac]
|
||||
'[?e ?at ?a]
|
||||
'[?at :db/ident ?id]]}
|
||||
@@ -122,7 +124,7 @@
|
||||
(into {} (map vector header r))))
|
||||
(map (fn parse-map [r]
|
||||
{:old-account-id (:db/id (code->existing-account
|
||||
(or
|
||||
(or
|
||||
(if (= (get r "IOL Account #")
|
||||
"NEW")
|
||||
nil
|
||||
@@ -157,8 +159,7 @@
|
||||
(if also-merge
|
||||
(into tx
|
||||
(also-merge-txes also-merge old-account-id))
|
||||
tx)
|
||||
))))
|
||||
tx)))))
|
||||
|
||||
|
||||
conj
|
||||
@@ -168,7 +169,7 @@
|
||||
|
||||
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
|
||||
(defn find-bad-accounts []
|
||||
(set (map second (dc/q {:find ['(pull ?x [*]) '?z]
|
||||
(set (map second (dc/q {:find ['(pull ?x [*]) '?z]
|
||||
:in ['$]
|
||||
:where ['[?e :account/numeric-code ?z]
|
||||
'[(<= ?z 9999)]
|
||||
@@ -177,32 +178,31 @@
|
||||
|
||||
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
|
||||
(defn delete-4-digit-accounts []
|
||||
@(dc/transact conn
|
||||
(transduce
|
||||
@(dc/transact conn
|
||||
(transduce
|
||||
(comp
|
||||
(map first)
|
||||
(map (fn [old-account-id]
|
||||
[:db/retractEntity old-account-id])))
|
||||
(map first)
|
||||
(map (fn [old-account-id]
|
||||
[:db/retractEntity old-account-id])))
|
||||
conj
|
||||
[]
|
||||
(dc/q {:find ['?e]
|
||||
(dc/q {:find ['?e]
|
||||
:in ['$]
|
||||
:where ['[?e :account/numeric-code ?z]
|
||||
'[(<= ?z 9999)]]}
|
||||
(dc/db conn))))
|
||||
)
|
||||
(dc/db conn)))))
|
||||
|
||||
|
||||
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
|
||||
(defn find-conflicting-accounts []
|
||||
(filter
|
||||
(filter
|
||||
(fn [[_ v]]
|
||||
(> (count v) 1))
|
||||
(reduce
|
||||
(reduce
|
||||
(fn [acc [e z]]
|
||||
(update acc z conj e))
|
||||
{}
|
||||
(dc/q {:find ['?e '?z]
|
||||
(dc/q {:find ['?e '?z]
|
||||
:in ['$]
|
||||
:where ['[?e :account/numeric-code ?z]]}
|
||||
(dc/db conn)))))
|
||||
@@ -214,31 +214,29 @@
|
||||
:in ['$ '?z]
|
||||
:where [['?e :client/code '?z]]}
|
||||
(dc/db conn) customer)))
|
||||
_ (println client-id)
|
||||
code->existing-account (by :account/numeric-code (map first (dc/q {:find ['(pull ?e [:account/numeric-code
|
||||
{:account/applicability [:db/ident]}
|
||||
:db/id])]
|
||||
:db/id])]
|
||||
:in ['$]
|
||||
:where ['[?e :account/name]]}
|
||||
(dc/db conn))))
|
||||
(dc/db conn))))
|
||||
|
||||
existing-account-overrides (dc/q {:find ['?e]
|
||||
:in ['$ '?client-id]
|
||||
:where [['?e :account-client-override/client '?client-id]]}
|
||||
(dc/db conn) client-id)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
_ (when-let [bad-rows (seq (->> rows
|
||||
(group-by (fn [[_ account]]
|
||||
account))
|
||||
vals
|
||||
(filter #(> (count %) 1))
|
||||
(filter (fn [duplicates]
|
||||
(apply not= (map rest duplicates))))
|
||||
#_(map (fn [[[_ account]]]
|
||||
account))
|
||||
))]
|
||||
(group-by (fn [[_ account]]
|
||||
account))
|
||||
vals
|
||||
(filter #(> (count %) 1))
|
||||
(filter (fn [duplicates]
|
||||
(apply not= (map rest duplicates))))
|
||||
#_(map (fn [[[_ account]]]
|
||||
account))))]
|
||||
(throw (Exception. (str "These accounts are duplicated:" (str bad-rows)))))
|
||||
rows (vec (set (map rest rows)))
|
||||
|
||||
@@ -256,8 +254,7 @@
|
||||
(:db/ident (:account/applicability existing)))
|
||||
(and (not-empty override-name)
|
||||
(not-empty account-name)
|
||||
(not= override-name account-name)
|
||||
)))
|
||||
(not= override-name account-name))))
|
||||
[{:db/id (:db/id existing)
|
||||
:account/client-overrides [{:account-client-override/client client-id
|
||||
:account-client-override/name (or (not-empty override-name)
|
||||
@@ -284,34 +281,30 @@
|
||||
[:db/retractEntity x])
|
||||
existing-account-overrides)
|
||||
rows)]
|
||||
|
||||
|
||||
txes
|
||||
#_@(d/transact conn txes)))
|
||||
|
||||
|
||||
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
|
||||
(defn fix-transactions-without-locations [client-code location]
|
||||
(->>
|
||||
(dc/q {:find ['(pull ?e [*])]
|
||||
:in ['$ '?client-code]
|
||||
:where ['[?e :transaction/accounts ?ta]
|
||||
'[?e :transaction/matched-rule]
|
||||
'[?e :transaction/approval-status :transaction-approval-status/approved]
|
||||
'(not [?ta :transaction-account/location])
|
||||
'[?e :transaction/client ?c]
|
||||
'[?c :client/code ?client-code]
|
||||
]}
|
||||
(dc/db conn) client-code)
|
||||
(->>
|
||||
(dc/q {:find ['(pull ?e [*])]
|
||||
:in ['$ '?client-code]
|
||||
:where ['[?e :transaction/accounts ?ta]
|
||||
'[?e :transaction/matched-rule]
|
||||
'[?e :transaction/approval-status :transaction-approval-status/approved]
|
||||
'(not [?ta :transaction-account/location])
|
||||
'[?e :transaction/client ?c]
|
||||
'[?c :client/code ?client-code]]}
|
||||
(dc/db conn) client-code)
|
||||
(mapcat
|
||||
(fn [[{:transaction/keys [accounts]}]]
|
||||
(mapv
|
||||
(fn [a]
|
||||
{:db/id (:db/id a)
|
||||
:transaction-account/location location}
|
||||
)
|
||||
accounts)
|
||||
)
|
||||
)
|
||||
:transaction-account/location location})
|
||||
accounts)))
|
||||
vec))
|
||||
|
||||
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
|
||||
@@ -323,21 +316,21 @@
|
||||
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
|
||||
(defn entity-history [i]
|
||||
(vec (sort-by first (dc/q
|
||||
{:find ['?tx '?z '?v ]
|
||||
:in ['?i '$]
|
||||
:where ['[?i ?a ?v ?tx ?ad]
|
||||
'[?a :db/ident ?z]
|
||||
'[(= ?ad true)]]}
|
||||
i (dc/history (dc/db conn))))))
|
||||
{:find ['?tx '?z '?v]
|
||||
:in ['?i '$]
|
||||
:where ['[?i ?a ?v ?tx ?ad]
|
||||
'[?a :db/ident ?z]
|
||||
'[(= ?ad true)]]}
|
||||
i (dc/history (dc/db conn))))))
|
||||
|
||||
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
|
||||
(defn entity-history-with-revert [i]
|
||||
(vec (sort-by first (dc/q
|
||||
{:find ['?tx '?z '?v '?ad ]
|
||||
:in ['?i '$]
|
||||
:where ['[?i ?a ?v ?tx ?ad]
|
||||
'[?a :db/ident ?z]]}
|
||||
i (dc/history (dc/db conn))))))
|
||||
{:find ['?tx '?z '?v '?ad]
|
||||
:in ['?i '$]
|
||||
:where ['[?i ?a ?v ?tx ?ad]
|
||||
'[?a :db/ident ?z]]}
|
||||
i (dc/history (dc/db conn))))))
|
||||
|
||||
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
|
||||
(defn tx-detail [i]
|
||||
@@ -357,47 +350,71 @@
|
||||
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
|
||||
(defn start-db []
|
||||
(mu/start-publisher! {:type :dev})
|
||||
(mount.core/start (mount.core/only #{#'auto-ap.datomic/conn })))
|
||||
(mount.core/start (mount.core/only #{#'auto-ap.datomic/conn})))
|
||||
|
||||
|
||||
(defn- auto-reset-handler [ctx event]
|
||||
(require 'figwheel.main.api)
|
||||
(binding [*ns* *ns*]
|
||||
(clojure.tools.namespace.repl/refresh)
|
||||
ctx))
|
||||
|
||||
(defn auto-reset
|
||||
"Automatically reset the system when a Clojure or edn file is changed in
|
||||
`src` or `resources`."
|
||||
[]
|
||||
(println "starting auto reset")
|
||||
(hawk.core/watch! [{:paths ["src/"]
|
||||
:handler auto-reset-handler}]))
|
||||
|
||||
(defn start-http []
|
||||
(mount.core/start (mount.core/only #{#'auto-ap.server/port #'auto-ap.server/jetty})))
|
||||
|
||||
|
||||
(defn start-dev []
|
||||
(set-refresh-dirs "src")
|
||||
(start-db)
|
||||
(start-http)
|
||||
(auto-reset))
|
||||
|
||||
#_(defn start-search []
|
||||
(mount.core/start (mount.core/only #{#'auto-ap.graphql.vendors/indexer #'auto-ap.graphql.accounts/indexer})))
|
||||
(mount.core/start (mount.core/only #{#'auto-ap.graphql.vendors/indexer #'auto-ap.graphql.accounts/indexer})))
|
||||
|
||||
(defn restart-db []
|
||||
#_(require 'datomic.dev-local)
|
||||
#_(datomic.dev-local/release-db {:system "dev" :db-name "prod-migration"})
|
||||
(mount.core/stop (mount.core/only #{#'auto-ap.datomic/conn }))
|
||||
(mount.core/stop (mount.core/only #{#'auto-ap.datomic/conn}))
|
||||
(start-db))
|
||||
|
||||
|
||||
|
||||
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
|
||||
(defn spit-csv [columns data ]
|
||||
(defn spit-csv [columns data]
|
||||
(csv/write-csv *out*
|
||||
(into [(map name columns)]
|
||||
(for [r data]
|
||||
((apply juxt columns) r )))))
|
||||
((apply juxt columns) r)))))
|
||||
|
||||
|
||||
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
|
||||
(defn find-queries [words]
|
||||
(let [obj (s3/list-objects-v2 :bucket-name (:data-bucket env)
|
||||
:prefix (str "queries/"))
|
||||
:prefix (str "queries/"))
|
||||
concurrent 30
|
||||
output-chan (async/chan)]
|
||||
(async/pipeline-blocking concurrent
|
||||
output-chan
|
||||
(comp
|
||||
(map #(do
|
||||
[(:key %)
|
||||
(str (slurp (:object-content (s3/get-object
|
||||
:bucket-name (:data-bucket env)
|
||||
:key (:key %)))))]))
|
||||
|
||||
(filter #(->> words
|
||||
(every? (fn [w] (str/includes? (second %) w)))))
|
||||
(map first)
|
||||
(map #(str/replace % #"queries/" ""))
|
||||
)
|
||||
(comp
|
||||
(map #(do
|
||||
[(:key %)
|
||||
(str (slurp (:object-content (s3/get-object
|
||||
:bucket-name (:data-bucket env)
|
||||
:key (:key %)))))]))
|
||||
|
||||
(filter #(->> words
|
||||
(every? (fn [w] (str/includes? (second %) w)))))
|
||||
(map first)
|
||||
(map #(str/replace % #"queries/" "")))
|
||||
(async/to-chan! (:object-summaries obj))
|
||||
true
|
||||
(fn [e]
|
||||
@@ -408,13 +425,13 @@
|
||||
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
|
||||
(defn upsert-invoice-amounts [tsv]
|
||||
(let [data (with-open [reader (io/reader (char-array tsv))]
|
||||
(doall (csv/read-csv reader :separator \tab)))
|
||||
(doall (csv/read-csv reader :separator \tab)))
|
||||
db (dc/db conn)
|
||||
i->invoice-id (fn [i]
|
||||
(try (Long/parseLong i)
|
||||
(catch Exception e
|
||||
(:db/id (dc/pull db '[:db/id]
|
||||
[:invoice/original-id (Long/parseLong (first (str/split i #"-")))])))))
|
||||
i->invoice-id (fn [i]
|
||||
(try (Long/parseLong i)
|
||||
(catch Exception e
|
||||
(:db/id (dc/pull db '[:db/id]
|
||||
[:invoice/original-id (Long/parseLong (first (str/split i #"-")))])))))
|
||||
invoice-totals (->> data
|
||||
(drop 1)
|
||||
(group-by first)
|
||||
@@ -423,13 +440,11 @@
|
||||
(reduce + 0.0
|
||||
(->> values
|
||||
(map (fn [[_ _ _ _ amount]]
|
||||
(- (Double/parseDouble amount))))))
|
||||
]))
|
||||
(- (Double/parseDouble amount))))))]))
|
||||
(into {}))]
|
||||
(->>
|
||||
(->>
|
||||
(for [[i invoice-expense-account-id target-account target-date amount _ location] (drop 1 data)
|
||||
:let [
|
||||
invoice-id (i->invoice-id i)
|
||||
:let [invoice-id (i->invoice-id i)
|
||||
|
||||
invoice (dc/pull db '[FILL_IN] invoice-id)
|
||||
current-total (:invoice/total invoice)
|
||||
@@ -441,34 +456,32 @@
|
||||
(:db/id (first (:invoice/expense-accounts invoice)))
|
||||
(random-tempid))
|
||||
invoice-expense-account (when-not new-account?
|
||||
(dc/pull db '[FILL_IN]invoice-expense-account-id))
|
||||
current-account-id (:db/id (:invoice-expense-account/account invoice-expense-account))
|
||||
(dc/pull db '[FILL_IN] invoice-expense-account-id))
|
||||
current-account-id (:db/id (:invoice-expense-account/account invoice-expense-account))
|
||||
target-account-id (Long/parseLong (str/trim target-account))
|
||||
|
||||
target-date (clj-time.coerce/to-date (atime/parse target-date atime/normal-date))
|
||||
current-date (:invoice/date invoice)
|
||||
|
||||
|
||||
current-expense-account-amount (:invoice-expense-account/amount invoice-expense-account 0.0)
|
||||
|
||||
current-expense-account-amount (:invoice-expense-account/amount invoice-expense-account 0.0)
|
||||
target-expense-account-amount (- (Double/parseDouble amount))
|
||||
|
||||
|
||||
current-expense-account-location (:invoice-expense-account/location invoice-expense-account)
|
||||
current-expense-account-location (:invoice-expense-account/location invoice-expense-account)
|
||||
target-expense-account-location location
|
||||
|
||||
|
||||
[[_ _ invoice-payment]] (vec (dc/q
|
||||
'[:find ?p ?a ?ip
|
||||
:in $ ?i
|
||||
:where [?ip :invoice-payment/invoice ?i]
|
||||
[?ip :invoice-payment/amount ?a]
|
||||
[?ip :invoice-payment/payment ?p]
|
||||
]
|
||||
db invoice-id))]
|
||||
'[:find ?p ?a ?ip
|
||||
:in $ ?i
|
||||
:where [?ip :invoice-payment/invoice ?i]
|
||||
[?ip :invoice-payment/amount ?a]
|
||||
[?ip :invoice-payment/payment ?p]]
|
||||
db invoice-id))]
|
||||
:when current-total]
|
||||
|
||||
[
|
||||
(when (not (auto-ap.utils/dollars= current-total target-total))
|
||||
[(when (not (auto-ap.utils/dollars= current-total target-total))
|
||||
{:db/id invoice-id
|
||||
:invoice/total target-total})
|
||||
|
||||
@@ -486,7 +499,7 @@
|
||||
[:db/retractEntity invoice-payment])
|
||||
|
||||
(when (or new-account?
|
||||
(not (auto-ap.utils/dollars= current-expense-account-amount target-expense-account-amount)))
|
||||
(not (auto-ap.utils/dollars= current-expense-account-amount target-expense-account-amount)))
|
||||
{:db/id invoice-expense-account-id
|
||||
:invoice-expense-account/amount target-expense-account-amount})
|
||||
|
||||
@@ -495,7 +508,7 @@
|
||||
{:db/id invoice-expense-account-id
|
||||
:invoice-expense-account/location target-expense-account-location})
|
||||
|
||||
(when (not= current-account-id target-account-id )
|
||||
(when (not= current-account-id target-account-id)
|
||||
{:db/id invoice-expense-account-id
|
||||
:invoice-expense-account/account target-account-id})])
|
||||
(mapcat identity)
|
||||
@@ -506,18 +519,18 @@
|
||||
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
|
||||
(defn get-schema [prefix]
|
||||
(->> (dc/q '[:find ?i
|
||||
:in $ ?p
|
||||
:where [_ :db/ident ?i]
|
||||
[(namespace ?i) ?p]] (dc/db auto-ap.datomic/conn) prefix)
|
||||
:in $ ?p
|
||||
:where [_ :db/ident ?i]
|
||||
[(namespace ?i) ?p]] (dc/db auto-ap.datomic/conn) prefix)
|
||||
(mapcat identity)
|
||||
vec))
|
||||
|
||||
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
|
||||
(defn get-idents []
|
||||
(->> (dc/q '[:find ?i
|
||||
:in $
|
||||
:where [_ :db/ident ?i]]
|
||||
(dc/db conn) )
|
||||
:in $
|
||||
:where [_ :db/ident ?i]]
|
||||
(dc/db conn))
|
||||
(mapcat identity)
|
||||
(map str)
|
||||
(sort)
|
||||
@@ -532,45 +545,45 @@
|
||||
(defn sample-ledger-import
|
||||
([client-code]
|
||||
(sample-ledger-import client-code 10))
|
||||
([client-code n]
|
||||
([client-code n]
|
||||
(let [client-location (ffirst (d/q '[:find ?l :in $ ?c :where [?c :client/locations ?l]] (dc/db conn) [:client/code client-code]))]
|
||||
(clojure.data.csv/write-csv
|
||||
*out*
|
||||
(for [n (range n)
|
||||
:let [v (rand-nth (map first (d/q '[:find ?vn :where [_ :vendor/name ?vn]] (dc/db conn))))
|
||||
[{a-1 :account/numeric-code a-1-location :account/location}
|
||||
{a-2 :account/numeric-code a-2-location :account/location}] (->> (d/q '[:find (pull ?a [:account/numeric-code :account/location]) :where [?a :account/numeric-code]]
|
||||
(dc/db conn))
|
||||
(map first)
|
||||
(shuffle)
|
||||
(take 2))
|
||||
amount (rand-int 2000)
|
||||
d (-> (t/now)
|
||||
(t/minus (t/days (rand-int 60)))
|
||||
(atime/unparse atime/normal-date))
|
||||
id (rand-int 100000)]
|
||||
a [[(str id) client-code "synthetic" v d a-1 (or a-1-location client-location) 0 amount ]
|
||||
[(str id) client-code "synthetic" v d a-2 (or a-2-location client-location) amount 0]]]
|
||||
a)
|
||||
:separator \tab))))
|
||||
*out*
|
||||
(for [n (range n)
|
||||
:let [v (rand-nth (map first (d/q '[:find ?vn :where [_ :vendor/name ?vn]] (dc/db conn))))
|
||||
[{a-1 :account/numeric-code a-1-location :account/location}
|
||||
{a-2 :account/numeric-code a-2-location :account/location}] (->> (d/q '[:find (pull ?a [:account/numeric-code :account/location]) :where [?a :account/numeric-code]]
|
||||
(dc/db conn))
|
||||
(map first)
|
||||
(shuffle)
|
||||
(take 2))
|
||||
amount (rand-int 2000)
|
||||
d (-> (t/now)
|
||||
(t/minus (t/days (rand-int 60)))
|
||||
(atime/unparse atime/normal-date))
|
||||
id (rand-int 100000)]
|
||||
a [[(str id) client-code "synthetic" v d a-1 (or a-1-location client-location) 0 amount]
|
||||
[(str id) client-code "synthetic" v d a-2 (or a-2-location client-location) amount 0]]]
|
||||
a)
|
||||
:separator \tab))))
|
||||
|
||||
|
||||
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
|
||||
(defn sample-manual-yodlee
|
||||
([client-code]
|
||||
(sample-ledger-import client-code 10))
|
||||
([client-code n]
|
||||
([client-code n]
|
||||
(let [bank-accounts (map first (d/q '[:find ?bac :in $ ?c :where [?c :client/bank-accounts ?b] [?b :bank-account/code ?bac]] (dc/db conn) [:client/code client-code]))]
|
||||
(clojure.data.csv/write-csv
|
||||
*out*
|
||||
(for [n (range n)
|
||||
:let [amount (rand-int 2000)
|
||||
d (-> (t/now)
|
||||
(t/minus (t/days (rand-int 60)))
|
||||
(atime/unparse atime/normal-date))
|
||||
id (rand-int 100000)]]
|
||||
["posted" d (str "Random Description - " id) "Travel" nil nil (- amount) nil nil nil nil nil (rand-nth bank-accounts) client-code])
|
||||
:separator \tab))))
|
||||
*out*
|
||||
(for [n (range n)
|
||||
:let [amount (rand-int 2000)
|
||||
d (-> (t/now)
|
||||
(t/minus (t/days (rand-int 60)))
|
||||
(atime/unparse atime/normal-date))
|
||||
id (rand-int 100000)]]
|
||||
["posted" d (str "Random Description - " id) "Travel" nil nil (- amount) nil nil nil nil nil (rand-nth bank-accounts) client-code])
|
||||
:separator \tab))))
|
||||
|
||||
|
||||
|
||||
@@ -580,8 +593,7 @@
|
||||
:in $
|
||||
:where [?i :invoice/invoice-number]
|
||||
(not [?i :invoice/status :invoice-status/voided])]
|
||||
:args [
|
||||
(dc/db conn)]})
|
||||
:args [(dc/db conn)]})
|
||||
(map first)
|
||||
(partition-all 1000))]
|
||||
(print ".")
|
||||
@@ -627,4 +639,4 @@
|
||||
(print ".")
|
||||
@(dc/transact auto-ap.datomic/conn n)))
|
||||
|
||||
|
||||
|
||||
@@ -31,3 +31,87 @@
|
||||
[?c :client/code ?cd]
|
||||
[?c :client/locations ?l]]
|
||||
(dc/db conn))
|
||||
|
||||
|
||||
(init-repl)
|
||||
|
||||
(seq (dc/q
|
||||
'[:find ?a ?n ?n2
|
||||
:where [?a :account/name ?n]
|
||||
[?a :account/numeric-code ?n2]
|
||||
(not [?a :account/code])]
|
||||
(dc/db conn)))
|
||||
|
||||
(dc/q
|
||||
'[:find (pull ?a [* {:account/applicability [:db/ident] :account/default-allowance [*]}])
|
||||
:where [?a :account/numeric-code 34090]]
|
||||
(dc/db conn))
|
||||
|
||||
|
||||
(dc/q
|
||||
'[:find ?a
|
||||
:where [?a :account/numeric-code ?nc]
|
||||
(not [?a :account/default-allowance])]
|
||||
(dc/since (dc/db conn) #inst "2023-02-01"))
|
||||
|
||||
@(dc/transact conn
|
||||
(->>
|
||||
(dc/q
|
||||
'[:find ?a
|
||||
:where [?a :account/numeric-code ?nc]
|
||||
(not [?a :account/default-allowance])]
|
||||
(dc/since (dc/db conn) #inst "2023-02-01"))
|
||||
(map (fn [[a]]
|
||||
{:db/id a
|
||||
:account/default-allowance :allowance/allowed})))
|
||||
|
||||
)
|
||||
|
||||
|
||||
(dc/q '[:find (pull ?l [*])
|
||||
:in $ ?a
|
||||
:where [?a :invoice/client]
|
||||
[?l :journal-entry/original-entity ?a]]
|
||||
(dc/db conn)
|
||||
17592316421929)
|
||||
|
||||
(dc/pull (dc/db conn) '[*] 17592316421929)
|
||||
|
||||
(entity-history 17592316421929)
|
||||
|
||||
|
||||
(dc/q '[:find (pull ?l [*])
|
||||
:in $ ?a
|
||||
:where [?a :invoice/client]
|
||||
[?l :journal-entry/original-entity ?a]]
|
||||
(dc/db conn)
|
||||
17592316421929)
|
||||
|
||||
|
||||
;; Find journal entries that have been divorced from the original entity
|
||||
@(dc/transact auto-ap.datomic/conn
|
||||
(->>
|
||||
(dc/q '[:find ?l
|
||||
:in $ $$ $$$
|
||||
:where [$$ ?l :journal-entry/amount]
|
||||
(not [$ ?l :journal-entry/external-id])
|
||||
[$ ?l :journal-entry/source "invoice"]
|
||||
(not [$ ?l :journal-entry/original-entity])
|
||||
[$ ?l :journal-entry/client ?c]
|
||||
[$ ?c :client/code ?cd]
|
||||
[$$$ ?l :journal-entry/original-entity _ ?tx false]]
|
||||
(dc/db conn)
|
||||
(dc/since (dc/db conn) #inst "2024-02-04")
|
||||
(dc/history (dc/db conn)))
|
||||
(map (fn [[jl]]
|
||||
[:db/retractEntity jl]))
|
||||
seq))
|
||||
|
||||
|
||||
(entity-history 13194269907490)
|
||||
|
||||
(user/tx-detail 13194269907766)
|
||||
|
||||
(dc/tx-range (dc/log conn)
|
||||
13194269907490
|
||||
13194269907490)
|
||||
|
||||
Reference in New Issue
Block a user