merged.
This commit is contained in:
@@ -1,530 +0,0 @@
|
||||
;; This buffer is for Clojure experiments and evaluation.
|
||||
|
||||
;; Press C-j to evaluate the last expression.
|
||||
|
||||
;; You can also press C-u C-j to evaluate the expression and pretty-print its result.
|
||||
|
||||
(comment
|
||||
(ns auto-ap.backup
|
||||
(:require [datomic.api :as d]
|
||||
[manifold.deferred :as de]
|
||||
[manifold.executor :as ex]
|
||||
[manifold.stream :as s]
|
||||
[manifold.time :as mt]
|
||||
[auto-ap.jobs.core :refer [execute]]
|
||||
[clojure.java.io :as io]
|
||||
[amazonica.aws.s3 :as s3]
|
||||
[config.core :refer [env]]
|
||||
[clojure.core.async :as a]
|
||||
[lambdaisland.edn-lines :as ednl]
|
||||
[clojure.set :as set]
|
||||
[com.brunobonacci.mulog :as mu]))
|
||||
|
||||
|
||||
|
||||
|
||||
(def request-pool (ex/fixed-thread-executor 30))
|
||||
|
||||
(def buffered (ex/fixed-thread-executor 30))
|
||||
|
||||
(defn get-schema [remote-db]
|
||||
(let [everything (->> (d/q '[:find [(pull ?e [:db/ident
|
||||
{:db/valueType [:db/ident]}
|
||||
{:db/cardinality [:db/ident]}
|
||||
:db.attr/preds
|
||||
{:db/unique [:db/ident]}
|
||||
:db/isComponent
|
||||
:db/id
|
||||
:db/noHistory
|
||||
:db/tupleAttrs
|
||||
:db.entity/attrs
|
||||
:db.entity/preds
|
||||
:db/doc]) ...]
|
||||
:where [?e :db/ident]]
|
||||
remote-db))
|
||||
schema-attrs (->> everything
|
||||
(filter :db/ident)
|
||||
(filter (fn [{:db/keys [ident]}]
|
||||
(if (namespace ident)
|
||||
(re-matches #"^(?!cartographer)(?!db)(?!fressian).+" (namespace ident))
|
||||
true
|
||||
))))
|
||||
meta-schema-schema (filter #(-> % :db/ident not) everything)]
|
||||
schema-attrs))
|
||||
|
||||
|
||||
(def entity->best-key
|
||||
{"transaction-rule"
|
||||
[:transaction-rule/description, :transaction-rule/note :transaction-rule/vendor]
|
||||
"square-location"
|
||||
:square-location/square-id,
|
||||
"expected-deposit"
|
||||
:expected-deposit/date,
|
||||
"journal-entry-line"
|
||||
[:journal-entry-line/account, :journal-entry-line/debit :journal-entry-line/credit]
|
||||
"vendor"
|
||||
[:vendor/name, :vendor/default-account, :vendor/hidden]
|
||||
"transaction"
|
||||
:transaction/amount,
|
||||
"yodlee-provider-account"
|
||||
:yodlee-provider-account/id,
|
||||
"journal-entry"
|
||||
:journal-entry/source,
|
||||
"yodlee-merchant" :yodlee-merchant/yodlee-id,
|
||||
"invoice"
|
||||
:invoice/invoice-number,
|
||||
"vendor-terms-override"
|
||||
:vendor-terms-override/client,
|
||||
"integration-status"
|
||||
:integration-status/state,
|
||||
"conformity" :conformity/conformed-norms-index,
|
||||
"user"
|
||||
:user/provider-id,
|
||||
"sales-refund"
|
||||
:sales-refund/total,
|
||||
"plaid-account"
|
||||
:plaid-account/name,
|
||||
"charge"
|
||||
[:charge/total, :charge/external-id]
|
||||
"location-match" :location-match/location,
|
||||
"vendor-schedule-payment-dom"
|
||||
:vendor-schedule-payment-dom/dom,
|
||||
"account-client-override"
|
||||
:account-client-override/client,
|
||||
"plaid-item"
|
||||
:plaid-item/client,
|
||||
"transaction-account"
|
||||
:transaction-account/account,
|
||||
"address"
|
||||
[:address/street1, :address/city :address/state :address/zip]
|
||||
"order-line-item"
|
||||
:order-line-item/total,
|
||||
"ezcater-location" [:ezcater-location/location, :ezcater-location/caterer]
|
||||
"account"
|
||||
[:account/numeric-code, :account/code :account/name :account/type]
|
||||
"intuit-bank-account"
|
||||
:intuit-bank-account/name,
|
||||
"saved-query"
|
||||
:saved-query/guid,
|
||||
"ezcater-caterer"
|
||||
:ezcater-caterer/uuid,
|
||||
"forecasted-transaction"
|
||||
:forecasted-transaction/day-of-month,
|
||||
"audit" :audit/user,
|
||||
"yodlee-account"
|
||||
:yodlee-account/id,
|
||||
"transaction-rule-account"
|
||||
[:transaction-rule-account/account, :transaction-rule-account/location]
|
||||
"ezcater-integration"
|
||||
:ezcater-integration/subscriber-uuid,
|
||||
"report"
|
||||
:report/created,
|
||||
"bank-account"
|
||||
:bank-account/code,
|
||||
"vendor-usage"
|
||||
:vendor-usage/key,
|
||||
"invoice-expense-account"
|
||||
[:invoice-expense-account/expense-account-id, :invoice-expense-account/account :invoice-expense-account/location :invoice-expense-account/amount]
|
||||
"sales-order"
|
||||
:sales-order/date,
|
||||
"client"
|
||||
:client/code,
|
||||
"email-contact" :email-contact/email,
|
||||
"invoice-payment"
|
||||
:invoice-payment/amount,
|
||||
"contact"
|
||||
[:contact/name, :contact/phone :contact/email]
|
||||
"import-batch"
|
||||
:import-batch/date,
|
||||
"payment"
|
||||
[:payment/date, :payment/bank-account]
|
||||
"vendor-account-override"
|
||||
:vendor-account-override/client})
|
||||
|
||||
#_(defn references [schema]
|
||||
(filter (comp #{:db.type/ref} :db/ident :db/valueType) schema ))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#_(defn reference->entity [remote-db]
|
||||
(->> (d/q '[:find ?a ?v3
|
||||
:in $ $$ [?a ...]
|
||||
:where [$$ _ ?a ?e]
|
||||
[$ ?e ?v _ _]
|
||||
[$ ?v :db/ident ?v2 _ _]
|
||||
[(namespace ?v2) ?v3]
|
||||
[(namespace ?v2) ?v3]]
|
||||
remote-db
|
||||
(d/since remote-db #inst "2022-06-01")
|
||||
(map :db/ident references)
|
||||
)
|
||||
(group-by first)
|
||||
(map (fn [[k v]]
|
||||
[k (disj (set (map second v)) "db")]))
|
||||
(into {})))
|
||||
|
||||
#_(def manual-dependencies
|
||||
{:client/location-matches #{"location-match"}
|
||||
:transaction/yodlee-merchant #{"yodlee-merchant"}
|
||||
:vendor-account-override/account #{"account"}
|
||||
:vendor-account-override/client #{"client"}
|
||||
:vendor/secondary-contact #{"contact"}
|
||||
:vendor/account-overrides #{"vendor-account-override"}
|
||||
:client/bank-accounts #{"bank-account"}
|
||||
:transaction-rule/yodlee-merchant #{"yodlee-merchant"}
|
||||
:client/forecasted-transactions #{"forecasted-transaction"}
|
||||
:transaction/forecast-match #{"forecasted-transaction"}
|
||||
:vendor/automatically-paid-when-due #{"client"}
|
||||
:vendor/schedule-payment-dom #{"vendor-schedule-payment-dom"}
|
||||
:vendor/terms-overrides #{"vendor-terms-override"}
|
||||
:vendor-schedule-payment-dom/client #{"client"}})
|
||||
|
||||
#_(defn full-dependencies [remote-db]
|
||||
(update (merge-with into (reference->entity remote-db) manual-dependencies)
|
||||
:journal-entry/original-entity
|
||||
#(disj % "journal-entry")))
|
||||
|
||||
#_(defn entity-dependencies [schema]
|
||||
(let [base-dependencies
|
||||
(into
|
||||
{}
|
||||
(map (fn [i]
|
||||
[i #{}])
|
||||
(set (map (comp namespace :db/ident)
|
||||
(filter :db/valueType
|
||||
schema))))
|
||||
)
|
||||
]
|
||||
(into base-dependencies (reduce
|
||||
(fn [acc [ref deps]]
|
||||
(update acc (namespace ref) (fnil #(into % deps) #{})))
|
||||
{}
|
||||
(full-dependencies remote-db)))))
|
||||
|
||||
(def full-dependencies
|
||||
{:invoice/client #{"client"},
|
||||
:sales-order/client #{"client"},
|
||||
:transaction-rule/transaction-approval-status #{},
|
||||
:transaction/forecast-match #{"forecasted-transaction"},
|
||||
:user/role #{},
|
||||
:vendor-schedule-payment-dom/client #{"client"},
|
||||
:invoice-payment/payment #{"payment"},
|
||||
:transaction-rule/client #{"client"},
|
||||
:invoice/status #{},
|
||||
:payment/type #{},
|
||||
:expected-deposit/client #{"client"},
|
||||
:transaction/bank-account #{"bank-account"},
|
||||
:transaction-rule-account/account #{"account"},
|
||||
:import-batch/status #{},
|
||||
:user/clients #{"client"},
|
||||
:payment/client #{"client"},
|
||||
:expected-deposit/charges #{"charge"},
|
||||
:vendor/automatically-paid-when-due #{"client"},
|
||||
:payment/invoices #{"invoice"},
|
||||
:client/forecasted-transactions #{"forecasted-transaction"},
|
||||
:transaction/matched-rule #{"transaction-rule"},
|
||||
:invoice/import-status #{},
|
||||
:charge/processor #{},
|
||||
:expected-deposit/vendor #{"vendor"},
|
||||
:client/square-locations #{"square-location"},
|
||||
:payment/status #{},
|
||||
:client/location-matches #{"location-match"},
|
||||
:saved-query/client #{"client"},
|
||||
:transaction/payment #{"payment"},
|
||||
:transaction-rule/vendor #{"vendor"},
|
||||
:plaid-item/client #{"client"},
|
||||
:account/applicability #{},
|
||||
:journal-entry-line/account #{"account" "bank-account"},
|
||||
:client/bank-accounts #{"bank-account"},
|
||||
:yodlee-provider-account/client #{"client"},
|
||||
:account/vendor-allowance #{},
|
||||
:payment/bank-account #{"bank-account"},
|
||||
:account/default-allowance #{},
|
||||
:transaction-rule/yodlee-merchant #{"yodlee-merchant"},
|
||||
:vendor/account-overrides #{"vendor-account-override"},
|
||||
:transaction/client #{"client"},
|
||||
:invoice/vendor #{"vendor"},
|
||||
:sales-order/vendor #{"vendor"},
|
||||
:expected-deposit/status #{},
|
||||
:journal-entry/original-entity #{"transaction" "invoice"},
|
||||
:vendor-usage/client #{"client"},
|
||||
:transaction/expected-deposit #{"expected-deposit"},
|
||||
:client/ezcater-locations #{"ezcater-location"},
|
||||
:journal-entry/client #{"client"},
|
||||
:vendor/secondary-contact #{"contact"},
|
||||
:journal-entry/line-items #{"journal-entry-line"},
|
||||
:vendor/legal-entity-1099-type #{},
|
||||
:transaction-rule/bank-account #{"bank-account"},
|
||||
:transaction-account/account #{"account"},
|
||||
:vendor/terms-overrides #{"vendor-terms-override"},
|
||||
:vendor/default-account #{"account"},
|
||||
:transaction/yodlee-merchant #{"yodlee-merchant"},
|
||||
:sales-refund/client #{"client"},
|
||||
:client/emails #{"email-contact"},
|
||||
:payment/vendor #{"vendor"},
|
||||
:invoice-payment/invoice #{"invoice"},
|
||||
:report/client #{"client"},
|
||||
:transaction-rule/accounts #{"transaction-rule-account"},
|
||||
:charge/client #{"client"},
|
||||
:bank-account/type #{},
|
||||
:invoice-expense-account/account #{"account"},
|
||||
:vendor/legal-entity-tin-type #{},
|
||||
:transaction/approval-status #{},
|
||||
:import-batch/entry #{"transaction"},
|
||||
:bank-account/intuit-bank-account #{"intuit-bank-account"},
|
||||
:account/type #{},
|
||||
:sales-refund/vendor #{"vendor"},
|
||||
:bank-account/yodlee-account #{"yodlee-account"},
|
||||
:vendor/address #{"address"},
|
||||
:integration-status/state #{},
|
||||
:transaction/accounts #{"transaction-account"},
|
||||
:sales-order/charges #{"charge"},
|
||||
:client/address #{"address"},
|
||||
:ezcater-location/caterer #{"ezcater-caterer"},
|
||||
:vendor-account-override/client #{"client"},
|
||||
:bank-account/integration-status #{"integration-status"},
|
||||
:yodlee-provider-account/accounts #{"yodlee-account"},
|
||||
:account/invoice-allowance #{},
|
||||
:journal-entry/vendor #{"vendor"},
|
||||
:plaid-item/accounts #{"plaid-account"},
|
||||
:vendor-usage/vendor #{"vendor"},
|
||||
:sales-order/line-items #{"order-line-item"},
|
||||
:invoice/expense-accounts #{"invoice-expense-account"},
|
||||
:account-client-override/client #{"client"},
|
||||
:vendor/primary-contact #{"contact"},
|
||||
:vendor/schedule-payment-dom #{"vendor-schedule-payment-dom"},
|
||||
:account/client-overrides #{"account-client-override"},
|
||||
:transaction/vendor #{"vendor"},
|
||||
:client/square-integration-status #{"integration-status"},
|
||||
:ezcater-integration/caterers #{"ezcater-caterer"},
|
||||
:ezcater-integration/integration-status #{"integration-status"}
|
||||
:vendor-account-override/account #{"account"},
|
||||
:import-batch/source #{}})
|
||||
|
||||
(def entity-dependencies
|
||||
{"transaction-rule"
|
||||
#{"vendor" "yodlee-merchant" "transaction-rule-account" "bank-account"
|
||||
"client"},
|
||||
"square-location" #{},
|
||||
"expected-deposit" #{"vendor" "charge" "client"},
|
||||
"journal-entry-line" #{"account" "bank-account"},
|
||||
"vendor"
|
||||
#{"vendor-schedule-payment-dom" "address" "account" "client" "contact"
|
||||
"vendor-account-override"},
|
||||
"transaction"
|
||||
#{"transaction-rule" "expected-deposit" "vendor" "yodlee-merchant"
|
||||
"transaction-account" "forecasted-transaction" "bank-account" "client"
|
||||
"payment"},
|
||||
"yodlee-provider-account" #{"yodlee-account" "client"},
|
||||
"journal-entry"
|
||||
#{"journal-entry-line" "vendor" "transaction" "invoice" "client"},
|
||||
"yodlee-merchant" #{},
|
||||
"invoice" #{"vendor" "invoice-expense-account" "client"},
|
||||
"vendor-terms-override" #{},
|
||||
"integration-status" #{},
|
||||
"conformity" #{},
|
||||
"user" #{"client"},
|
||||
"sales-refund" #{"vendor" "client"},
|
||||
"plaid-account" #{},
|
||||
"charge" #{"client"},
|
||||
"location-match" #{},
|
||||
"vendor-schedule-payment-dom" #{"client"},
|
||||
"account-client-override" #{"client"},
|
||||
"plaid-item" #{"plaid-account" "client"},
|
||||
"transaction-account" #{"account"},
|
||||
"address" #{},
|
||||
"order-line-item" #{},
|
||||
"ezcater-location" #{"ezcater-caterer"},
|
||||
"account" #{"account-client-override"},
|
||||
"intuit-bank-account" #{},
|
||||
"saved-query" #{"client"},
|
||||
"ezcater-caterer" #{},
|
||||
"forecasted-transaction" #{},
|
||||
"audit" #{},
|
||||
"yodlee-account" #{},
|
||||
"transaction-rule-account" #{"account"},
|
||||
"ezcater-integration" #{"ezcater-caterer" "integration-status"},
|
||||
"report" #{"client"},
|
||||
"bank-account" #{"integration-status" "intuit-bank-account" "yodlee-account"},
|
||||
"vendor-usage" #{"vendor" "client"},
|
||||
"invoice-expense-account" #{"account"},
|
||||
"sales-order" #{"vendor" "charge" "order-line-item" "client"},
|
||||
"client"
|
||||
#{"square-location" "integration-status" "location-match" "address"
|
||||
"ezcater-location" "forecasted-transaction" "bank-account" "email-contact"},
|
||||
"email-contact" #{},
|
||||
"invoice-payment" #{"invoice" "payment"},
|
||||
"contact" #{},
|
||||
"import-batch" #{"transaction"},
|
||||
"payment" #{"vendor" "invoice" "bank-account" "client"},
|
||||
"vendor-account-override" #{"account" "client"}})
|
||||
|
||||
(defn order-of-insert [entity-dependencies]
|
||||
(loop [entity-dependencies entity-dependencies
|
||||
order []]
|
||||
(let [next-order (for [[entity deps] entity-dependencies
|
||||
:when (not (seq deps))]
|
||||
entity)
|
||||
next-deps (reduce
|
||||
(fn [entity-dependencies next-entity]
|
||||
(into {}
|
||||
(map
|
||||
(fn [[k v]]
|
||||
[k (disj v next-entity)])
|
||||
entity-dependencies)))
|
||||
(apply dissoc entity-dependencies next-order)
|
||||
next-order)]
|
||||
(if (seq next-deps)
|
||||
(recur next-deps (into order next-order))
|
||||
(into order next-order)))))
|
||||
|
||||
|
||||
(def loaded (atom #{}))
|
||||
|
||||
|
||||
(def dumped (atom #{}))
|
||||
|
||||
(defn write-s3 [data location]
|
||||
(spit (io/file "/tmp/temp-edn")
|
||||
(with-out-str (clojure.pprint/pprint data)))
|
||||
(s3/put-object :bucket-name (:data-bucket env)
|
||||
:key location
|
||||
:input-stream (io/make-input-stream (io/file "/tmp/temp-edn") {})))
|
||||
|
||||
(defn dump-schema [schema backup]
|
||||
(write-s3 (map
|
||||
(fn [s]
|
||||
(set/rename-keys s {:db/id :entity/migration-key}))
|
||||
schema)
|
||||
(str backup "/schema.edn"))
|
||||
(write-s3 full-dependencies
|
||||
(str backup "/full-dependencies.edn"))
|
||||
(write-s3 entity-dependencies
|
||||
(str backup "/entity-dependencies.edn")))
|
||||
|
||||
(defn pull-batch [remote-db schema entity entities]
|
||||
(de/future-with request-pool
|
||||
(mu/with-context {:entity entity}
|
||||
(try
|
||||
(when (= 0 (rand-int 100))
|
||||
(mu/log ::pulling :count (count entities)))
|
||||
(->> (d/pull-many remote-db
|
||||
(->> schema
|
||||
(filter :db/valueType)
|
||||
(mapv :db/ident)
|
||||
(filter #(= entity (namespace %)))
|
||||
(into [:db/id]))
|
||||
entities)
|
||||
(mapv (fn [m ]
|
||||
(reduce
|
||||
(fn [m [k v]]
|
||||
(cond
|
||||
(= k :db/id)
|
||||
(-> m
|
||||
(assoc :entity/migration-key v)
|
||||
(dissoc :db/id))
|
||||
(full-dependencies k)
|
||||
(if (vector? v)
|
||||
(assoc m k (mapv (fn [r] [:entity/migration-key (:db/id r)]) v))
|
||||
(assoc m k [:entity/migration-key (:db/id v)]))
|
||||
:else
|
||||
(dissoc m :payment/pdf-data
|
||||
:payment/memo
|
||||
:vendor/invoice-reminder-schedule)))
|
||||
m
|
||||
m))))
|
||||
(catch Throwable e
|
||||
(mu/log ::pull-error
|
||||
:exception e)
|
||||
|
||||
(throw e))))))
|
||||
|
||||
(def in-flight (atom 0))
|
||||
|
||||
(def so-far (atom 0))
|
||||
(def total (atom 0))
|
||||
|
||||
(defn dump-all
|
||||
([] (dump-all nil))
|
||||
([item-list]
|
||||
(let [backup-id (str "/datomic-backup/" (java.util.UUID/randomUUID))
|
||||
_ (mu/log ::starting-backup :backup backup-id)
|
||||
remote-db (d/db (datomic.api/connect "datomic:ddb://us-east-1/integreat/integreat-prod"))
|
||||
_ (mu/log ::fetching-schema)
|
||||
schema (get-schema remote-db)
|
||||
]
|
||||
(mu/log ::dumping-schema)
|
||||
(dump-schema schema backup-id)
|
||||
(mu/log ::schema-dumped)
|
||||
(doseq [entity (or item-list (filter (complement (conj @loaded "audit")) (order-of-insert entity-dependencies)))
|
||||
:let [_ (swap! dumped conj entity)
|
||||
_ (reset! so-far 0)
|
||||
_ (mu/log ::querying :entity entity)
|
||||
entities (d/q '[:find [?e ...]
|
||||
:in $ [?a ...]
|
||||
:where [?e ?a]]
|
||||
remote-db
|
||||
(cond-> (entity->best-key entity)
|
||||
(not (vector? (entity->best-key entity))) vector))
|
||||
|
||||
_ (reset! total (count entities))
|
||||
_ (mu/log ::entity-total-found :count (count entities) :entity entity)]]
|
||||
(mu/trace ::single-entity
|
||||
[:entity entity]
|
||||
(mu/with-context {:entity entity :total @total}
|
||||
(mu/log ::starting)
|
||||
(mu/log ::deleting)
|
||||
(io/delete-file (io/file "/tmp/tmp-ednl") true)
|
||||
(mu/log ::pulling)
|
||||
(ednl/with-append [append "/tmp/tmp-ednl" ]
|
||||
@(s/consume (fn [batch]
|
||||
(mu/with-context {:entity entity :total @total}
|
||||
(doseq [a batch]
|
||||
(try
|
||||
(append a)
|
||||
(catch Exception e
|
||||
(mu/log ::error
|
||||
:exception e)
|
||||
(throw e)))
|
||||
)
|
||||
(swap! so-far #(+ % (count batch)))
|
||||
(when (= 0 (rand-int 100))
|
||||
(mu/log ::appended :count (count batch)
|
||||
:so-far @so-far))))
|
||||
(->> (partition-all 100 entities)
|
||||
(into [])
|
||||
(s/->source)
|
||||
(s/onto buffered)
|
||||
(s/buffer 20)
|
||||
(s/map (fn [entities]
|
||||
(pull-batch remote-db schema entity entities)))
|
||||
(s/buffer 20)
|
||||
(s/realize-each)))
|
||||
)
|
||||
(try
|
||||
(mu/log ::copying)
|
||||
(let [f (io/file "/tmp/tmp-ednl")]
|
||||
(s3/put-object :bucket-name (:data-bucket env)
|
||||
:key (str backup-id "/" entity ".ednl")
|
||||
:input-stream (io/make-input-stream f {})
|
||||
:metadata {:content-length (.length f)}))
|
||||
(mu/log ::copied)
|
||||
(catch Exception e
|
||||
(mu/log ::upload-error
|
||||
:exception e)
|
||||
(throw e)))))))))
|
||||
|
||||
(defn -main [& _]
|
||||
(try
|
||||
(execute "export-backup" #(dump-all))
|
||||
(catch Exception e
|
||||
(println e)
|
||||
(mu/log ::quit-error
|
||||
:exception e
|
||||
:background-job "export-backup"
|
||||
:service "export-backup")
|
||||
(Thread/sleep 5000)
|
||||
(throw e))))
|
||||
)
|
||||
@@ -18,7 +18,6 @@
|
||||
[com.brunobonacci.mulog :as mu]
|
||||
[mount.core :as mount]
|
||||
[clojure.java.io :as io]
|
||||
[clojure.edn :as edn]
|
||||
[datomic.db :refer [id-literal]]
|
||||
[datomic.function :refer [construct]])
|
||||
(:import
|
||||
@@ -623,9 +622,13 @@
|
||||
(range length)))]
|
||||
(sort comparator results )))
|
||||
|
||||
(defn apply-pagination [args results]
|
||||
(log/info (take 4 results))
|
||||
(defn apply-pagination-raw [args results]
|
||||
{:entries (->> results
|
||||
(drop (:start args 0))
|
||||
(take (:count args (or (:per-page args) default-pagination-size))))
|
||||
:count (count results)})
|
||||
|
||||
(defn apply-pagination [args results]
|
||||
{:ids (->> results
|
||||
(drop (:start args 0))
|
||||
(take (:count args (or (:per-page args) default-pagination-size)))
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
[com.brunobonacci.mulog :as mu]
|
||||
[datomic.api :as dc]
|
||||
[clj-http.client :as client]
|
||||
[auto-ap.solr :as solr]))
|
||||
[auto-ap.solr :as solr]
|
||||
[clojure.set :as set]))
|
||||
|
||||
(def full-read '[*
|
||||
{:client/square-integration-status [:integration-status/message
|
||||
@@ -77,15 +78,19 @@
|
||||
(map cleanse)))
|
||||
|
||||
(defn get-minimal []
|
||||
(->> (dc/q '[:find (pull ?e [:client/name :client/code :client/locations :db/id {:client/bank-accounts [{:bank-account/type [:db/ident]}
|
||||
:bank-account/name
|
||||
:bank-account/sort-order
|
||||
:bank-account/bank-name
|
||||
:bank-account/visible
|
||||
:bank-account/current-balance
|
||||
:bank-account/locations
|
||||
:bank-account/code
|
||||
:db/id]}])
|
||||
(->> (dc/q '[:find (pull ?e [:client/name :client/code :client/locations :db/id
|
||||
{:client/bank-accounts [{:bank-account/type [:db/ident]}
|
||||
:bank-account/name
|
||||
:bank-account/sort-order
|
||||
:bank-account/bank-name
|
||||
:bank-account/visible
|
||||
:bank-account/current-balance
|
||||
:bank-account/locations
|
||||
:bank-account/code
|
||||
:db/id]
|
||||
:client/emails [:db/id
|
||||
:email-contact/email
|
||||
:email-contact/description]}])
|
||||
:where [?e :client/name]]
|
||||
(dc/db conn))
|
||||
(map first)
|
||||
@@ -145,34 +150,48 @@
|
||||
|
||||
|
||||
(defn raw-graphql-ids [db args]
|
||||
(let [query (cond-> {:query {:find []
|
||||
:in ['$ ]
|
||||
:where []}
|
||||
:args [db]}
|
||||
(let [name-like-ids (cond (not (str/blank? (:name-like args)))
|
||||
(set (map (comp #(Long/parseLong %) :id)
|
||||
(solr/query solr/impl "clients"
|
||||
{"query" (format "_text_:(%s*)" (str/upper-case (solr/escape (:name-like args))))
|
||||
"fields" "id"
|
||||
"limit" 300})))
|
||||
|
||||
|
||||
(not (str/blank? (:code args)))
|
||||
(merge-query {:query {:in ['?client-code]
|
||||
:where ['[?e :client/code ?client-code]]}
|
||||
:args [(:code args)]})
|
||||
(not (str/blank? (:code args)))
|
||||
(set (map (comp #(Long/parseLong %) :id)
|
||||
(solr/query solr/impl "clients" {"query" (format "_text_:(%s*)" (str/upper-case (solr/escape (:code args))))
|
||||
"fields" "id"
|
||||
"limit" 300})))
|
||||
|
||||
(not (str/blank? (:name-like args)))
|
||||
(merge-query {:query {:in ['?name-like]
|
||||
:where ['[?e :client/name ?name]
|
||||
'[(clojure.string/includes? ?name ?name-like)]]}
|
||||
:args [(:name-like args)]})
|
||||
:else
|
||||
nil)
|
||||
valid-ids (cond
|
||||
(and name-like-ids (limited-clients (:id args)))
|
||||
(set/intersection name-like-ids (limited-clients (:id args)))
|
||||
|
||||
(limited-clients (:id args))
|
||||
(merge-query {:query {:in ['[?e ...]]
|
||||
:where []}
|
||||
:args [(set (map :db/id (limited-clients (:id args))))]})
|
||||
(limited-clients (:id args))
|
||||
(limited-clients (:id args))
|
||||
|
||||
|
||||
(:sort args) (add-sorter-fields {"name" ['[?e :client/name ?sort-name]]}
|
||||
args)
|
||||
name-like-ids
|
||||
name-like-ids
|
||||
|
||||
true
|
||||
(merge-query {:query {:find ['?sort-default '?e] :where ['[?e :client/name ?sort-default]]}}))]
|
||||
:else
|
||||
nil)
|
||||
|
||||
query (cond-> {:query {:find []
|
||||
:in ['$ ]
|
||||
:where []}
|
||||
:args [db]}
|
||||
valid-ids
|
||||
(merge-query {:query {:in ['[?e ...]]}
|
||||
:args [(set valid-ids)]})
|
||||
|
||||
|
||||
(:sort args) (add-sorter-fields {"name" ['[?e :client/name ?sort-name]]}
|
||||
args)
|
||||
|
||||
true
|
||||
(merge-query {:query {:find ['?sort-default '?e] :where ['[?e :client/name ?sort-default]]}}))]
|
||||
(->> (query2 query)
|
||||
(apply-sort-3 (update args :sort conj {:sort-key "default-2" :asc true}))
|
||||
(apply-pagination args))))
|
||||
|
||||
@@ -297,8 +297,10 @@
|
||||
(mu/log ::trying-to-code-invoice
|
||||
:invoice invoice)
|
||||
(let [db (dc/db auto-ap.datomic/conn)
|
||||
client-id (:invoice/client invoice)
|
||||
vendor-id (:invoice/vendor invoice)
|
||||
client-id (or (:db/id (:invoice/client invoice))
|
||||
(:invoice/client invoice))
|
||||
vendor-id (or (:db/id (:invoice/vendor invoice))
|
||||
(:invoice/vendor invoice))
|
||||
date (:invoice/date invoice)
|
||||
vendor (dc/pull db '[*] vendor-id)
|
||||
due (when (:vendor/terms vendor)
|
||||
|
||||
@@ -59,7 +59,6 @@
|
||||
(map :db/id (:report/client r))))))))
|
||||
|
||||
(defn get-graphql [args]
|
||||
(clojure.pprint/pprint args)
|
||||
(let [db (dc/db conn)
|
||||
{ids-to-retrieve :ids matching-count :count} (raw-graphql-ids db args)]
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
(ns auto-ap.datomic.users
|
||||
(:require
|
||||
[auto-ap.datomic :refer [conn]]
|
||||
[datomic.api :as dc]))
|
||||
[datomic.api :as dc]
|
||||
[datomic.api :as d]))
|
||||
|
||||
(defn add-arg [query name value where & rest]
|
||||
(let [query (-> query
|
||||
@@ -19,27 +20,27 @@
|
||||
(map #(update % :user/role :db/ident))
|
||||
first))
|
||||
|
||||
(defn find-or-insert! [{:keys [:user/provider :user/provider-id] :as new-user}]
|
||||
|
||||
(defn find-or-insert! [{:keys [:user/provider :user/provider-id ] :as new-user}]
|
||||
(let [is-first-user? (not (seq (dc/q [:find '?e
|
||||
:in '$
|
||||
:where '[?e :user/provider]]
|
||||
(dc/db conn))))
|
||||
user (some-> (dc/q [:find '(pull ?e [*
|
||||
{:user/clients [*]}
|
||||
{:user/role [:db/ident]}])
|
||||
:in '$ '?provider '?provider-id
|
||||
:where '[?e :user/provider ?provider]
|
||||
'[?e :user/provider-id ?provider-id]]
|
||||
(dc/db conn) provider provider-id)
|
||||
first
|
||||
first
|
||||
(update :user/role :db/ident))]
|
||||
(if user
|
||||
user
|
||||
(let [new-user-trans @(dc/transact conn [(cond-> new-user
|
||||
true (assoc :db/id "user")
|
||||
is-first-user? (assoc :user/role :user-role/admin))])]
|
||||
(get-by-id (-> new-user-trans :tempids (get "user")))))))
|
||||
user-id (ffirst (dc/q '[:find ?e
|
||||
:in $ ?provider ?provider-id
|
||||
:where [?e :user/provider ?provider]
|
||||
[?e :user/provider-id ?provider-id]]
|
||||
(dc/db conn) provider provider-id))
|
||||
result @(dc/transact conn [[:upsert-entity (cond-> (assoc new-user :db/id (or user-id "user"))
|
||||
(not user-id) (assoc :user/role :user-role/none)
|
||||
is-first-user? (assoc :user/role :user-role/admin))]])
|
||||
user-id (or user-id (get-in result [:tempids "user"]))]
|
||||
(update (dc/pull (dc/db conn)
|
||||
'[*
|
||||
{:user/clients [*]}
|
||||
{:user/role [:db/ident]}]
|
||||
user-id)
|
||||
:user/role :db/ident)))
|
||||
|
||||
(defn raw-graphql [_]
|
||||
(->> (dc/q {:find ['(pull ?e [*
|
||||
|
||||
@@ -236,6 +236,8 @@
|
||||
:user
|
||||
{:fields {:id {:type :id}
|
||||
:name {:type 'String}
|
||||
:profile_image_url {:type 'String}
|
||||
:email {:type 'String}
|
||||
:role {:type :role}
|
||||
:clients {:type '(list :client)}}}
|
||||
|
||||
|
||||
@@ -71,89 +71,88 @@
|
||||
(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 {:db/id id
|
||||
:client/code client-code
|
||||
:client/name (:name edit_client)
|
||||
:client/matches (:matches edit_client)
|
||||
:client/signature-file signature-file
|
||||
: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))
|
||||
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/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)
|
||||
: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/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))
|
||||
: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]])
|
||||
_ (log/info "upserting client" 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)))
|
||||
@@ -167,18 +166,18 @@
|
||||
(: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))))
|
||||
(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))))
|
||||
|
||||
|
||||
(defn refresh-all-current-balance []
|
||||
|
||||
@@ -530,10 +530,17 @@
|
||||
(when (seq ignore-retraction)
|
||||
(audit-transact-batch ignore-retraction (:id context))))
|
||||
#_(log/info (map :tx success))
|
||||
(mu/trace ::success-tx
|
||||
[:count (count success)]
|
||||
(doseq [[_ n] (:tempids (audit-transact-batch (map :tx success) (:id context)))]
|
||||
(solr/touch n)))
|
||||
(let [invalidated
|
||||
(mu/trace ::success-tx
|
||||
[:count (count success)]
|
||||
(for [[_ n] (:tempids (audit-transact-batch (map :tx success) (:id context)))]
|
||||
n))]
|
||||
(future
|
||||
(mu/log ::indexing-solr :count (count invalidated))
|
||||
(mu/trace ::indexed-external-solr
|
||||
[:count (count invalidated)]
|
||||
(doseq [n invalidated]
|
||||
(solr/touch n)))))
|
||||
|
||||
{:successful (map (fn [x] {:external_id (:external_id x)}) success)
|
||||
:ignored (map (fn [x]
|
||||
|
||||
@@ -184,28 +184,29 @@
|
||||
matches))
|
||||
|
||||
(defn search [context args _]
|
||||
(let [search-query (str "name:(" (cleanse-query (:query args)) ")")]
|
||||
|
||||
(if-let [query (not-empty (cleanse-query (:query args)))]
|
||||
(let [search-query (str "name:(" query ")")]
|
||||
|
||||
|
||||
(for [{:keys [id name]} (solr/query solr/impl "vendors" {"query" (cond-> search-query
|
||||
(not (is-admin? (:id context))) (str " hidden:false"))
|
||||
"fields" "id, name"})]
|
||||
{:id (Long/parseLong id)
|
||||
:name (first name)})))
|
||||
(for [{:keys [id name]} (solr/query solr/impl "vendors" {"query" (cond-> search-query
|
||||
(not (is-admin? (:id context))) (str " hidden:false"))
|
||||
"fields" "id, name"})]
|
||||
{:id (Long/parseLong id)
|
||||
:name (first name)}))
|
||||
[]))
|
||||
|
||||
(def single-thread (ex/fixed-thread-executor 1))
|
||||
|
||||
(defn rebuild-search-index []
|
||||
(de/future-with
|
||||
single-thread
|
||||
(auto-ap.solr/index-documents-raw
|
||||
auto-ap.solr/impl
|
||||
"vendors"
|
||||
(for [[result] (dc/qseq {:query '[:find (pull ?v [:vendor/search-terms :db/id :vendor/name :vendor/hidden])
|
||||
:in $
|
||||
:where [?v :vendor/search-terms ]]
|
||||
:args [(dc/db conn)]})]
|
||||
{"id" (:db/id result)
|
||||
"name" (or (first (:vendor/search-terms result))
|
||||
(:vendor/name result))
|
||||
"hidden" (boolean (:vendor/hidden result))}))))
|
||||
single-thread
|
||||
(auto-ap.solr/index-documents-raw
|
||||
auto-ap.solr/impl
|
||||
"vendors"
|
||||
(for [[result] (dc/qseq {:query '[:find (pull ?v [:vendor/search-terms :db/id :vendor/name :vendor/hidden])
|
||||
:in $
|
||||
:where [?v :vendor/name]]
|
||||
:args [(dc/db conn)]})]
|
||||
{"id" (:db/id result)
|
||||
"name" (:vendor/name result)
|
||||
"hidden" (boolean (:vendor/hidden result))}))))
|
||||
|
||||
@@ -96,6 +96,7 @@
|
||||
(-> vendor :vendor/default-account :db/id)
|
||||
:invoice-expense-account/location location
|
||||
:invoice-expense-account/amount (Math/abs (Double/parseDouble invoice-total))
|
||||
:db/id (random-tempid)
|
||||
}]})))
|
||||
(filter :invoice/client)
|
||||
(into []))
|
||||
@@ -167,7 +168,7 @@
|
||||
:content
|
||||
first
|
||||
Double/parseDouble)
|
||||
invoice {:db/id (random-tempid)
|
||||
invoice {:db/id (random-tempid )
|
||||
:invoice/vendor :vendor/cintas
|
||||
:invoice/import-status :import-status/imported
|
||||
:invoice/status :invoice-status/unpaid
|
||||
@@ -267,7 +268,8 @@
|
||||
(mark-error k)
|
||||
[]))))
|
||||
(into []))]
|
||||
(audit-transact transaction {:user/name "sysco importer" :user/role "admin"})
|
||||
(doseq [t transaction]
|
||||
(audit-transact [t] {:user/name "sysco importer" :user/role "admin"}))
|
||||
(log/info ::success
|
||||
:count (count transaction)
|
||||
:sample (take 3 transaction)))
|
||||
|
||||
@@ -87,7 +87,7 @@
|
||||
[{:client/location-matches [:location-match/location :location-match/matches]}
|
||||
:client/default-location
|
||||
:client/locations]
|
||||
matching-client)
|
||||
(:db/id matching-client))
|
||||
location-hint
|
||||
location-hint )
|
||||
:date (coerce/to-date date)
|
||||
|
||||
@@ -124,7 +124,7 @@
|
||||
(defn exact-match
|
||||
([clients invoice-client-name]
|
||||
(->> clients
|
||||
(filter (fn [{:keys [:client/matches :client/name] :as client :or {matches []}}]
|
||||
(filter (fn [{:keys [:client/matches :client/location-matches :client/locations :client/name] :as client :or {matches []}}]
|
||||
(seq
|
||||
(filter (fn [m]
|
||||
(and
|
||||
|
||||
@@ -44,12 +44,14 @@
|
||||
extract)]))
|
||||
|
||||
(defn extract-sheet-details [bucket object]
|
||||
(-> (lambda/invoke {:function-name "xls-extractor" :payload
|
||||
(json/write-str
|
||||
(doto
|
||||
(-> (lambda/invoke {:function-name "xls-extractor" :payload
|
||||
(json/write-str
|
||||
{"s3_url" object "s3_bucket" bucket})})
|
||||
:payload
|
||||
slurp
|
||||
json/read-str))
|
||||
:payload
|
||||
slurp
|
||||
json/read-str)
|
||||
println))
|
||||
|
||||
(defn parse-file
|
||||
[file _]
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
|
||||
(def pdf-templates
|
||||
[
|
||||
;; CHEF's WAREHOUSE
|
||||
[;; CHEF's WAREHOUSE
|
||||
{:vendor "CHFW"
|
||||
:keywords [#"CHEF'S WAREHOUSE"]
|
||||
:extract {:total #"2 WKS C\.C\.\s+([\d.,]+)"
|
||||
@@ -75,9 +74,7 @@
|
||||
:parser {:date [:clj-time "MM/dd/yy"]
|
||||
:total [:trim-commas-and-negate nil]}}
|
||||
|
||||
|
||||
|
||||
;; SOUTHBAY FRESH
|
||||
;; SOUTHBAY FRESH
|
||||
{:vendor "Southbay Fresh Produce"
|
||||
:keywords [#"SOUTH BAY FRESH PRODUCE"]
|
||||
:extract {:date #"^([0-9]+/[0-9]+/[0-9]+)"
|
||||
@@ -173,10 +170,9 @@
|
||||
:parser {:date [:clj-time "MM/dd/yy"]
|
||||
:total [:trim-commas nil]}
|
||||
:multi (. java.util.regex.Pattern (compile (-> \formfeed str) java.util.regex.Pattern/CASE_INSENSITIVE))
|
||||
:multi-match? #"(Total\s+[0-9\.]+|Total Order)"
|
||||
}
|
||||
:multi-match? #"(Total\s+[0-9\.]+|Total Order)"}
|
||||
|
||||
;; AUTO-CHLOR
|
||||
;; AUTO-CHLOR
|
||||
{:vendor "Auto-Chlor"
|
||||
:keywords [#"AUTO-CHLOR"]
|
||||
:extract {:date #"DATE : ([0-9]+/[0-9]+/[0-9]+)"
|
||||
@@ -208,8 +204,7 @@
|
||||
:multi #"\n"
|
||||
:multi-match? #"^\s+.*?\d{6,}.*?\$"}
|
||||
|
||||
|
||||
;; C & L
|
||||
;; C & L
|
||||
{:vendor "C&L Produce"
|
||||
:keywords [#"440 Franklin Street"]
|
||||
:extract {:date #"([0-9]+/[0-9]+/[0-9]+)"
|
||||
@@ -239,8 +234,7 @@
|
||||
:parser {:date [:clj-time "dd-MMM-yy"]
|
||||
:total [:trim-commas-and-negate nil]}}
|
||||
|
||||
|
||||
;;; credits don't have the same format
|
||||
;;; credits don't have the same format
|
||||
{:vendor "General Produce Company"
|
||||
:keywords [#"1330 NORTH B"]
|
||||
:extract {:date #"DATE.*\n.*\n.*?([0-9]+/[0-9]+/[0-9]+)"
|
||||
@@ -362,9 +356,7 @@
|
||||
:parser {:date [:clj-time "MM/dd/yyyy"]
|
||||
:total [:trim-commas nil]}}
|
||||
|
||||
|
||||
|
||||
;; PACIFIC SEAFOOD
|
||||
;; PACIFIC SEAFOOD
|
||||
{:vendor "Pacific Seafood"
|
||||
:keywords [#"(pacseafood|PACIFIC FRESH)"]
|
||||
:extract {:date #"DATE(?:.*\n.*(?=([0-9]+/[0-9]+/[0-9]+)))([0-9]+/[0-9]+/[0-9]+)"
|
||||
@@ -421,13 +413,12 @@
|
||||
{:vendor "Le Boulanger"
|
||||
:keywords [#"Le Boulanger"]
|
||||
:extract {:date #"Invoice Date: ([^\n]+)\n"
|
||||
:customer-identifier #"Ship to\n+\s+([\S ]+?)(?=\s{2,})"
|
||||
:customer-identifier #"Ship to\n+\s+([\S ]+?)(?=\s{2,})"
|
||||
:invoice-number #"Invoice No: ([^\n]+)\n"
|
||||
:total #" Total:\s+([\d\.]+)"}
|
||||
:parser {:date [:clj-time "MMM dd, yyyy"]}}
|
||||
|
||||
|
||||
;; A&B
|
||||
;; A&B
|
||||
{:vendor "A&B Produce"
|
||||
:keywords [#"ABProduce"]
|
||||
:extract {:date #"^\s+([0-9]+/[0-9]+/[0-9]+)"
|
||||
@@ -530,19 +521,19 @@
|
||||
{:vendor "Performance Food Group - ROMA"
|
||||
:keywords [#"inquiries call 1-800-233-6211"]
|
||||
:extract {:date #"([0-9]+/[0-9]+/[0-9]+)"
|
||||
:customer-identifier #"BILL TO:\s+([\S ]+?)(?=\s{2,})"
|
||||
:customer-identifier #"BILL TO:\s+([\S ]+?)(?=\s{2,})"
|
||||
:invoice-number #"^\s+([\dA-Z]+)"
|
||||
:total #"([\d\.,\-]+\.[\d\-]+)"}
|
||||
:parser {:date [:clj-time "MM/dd/yyyy"]
|
||||
:total [:trim-commas-and-negate nil]}
|
||||
:multi #"\n"
|
||||
:multi-match? #"^\s+[\d]{6,8}\s+\d+"}
|
||||
:multi-match? #"^\s+[\d]{6,8}\s+\d+"}
|
||||
|
||||
;; ACME BREAD
|
||||
{:vendor "Acme Bread"
|
||||
:keywords [#"acmebread\.com"]
|
||||
:extract {:date #"([0-9]+/[0-9]+/[0-9]+)"
|
||||
:customer-identifier #"Print Date.*\n.*\n(.*)"
|
||||
:customer-identifier #"Print Date.*\n.*\n(.*)"
|
||||
:invoice-number #"^\s*(\d+)"
|
||||
:total #"\s{2,}(\d+\.\d{2})\s{2,}"}
|
||||
:parser {:date [:clj-time "MM/dd/yyyy"]
|
||||
@@ -554,19 +545,17 @@
|
||||
{:vendor "Performance Food Group - ROMA"
|
||||
:keywords [#"Performance Food Group, Inc\n\f"]
|
||||
:extract {:date #"Date: ([0-9]+/[0-9]+/[0-9]+)"
|
||||
:customer-identifier #"BILL TO:\s+([\S ]+?)(?=\s{2,})"
|
||||
:customer-identifier #"BILL TO:\s+([\S ]+?)(?=\s{2,})"
|
||||
:invoice-number #"INVOICE NO.\s+ ([\d]+)"
|
||||
:total #"([\d\.,]+)\s+INVOICE TOTAL"}
|
||||
:parser {:date [:clj-time "MM/dd/yy"]
|
||||
:total [:trim-commas nil]}}
|
||||
|
||||
|
||||
|
||||
;; JFC
|
||||
;; JFC
|
||||
{:vendor "JFC International"
|
||||
:keywords [#"48490 MILMONT DRIVE"]
|
||||
:extract {:date #"([0-9]+/[0-9]+/[0-9]+)"
|
||||
:customer-identifier #"SOLD\s+([\S ]+?)(?=(\s{2,}|\n))"
|
||||
:customer-identifier #"SOLD\s+([\S ]+?)(?=(\s{2,}|\n))"
|
||||
:invoice-number #"(\S+)\s+(?=[0-9]+/[0-9]+/[0-9]+)"
|
||||
:total #"(?:INVOICE|TOTAL|CREDIT)\s+([\d\.,\-]+\.[\d\-]+( CR)?)"}
|
||||
:parser {:date [:clj-time "MM/dd/yyyy"]
|
||||
@@ -576,7 +565,7 @@
|
||||
{:vendor "Roma Bakery Inc."
|
||||
:keywords [#"Roma Bakery Inc"]
|
||||
:extract {:date #"([0-9]+/[0-9]+/[0-9]+)"
|
||||
:customer-identifier #"Bill To.*\n\s+(.*?)\s{2,}"
|
||||
:customer-identifier #"Bill To.*\n\s+(.*?)\s{2,}"
|
||||
:invoice-number #"Invoice (\d+)"
|
||||
:total #"Total\s+([\d\-\.]+)"}
|
||||
:parser {:date [:clj-time "MM/dd/yy"]
|
||||
@@ -586,7 +575,7 @@
|
||||
{:vendor "Kael Foods"
|
||||
:keywords [#"kaelfoods.com"]
|
||||
:extract {:date #"([0-9]+/[0-9]+/[0-9]+)"
|
||||
:customer-identifier #"Bill To.*\n\s+(.*?)\s{2,}"
|
||||
:customer-identifier #"Bill To.*\n\s+(.*?)\s{2,}"
|
||||
:invoice-number #"INVOICE 0*(\d+)"
|
||||
:total #"TOTAL:\s+\$([\d\-\.,]+)"}
|
||||
:parser {:date [:clj-time "MM/dd/yyyy"]
|
||||
@@ -596,7 +585,7 @@
|
||||
{:vendor "Starter Bakery"
|
||||
:keywords [#"starterbakery.com"]
|
||||
:extract {:date #"INVOICE DATE:\s+(.*?)\s{2,}"
|
||||
:customer-identifier #"BILL TO:.*\n\s+(.*?)\s{2,}"
|
||||
:customer-identifier #"BILL TO:.*\n\s+(.*?)\s{2,}"
|
||||
:invoice-number #"Invoice.*?(\d+)"
|
||||
:total #"Total:.*?([\d\-,]+\.\d{2,2}+)"}
|
||||
:parser {:date [:clj-time "MMMM dd, yyyy"]
|
||||
@@ -606,9 +595,18 @@
|
||||
{:vendor "TriMark R.W. Smith"
|
||||
:keywords [#"TriMark"]
|
||||
:extract {:date #"([0-9]+/[0-9]+/[0-9]+)"
|
||||
:customer-identifier #"Bill To\s+(.*?)\s{2,}"
|
||||
:customer-identifier #"Bill To\s+(.*?)\s{2,}"
|
||||
:invoice-number #"Invoice #\n.*?([\d\-]+)\n"
|
||||
:total #"Invoice Total\s+([\d\-,]+\.\d{2,2}+)"}
|
||||
:parser {:date [:clj-time "MM/dd/yy"]
|
||||
:total [:trim-commas-and-negate nil]}}
|
||||
|
||||
{:vendor "Reel Produce"
|
||||
:keywords [#"reelproduce.com"]
|
||||
:extract {:date #"([0-9]+/[0-9]+/[0-9]+)"
|
||||
:customer-identifier #"Bill To(?:.*?)\n\n\s+(.*?)\s{2,}"
|
||||
:invoice-number #"Invoice #\n.*?([\d\-]+)\n"
|
||||
:total #"Total\s*\n\s+\$([\d\-,]+\.\d{2,2}+)"}
|
||||
:parser {:date [:clj-time "MM/dd/yy"]
|
||||
:total [:trim-commas-and-negate nil]}}])
|
||||
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
[clj-http.client :as http]
|
||||
[clj-time.core :as time]
|
||||
[clojure.tools.logging :as log]
|
||||
[config.core :refer [env]]))
|
||||
[config.core :refer [env]]
|
||||
[com.brunobonacci.mulog :as mu]))
|
||||
|
||||
(def google-client-id "264081895820-0nndcfo3pbtqf30sro82vgq5r27h8736.apps.googleusercontent.com")
|
||||
(def google-client-secret "OC-WemHurPXYpuIw5cT-B90g")
|
||||
@@ -30,23 +31,28 @@
|
||||
"grant_type" "authorization_code"}
|
||||
:as :json})
|
||||
:body)
|
||||
|
||||
token (:access_token auth)
|
||||
profile (-> (http/get "https://www.googleapis.com/oauth2/v1/userinfo"
|
||||
{:headers {"Authorization" (str "Bearer " token)} :as :json})
|
||||
:body)
|
||||
_ (mu/log ::got-profile
|
||||
:profile profile)
|
||||
user (users/find-or-insert! {:user/provider "google"
|
||||
:user/provider-id (:id profile)
|
||||
:user/role :user-role/none
|
||||
:user/email (:email profile)
|
||||
:user/profile-image-url (:picture profile)
|
||||
:user/name (:name profile)})
|
||||
auth {:user (:name profile)
|
||||
:exp (time/plus (time/now) (time/days 30))
|
||||
:db/id (:db/id user)
|
||||
:user/clients (map (fn [c]
|
||||
(select-keys c [:client/code :db/id :client/locations]))
|
||||
(:user/clients user))
|
||||
:user/role (name (:user/role user))
|
||||
:user/name (:name profile)}
|
||||
]
|
||||
(log/info "authenticated as user" user)
|
||||
_ (mu/log ::logged-in-as
|
||||
:auth auth)]
|
||||
;; TODO - these namespaces are not being transmitted/deserialized properly
|
||||
|
||||
(if (and token user)
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
[com.brunobonacci.mulog :as mu]
|
||||
[datomic.api :as dc]
|
||||
[hiccup2.core :as hiccup]
|
||||
[amazonica.aws.s3 :as s3]))
|
||||
[amazonica.aws.s3 :as s3]
|
||||
[auto-ap.ssr.components :as com]))
|
||||
|
||||
(defn fmt-amount [a]
|
||||
(with-precision 2
|
||||
@@ -27,19 +28,14 @@
|
||||
(.setScale 2 java.math.RoundingMode/HALF_UP)
|
||||
(double))))
|
||||
|
||||
|
||||
|
||||
|
||||
(defn rows->maps [rows]
|
||||
(let [[headers & rows] rows]
|
||||
(for [r rows]
|
||||
(into {}
|
||||
(map vector headers r)))))
|
||||
|
||||
|
||||
|
||||
|
||||
(defn map->sales-order [r clients]
|
||||
(println r)
|
||||
(let [order-number (get r "Order Number")
|
||||
event-date (get r "Event Date")
|
||||
store-name (get r "Store Name")
|
||||
@@ -89,7 +85,6 @@
|
||||
:tip (fmt-amount tip)}]
|
||||
:total (fmt-amount (+ food-total
|
||||
tax
|
||||
tip
|
||||
(or adjustments 0.0)))
|
||||
:discount (fmt-amount (or adjustments 0.0))
|
||||
:service-charge (fmt-amount (+ fee commission))
|
||||
@@ -136,30 +131,29 @@
|
||||
(stream->sales-orders s)))
|
||||
|
||||
(defn page* []
|
||||
[:div
|
||||
[:h1.title "EZCater XLS Import"]
|
||||
[:div.card.block {:style {:width "500px"}}
|
||||
[:div.card-content
|
||||
"Please go to "
|
||||
[:a {:href "https://www.ezcater.com/ez_manage/reports/new" :target "_blank"} "EZCater's report page"]
|
||||
" to generate a new report. Then drop it below."]]
|
||||
[:div#page-notification.notification.block {:style {:display "none"}}]
|
||||
[:div.card.block
|
||||
[:div.card-content
|
||||
[:form {:action (bidi/path-for ssr-routes/only-routes
|
||||
:admin-ezcater-xls)
|
||||
:method "POST"
|
||||
:class "dropzone"
|
||||
:id "ezcater"}]]]
|
||||
[:script
|
||||
(hiccup/raw
|
||||
"
|
||||
[:div.mt-4
|
||||
(com/card {}
|
||||
[:div.px-4.py-3.space-y-4.flex.flex-col
|
||||
[:h1.text-2xl.mb-3.font-bold "EZCater XLS Import"]
|
||||
[:p.text-sm.italic
|
||||
"Please go to "
|
||||
(com/link {:href "https://www.ezcater.com/ez_manage/reports/new" :target "_blank"} "EZCater's report page")
|
||||
" to generate a new report. Then drop it below."]
|
||||
[:div#page-notification.notification.block {:style {:display "none"}}]
|
||||
[:form.bg-blue-300 {:action (bidi/path-for ssr-routes/only-routes
|
||||
:admin-ezcater-xls)
|
||||
:method "POST"
|
||||
:class "dropzone"
|
||||
:id "ezcater"}]
|
||||
[:script
|
||||
(hiccup/raw
|
||||
"
|
||||
Dropzone.options.ezcater = {
|
||||
success: function (file, response) {
|
||||
document.getElementById(\"page-notification\").innerHTML = response;
|
||||
document.getElementById(\"page-notification\").style[\"display\"] = \"block\";
|
||||
}
|
||||
}")]])
|
||||
}")]])])
|
||||
|
||||
(defn upload-xls [{:keys [identity] :as request}]
|
||||
|
||||
@@ -195,8 +189,23 @@
|
||||
(if (= :post request-method)
|
||||
(upload-xls request)
|
||||
(base-page
|
||||
request
|
||||
(page*)
|
||||
|
||||
(admin-side-bar matched-route))))
|
||||
request
|
||||
(com/page {:nav (com/admin-aside-nav)
|
||||
:active-client (:client (:session request))
|
||||
:identity (:identity request)
|
||||
:app-params {:hx-get (bidi/path-for ssr-routes/only-routes
|
||||
:admin-ezcater-xls)
|
||||
: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
|
||||
:admin)}
|
||||
"Admin"]
|
||||
[:a {:href (bidi/path-for ssr-routes/only-routes
|
||||
:admin-ezcater-xls)}
|
||||
"EZCater XLS Import"])
|
||||
(page*))
|
||||
|
||||
"EZCater upload")))
|
||||
|
||||
|
||||
@@ -136,8 +136,8 @@
|
||||
|
||||
(defn import->invoice [{:keys [invoice-number source-url customer-identifier account-number total date vendor-code text full-text client-override vendor-override location-override import-status]}]
|
||||
(let [matching-client (cond
|
||||
account-number (d-clients/exact-match account-number)
|
||||
customer-identifier (d-clients/best-match customer-identifier)
|
||||
account-number (:db/id (d-clients/exact-match account-number))
|
||||
customer-identifier (:db/id (d-clients/best-match customer-identifier))
|
||||
client-override (Long/parseLong client-override))
|
||||
|
||||
matching-vendor (match-vendor vendor-code vendor-override)
|
||||
|
||||
@@ -136,7 +136,8 @@
|
||||
(index-documents-raw [this index xs]
|
||||
(client/post
|
||||
(str (assoc (url/url solr-uri "solr" index "update")
|
||||
:query {"commitWithin" 60000}))
|
||||
:query {"commitWithin" 5000
|
||||
"commit" true}))
|
||||
|
||||
{:headers {"Content-Type" "application/json"}
|
||||
:socket-timeout 30000
|
||||
@@ -147,7 +148,8 @@
|
||||
(index-documents [this index xs]
|
||||
(client/post
|
||||
(str (assoc (url/url solr-uri "solr" index "update")
|
||||
:query {"commitWithin" 60000}))
|
||||
:query {"commitWithin" 5000
|
||||
"commit" true}))
|
||||
{:headers {"Content-Type" "application/json"}
|
||||
:socket-timeout 30000
|
||||
:connection-timeout 30000
|
||||
@@ -168,7 +170,8 @@
|
||||
(delete [this index]
|
||||
(client/post
|
||||
(str (assoc (url/url solr-uri "solr" index "update")
|
||||
:query {"commitWithin" 15000}))
|
||||
:query {"commitWithin" 15000
|
||||
"commit" true}))
|
||||
{:headers {"Content-Type" "application/json"}
|
||||
:method "POST"
|
||||
:body (json/write-str {"delete" {"query" "*:*"}})})))
|
||||
@@ -203,3 +206,34 @@
|
||||
(index-documents impl index [i])))
|
||||
|
||||
|
||||
(defrecord InMemSolrClient [data-set-atom]
|
||||
SolrClient
|
||||
(index-documents [this index xs]
|
||||
(swap! data-set-atom
|
||||
(fn [data-set]
|
||||
(reduce
|
||||
(fn [data-set x]
|
||||
(let [thing (datomic->solr x)]
|
||||
(update data-set index conj [(str/join " " (vals x)) thing])))
|
||||
data-set
|
||||
xs)))
|
||||
nil)
|
||||
|
||||
(index-documents-raw [this index xs]
|
||||
(swap! data-set-atom
|
||||
(fn [data-set]
|
||||
(reduce
|
||||
(fn [data-set x]
|
||||
(update data-set index conj [(str/join " " (vals x)) x]))
|
||||
data-set
|
||||
xs))))
|
||||
|
||||
(query [this index q]
|
||||
(filter
|
||||
(fn [[x e]]
|
||||
(str/includes? x (get q "query")))
|
||||
(get @data-set-atom index)))
|
||||
(delete [this index]
|
||||
(swap! data-set-atom dissoc index)))
|
||||
|
||||
|
||||
|
||||
@@ -1,200 +0,0 @@
|
||||
(ns auto-ap.ssr.admin
|
||||
(:require
|
||||
[auto-ap.datomic :refer [conn]]
|
||||
[auto-ap.logging :as alog]
|
||||
[auto-ap.shared-views.admin.side-bar :refer [admin-side-bar]]
|
||||
[auto-ap.ssr.ui :refer [base-page]]
|
||||
[auto-ap.ssr.utils :refer [html-response]]
|
||||
[auto-ap.time :as atime]
|
||||
[clj-time.coerce :as coerce]
|
||||
[clojure.string :as str]
|
||||
[datomic.api :as dc]
|
||||
[hiccup2.core :as hiccup]))
|
||||
|
||||
(defn tx-rows->changes [history]
|
||||
(->> history
|
||||
(group-by (fn [[a _ t]]
|
||||
[a t]))
|
||||
(map (fn [[[a t] changes]]
|
||||
(let [changes (-> (reduce
|
||||
(fn [acc [_ v _ added]]
|
||||
(if added
|
||||
(assoc acc :added v)
|
||||
(assoc acc :removed v)))
|
||||
{}
|
||||
changes))]
|
||||
[t a changes])))))
|
||||
|
||||
(def error-script
|
||||
(hiccup/raw "on htmx:responseError from me set event.detail.target's innerHTML to event.detail.xhr.responseText end"))
|
||||
|
||||
(defn format-value [v]
|
||||
(cond (inst? v)
|
||||
(-> v
|
||||
coerce/to-date-time
|
||||
atime/localize
|
||||
(atime/unparse atime/normal-date))
|
||||
|
||||
(nil? v)
|
||||
[:em "(none)"]
|
||||
|
||||
(and (integer? v)
|
||||
(> v 1000000))
|
||||
[:span
|
||||
[:a {:hx-get (str "/admin/history/" v)
|
||||
:hx-swap "innerHTML"
|
||||
:hx-push-url "true"
|
||||
:hx-target "#history-table"}
|
||||
v]
|
||||
" [" [:a
|
||||
{:hx-get (str "/admin/history/inspect/" v)
|
||||
:hx-swap "innerHTML"
|
||||
:hx-target "#inspector"
|
||||
:hx-trigger "click"
|
||||
"_" error-script}
|
||||
"snapshot"] "]"]
|
||||
|
||||
|
||||
:else
|
||||
(pr-str v)))
|
||||
|
||||
(defn page-template [& {:keys [table entity-id]}]
|
||||
[:div
|
||||
[:div.columns
|
||||
[:div.column.is-4
|
||||
[:form {"hx-target" "#history-table"
|
||||
"hx-post" "/admin/history/search"
|
||||
"hx-swap" "innerHTML"
|
||||
"_" (hiccup/raw "on htmx:beforeRequest toggle @disabled on me then toggle .is-loading on <#dig/> end
|
||||
on htmx:afterRequest toggle @disabled on me then toggle .is-loading on <#dig /> end")
|
||||
}
|
||||
[:div.field.is-grouped
|
||||
[:p.control {}
|
||||
[:input.input {:type "text" :name "entity-id" :placeholder "Entity id" :value entity-id}]]
|
||||
[:p.control
|
||||
[:button#dig.button.is-primary {}
|
||||
"Dig"]]]]]]
|
||||
[:div#history-table
|
||||
table]])
|
||||
|
||||
(defn table [entity-id best-guess-entity history]
|
||||
[:div [:h1.title "History for "
|
||||
(str/capitalize best-guess-entity)
|
||||
" "
|
||||
entity-id]
|
||||
[:div.columns
|
||||
[:div.column.is-9
|
||||
[:table.table.compact.grid {:style "width: 100%"}
|
||||
[:thead
|
||||
[:tr
|
||||
[:td {:style "width: 14em"} "Date"]
|
||||
[:td {:style "width: 14em"} "User"]
|
||||
[:td {:style "width: 18em"} "Field"]
|
||||
[:td "From"]
|
||||
[:td "To"]]]
|
||||
[:tbody
|
||||
(for [[tx a c] history]
|
||||
[:tr
|
||||
[:td [:div [:div (some-> (:db/txInstant tx)
|
||||
coerce/to-date-time
|
||||
atime/localize
|
||||
(atime/unparse atime/standard-time))
|
||||
]
|
||||
[:div.tag (:db/id tx)]]]
|
||||
[:td (str (:audit/user tx))]
|
||||
[:td (namespace a) ": " (name a)]
|
||||
|
||||
[:td
|
||||
[:div.tag.is-danger.is-light
|
||||
[:span
|
||||
(format-value (:removed c))]]]
|
||||
[:td
|
||||
[:div.tag.is-primary.is-light
|
||||
[:span
|
||||
(format-value (:added c))]]]])]
|
||||
]]
|
||||
[:div.column.is-3
|
||||
[:div#inspector]]]])
|
||||
|
||||
(defn history-search [{:keys [form-params params] :as request}]
|
||||
(try
|
||||
(let [entity-id (Long/parseLong (or (some-> (:entity-id form-params) not-empty)
|
||||
(:entity-id params)
|
||||
(get params "entity-id")
|
||||
(get form-params "entity-id")))
|
||||
history (->>
|
||||
(dc/q '[:find ?a2 ?v (pull ?tx [:db/txInstant :audit/user :db/id]) ?ad
|
||||
:in $ $$ ?i
|
||||
:where
|
||||
[$$ ?i ?a ?v ?tx ?ad]
|
||||
[$ ?a :db/ident ?a2]]
|
||||
(dc/db conn)
|
||||
(dc/history (dc/db conn))
|
||||
entity-id )
|
||||
tx-rows->changes
|
||||
(sort-by (comp :db/id first))
|
||||
vec)
|
||||
best-guess-entity (or (->> history
|
||||
(group-by
|
||||
(comp
|
||||
namespace
|
||||
second)
|
||||
)
|
||||
(map (fn [[k v]]
|
||||
[k v]))
|
||||
(sort-by second)
|
||||
last
|
||||
first)
|
||||
"?")]
|
||||
|
||||
(if (get (:headers request) "hx-request")
|
||||
(html-response
|
||||
(table entity-id best-guess-entity history))
|
||||
(base-page request (page-template :table (table entity-id best-guess-entity history)
|
||||
:entity-id entity-id)
|
||||
(admin-side-bar :admin-history))))
|
||||
(catch NumberFormatException _
|
||||
(html-response
|
||||
[:div.notification.is-danger.is-light
|
||||
"Cannot parse the entity-id " (or (:entity-id form-params)
|
||||
(:entity-id params))
|
||||
|
||||
". It should be a number."]))))
|
||||
|
||||
(defn inspect [{{:keys [entity-id]} :params :as request}]
|
||||
(alog/info ::inspect
|
||||
:request request)
|
||||
(try
|
||||
(let [entity-id (Long/parseLong entity-id)
|
||||
data (dc/pull (dc/db conn)
|
||||
'[*]
|
||||
entity-id)]
|
||||
|
||||
(html-response
|
||||
[:div.box {:style {:position "sticky"
|
||||
:display "inline-block"
|
||||
:vertical-align "top"
|
||||
:overflow-y "auto"
|
||||
:max-height "100vh"
|
||||
:top "0px"
|
||||
:bottom "0px"}}
|
||||
[:div {:style {:display "inline-block"}}
|
||||
[:h1.title "Snapshot of "
|
||||
entity-id]
|
||||
[:ul
|
||||
(for [[k v] data]
|
||||
[:li [:strong k] ":" (format-value v)]
|
||||
)]]]))
|
||||
(catch NumberFormatException _
|
||||
(html-response
|
||||
[:div.notification.is-danger.is-light
|
||||
"Cannot parse the entity-id " entity-id ". It should be a number."]))))
|
||||
|
||||
(defn history [{:keys [matched-route] :as request}]
|
||||
(base-page request
|
||||
(page-template )
|
||||
(admin-side-bar matched-route)))
|
||||
|
||||
|
||||
|
||||
|
||||
190
src/clj/auto_ap/ssr/admin/history.clj
Normal file
190
src/clj/auto_ap/ssr/admin/history.clj
Normal file
@@ -0,0 +1,190 @@
|
||||
(ns auto-ap.ssr.admin.history
|
||||
(:require
|
||||
[auto-ap.datomic :refer [conn]]
|
||||
[auto-ap.logging :as alog]
|
||||
[auto-ap.ssr.ui :refer [base-page]]
|
||||
[auto-ap.ssr.utils :refer [html-response]]
|
||||
[auto-ap.time :as atime]
|
||||
[clj-time.coerce :as coerce]
|
||||
[clojure.string :as str]
|
||||
[datomic.api :as dc]
|
||||
[auto-ap.ssr.components :as com]
|
||||
[auto-ap.ssr-routes :as ssr-routes]
|
||||
[bidi.bidi :as bidi]))
|
||||
|
||||
(defn tx-rows->changes [history]
|
||||
(->> history
|
||||
(group-by (fn [[a _ t]]
|
||||
[a t]))
|
||||
(map (fn [[[a t] changes]]
|
||||
(let [changes (-> (reduce
|
||||
(fn [acc [_ v _ added]]
|
||||
(if added
|
||||
(assoc acc :added v)
|
||||
(assoc acc :removed v)))
|
||||
{}
|
||||
changes))]
|
||||
[t a changes])))))
|
||||
|
||||
(defn format-value [v]
|
||||
(cond (inst? v)
|
||||
(-> v
|
||||
coerce/to-date-time
|
||||
atime/localize
|
||||
(atime/unparse atime/normal-date))
|
||||
|
||||
(nil? v)
|
||||
[:em "(none)"]
|
||||
|
||||
(and (integer? v)
|
||||
(> v 1000000))
|
||||
[:span
|
||||
(com/link
|
||||
{:href "#"
|
||||
:hx-get (str "/admin/history/" v)
|
||||
:hx-swap "innerHTML"
|
||||
:hx-push-url "true"
|
||||
:hx-select "#history-table"
|
||||
:hx-target "#history-table"}
|
||||
v)
|
||||
" ["
|
||||
(com/link
|
||||
{:href "#"
|
||||
:hx-get (str "/admin/history/inspect/" v)
|
||||
:hx-swap "innerHTML"
|
||||
:hx-target "#inspector"
|
||||
:hx-trigger "click"}
|
||||
"snapshot") "]"]
|
||||
|
||||
:else
|
||||
(pr-str v)))
|
||||
|
||||
|
||||
(defn inspect [{{:keys [entity-id]} :params :as request}]
|
||||
(alog/info ::inspect
|
||||
:request request)
|
||||
(try
|
||||
(let [entity-id (Long/parseLong entity-id)
|
||||
data (dc/pull (dc/db conn)
|
||||
'[*]
|
||||
entity-id)]
|
||||
|
||||
(html-response
|
||||
[:section.py-3.sm:py-5.max-w-lg
|
||||
(com/card {:class "p-2"}
|
||||
[:div {:style {:display "inline-block"}}
|
||||
[:h1.title "Snapshot of "
|
||||
entity-id]
|
||||
[:ul
|
||||
(for [[k v] data]
|
||||
[:li [:strong k] ":" (format-value v)])]])]))
|
||||
(catch NumberFormatException _
|
||||
(html-response
|
||||
[:div.notification.is-danger.is-light
|
||||
"Cannot parse the entity-id " entity-id ". It should be a number."]))))
|
||||
|
||||
(defn result-table [{:keys [entity-id]}]
|
||||
(try
|
||||
(let [history (->>
|
||||
(dc/q '[:find ?a2 ?v (pull ?tx [:db/txInstant :audit/user :db/id]) ?ad
|
||||
:in $ $$ ?i
|
||||
:where
|
||||
[$$ ?i ?a ?v ?tx ?ad]
|
||||
[$ ?a :db/ident ?a2]]
|
||||
(dc/db conn)
|
||||
(dc/history (dc/db conn))
|
||||
entity-id)
|
||||
tx-rows->changes
|
||||
(sort-by (comp :db/id first))
|
||||
vec)
|
||||
best-guess-entity (or (->> history
|
||||
(group-by
|
||||
(comp
|
||||
namespace
|
||||
second))
|
||||
(map (fn [[k v]]
|
||||
[k v]))
|
||||
(sort-by second)
|
||||
last
|
||||
first)
|
||||
"?")]
|
||||
(com/data-grid-card {:id "history-table"
|
||||
:title (format "History for %s: %d" (str/capitalize best-guess-entity) entity-id)
|
||||
:route :history-table
|
||||
:paginate? false
|
||||
:total (count history)
|
||||
:subtitle nil
|
||||
:action-buttons nil
|
||||
:rows (for [[tx a c] history]
|
||||
(com/data-grid-row
|
||||
{}
|
||||
(com/data-grid-cell {} [:div [:div (some-> (:db/txInstant tx)
|
||||
coerce/to-date-time
|
||||
atime/localize
|
||||
(atime/unparse atime/standard-time))]
|
||||
[:div.tag (:db/id tx)]])
|
||||
(com/data-grid-cell {} (str (:audit/user tx)))
|
||||
(com/data-grid-cell {} (namespace a) ": " (name a))
|
||||
|
||||
(com/data-grid-cell {}
|
||||
(com/pill {:color :red}
|
||||
(format-value (:removed c))))
|
||||
(com/data-grid-cell {}
|
||||
[:div.tag.is-primary.is-light
|
||||
[:span
|
||||
(format-value (:added c))]])))
|
||||
:headers
|
||||
[(com/data-grid-header {}
|
||||
"Date")
|
||||
(com/data-grid-header {}
|
||||
"User")
|
||||
(com/data-grid-header {}
|
||||
"Field")
|
||||
(com/data-grid-header {}
|
||||
"From")
|
||||
(com/data-grid-header {}
|
||||
"To")]}))
|
||||
(catch NumberFormatException e
|
||||
(throw e))))
|
||||
|
||||
(defn search-box [{:keys [entity-id]}]
|
||||
[:div.mt-4
|
||||
[:form.flex.gap-2 {"hx-target" "#history-table"
|
||||
"hx-get" (bidi/path-for ssr-routes/only-routes
|
||||
:admin-history)
|
||||
"hx-select" "#history-table"
|
||||
"hx-swap" "innerHTML"
|
||||
"hx-ext" "debug"
|
||||
"hx-push-url" "true"}
|
||||
(com/text-input {:name "entity-id" :placeholder "Entity Id" :value entity-id
|
||||
:style {:width "300px"}})
|
||||
(com/button {:color :primary}
|
||||
"DIG")]])
|
||||
|
||||
(defn page [{:keys [matched-route route-params query-params] :as request}]
|
||||
(let [entity-id (or (some-> query-params (get "entity-id") Long/parseLong)
|
||||
(some-> route-params (get :entity-id) Long/parseLong))]
|
||||
(base-page request
|
||||
(com/page {:nav (com/admin-aside-nav)
|
||||
:active-client (:client (:session request))
|
||||
:identity (:identity request)
|
||||
:app-params {:hx-get (bidi/path-for ssr-routes/only-routes
|
||||
:admin-history)
|
||||
: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
|
||||
:admin)}
|
||||
"Admin"]
|
||||
[:a {:href (bidi/path-for ssr-routes/only-routes
|
||||
:admin-history)}
|
||||
"History"])
|
||||
(search-box {:entity-id entity-id})
|
||||
[:div.flex.gap-4.flex-col.lg:flex-row
|
||||
(if entity-id
|
||||
(result-table {:entity-id entity-id})
|
||||
[:div#history-table])
|
||||
[:div#inspector]
|
||||
])
|
||||
"History")))
|
||||
@@ -56,11 +56,12 @@
|
||||
: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 (:session request))}))
|
||||
nil))
|
||||
"My Company"))
|
||||
|
||||
|
||||
@@ -1,222 +1,234 @@
|
||||
(ns auto-ap.ssr.company.company-1099
|
||||
(:require
|
||||
[auto-ap.datomic :refer [conn remove-nils]]
|
||||
[auto-ap.graphql.utils :refer [is-admin?]]
|
||||
[auto-ap.datomic :refer [apply-pagination-raw conn remove-nils]]
|
||||
[auto-ap.graphql.utils :refer [assert-can-see-client is-admin?]]
|
||||
[auto-ap.ssr-routes :as ssr-routes]
|
||||
[auto-ap.ssr.components :as com]
|
||||
[auto-ap.ssr.grid-page-helper :as helper]
|
||||
[auto-ap.ssr.svg :as svg]
|
||||
[auto-ap.ssr.ui :refer [base-page]]
|
||||
[auto-ap.ssr.utils :refer [html-response form-data->map path->name]]
|
||||
[auto-ap.ssr.utils :refer [form-data->map html-response path->name]]
|
||||
[bidi.bidi :as bidi]
|
||||
[cemerick.url :as url]
|
||||
[clojure.string :as str]
|
||||
[datomic.api :as dc]
|
||||
[iol-ion.query :refer [can-see-client?]]))
|
||||
|
||||
<<<<<<< HEAD
|
||||
(defn get-1099-companies [user session]
|
||||
=======
|
||||
(def vendor-read '[:db/id
|
||||
:vendor/name
|
||||
{:vendor/legal-entity-1099-type [:db/ident]}
|
||||
{:vendor/legal-entity-tin-type [:db/ident]}
|
||||
{:vendor/address [:address/street1
|
||||
:address/city
|
||||
:address/state
|
||||
:address/zip]}
|
||||
:vendor/legal-entity-tin
|
||||
:vendor/legal-entity-name
|
||||
:vendor/legal-entity-first-name
|
||||
:vendor/legal-entity-middle-name
|
||||
:vendor/legal-entity-last-name])
|
||||
|
||||
(defn sum-for-client-vendor [client-id vendor-id]
|
||||
(ffirst (dc/q '[:find
|
||||
(sum ?a)
|
||||
:with ?d
|
||||
:in $ ?c ?v
|
||||
:where
|
||||
[?p :payment/client ?c]
|
||||
[?p :payment/date ?d ]
|
||||
[(>= ?d #inst "2022-01-01T08:00")]
|
||||
[(< ?d #inst "2023-01-01T08:00")]
|
||||
[?p :payment/type :payment-type/check]
|
||||
[?p :payment/amount ?a]
|
||||
[?p :payment/vendor ?v]]
|
||||
(dc/db conn)
|
||||
client-id
|
||||
vendor-id)))
|
||||
|
||||
(defn get-1099-companies [user {:keys [client-id] :as args}]
|
||||
>>>>>>> 425942581295562d2c57843d7b19d9d4de962eda
|
||||
(let [clients (->> (dc/q '[:find ?c
|
||||
:in $ ?user
|
||||
:where [?c :client/code]
|
||||
[(iol-ion.query/can-see-client? ?user ?c)]]
|
||||
(dc/db conn) user)
|
||||
:in $ ?user
|
||||
:where [?c :client/code]
|
||||
[(iol-ion.query/can-see-client? ?user ?c)]]
|
||||
(dc/db conn) user)
|
||||
(map first)
|
||||
set)
|
||||
results (cond
|
||||
(and (some-> session :client :db/id)
|
||||
(can-see-client? user
|
||||
(some-> session :client :db/id)))
|
||||
(and client-id
|
||||
(can-see-client? user client-id))
|
||||
(dc/q '[:find
|
||||
(pull ?c [:client/code :db/id])
|
||||
(pull ?v [:db/id
|
||||
:vendor/name
|
||||
{:vendor/legal-entity-1099-type [:db/ident]}
|
||||
{:vendor/legal-entity-tin-type [:db/ident]}
|
||||
{:vendor/address [:address/street1
|
||||
:address/city
|
||||
:address/state
|
||||
:address/zip]}
|
||||
:vendor/legal-entity-tin
|
||||
:vendor/legal-entity-name
|
||||
:vendor/legal-entity-first-name
|
||||
:vendor/legal-entity-middle-name
|
||||
:vendor/legal-entity-last-name])
|
||||
(sum ?a)
|
||||
:with ?d
|
||||
:in $ ?c
|
||||
:where
|
||||
[?p :payment/client ?c]
|
||||
[?p :payment/date ?d ]
|
||||
[(>= ?d #inst "2022-01-01T08:00")]
|
||||
[(< ?d #inst "2023-01-01T08:00")]
|
||||
[?p :payment/type :payment-type/check]
|
||||
[?p :payment/amount ?a]
|
||||
[?p :payment/vendor ?v]]
|
||||
(dc/db conn)
|
||||
(some-> session :client :db/id))
|
||||
(pull ?c [:client/code :db/id])
|
||||
(pull ?v vendor-read)
|
||||
(sum ?a)
|
||||
:with ?d
|
||||
:in $ ?c vendor-read
|
||||
:where
|
||||
[?p :payment/client ?c]
|
||||
[?p :payment/date ?d ]
|
||||
[(>= ?d #inst "2022-01-01T08:00")]
|
||||
[(< ?d #inst "2023-01-01T08:00")]
|
||||
[?p :payment/type :payment-type/check]
|
||||
[?p :payment/amount ?a]
|
||||
[?p :payment/vendor ?v]]
|
||||
(dc/db conn)
|
||||
client-id
|
||||
vendor-read)
|
||||
|
||||
(is-admin? user)
|
||||
(dc/q '[:find
|
||||
(pull ?c [:client/code :db/id])
|
||||
(pull ?v [:db/id
|
||||
:vendor/name
|
||||
{:vendor/legal-entity-1099-type [:db/ident]}
|
||||
{:vendor/legal-entity-tin-type [:db/ident]}
|
||||
{:vendor/address [:address/street1
|
||||
:address/city
|
||||
:address/state
|
||||
:address/zip]}
|
||||
:vendor/legal-entity-tin
|
||||
:vendor/legal-entity-name
|
||||
:vendor/legal-entity-first-name
|
||||
:vendor/legal-entity-middle-name
|
||||
:vendor/legal-entity-last-name])
|
||||
(sum ?a)
|
||||
:with ?d
|
||||
:in $
|
||||
:where
|
||||
[?p :payment/date ?d ]
|
||||
[(>= ?d #inst "2022-01-01T08:00")]
|
||||
[(< ?d #inst "2023-01-01T08:00")]
|
||||
[?p :payment/type :payment-type/check]
|
||||
[?p :payment/client ?c]
|
||||
[?p :payment/amount ?a]
|
||||
[?p :payment/vendor ?v]]
|
||||
(dc/db conn))
|
||||
(pull ?c [:client/code :db/id])
|
||||
(pull ?v vendor-read)
|
||||
(sum ?a)
|
||||
:with ?d
|
||||
:in $ vendor-read
|
||||
:where
|
||||
[?p :payment/date ?d ]
|
||||
[(>= ?d #inst "2022-01-01T08:00")]
|
||||
[(< ?d #inst "2023-01-01T08:00")]
|
||||
[?p :payment/type :payment-type/check]
|
||||
[?p :payment/client ?c]
|
||||
[?p :payment/amount ?a]
|
||||
[?p :payment/vendor ?v]]
|
||||
(dc/db conn)
|
||||
vendor-read)
|
||||
|
||||
:else
|
||||
(dc/q '[:find
|
||||
(pull ?c [:client/code :db/id])
|
||||
(pull ?v [:db/id
|
||||
:vendor/name
|
||||
{:vendor/legal-entity-1099-type [:db/ident]}
|
||||
{:vendor/legal-entity-tin-type [:db/ident]}
|
||||
{:vendor/address [:address/street1
|
||||
:address/city
|
||||
:address/state
|
||||
:address/zip]}
|
||||
:vendor/legal-entity-tin
|
||||
:vendor/legal-entity-name
|
||||
:vendor/legal-entity-first-name
|
||||
:vendor/legal-entity-middle-name
|
||||
:vendor/legal-entity-last-name])
|
||||
(sum ?a)
|
||||
:with ?d
|
||||
:in $ [?c ...]
|
||||
:where
|
||||
[?p :payment/client ?c]
|
||||
[?p :payment/date ?d ]
|
||||
[(>= ?d #inst "2022-01-01T08:00")]
|
||||
[(< ?d #inst "2023-01-01T08:00")]
|
||||
[?p :payment/type :payment-type/check]
|
||||
[?p :payment/amount ?a]
|
||||
[?p :payment/vendor ?v]]
|
||||
(dc/db conn)
|
||||
clients))]
|
||||
(->> results
|
||||
(filter (fn [[_ _ a]]
|
||||
(>= (or a 0.0) 600.0)))
|
||||
(sort-by (fn [[client _ amount]]
|
||||
[(:client/code client ) amount]))
|
||||
(into []))))
|
||||
(pull ?c [:client/code :db/id])
|
||||
(pull ?v vendor-read)
|
||||
(sum ?a)
|
||||
:with ?d
|
||||
:in $ [?c ...] vendor-read
|
||||
:where
|
||||
[?p :payment/client ?c]
|
||||
[?p :payment/date ?d ]
|
||||
[(>= ?d #inst "2022-01-01T08:00")]
|
||||
[(< ?d #inst "2023-01-01T08:00")]
|
||||
[?p :payment/type :payment-type/check]
|
||||
[?p :payment/amount ?a]
|
||||
[?p :payment/vendor ?v]]
|
||||
(dc/db conn)
|
||||
clients
|
||||
vendor-read))
|
||||
all (->> results
|
||||
(filter (fn [[_ _ a]]
|
||||
(>= (or a 0.0) 600.0)))
|
||||
(sort-by (fn [[client _ amount]]
|
||||
[(:client/code client ) amount]))
|
||||
(into []))
|
||||
paginated (apply-pagination-raw args all)]
|
||||
[(:entries paginated) (:count paginated)]))
|
||||
|
||||
(defn row* [{:keys [client vendor amount flash?]}]
|
||||
(com/data-grid-row
|
||||
{:class (when flash?
|
||||
"live-added")}
|
||||
(com/data-grid-cell {} (:client/code client))
|
||||
(com/data-grid-cell
|
||||
{}
|
||||
[:div.flex.whitespace-nowrap.items-center.gap-4
|
||||
[:div [:div (:vendor/name vendor)]
|
||||
[:div.text-sm.text-gray-400
|
||||
(or (-> vendor :vendor/legal-entity-name not-empty)
|
||||
(str (-> vendor :vendor/legal-entity-first-name) " "
|
||||
(-> vendor :vendor/legal-entity-middle-name) " "
|
||||
(-> vendor :vendor/legal-entity-last-name)))]]
|
||||
(when-let [t99-type (some-> vendor :vendor/legal-entity-1099-type :db/ident name)]
|
||||
(com/pill
|
||||
{:class "text-xs font-medium"
|
||||
:color :primary}
|
||||
(str/capitalize t99-type))
|
||||
)])
|
||||
(com/data-grid-cell
|
||||
{:class "hidden md:table-cell"}
|
||||
[:div.flex.gap-4
|
||||
(when-let [tin (-> vendor :vendor/legal-entity-tin)]
|
||||
[:span {:class "text-xs font-medium py-0.5 "}
|
||||
tin])
|
||||
(when-let [tin-type (some-> vendor :vendor/legal-entity-tin-type :db/ident name)]
|
||||
(com/pill {:class "text-xs font-medium"
|
||||
:color :yellow}
|
||||
(name tin-type)))])
|
||||
(com/data-grid-cell
|
||||
{:class "hidden lg:table-cell"}
|
||||
(if (-> vendor :vendor/address :address/street1)
|
||||
[:div
|
||||
[:div (-> vendor :vendor/address :address/street1)] " "
|
||||
[:div
|
||||
(-> vendor :vendor/address :address/street2)] " "
|
||||
[:div
|
||||
(-> vendor :vendor/address :address/city) " "
|
||||
(-> vendor :vendor/address :address/state) ","
|
||||
(-> vendor :vendor/address :address/zip)]]
|
||||
[:p.text-sm.italic.text-gray-400 "No address"]))
|
||||
(com/data-grid-cell {}
|
||||
(com/pill {:class "text-xs font-medium"
|
||||
:color :primary}
|
||||
"Paid $" (Math/round amount)))
|
||||
(com/data-grid-right-stack-cell
|
||||
{}
|
||||
(com/icon-button {:hx-get (bidi/path-for ssr-routes/only-routes
|
||||
:company-1099-vendor-dialog
|
||||
:vendor-id (:db/id vendor))
|
||||
:hx-target "#modal-holder"
|
||||
:hx-swap "outerHTML"}
|
||||
svg/pencil))))
|
||||
(defn table* [{:keys [identity session query-params hx-query-params]} & {:keys [flash-id]}]
|
||||
(let [start (or (some-> (or (get query-params "start") (get hx-query-params "start")) not-empty (Long/parseLong ))
|
||||
0)
|
||||
per-page (or (some-> (or (get query-params "per-page") (get hx-query-params "per-page")) not-empty (Long/parseLong ))
|
||||
30)
|
||||
companies (get-1099-companies identity session)
|
||||
total (count companies)
|
||||
companies (subvec companies (Math/min start total) (Math/min (+ start per-page) total))]
|
||||
(com/data-grid-card {:id "vendor-table"
|
||||
:title "1099 Vendor Info"
|
||||
:entity-name "vendors"
|
||||
:route :company-1099-vendor-table
|
||||
:start start
|
||||
:per-page per-page
|
||||
:total total
|
||||
:action-buttons [(com/button {:color :primary}
|
||||
(com/button-icon {} svg/refresh)
|
||||
"Add new product")
|
||||
(com/button {:color :secondary}
|
||||
(com/button-icon {} svg/refresh)
|
||||
"Update stocks 1/250")
|
||||
(com/icon-button {}
|
||||
svg/upload)]
|
||||
:rows (for [[client vendor amount] companies]
|
||||
(row* {:client client
|
||||
:vendor vendor
|
||||
:amount amount
|
||||
:flash? (= flash-id
|
||||
(:db/id vendor))}))
|
||||
:headers [(com/data-grid-header {} "Client")
|
||||
(com/data-grid-header {} "Vendor Name")
|
||||
(com/data-grid-header {:class "hidden md:table-cell"} "TIN")
|
||||
(com/data-grid-header {:class "hidden lg:table-cell"} "Address")
|
||||
(com/data-grid-header {})
|
||||
(com/data-grid-header {})]})))
|
||||
(def grid-page {:id "vendor-table"
|
||||
:nav (com/company-aside-nav)
|
||||
:id-fn (comp :db/id second)
|
||||
:fetch-page (fn [user args]
|
||||
(get-1099-companies user args)
|
||||
#_(r/get-graphql (into args {:id user})))
|
||||
:breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes
|
||||
:company)}
|
||||
"My Company"]
|
||||
|
||||
(defn vendor-save [{:keys [form-params identity route-params] :as request}]
|
||||
@(dc/transact conn [(remove-nils
|
||||
(-> (form-data->map form-params)
|
||||
(assoc :db/id (Long/parseLong (:vendor-id route-params)))
|
||||
(update :vendor/legal-entity-1099-type #(some->> % not-empty (keyword "legal-entity-1099-type")))
|
||||
(update :vendor/legal-entity-tin-type #(some->> % not-empty (keyword "legal-entity-tin-type")))))])
|
||||
(html-response
|
||||
(table* request :flash-id (Long/parseLong (:vendor-id route-params)))
|
||||
:headers {"hx-trigger" "closeModal"}))
|
||||
[:a {:href (bidi/path-for ssr-routes/only-routes
|
||||
:company-1099)}
|
||||
"1099 Vendor Info"]]
|
||||
:title "1099 Vendors"
|
||||
:entity-name "Vendors"
|
||||
:route :company-1099-vendor-table
|
||||
:action-buttons (fn [user]
|
||||
nil)
|
||||
:row-buttons (fn [user e]
|
||||
[(com/icon-button {:hx-get (str (bidi/path-for ssr-routes/only-routes
|
||||
:company-1099-vendor-dialog
|
||||
:vendor-id (:db/id (second e)))
|
||||
"?"
|
||||
(url/map->query {:client-id (:db/id (first e))}))
|
||||
:hx-ext "debug"
|
||||
:hx-target "#modal-holder"
|
||||
:hx-swap "outerHTML"}
|
||||
svg/pencil)])
|
||||
:headers [{:key "Client"
|
||||
:name "Client"
|
||||
:sort-key "client"
|
||||
:render (comp :client/code first)}
|
||||
{:key "vendor-name"
|
||||
:name "Vendor Name"
|
||||
:sort-key "vendor"
|
||||
:render (fn [[_ vendor]]
|
||||
[:div.flex.whitespace-nowrap.items-center.gap-4
|
||||
[:div [:div (:vendor/name vendor)]
|
||||
[:div.text-sm.text-gray-400
|
||||
(or (-> vendor :vendor/legal-entity-name not-empty)
|
||||
(str (-> vendor :vendor/legal-entity-first-name) " "
|
||||
(-> vendor :vendor/legal-entity-middle-name) " "
|
||||
(-> vendor :vendor/legal-entity-last-name)))]]
|
||||
(when-let [t99-type (some-> vendor :vendor/legal-entity-1099-type :db/ident name)]
|
||||
(com/pill
|
||||
{:class "text-xs font-medium"
|
||||
:color :primary}
|
||||
(str/capitalize t99-type))
|
||||
)])}
|
||||
{:key "tin"
|
||||
:name "TIN"
|
||||
:sort-key "tin"
|
||||
:show-starting "md"
|
||||
:render (fn [[_ vendor]]
|
||||
[:div.flex.gap-4
|
||||
(when-let [tin (-> vendor :vendor/legal-entity-tin)]
|
||||
[:span {:class "text-xs font-medium py-0.5 "}
|
||||
tin])
|
||||
(when-let [tin-type (some-> vendor :vendor/legal-entity-tin-type :db/ident name)]
|
||||
(com/pill {:class "text-xs font-medium"
|
||||
:color :yellow}
|
||||
(name tin-type)))]
|
||||
)}
|
||||
{:key "address"
|
||||
:name "Address"
|
||||
:sort-key "address"
|
||||
:show-starting "lg"
|
||||
:render (fn [[_ vendor]]
|
||||
(if (-> vendor :vendor/address :address/street1)
|
||||
[:div
|
||||
[:div (-> vendor :vendor/address :address/street1)] " "
|
||||
[:div
|
||||
(-> vendor :vendor/address :address/street2)] " "
|
||||
[:div
|
||||
(-> vendor :vendor/address :address/city) " "
|
||||
(-> vendor :vendor/address :address/state) ","
|
||||
(-> vendor :vendor/address :address/zip)]]
|
||||
[:p.text-sm.italic.text-gray-400 "No address"]))}
|
||||
{:key "paid"
|
||||
:name "Paid"
|
||||
:sort-key "paid"
|
||||
:render (fn [[_ _ paid]]
|
||||
(com/pill {:class "text-xs font-medium"
|
||||
:color :primary}
|
||||
"Paid $" (Math/round paid)))}]})
|
||||
|
||||
|
||||
|
||||
(def table* (partial helper/table* grid-page))
|
||||
(def row* (partial helper/row* grid-page))
|
||||
|
||||
(defn vendor-save [{:keys [form-params identity route-params query-params] :as request}]
|
||||
(let [client-id (Long/parseLong (get query-params "client-id"))
|
||||
vendor-id (Long/parseLong (:vendor-id route-params))]
|
||||
(assert-can-see-client identity client-id)
|
||||
@(dc/transact conn [(remove-nils
|
||||
(-> (form-data->map form-params)
|
||||
(assoc :db/id (Long/parseLong (:vendor-id route-params)))
|
||||
(update :vendor/legal-entity-1099-type #(some->> % not-empty (keyword "legal-entity-1099-type")))
|
||||
(update :vendor/legal-entity-tin-type #(some->> % not-empty (keyword "legal-entity-tin-type")))))])
|
||||
(html-response
|
||||
|
||||
(row* identity [(dc/pull (dc/db conn) [:db/id :client/code] client-id)
|
||||
(dc/pull (dc/db conn) vendor-read vendor-id)
|
||||
(sum-for-client-vendor client-id vendor-id)
|
||||
] {:flash? true})
|
||||
:headers {"hx-trigger" "closeModal"})))
|
||||
|
||||
(defn vendor-dialog [request]
|
||||
(let [vendor (dc/pull (dc/db conn) '[* {:vendor/legal-entity-1099-type [:db/ident]
|
||||
@@ -227,8 +239,10 @@
|
||||
[:form {:hx-post (str (bidi/path-for ssr-routes/only-routes
|
||||
:company-1099-vendor-save
|
||||
:request-method :post
|
||||
:vendor-id (Long/parseLong (:vendor-id (:params request)))))
|
||||
:hx-target "#vendor-table"
|
||||
:vendor-id (Long/parseLong (:vendor-id (:params request))))
|
||||
"?"
|
||||
(url/map->query {:client-id (:client-id (:params request))}))
|
||||
:hx-target (format "#vendor-table tr[data-id=\"%d\"]" (:db/id vendor))
|
||||
:hx-swap "outerHTML swap:300ms"}
|
||||
[:fieldset {:class "hx-disable"}
|
||||
(com/modal-card
|
||||
@@ -236,6 +250,7 @@
|
||||
[:div.flex [:div.p-2 "Vendor 1099 Info"] [:p.ml-2.rounded.bg-gray-200.p-2.dark:bg-gray-600 (:vendor/name vendor)]]
|
||||
[:div.space-y-6
|
||||
[:div.grid.grid-cols-6.gap-4
|
||||
|
||||
[:h4.text-xl.border-b.col-span-6 "Address"]
|
||||
[:div.col-span-6
|
||||
(com/field {:label "Street 1"}
|
||||
@@ -310,20 +325,5 @@
|
||||
"Save")]]]
|
||||
[:div])]]))))
|
||||
|
||||
(defn vendor-table [request]
|
||||
(html-response (table* request)
|
||||
:headers {"hx-push-url" (str "?start=" (get (:query-params request) "start"))}))
|
||||
|
||||
(defn page [{:keys [identity matched-route] :as request}]
|
||||
(base-page
|
||||
request
|
||||
(com/page {:nav (com/company-aside-nav)
|
||||
:active-client (:client (:session request))
|
||||
:identity (:identity request)}
|
||||
(com/breadcrumbs {}
|
||||
[:a {:href (bidi/path-for ssr-routes/only-routes
|
||||
:company)} "My Company"]
|
||||
[:a {:href (bidi/path-for ssr-routes/only-routes
|
||||
:company-1099)} "1099 Vendor Info"])
|
||||
(table* request))
|
||||
nil))
|
||||
(def vendor-table (partial helper/table grid-page))
|
||||
(def page (partial helper/page grid-page))
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
(ns auto-ap.ssr.company-dropdown
|
||||
(:require
|
||||
[auto-ap.datomic :refer [conn]]
|
||||
[auto-ap.graphql.utils :refer [assert-can-see-client]]
|
||||
[auto-ap.datomic :refer [conn pull-many]]
|
||||
[auto-ap.graphql.utils :refer [assert-can-see-client cleanse-query]]
|
||||
[auto-ap.solr :as solr]
|
||||
[auto-ap.ssr-routes :as ssr-routes]
|
||||
[auto-ap.ssr.svg :as svg]
|
||||
[auto-ap.ssr.utils :refer [html-response]]
|
||||
@@ -12,24 +13,30 @@
|
||||
|
||||
(defn dropdown-search-results* [{:keys [options]}]
|
||||
[:ul
|
||||
(for [[id company-name]options]
|
||||
(for [{:keys [id name]} options]
|
||||
[:li
|
||||
[:div {:class "flex items-center pl-2 rounded hover:bg-green-100 dark:hover:bg-green-600"}
|
||||
|
||||
[:a {:href "#" :class "w-full py-2 ml-2 text-sm font-medium text-gray-900 rounded dark:text-gray-300"
|
||||
"_" (hiccup/raw "on click set value of <#company-search-value/> to @data-value then send selected to #company-dropdown")
|
||||
:data-value id}
|
||||
company-name]]])])
|
||||
name]]])])
|
||||
|
||||
|
||||
|
||||
(defn get-clients [identity query]
|
||||
(dc/q '[:find ?c ?n
|
||||
:in $ ?user ?q
|
||||
:where [?c :client/name ?n]
|
||||
[(clojure.string/includes? ?n ?q)]
|
||||
[(iol-ion.query/can-see-client? ?user ?c)]]
|
||||
(dc/db conn)
|
||||
identity
|
||||
(or query "")))
|
||||
(if-let [query (not-empty (cleanse-query query))]
|
||||
(let [search-query (str "name:(" query ")")]
|
||||
|
||||
(for [n (pull-many (dc/db conn) [:client/name :db/id]
|
||||
(for [{:keys [id name]} (solr/query solr/impl "clients" {"query" search-query
|
||||
"fields" "id, name"})
|
||||
:let [client-id (Long/parseLong id)]
|
||||
:when (can-see-client? identity client-id)]
|
||||
client-id))]
|
||||
{:id (:db/id n)
|
||||
:name (:client/name n)}))
|
||||
[]))
|
||||
|
||||
(defn dropdown-search-results [{:keys [identity] :as request}]
|
||||
(html-response
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
|
||||
(def left-aside aside/left-aside-)
|
||||
(def company-aside-nav aside/company-aside-nav-)
|
||||
(def admin-aside-nav aside/admin-aside-nav-)
|
||||
(def main-aside-nav aside/main-aside-nav-)
|
||||
(def content-card card/content-card-)
|
||||
(def card card/card-)
|
||||
|
||||
@@ -42,9 +44,8 @@
|
||||
(def data-grid-cell data-grid/cell-)
|
||||
(def data-grid-right-stack-cell data-grid/right-stack-cell-)
|
||||
|
||||
(defn link [{:keys [class href]} & children]
|
||||
(into [:a {:href href
|
||||
:class (str "font-medium text-blue-600 dark:text-blue-500 hover:underline " class)}]
|
||||
(defn link [params & children]
|
||||
(into [:a (update params :class str " font-medium text-blue-600 dark:text-blue-500 hover:underline ")]
|
||||
children))
|
||||
|
||||
|
||||
|
||||
@@ -2,49 +2,57 @@
|
||||
(:require [auto-ap.ssr.svg :as svg]
|
||||
[hiccup2.core :as hiccup]
|
||||
[bidi.bidi :as bidi]
|
||||
[auto-ap.ssr-routes :as ssr-routes]))
|
||||
[auto-ap.ssr-routes :as ssr-routes]
|
||||
[auto-ap.client-routes :as client-routes]))
|
||||
|
||||
(defn menu-button- [params & children]
|
||||
[:a (-> params
|
||||
(dissoc :icon)
|
||||
(assoc :type "button",)
|
||||
(update :class str " cursor-pointer flex items-center p-2 w-full text-sm text-gray-600 rounded-lg transition duration-75 group hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700"))
|
||||
|
||||
(when (:icon params)
|
||||
[:span {:class "flex-shrink-0 w-6 h-6 text-gray-400 transition duration-75 group-hover:text-blue-500 dark:text-gray-400 group-hover:scale-110 dark:group-hover:text-white mr-3"}
|
||||
(:icon params)])
|
||||
[:div
|
||||
[:a (-> params
|
||||
(dissoc :icon)
|
||||
(assoc :type "button")
|
||||
(update :class str " cursor-pointer flex items-center p-2 w-full text-sm text-gray-600 rounded-lg transition duration-75 group hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700")
|
||||
(assoc :hx-indicator "find .htmx-indicator")
|
||||
(assoc :hx-select "#app-contents")
|
||||
(assoc :hx-target "#app-contents")
|
||||
(assoc :hx-swap "outerHTML"))
|
||||
|
||||
(into [:span {:class "flex-1 text-left whitespace-nowrap text-gray-600 dark:text-white"} ] children)
|
||||
(when (:data-collapse-toggle params)
|
||||
[:svg {:aria-hidden "true", :class "w-6 h-6", :fill "currentColor", :viewbox "0 0 20 20", :xmlns "http://www.w3.org/2000/svg"}
|
||||
[:path {:fill-rule "evenodd", :d "M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z", :clip-rule "evenodd"}]])])
|
||||
(when (:icon params)
|
||||
[:span {:class "flex-shrink-0 w-6 h-6 text-gray-400 transition duration-75 group-hover:text-blue-500 dark:text-gray-400 group-hover:scale-110 dark:group-hover:text-white mr-3"}
|
||||
(:icon params)])
|
||||
|
||||
(into [:span {:class "flex-1 text-left whitespace-nowrap text-gray-600 dark:text-white"}] children)
|
||||
(when (:data-collapse-toggle params)
|
||||
[:svg {:aria-hidden "true", :class "w-6 h-6", :fill "currentColor", :viewbox "0 0 20 20", :xmlns "http://www.w3.org/2000/svg"}
|
||||
[:path {:fill-rule "evenodd", :d "M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z", :clip-rule "evenodd"}]])
|
||||
[:div.htmx-indicator.flex.items-center
|
||||
(svg/spinner-primary {:class "inline w-4 h-4 text-white"})]]])
|
||||
|
||||
(defn sub-menu- [params & children]
|
||||
[:ul {:id (:id params) :class "hidden py-2 space-y-2"}
|
||||
(for [c children]
|
||||
[:li
|
||||
(update-in c [1 :class ] str " flex items-center p-2 pl-11 w-full text-base font-normal text-gray-900 rounded-lg transition duration-75 group hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700")])])
|
||||
(update-in c [1 1 :class ] str " flex items-center p-2 pl-11 w-full text-base font-normal text-gray-900 rounded-lg transition duration-75 group hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700")])])
|
||||
|
||||
(defn left-aside- [{:keys [nav page-specific]} & children]
|
||||
[:aside {:id "left-nav", :class "fixed top-0 left-0 pt-16 z-20 w-64 h-screen transition-transform -translate-x-full lg:translate-x-0", :aria-labelledby "left-nav" :aria-hidden "true"}
|
||||
[:aside {:id "left-nav", :class "fixed top-0 left-0 pt-16 z-20 w-64 h-screen transition-transform -translate-x-full lg:translate-x-0", :aria-labelledby "left-nav" :aria-hidden "true"
|
||||
"_" (hiccup/raw "init call initSidebarToggle()")}
|
||||
|
||||
[:div {:class "overflow-y-auto py-5 px-3 h-full bg-gray-50 border-r border-gray-200 dark:bg-gray-800 dark:border-gray-700"}
|
||||
nav
|
||||
|
||||
|
||||
|
||||
[:ul {:class "pt-5 mt-5 space-y-2 border-t border-gray-200 dark:border-gray-700"}
|
||||
#_[:li
|
||||
#_[:li
|
||||
[:a {:href "#", :class "flex items-center p-2 text-base font-normal text-gray-900 rounded-lg transition duration-75 hover:bg-gray-100 dark:hover:bg-gray-700 dark:text-white group"}
|
||||
[:svg {:aria-hidden "true", :class "flex-shrink-0 w-6 h-6 text-gray-400 transition duration-75 dark:text-gray-400 group-hover:text-gray-900 dark:group-hover:text-white", :fill "currentColor", :viewbox "0 0 20 20", :xmlns "http://www.w3.org/2000/svg"}
|
||||
[:path {:d "M9 2a1 1 0 000 2h2a1 1 0 100-2H9z"}]
|
||||
[:path {:fill-rule "evenodd", :d "M4 5a2 2 0 012-2 3 3 0 003 3h2a3 3 0 003-3 2 2 0 012 2v11a2 2 0 01-2 2H6a2 2 0 01-2-2V5zm3 4a1 1 0 000 2h.01a1 1 0 100-2H7zm3 0a1 1 0 000 2h3a1 1 0 100-2h-3zm-3 4a1 1 0 100 2h.01a1 1 0 100-2H7zm3 0a1 1 0 100 2h3a1 1 0 100-2h-3z", :clip-rule "evenodd"}]]
|
||||
[:span {:class "ml-3"} "Docs"]]]
|
||||
#_[:li
|
||||
#_[:li
|
||||
[:a {:href "#", :class "flex items-center p-2 text-base font-normal text-gray-900 rounded-lg transition duration-75 hover:bg-gray-100 dark:hover:bg-gray-700 dark:text-white group"}
|
||||
[:svg {:aria-hidden "true", :class "flex-shrink-0 w-6 h-6 text-gray-400 transition duration-75 dark:text-gray-400 group-hover:text-gray-900 dark:group-hover:text-white", :fill "currentColor", :viewbox "0 0 20 20", :xmlns "http://www.w3.org/2000/svg"}
|
||||
[:path {:d "M7 3a1 1 0 000 2h6a1 1 0 100-2H7zM4 7a1 1 0 011-1h10a1 1 0 110 2H5a1 1 0 01-1-1zM2 11a2 2 0 012-2h12a2 2 0 012 2v4a2 2 0 01-2 2H4a2 2 0 01-2-2v-4z"}]]
|
||||
[:span {:class "ml-3"} "Components"]]]
|
||||
#_[:li
|
||||
#_[:li
|
||||
[:a {:href "#", :class "flex items-center p-2 text-base font-normal text-gray-900 rounded-lg transition duration-75 hover:bg-gray-100 dark:hover:bg-gray-700 dark:text-white group"}
|
||||
[:svg {:aria-hidden "true", :class "flex-shrink-0 w-6 h-6 text-gray-400 transition duration-75 dark:text-gray-400 group-hover:text-gray-900 dark:group-hover:text-white", :fill "currentColor", :viewbox "0 0 20 20", :xmlns "http://www.w3.org/2000/svg"}
|
||||
[:path {:fill-rule "evenodd", :d "M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-2 0c0 .993-.241 1.929-.668 2.754l-1.524-1.525a3.997 3.997 0 00.078-2.183l1.562-1.562C15.802 8.249 16 9.1 16 10zm-5.165 3.913l1.58 1.58A5.98 5.98 0 0110 16a5.976 5.976 0 01-2.516-.552l1.562-1.562a4.006 4.006 0 001.789.027zm-4.677-2.796a4.002 4.002 0 01-.041-2.08l-.08.08-1.53-1.533A5.98 5.98 0 004 10c0 .954.223 1.856.619 2.657l1.54-1.54zm1.088-6.45A5.974 5.974 0 0110 4c.954 0 1.856.223 2.657.619l-1.54 1.54a4.002 4.002 0 00-2.346.033L7.246 4.668zM12 10a2 2 0 11-4 0 2 2 0 014 0z", :clip-rule "evenodd"}]]
|
||||
@@ -57,8 +65,7 @@
|
||||
[:a {:href "#", :data-tooltip-target "tooltip-settings", :class "inline-flex justify-center p-2 text-gray-500 rounded cursor-pointer dark:text-gray-400 dark:hover:text-white hover:text-gray-900 hover:bg-gray-100 dark:hover:bg-gray-600"}
|
||||
[:svg {:aria-hidden "true", :class "w-6 h-6", :fill "currentColor", :viewbox "0 0 20 20", :xmlns "http://www.w3.org/2000/svg"}
|
||||
[:path {:fill-rule "evenodd", :d "M11.49 3.17c-.38-1.56-2.6-1.56-2.98 0a1.532 1.532 0 01-2.286.948c-1.372-.836-2.942.734-2.106 2.106.54.886.061 2.042-.947 2.287-1.561.379-1.561 2.6 0 2.978a1.532 1.532 0 01.947 2.287c-.836 1.372.734 2.942 2.106 2.106a1.532 1.532 0 012.287.947c.379 1.561 2.6 1.561 2.978 0a1.533 1.533 0 012.287-.947c1.372.836 2.942-.734 2.106-2.106a1.533 1.533 0 01.947-2.287c1.561-.379 1.561-2.6 0-2.978a1.532 1.532 0 01-.947-2.287c.836-1.372-.734-2.942-2.106-2.106a1.532 1.532 0 01-2.287-.947zM10 13a3 3 0 100-6 3 3 0 000 6z", :clip-rule "evenodd"}]]]
|
||||
[:div {:id "tooltip-settings", :role "tooltip", :class "inline-block absolute invisible z-10 py-2 px-3 text-sm font-medium text-white bg-gray-900 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip"} "Settings page"
|
||||
]
|
||||
[:div {:id "tooltip-settings", :role "tooltip", :class "inline-block absolute invisible z-10 py-2 px-3 text-sm font-medium text-white bg-gray-900 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip"} "Settings page"]
|
||||
[:button {:type "button", :data-dropdown-toggle "language-dropdown", :class "inline-flex justify-center p-2 text-gray-500 rounded cursor-pointer dark:hover:text-white dark:text-gray-400 hover:text-gray-900 hover:bg-gray-100 dark:hover:bg-gray-600"}
|
||||
[:svg {:aria-hidden "true", :class "h-5 w-5 rounded-full mt-0.5", :xmlns "http://www.w3.org/2000/svg", :xmlns:xlink "http://www.w3.org/1999/xlink", :viewbox "0 0 3900 3900"}
|
||||
[:path {:fill "#b22234", :d "M0 0h7410v3900H0z"}]
|
||||
@@ -81,7 +88,7 @@
|
||||
[:use {:xlink:href "#e", :x "2470"}]]]]
|
||||
[:div {:class "hidden z-50 my-4 text-base list-none bg-white rounded divide-y divide-gray-100 shadow dark:bg-gray-700", :id "language-dropdown"}
|
||||
[:ul {:class "py-1", :role "none"}
|
||||
[:li
|
||||
[:li
|
||||
[:a {:href "#", :class "block py-2 px-4 text-sm text-gray-700 hover:bg-gray-100 dark:hover:text-white dark:text-gray-300 dark:hover:bg-gray-600", :role "menuitem"}
|
||||
[:div {:class "inline-flex items-center"}
|
||||
[:svg {:aria-hidden "true", :class "h-3.5 w-3.5 rounded-full mr-2", :xmlns "http://www.w3.org/2000/svg", :id "flag-icon-css-us", :viewbox "0 0 512 512"}
|
||||
@@ -90,15 +97,15 @@
|
||||
[:path {:fill "#bd3d44", :d "M0 0h247v10H0zm0 20h247v10H0zm0 20h247v10H0zm0 20h247v10H0zm0 20h247v10H0zm0 20h247v10H0zm0 20h247v10H0z", :transform "scale(3.9385)"}]
|
||||
[:path {:fill "#fff", :d "M0 10h247v10H0zm0 20h247v10H0zm0 20h247v10H0zm0 20h247v10H0zm0 20h247v10H0zm0 20h247v10H0z", :transform "scale(3.9385)"}]]
|
||||
[:path {:fill "#192f5d", :d "M0 0h98.8v70H0z", :transform "scale(3.9385)"}]
|
||||
[:path {:fill "#fff", :d "M8.2 3l1 2.8H12L9.7 7.5l.9 2.7-2.4-1.7L6 10.2l.9-2.7-2.4-1.7h3zm16.5 0l.9 2.8h2.9l-2.4 1.7 1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9zm16.5 0l.9 2.8H45l-2.4 1.7 1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9zm16.4 0l1 2.8h2.8l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h3zm16.5 0l.9 2.8h2.9l-2.4 1.7 1 2.7L74 8.5l-2.3 1.7.9-2.7-2.4-1.7h2.9zm16.5 0l.9 2.8h2.9L92 7.5l1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9zm-74.1 7l.9 2.8h2.9l-2.4 1.7 1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9zm16.4 0l1 2.8h2.8l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h3zm16.5 0l.9 2.8h2.9l-2.4 1.7 1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9zm16.5 0l.9 2.8h2.9l-2.4 1.7 1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7H65zm16.4 0l1 2.8H86l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h3zm-74 7l.8 2.8h3l-2.4 1.7.9 2.7-2.4-1.7L6 24.2l.9-2.7-2.4-1.7h3zm16.4 0l.9 2.8h2.9l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h2.9zm16.5 0l.9 2.8H45l-2.4 1.7 1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9zm16.4 0l1 2.8h2.8l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h3zm16.5 0l.9 2.8h2.9l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h2.9zm16.5 0l.9 2.8h2.9L92 21.5l1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9zm-74.1 7l.9 2.8h2.9l-2.4 1.7 1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9zm16.4 0l1 2.8h2.8l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h3zm16.5 0l.9 2.8h2.9l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h2.9zm16.5 0l.9 2.8h2.9l-2.4 1.7 1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7H65zm16.4 0l1 2.8H86l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h3zm-74 7l.8 2.8h3l-2.4 1.7.9 2.7-2.4-1.7L6 38.2l.9-2.7-2.4-1.7h3zm16.4 0l.9 2.8h2.9l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h2.9zm16.5 0l.9 2.8H45l-2.4 1.7 1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9zm16.4 0l1 2.8h2.8l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h3zm16.5 0l.9 2.8h2.9l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h2.9zm16.5 0l.9 2.8h2.9L92 35.5l1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9zm-74.1 7l.9 2.8h2.9l-2.4 1.7 1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9zm16.4 0l1 2.8h2.8l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h3zm16.5 0l.9 2.8h2.9l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h2.9zm16.5 0l.9 2.8h2.9l-2.4 1.7 1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7H65zm16.4 0l1 2.8H86l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h3zm-74 7l.8 2.8h3l-2.4 1.7.9 2.7-2.4-1.7L6 52.2l.9-2.7-2.4-1.7h3zm16.4 0l.9 2.8h2.9l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h2.9zm16.5 0l.9 2.8H45l-2.4 1.7 1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9zm16.4 0l1 2.8h2.8l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h3zm16.5 0l.9 2.8h2.9l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h2.9zm16.5 0l.9 2.8h2.9L92 49.5l1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9zm-74.1 7l.9 2.8h2.9l-2.4 1.7 1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9zm16.4 0l1 2.8h2.8l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h3zm16.5 0l.9 2.8h2.9l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h2.9zm16.5 0l.9 2.8h2.9l-2.4 1.7 1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7H65zm16.4 0l1 2.8H86l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h3zm-74 7l.8 2.8h3l-2.4 1.7.9 2.7-2.4-1.7L6 66.2l.9-2.7-2.4-1.7h3zm16.4 0l.9 2.8h2.9l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h2.9zm16.5 0l.9 2.8H45l-2.4 1.7 1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9zm16.4 0l1 2.8h2.8l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h3zm16.5 0l.9 2.8h2.9l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h2.9zm16.5 0l.9 2.8h2.9L92 63.5l1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9z", :transform "scale(3.9385)"}]]]" \n English (US)"]]]
|
||||
[:li
|
||||
[:path {:fill "#fff", :d "M8.2 3l1 2.8H12L9.7 7.5l.9 2.7-2.4-1.7L6 10.2l.9-2.7-2.4-1.7h3zm16.5 0l.9 2.8h2.9l-2.4 1.7 1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9zm16.5 0l.9 2.8H45l-2.4 1.7 1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9zm16.4 0l1 2.8h2.8l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h3zm16.5 0l.9 2.8h2.9l-2.4 1.7 1 2.7L74 8.5l-2.3 1.7.9-2.7-2.4-1.7h2.9zm16.5 0l.9 2.8h2.9L92 7.5l1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9zm-74.1 7l.9 2.8h2.9l-2.4 1.7 1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9zm16.4 0l1 2.8h2.8l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h3zm16.5 0l.9 2.8h2.9l-2.4 1.7 1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9zm16.5 0l.9 2.8h2.9l-2.4 1.7 1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7H65zm16.4 0l1 2.8H86l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h3zm-74 7l.8 2.8h3l-2.4 1.7.9 2.7-2.4-1.7L6 24.2l.9-2.7-2.4-1.7h3zm16.4 0l.9 2.8h2.9l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h2.9zm16.5 0l.9 2.8H45l-2.4 1.7 1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9zm16.4 0l1 2.8h2.8l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h3zm16.5 0l.9 2.8h2.9l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h2.9zm16.5 0l.9 2.8h2.9L92 21.5l1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9zm-74.1 7l.9 2.8h2.9l-2.4 1.7 1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9zm16.4 0l1 2.8h2.8l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h3zm16.5 0l.9 2.8h2.9l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h2.9zm16.5 0l.9 2.8h2.9l-2.4 1.7 1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7H65zm16.4 0l1 2.8H86l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h3zm-74 7l.8 2.8h3l-2.4 1.7.9 2.7-2.4-1.7L6 38.2l.9-2.7-2.4-1.7h3zm16.4 0l.9 2.8h2.9l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h2.9zm16.5 0l.9 2.8H45l-2.4 1.7 1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9zm16.4 0l1 2.8h2.8l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h3zm16.5 0l.9 2.8h2.9l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h2.9zm16.5 0l.9 2.8h2.9L92 35.5l1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9zm-74.1 7l.9 2.8h2.9l-2.4 1.7 1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9zm16.4 0l1 2.8h2.8l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h3zm16.5 0l.9 2.8h2.9l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h2.9zm16.5 0l.9 2.8h2.9l-2.4 1.7 1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7H65zm16.4 0l1 2.8H86l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h3zm-74 7l.8 2.8h3l-2.4 1.7.9 2.7-2.4-1.7L6 52.2l.9-2.7-2.4-1.7h3zm16.4 0l.9 2.8h2.9l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h2.9zm16.5 0l.9 2.8H45l-2.4 1.7 1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9zm16.4 0l1 2.8h2.8l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h3zm16.5 0l.9 2.8h2.9l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h2.9zm16.5 0l.9 2.8h2.9L92 49.5l1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9zm-74.1 7l.9 2.8h2.9l-2.4 1.7 1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9zm16.4 0l1 2.8h2.8l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h3zm16.5 0l.9 2.8h2.9l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h2.9zm16.5 0l.9 2.8h2.9l-2.4 1.7 1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7H65zm16.4 0l1 2.8H86l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h3zm-74 7l.8 2.8h3l-2.4 1.7.9 2.7-2.4-1.7L6 66.2l.9-2.7-2.4-1.7h3zm16.4 0l.9 2.8h2.9l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h2.9zm16.5 0l.9 2.8H45l-2.4 1.7 1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9zm16.4 0l1 2.8h2.8l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h3zm16.5 0l.9 2.8h2.9l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h2.9zm16.5 0l.9 2.8h2.9L92 63.5l1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9z", :transform "scale(3.9385)"}]]] " \n English (US)"]]]
|
||||
[:li
|
||||
[:a {:href "#", :class "block py-2 px-4 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:text-white dark:hover:bg-gray-600", :role "menuitem"}
|
||||
[:div {:class "inline-flex items-center"}
|
||||
[:svg {:aria-hidden "true", :class "h-3.5 w-3.5 rounded-full mr-2", :xmlns "http://www.w3.org/2000/svg", :id "flag-icon-css-de", :viewbox "0 0 512 512"}
|
||||
[:path {:fill "#ffce00", :d "M0 341.3h512V512H0z"}]
|
||||
[:path {:d "M0 0h512v170.7H0z"}]
|
||||
[:path {:fill "#d00", :d "M0 170.7h512v170.6H0z"}]]]]]
|
||||
[:li
|
||||
[:li
|
||||
[:a {:href "#", :class "block py-2 px-4 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:text-white dark:hover:bg-gray-600", :role "menuitem"}
|
||||
[:div {:class "inline-flex items-center"}
|
||||
[:svg {:aria-hidden "true", :class "h-3.5 w-3.5 rounded-full mr-2", :xmlns "http://www.w3.org/2000/svg", :id "flag-icon-css-it", :viewbox "0 0 512 512"}
|
||||
@@ -106,18 +113,39 @@
|
||||
[:path {:fill "#fff", :d "M0 0h512v512H0z"}]
|
||||
[:path {:fill "#009246", :d "M0 0h170.7v512H0z"}]
|
||||
[:path {:fill "#ce2b37", :d "M341.3 0H512v512H341.3z"}]]]]]]
|
||||
[:li
|
||||
[:li
|
||||
[:a {:href "#", :class "block py-2 px-4 text-sm text-gray-700 hover:bg-gray-100 dark:hover:text-white dark:text-gray-300 dark:hover:bg-gray-600", :role "menuitem"}
|
||||
[:div {:class "inline-flex items-center"}
|
||||
[:svg {:aria-hidden "true", :class "h-3.5 w-3.5 rounded-full mr-2", :xmlns "http://www.w3.org/2000/svg", :xmlns:xlink "http://www.w3.org/1999/xlink", :id "flag-icon-css-cn", :viewbox "0 0 512 512"}
|
||||
[:defs
|
||||
[:defs
|
||||
[:path {:id "a", :fill "#ffde00", :d "M1-.3L-.7.8 0-1 .6.8-1-.3z"}]]
|
||||
[:path {:fill "#de2910", :d "M0 0h512v512H0z"}]
|
||||
[:use {:width "30", :height "20", :transform "matrix(76.8 0 0 76.8 128 128)", :xlink:href "#a"}]
|
||||
[:use {:width "30", :height "20", :transform "rotate(-121 142.6 -47) scale(25.5827)", :xlink:href "#a"}]
|
||||
[:use {:width "30", :height "20", :transform "rotate(-98.1 198 -82) scale(25.6)", :xlink:href "#a"}]
|
||||
[:use {:width "30", :height "20", :transform "rotate(-74 272.4 -114) scale(25.6137)", :xlink:href "#a"}]
|
||||
[:use {:width "30", :height "20", :transform "matrix(16 -19.968 19.968 16 256 230.4)", :xlink:href "#a"}]]"中文 (繁體)"]]]]]]])
|
||||
[:use {:width "30", :height "20", :transform "matrix(16 -19.968 19.968 16 256 230.4)", :xlink:href "#a"}]] "中文 (繁體)"]]]]]]
|
||||
[:script {:lang "text/javascript"}
|
||||
(hiccup/raw "
|
||||
function initSidebarToggle() {
|
||||
var $targetEl = document.getElementById('left-nav');
|
||||
|
||||
var $triggerEl = document.getElementById('left-nav-toggle');
|
||||
|
||||
var options = {
|
||||
onCollapse: () => {
|
||||
document.getElementById('main-content').classList.remove('lg:pl-64')
|
||||
},
|
||||
onExpand: () => {
|
||||
document.getElementById('main-content').classList.add('lg:pl-64')
|
||||
},
|
||||
onToggle: () => {
|
||||
}
|
||||
};
|
||||
|
||||
var collapse = new Collapse($targetEl, $triggerEl, options);
|
||||
}
|
||||
")]])
|
||||
|
||||
(defn main-aside-nav- []
|
||||
[:ul {:class "space-y-2"}
|
||||
@@ -186,8 +214,9 @@
|
||||
(menu-button- {:href "Sales"} "Balance Sheet")
|
||||
(menu-button- {:href "Sales"} "External Ledger Import"))]])
|
||||
|
||||
|
||||
(defn company-aside-nav- []
|
||||
[:ul {:class "space-y-2"}
|
||||
[:ul {:class "space-y-2" :hx-boost "true"}
|
||||
[:li
|
||||
(menu-button- {:icon svg/vendors
|
||||
:href (bidi/path-for ssr-routes/only-routes
|
||||
@@ -210,3 +239,65 @@
|
||||
:company-1099)}
|
||||
"1099 Vendor Info"
|
||||
)]])
|
||||
|
||||
(defn admin-aside-nav- []
|
||||
[:ul {:class "space-y-2"}
|
||||
[:li
|
||||
(menu-button- {:icon svg/dashboard
|
||||
:href (bidi/path-for client-routes/routes
|
||||
:admin)}
|
||||
"Dashboard")]
|
||||
|
||||
[:li
|
||||
(menu-button- {:icon svg/restaurant
|
||||
:href (bidi/path-for client-routes/routes
|
||||
:admin-clients)}
|
||||
"Clients")]
|
||||
[:li
|
||||
(menu-button- {:icon svg/vendors
|
||||
:href (bidi/path-for client-routes/routes
|
||||
:admin-vendors)}
|
||||
"Vendors")]
|
||||
[:li
|
||||
(menu-button- {:icon svg/user
|
||||
:href (bidi/path-for client-routes/routes
|
||||
:admin-users)}
|
||||
"Users")]
|
||||
[:li
|
||||
(menu-button- {:icon svg/accounts
|
||||
:href (bidi/path-for client-routes/routes
|
||||
:admin-accounts)}
|
||||
"Accounts")]
|
||||
|
||||
[:li
|
||||
(menu-button- {:icon svg/cog
|
||||
:href (bidi/path-for client-routes/routes
|
||||
:admin-rules)}
|
||||
"Rules")]
|
||||
|
||||
[:li
|
||||
(menu-button- {:icon svg/question
|
||||
:href (bidi/path-for ssr-routes/only-routes
|
||||
:admin-history)
|
||||
:hx-boost "true"}
|
||||
"History")]
|
||||
|
||||
[:li
|
||||
(menu-button- {:icon svg/rabbit
|
||||
:href (bidi/path-for client-routes/routes
|
||||
:admin-jobs)}
|
||||
"Background Jobs")]
|
||||
[:li
|
||||
(menu-button- {:aria-controls "dropdown-import"
|
||||
:data-collapse-toggle "dropdown-import"
|
||||
:icon svg/arrow-in}
|
||||
"Import")
|
||||
|
||||
(sub-menu- {:id "dropdown-import"}
|
||||
(menu-button- {:href (bidi/path-for client-routes/routes
|
||||
:admin-excel-import)} "Excel Invoices")
|
||||
(menu-button- {:href (bidi/path-for client-routes/routes
|
||||
:admin-import-batches)} "Import Batches")
|
||||
(menu-button- {:href (bidi/path-for ssr-routes/only-routes
|
||||
:admin-ezcater-xls)
|
||||
:hx-boost "true"} "EZCater XLS Import"))]])
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
(ns auto-ap.ssr.components.card)
|
||||
|
||||
(defn card- [params & children]
|
||||
(into [:div {:class "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 str " shadow-md dark:bg-gray-800 sm:rounded-lg border-2 border-gray-200 dark:border-gray-900 bg-white overflow-hidden")]
|
||||
children))
|
||||
|
||||
(defn content-card- [params & children]
|
||||
|
||||
@@ -46,9 +46,13 @@
|
||||
[:tbody]
|
||||
rest)])
|
||||
|
||||
;; needed for tailwind
|
||||
;; lg:table-cell md:table-cell
|
||||
|
||||
(defn data-grid-card- [{:keys [id
|
||||
route
|
||||
title
|
||||
paginate?
|
||||
action-buttons
|
||||
total
|
||||
subtitle
|
||||
@@ -60,37 +64,36 @@
|
||||
rows] :as params}]
|
||||
[:div {:hx-get (bidi/path-for ssr-routes/only-routes
|
||||
route
|
||||
:request-method :get)
|
||||
:request-method :get)
|
||||
:hx-trigger "clientSelected from:body"
|
||||
:hx-swap "outerHTML swap:300ms"
|
||||
:id id}
|
||||
(content-card-
|
||||
{}
|
||||
[:div {:class "flex flex-col px-4 py-3 space-y-3 lg:flex-row lg:items-center lg:justify-between lg:space-y-0 lg:space-x-4 text-gray-800 dark:text-gray-100"}
|
||||
[:div
|
||||
[:h1.text-2xl.mb-3.font-bold title]
|
||||
[:div {:class "flex items-center flex-1 space-x-4"}
|
||||
[:h5
|
||||
(when subtitle
|
||||
[:span subtitle])]]]
|
||||
(into [:div {:class "flex flex-col flex-shrink-0 space-y-3 md:flex-row md:items-center lg:justify-end md:space-y-0 md:space-x-3"}
|
||||
]
|
||||
action-buttons)]
|
||||
[:div {:class "overflow-x-auto"}
|
||||
(data-grid- {:headers headers
|
||||
:thead-params thead-params
|
||||
}
|
||||
rows
|
||||
{}
|
||||
[:div {:class "flex flex-col px-4 py-3 space-y-3 lg:flex-row lg:items-center lg:justify-between lg:space-y-0 lg:space-x-4 text-gray-800 dark:text-gray-100"}
|
||||
[:div
|
||||
[:h1.text-2xl.mb-3.font-bold title]
|
||||
[:div {:class "flex items-center flex-1 space-x-4"}
|
||||
[:h5
|
||||
(when subtitle
|
||||
[:span subtitle])]]]
|
||||
(into [:div {:class "flex flex-col flex-shrink-0 space-y-3 md:flex-row md:items-center lg:justify-end md:space-y-0 md:space-x-3"}]
|
||||
action-buttons)]
|
||||
[:div {:class "overflow-x-auto"}
|
||||
(data-grid- {:headers headers
|
||||
:thead-params thead-params}
|
||||
rows)]
|
||||
|
||||
)]
|
||||
(paginator- {:start start
|
||||
:end (Math/min (+ start per-page) total)
|
||||
:per-page per-page
|
||||
:total total
|
||||
:a-params (fn [page]
|
||||
{:hx-get (str (bidi/path-for ssr-routes/only-routes
|
||||
route
|
||||
:request-method :get)
|
||||
"?start=" (* page per-page))
|
||||
:hx-target (str "#" id)
|
||||
:hx-swap "outerHTML show:#app:top"})}))])
|
||||
(when (or paginate?
|
||||
(nil? paginate?))
|
||||
(paginator- {:start start
|
||||
:end (Math/min (+ start per-page) total)
|
||||
:per-page per-page
|
||||
:total total
|
||||
:a-params (fn [page]
|
||||
{:hx-get (str (bidi/path-for ssr-routes/only-routes
|
||||
route
|
||||
:request-method :get)
|
||||
"?start=" (* page per-page))
|
||||
:hx-target (str "#" id)
|
||||
:hx-swap "outerHTML show:#app:top"})})))])
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
(ns auto-ap.ssr.components.navbar
|
||||
(:require [auto-ap.ssr.components.buttons :refer [icon-button-]]
|
||||
[auto-ap.ssr.svg :as svg]
|
||||
[hiccup2.core :as hiccup]
|
||||
[bidi.bidi :as bidi]
|
||||
[auto-ap.ssr-routes :as ssr-routes]
|
||||
[auto-ap.ssr.company-dropdown :as cd]))
|
||||
(:require
|
||||
[auto-ap.datomic :refer [conn pull-attr]]
|
||||
[auto-ap.client-routes :as client-routes2]
|
||||
[auto-ap.ssr-routes :as ssr-routes]
|
||||
[auto-ap.ssr.company-dropdown :as cd]
|
||||
[auto-ap.ssr.components.buttons :refer [icon-button-]]
|
||||
[auto-ap.ssr.svg :as svg]
|
||||
[bidi.bidi :as bidi]
|
||||
[datomic.api :as dc]
|
||||
[hiccup2.core :as hiccup]
|
||||
[auto-ap.ssr.components.user-dropdown :as user-dropdown]))
|
||||
|
||||
(defn navbar- [{:keys [client identity]}]
|
||||
[:nav {:class "fixed z-30 w-full bg-white border-b border-gray-200 dark:bg-gray-800 dark:border-gray-700"}
|
||||
@@ -15,7 +20,7 @@
|
||||
[:span {:class "sr-only"} "Open sidebar"]
|
||||
[:svg {:class "w-6 h-6", :aria-hidden "true", :fill "currentColor", :viewbox "0 0 20 20", :xmlns "http://www.w3.org/2000/svg"}
|
||||
[:path {:clip-rule "evenodd", :fill-rule "evenodd", :d "M2 4.75A.75.75 0 012.75 4h14.5a.75.75 0 010 1.5H2.75A.75.75 0 012 4.75zm0 10.5a.75.75 0 01.75-.75h7.5a.75.75 0 010 1.5h-7.5a.75.75 0 01-.75-.75zM2 10a.75.75 0 01.75-.75h14.5a.75.75 0 010 1.5H2.75A.75.75 0 012 10z"}]]]
|
||||
[:a {:href "https://flowbite-admin-dashboard.vercel.app/", :class "flex ml-2 md:mr-24"}
|
||||
[:a {:href "/" :class "flex ml-2 md:mr-24"}
|
||||
[:img {:src "/img/logo-big2.png", :class "h-10 mr-16", :alt "Integreat logo"}]
|
||||
]
|
||||
]
|
||||
@@ -38,77 +43,8 @@
|
||||
:hx-target "#modal-holder"
|
||||
:hx-swap "outerHTML"}
|
||||
svg/search)
|
||||
#_[:button
|
||||
[:div.w-4.h-4 svg/search]]
|
||||
(cd/dropdown {:client client :identity identity})
|
||||
|
||||
|
||||
[:div {:class "z-20 z-50 max-w-sm my-4 overflow-hidden text-base list-none bg-white divide-y divide-gray-100 rounded shadow-lg dark:bg-gray-700 dark:divide-gray-600 hidden", :id "apps-dropdown", :style "position: absolute; inset: 0px auto auto 0px; margin: 0px; transform: translate(1545px, 65px);", :data-popper-placement "bottom"}
|
||||
[:div {:class "block px-4 py-2 text-base font-medium text-center text-gray-700 bg-gray-50 dark:bg-gray-700 dark:text-gray-400"} ]
|
||||
[:div {:class "grid grid-cols-3 gap-4 p-4"}
|
||||
[:a {:href "#", :class "block p-4 text-center rounded-lg hover:bg-gray-100 dark:hover:bg-gray-600"}
|
||||
[:svg {:class "mx-auto mb-1 text-gray-500 w-7 h-7 dark:text-gray-400", :fill "currentColor", :viewbox "0 0 20 20", :xmlns "http://www.w3.org/2000/svg"}
|
||||
[:path {:fill-rule "evenodd", :d "M10 2a4 4 0 00-4 4v1H5a1 1 0 00-.994.89l-1 9A1 1 0 004 18h12a1 1 0 00.994-1.11l-1-9A1 1 0 0015 7h-1V6a4 4 0 00-4-4zm2 5V6a2 2 0 10-4 0v1h4zm-6 3a1 1 0 112 0 1 1 0 01-2 0zm7-1a1 1 0 100 2 1 1 0 000-2z", :clip-rule "evenodd"}]]
|
||||
[:div {:class "text-sm font-medium text-gray-900 dark:text-white"} "Sales"]]
|
||||
[:a {:href "#", :class "block p-4 text-center rounded-lg hover:bg-gray-100 dark:hover:bg-gray-600"}
|
||||
[:svg {:class "mx-auto mb-1 text-gray-500 w-7 h-7 dark:text-gray-400", :fill "currentColor", :viewbox "0 0 20 20", :xmlns "http://www.w3.org/2000/svg"}
|
||||
[:path {:d "M13 6a3 3 0 11-6 0 3 3 0 016 0zM18 8a2 2 0 11-4 0 2 2 0 014 0zM14 15a4 4 0 00-8 0v3h8v-3zM6 8a2 2 0 11-4 0 2 2 0 014 0zM16 18v-3a5.972 5.972 0 00-.75-2.906A3.005 3.005 0 0119 15v3h-3zM4.75 12.094A5.973 5.973 0 004 15v3H1v-3a3 3 0 013.75-2.906z"}]]
|
||||
[:div {:class "text-sm font-medium text-gray-900 dark:text-white"} "Users"]]
|
||||
[:a {:href "#", :class "block p-4 text-center rounded-lg hover:bg-gray-100 dark:hover:bg-gray-600"}
|
||||
[:svg {:class "mx-auto mb-1 text-gray-500 w-7 h-7 dark:text-gray-400", :fill "currentColor", :viewbox "0 0 20 20", :xmlns "http://www.w3.org/2000/svg"}
|
||||
[:path {:fill-rule "evenodd", :d "M5 3a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2V5a2 2 0 00-2-2H5zm0 2h10v7h-2l-1 2H8l-1-2H5V5z", :clip-rule "evenodd"}]]
|
||||
[:div {:class "text-sm font-medium text-gray-900 dark:text-white"} "Inbox"]]
|
||||
[:a {:href "#", :class "block p-4 text-center rounded-lg hover:bg-gray-100 dark:hover:bg-gray-600"}
|
||||
[:svg {:class "mx-auto mb-1 text-gray-500 w-7 h-7 dark:text-gray-400", :fill "currentColor", :viewbox "0 0 20 20", :xmlns "http://www.w3.org/2000/svg"}
|
||||
[:path {:fill-rule "evenodd", :d "M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-6-3a2 2 0 11-4 0 2 2 0 014 0zm-2 4a5 5 0 00-4.546 2.916A5.986 5.986 0 0010 16a5.986 5.986 0 004.546-2.084A5 5 0 0010 11z", :clip-rule "evenodd"}]]
|
||||
[:div {:class "text-sm font-medium text-gray-900 dark:text-white"} "Profile"]]
|
||||
[:a {:href "#", :class "block p-4 text-center rounded-lg hover:bg-gray-100 dark:hover:bg-gray-600"}
|
||||
[:svg {:class "mx-auto mb-1 text-gray-500 w-7 h-7 dark:text-gray-400", :fill "currentColor", :viewbox "0 0 20 20", :xmlns "http://www.w3.org/2000/svg"}
|
||||
[:path {:fill-rule "evenodd", :d "M11.49 3.17c-.38-1.56-2.6-1.56-2.98 0a1.532 1.532 0 01-2.286.948c-1.372-.836-2.942.734-2.106 2.106.54.886.061 2.042-.947 2.287-1.561.379-1.561 2.6 0 2.978a1.532 1.532 0 01.947 2.287c-.836 1.372.734 2.942 2.106 2.106a1.532 1.532 0 012.287.947c.379 1.561 2.6 1.561 2.978 0a1.533 1.533 0 012.287-.947c1.372.836 2.942-.734 2.106-2.106a1.533 1.533 0 01.947-2.287c1.561-.379 1.561-2.6 0-2.978a1.532 1.532 0 01-.947-2.287c.836-1.372-.734-2.942-2.106-2.106a1.532 1.532 0 01-2.287-.947zM10 13a3 3 0 100-6 3 3 0 000 6z", :clip-rule "evenodd"}]]
|
||||
[:div {:class "text-sm font-medium text-gray-900 dark:text-white"} "Settings"]]
|
||||
[:a {:href "#", :class "block p-4 text-center rounded-lg hover:bg-gray-100 dark:hover:bg-gray-600"}
|
||||
[:svg {:class "mx-auto mb-1 text-gray-500 w-7 h-7 dark:text-gray-400", :fill "currentColor", :viewbox "0 0 20 20", :xmlns "http://www.w3.org/2000/svg"}
|
||||
[:path {:d "M4 3a2 2 0 100 4h12a2 2 0 100-4H4z"}]
|
||||
[:path {:fill-rule "evenodd", :d "M3 8h14v7a2 2 0 01-2 2H5a2 2 0 01-2-2V8zm5 3a1 1 0 011-1h2a1 1 0 110 2H9a1 1 0 01-1-1z", :clip-rule "evenodd"}]]
|
||||
[:div {:class "text-sm font-medium text-gray-900 dark:text-white"} "Products"]]
|
||||
[:a {:href "#", :class "block p-4 text-center rounded-lg hover:bg-gray-100 dark:hover:bg-gray-600"}
|
||||
[:svg {:class "mx-auto mb-1 text-gray-500 w-7 h-7 dark:text-gray-400", :fill "currentColor", :viewbox "0 0 20 20", :xmlns "http://www.w3.org/2000/svg"}
|
||||
[:path {:d "M8.433 7.418c.155-.103.346-.196.567-.267v1.698a2.305 2.305 0 01-.567-.267C8.07 8.34 8 8.114 8 8c0-.114.07-.34.433-.582zM11 12.849v-1.698c.22.071.412.164.567.267.364.243.433.468.433.582 0 .114-.07.34-.433.582a2.305 2.305 0 01-.567.267z"}]
|
||||
[:path {:fill-rule "evenodd", :d "M10 18a8 8 0 100-16 8 8 0 000 16zm1-13a1 1 0 10-2 0v.092a4.535 4.535 0 00-1.676.662C6.602 6.234 6 7.009 6 8c0 .99.602 1.765 1.324 2.246.48.32 1.054.545 1.676.662v1.941c-.391-.127-.68-.317-.843-.504a1 1 0 10-1.51 1.31c.562.649 1.413 1.076 2.353 1.253V15a1 1 0 102 0v-.092a4.535 4.535 0 001.676-.662C13.398 13.766 14 12.991 14 12c0-.99-.602-1.765-1.324-2.246A4.535 4.535 0 0011 9.092V7.151c.391.127.68.317.843.504a1 1 0 101.511-1.31c-.563-.649-1.413-1.076-2.354-1.253V5z", :clip-rule "evenodd"}]]
|
||||
[:div {:class "text-sm font-medium text-gray-900 dark:text-white"} "Pricing"]]
|
||||
[:a {:href "#", :class "block p-4 text-center rounded-lg hover:bg-gray-100 dark:hover:bg-gray-600"}
|
||||
[:svg {:class "mx-auto mb-1 text-gray-500 w-7 h-7 dark:text-gray-400", :fill "currentColor", :viewbox "0 0 20 20", :xmlns "http://www.w3.org/2000/svg"}
|
||||
[:path {:fill-rule "evenodd", :d "M5 2a2 2 0 00-2 2v14l3.5-2 3.5 2 3.5-2 3.5 2V4a2 2 0 00-2-2H5zm2.5 3a1.5 1.5 0 100 3 1.5 1.5 0 000-3zm6.207.293a1 1 0 00-1.414 0l-6 6a1 1 0 101.414 1.414l6-6a1 1 0 000-1.414zM12.5 10a1.5 1.5 0 100 3 1.5 1.5 0 000-3z", :clip-rule "evenodd"}]]
|
||||
[:div {:class "text-sm font-medium text-gray-900 dark:text-white"} "Billing"]]
|
||||
[:a {:href "#", :class "block p-4 text-center rounded-lg hover:bg-gray-100 dark:hover:bg-gray-600"}
|
||||
[:svg {:class "mx-auto mb-1 text-gray-500 w-7 h-7 dark:text-gray-400", :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 "M11 16l-4-4m0 0l4-4m-4 4h14m-5 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h7a3 3 0 013 3v1"}]]
|
||||
[:div {:class "text-sm font-medium text-gray-900 dark:text-white"} "Logout"]]]]
|
||||
(icon-button- {"_" (hiccup/raw "on click toggle .dark on <body />")}
|
||||
[:div.h-4.w-4
|
||||
[:div.hidden.dark:block svg/sun]
|
||||
[:div.dark:hidden svg/moon]
|
||||
])
|
||||
#_[:button {:id "theme-toggle", :data-tooltip-target "tooltip-toggle", :type "button", :class "text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 focus:outline-none focus:ring-4 focus:ring-gray-200 dark:focus:ring-gray-700 rounded-lg text-sm p-2.5"
|
||||
}
|
||||
]
|
||||
[:div {:id "tooltip-toggle", :role "tooltip", :class "absolute z-10 inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-gray-900 rounded-lg shadow-sm tooltip opacity-0 invisible", :style "position: absolute; inset: 0px auto auto 0px; margin: 0px; transform: translate(2326px, 63px);", :data-popper-placement "bottom"} [:img {:src "https://flowbite.com/docs/images/people/profile-picture-5.jpg"}]
|
||||
[:div {:class "tooltip-arrow", :data-popper-arrow "data-popper-arrow", :style "position: absolute; left: 0px; transform: translate(69px);"}]]
|
||||
[:div {:class "flex items-center ml-3"}
|
||||
[:div
|
||||
[:button {:type "button", :class "flex text-sm bg-gray-800 rounded-full focus:ring-4 focus:ring-gray-300 dark:focus:ring-gray-600", :id "user-menu-button-2", :aria-expanded "false", :data-dropdown-toggle "dropdown-2"}
|
||||
[:span {:class "sr-only"} "Open user menu"]
|
||||
[:img {:class "w-8 h-8 rounded-full", :src "https://flowbite.com/docs/images/people/profile-picture-5.jpg", :alt "user photo"}]]]
|
||||
[:div {:class "z-50 hidden my-4 text-base list-none bg-white divide-y divide-gray-100 rounded shadow dark:bg-gray-700 dark:divide-gray-600", :id "dropdown-2", :style "position: absolute; inset: 0px auto auto 0px; margin: 0px; transform: translate(2446px, 61px);", :data-popper-placement "bottom"}
|
||||
[:div {:class "px-4 py-3", :role "none"}
|
||||
[:p {:class "text-sm text-gray-900 dark:text-white", :role "none"} "Neil Sims"]
|
||||
[:p {:class "text-sm font-medium text-gray-900 truncate dark:text-gray-300", :role "none"} "neil.sims@flowbite.com"]]
|
||||
[:ul {:class "py-1", :role "none"}
|
||||
[:li
|
||||
[:a {:href "#", :class "block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-600 dark:hover:text-white", :role "menuitem"} "Dashboard"]]
|
||||
[:li
|
||||
[:a {:href "#", :class "block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-600 dark:hover:text-white", :role "menuitem"} "Settings"]]
|
||||
[:li
|
||||
[:a {:href "#", :class "block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-600 dark:hover:text-white", :role "menuitem"} "Earnings"]]
|
||||
[:li
|
||||
[:a {:href "#", :class "block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-600 dark:hover:text-white", :role "menuitem"} "Sign out"]]]]]]]]])
|
||||
(user-dropdown/dropdown {:identity identity})
|
||||
]]]])
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
(ns auto-ap.ssr.components.navbar-dropdown
|
||||
(:require [hiccup2.core :as hiccup]))
|
||||
|
||||
(defn navbar-dropdown [id header children]
|
||||
[:div {
|
||||
:id id
|
||||
:class (str "navbar-item has-dropdown")
|
||||
"_" (hiccup/raw "on click elsewhere remove .is-active from me")
|
||||
|
||||
}
|
||||
[:a {:class "navbar-link login"
|
||||
"_" (hiccup/raw "on click toggle .is-active on the parentElement of me then add .appear to next <.navbar-dropdown />")} header]
|
||||
(into [:div {:class "navbar-dropdown"}
|
||||
children])])
|
||||
@@ -6,33 +6,31 @@
|
||||
[auto-ap.ssr.svg :as svg]))
|
||||
|
||||
(defn page- [{:keys [nav page-specific active-client identity app-params] :or {app-params {}}} & children]
|
||||
[:div#app app-params
|
||||
[:div#app
|
||||
(navbar- {:client active-client
|
||||
:identity identity})
|
||||
[:div.flex.pt-16.overflow-hidden
|
||||
[:div#app-contents.flex.pt-16.overflow-hidden (assoc app-params :hx-disinherit "*")
|
||||
(left-aside- {:nav nav
|
||||
:page-specific page-specific})
|
||||
[:div#main-content {:class "relative w-full h-full lg:pl-64 overflow-y-auto px-4 bg-gray-100 dark:bg-gray-900 min-h-content "
|
||||
"_" (hiccup/raw "on htmx:responseError put event.detail.xhr.response into #error-details then add .htmx-added to #error-holder then remove .hidden from #error-holder then wait 30ms then remove .htmx-added from #error-holder")}
|
||||
[:div#error-holder.hidden
|
||||
[:div.fixed.top-0.right-0.left-0.z-30.mx-auto.max-w-screen-lg.w-screen-lg.my-0.pt-8.rounded-lg
|
||||
[:div.relative
|
||||
[:div.relative
|
||||
[:button.absolute.right-2.top-2.w-6.h-6.z-50.text-red-600
|
||||
{"_" (hiccup/raw "on click add .hidden to #error-holder")}
|
||||
svg/filled-x
|
||||
]
|
||||
]
|
||||
svg/filled-x]]
|
||||
|
||||
[:div.m-4.overflow-auto.z-30.flex.center-items.justify-center.text-red-800.bg-red-50.dark:bg-gray-800.dark:text-red-400.border-red-300.rounded-lg.border.transition-all.duration-500.fade-in.slide-up.max-h-96
|
||||
|
||||
|
||||
[:div {:class "p-4 mb-4 text-lg w-full" :role "alert"}
|
||||
[:div.inline-block.w-8.h-8.mr-2 svg/alert]
|
||||
[:span.font-medium "Oh, drat! An unexpected error has occurred."]
|
||||
[:div.text-sm
|
||||
[:p "Integreat staff have been notified and are looking into it. " ]
|
||||
[:p "Integreat staff have been notified and are looking into it. "]
|
||||
[:p "To see error details, " [:a.underline {:href "#" :data-collapse-toggle "error-details"} "click here"] "."]
|
||||
[:pre#error-details.text-xs.hidden]]]]]]
|
||||
(into
|
||||
[:div.p-4]
|
||||
children)]]
|
||||
(into
|
||||
[:div.p-4]
|
||||
children)]]
|
||||
[:div#modal-holder]])
|
||||
|
||||
@@ -2,17 +2,19 @@
|
||||
|
||||
|
||||
(defn pill- [params & children]
|
||||
(into
|
||||
[:span (cond-> params
|
||||
true (update :class str " text-xs font-medium px-2 py-0.5 rounded whitespace-nowrap")
|
||||
(into
|
||||
[:span (cond-> params
|
||||
true (update :class str " text-xs font-medium px-2 py-0.5 rounded whitespace-nowrap")
|
||||
|
||||
(= :primary (:color params))
|
||||
(update :class str " bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300 ")
|
||||
(= :primary (:color params))
|
||||
(update :class str " bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300 ")
|
||||
|
||||
(= :secondary (:color params))
|
||||
(update :class str " bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300 ")
|
||||
(= :secondary (:color params))
|
||||
(update :class str " bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300 ")
|
||||
|
||||
(= :yellow (:color params))
|
||||
(update :class str " bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-300")
|
||||
)]
|
||||
children))
|
||||
(= :yellow (:color params))
|
||||
(update :class str " bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-300")
|
||||
|
||||
(= :red (:color params))
|
||||
(update :class str " bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-300"))]
|
||||
children))
|
||||
|
||||
61
src/clj/auto_ap/ssr/components/user_dropdown.clj
Normal file
61
src/clj/auto_ap/ssr/components/user_dropdown.clj
Normal file
@@ -0,0 +1,61 @@
|
||||
(ns auto-ap.ssr.components.user-dropdown
|
||||
(:require
|
||||
[auto-ap.client-routes :as client-routes2]
|
||||
[auto-ap.datomic :refer [conn pull-attr]]
|
||||
[auto-ap.ssr-routes :as ssr-routes]
|
||||
[bidi.bidi :as bidi]
|
||||
[datomic.api :as dc]
|
||||
[hiccup2.core :as hiccup]))
|
||||
|
||||
(defn dropdown [{:keys [identity]}]
|
||||
[:div {:class "flex items-center ml-3 mr-10"}
|
||||
[:div
|
||||
[:button#user-menu-button {:type "button", :class "flex text-sm bg-gray-800 rounded-full focus:ring-4 focus:ring-gray-300 dark:focus:ring-gray-600", :aria-expanded "false"
|
||||
"_" (hiccup/raw "init call initUserDropdown()")}
|
||||
[:span {:class "sr-only"} "Open user menu"]
|
||||
[:img {:class "w-8 h-8 rounded-full", :src (pull-attr (dc/db conn) :user/profile-image-url (:db/id identity)) :alt "user photo" :referrerpolicy "no-referrer"}]]]
|
||||
[:div#user-menu {:class "z-50 hidden my-4 text-base list-none bg-white divide-y divide-gray-100 rounded shadow dark:bg-gray-700 dark:divide-gray-600 mr-10"}
|
||||
[:div {:class "px-4 py-3", :role "none"}
|
||||
[:p {:class "text-sm text-gray-900 dark:text-white", :role "none"} (:user/name identity)]
|
||||
[:p {:class "text-sm font-medium text-gray-900 truncate dark:text-gray-300", :role "none"} (pull-attr (dc/db conn) :user/email (:db/id identity))]
|
||||
#_(icon-button-
|
||||
{"_" (hiccup/raw "on click toggle .dark on <body />")}
|
||||
[:div.h-4.w-4
|
||||
[:div.hidden.dark:block svg/sun]
|
||||
[:div.dark:hidden svg/moon]])]
|
||||
[:ul {:class "py-1", :role "none"}
|
||||
[:li
|
||||
[:a {:href (bidi/path-for ssr-routes/only-routes :company), :class "block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-600 dark:hover:text-white", :role "menuitem"} "My Company"]]
|
||||
(when (= "admin" (:user/role identity))
|
||||
[:a {:href (bidi/path-for client-routes2/routes :admin), :class "block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-600 dark:hover:text-white", :role "menuitem"} "Admin"])
|
||||
[:li
|
||||
[:a {:href "#", :class "block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-600 dark:hover:text-white", :role "menuitem"
|
||||
"_" (hiccup/raw "on click toggle .dark on <body />")}
|
||||
"Night Mode"]]
|
||||
[:li
|
||||
[:a {:href "/logout", :class "block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-600 dark:hover:text-white", :role "menuitem"} "Sign out"]]]]
|
||||
|
||||
[:script {:lang "text/javascript"}
|
||||
(hiccup/raw
|
||||
"
|
||||
function initUserDropdown() {
|
||||
var $dropdownTargetEl = document.getElementById('user-menu');
|
||||
|
||||
// set the element that trigger the dropdown menu on click
|
||||
var $dropdownTriggerEl = document.getElementById('user-menu-button');
|
||||
|
||||
var userDrowdown = new Dropdown($dropdownTargetEl, $dropdownTriggerEl, {
|
||||
placement: 'bottom',
|
||||
triggerType: 'click',
|
||||
offsetSkidding: 0,
|
||||
offsetDistance: 10,
|
||||
delay: 5000,
|
||||
onHide: () => {
|
||||
},
|
||||
onShow: () => {
|
||||
},
|
||||
onToggle: () => {
|
||||
}
|
||||
});
|
||||
}
|
||||
")]])
|
||||
@@ -2,7 +2,7 @@
|
||||
(:require
|
||||
[auto-ap.routes.utils
|
||||
:refer [wrap-admin wrap-client-redirect-unauthenticated wrap-secure]]
|
||||
[auto-ap.ssr.admin :as admin]
|
||||
[auto-ap.ssr.admin.history :as history]
|
||||
[auto-ap.ssr.auth :as auth]
|
||||
[auto-ap.ssr.transaction.insights :as insights]
|
||||
[auto-ap.ssr.company.company-1099 :as company-1099]
|
||||
@@ -17,9 +17,9 @@
|
||||
|
||||
|
||||
(def key->handler {:logout auth/logout
|
||||
:admin-history (wrap-client-redirect-unauthenticated (wrap-secure (wrap-admin admin/history)))
|
||||
:admin-history-search (wrap-client-redirect-unauthenticated (wrap-secure (wrap-admin admin/history-search)))
|
||||
:admin-history-inspect (wrap-client-redirect-unauthenticated (wrap-secure (wrap-admin admin/inspect)))
|
||||
:admin-history (wrap-client-redirect-unauthenticated (wrap-secure (wrap-admin history/page)))
|
||||
:admin-history-search (wrap-client-redirect-unauthenticated (wrap-secure (wrap-admin history/page)))
|
||||
:admin-history-inspect (wrap-client-redirect-unauthenticated (wrap-secure (wrap-admin history/inspect)))
|
||||
:active-client (wrap-client-redirect-unauthenticated (wrap-secure (wrap-admin company-dropdown/active-client)))
|
||||
:company-dropdown-search-results
|
||||
(wrap-client-redirect-unauthenticated (wrap-secure company-dropdown/dropdown-search-results))
|
||||
|
||||
@@ -12,7 +12,9 @@
|
||||
|
||||
(defn row* [gridspec user entity {:keys [flash? delete-after-settle?] :as options}]
|
||||
(let [cells (mapv (fn [header]
|
||||
(com/data-grid-cell {}
|
||||
(com/data-grid-cell {:class (if-let [show-starting (:show-starting header)]
|
||||
(format "hidden %s:table-cell" show-starting)
|
||||
(:class header))}
|
||||
((:render header) entity)))
|
||||
(:headers gridspec))
|
||||
cells (conj cells (com/data-grid-right-stack-cell {}
|
||||
@@ -24,7 +26,8 @@
|
||||
"live-added")
|
||||
"_" (hiccup/raw (when delete-after-settle?
|
||||
" on htmx:afterSettle wait 400ms then remove me"))
|
||||
}
|
||||
|
||||
:data-id ((:id-fn gridspec) entity)}
|
||||
cells)))
|
||||
|
||||
(defn sort-icon [sort key]
|
||||
@@ -48,7 +51,7 @@
|
||||
))
|
||||
"default sort"))
|
||||
|
||||
(defn table* [grid-spec user {:keys [start per-page client flash-id sort]}]
|
||||
(defn table* [grid-spec user {:keys [start per-page client flash-id sort request]}]
|
||||
(let [start (or start 0)
|
||||
per-page (or per-page 30)
|
||||
[entities total] ((:fetch-page grid-spec)
|
||||
@@ -56,7 +59,8 @@
|
||||
{:start start
|
||||
:per-page per-page
|
||||
:client-id (:db/id client)
|
||||
:sort sort})]
|
||||
:sort sort
|
||||
:request request})]
|
||||
(com/data-grid-card {:id (:id grid-spec)
|
||||
:title (:title grid-spec)
|
||||
:route (:route grid-spec)
|
||||
@@ -171,12 +175,12 @@
|
||||
|
||||
(defn page [grid-spec {:keys [identity] :as request}]
|
||||
(base-page
|
||||
request
|
||||
(com/page {:nav (:nav grid-spec)
|
||||
:active-client (:client (:session request))
|
||||
:identity (:identity request)}
|
||||
(apply com/breadcrumbs {} (:breadcrumbs grid-spec))
|
||||
(table* grid-spec
|
||||
identity
|
||||
(extract-params grid-spec request)))
|
||||
nil))
|
||||
request
|
||||
(com/page {:nav (:nav grid-spec)
|
||||
:active-client (:client (:session request))
|
||||
:identity (:identity request)}
|
||||
(apply com/breadcrumbs {} (:breadcrumbs grid-spec))
|
||||
(table* grid-spec
|
||||
identity
|
||||
(extract-params grid-spec request)))
|
||||
(:title grid-spec)))
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
(ns auto-ap.ssr.login-dropdown
|
||||
(:require
|
||||
[auto-ap.client-routes :as client-routes]
|
||||
[auto-ap.ssr.components.navbar-dropdown :refer [navbar-dropdown]]
|
||||
[bidi.bidi :as bidi]
|
||||
[hiccup2.core :as hiccup]
|
||||
[auto-ap.ssr-routes :as ssr-routes]))
|
||||
|
||||
(defn dropdown [{:keys [identity]}]
|
||||
(if identity
|
||||
(navbar-dropdown
|
||||
"login-dropdown"
|
||||
[:span [:span.icon [:i.fa.fa-user] ]
|
||||
[:span (:user/name identity)]]
|
||||
[:div
|
||||
[:a {:class "navbar-item"
|
||||
:href (bidi/path-for client-routes/routes :reports)} "My company"]
|
||||
#_[:a.dropdown-item {:on-click (dispatch-event-with-propagation [:vendor-dialog/started {}])} "New Vendor"] ;; double colorn
|
||||
#_[:a.dropdown-item {:on-click (dispatch-event-with-propagation [:vendor-dialog/edit {}])} "Edit Vendor"]
|
||||
(when (= "admin" (:user/role identity))
|
||||
[:a {:class "navbar-item" :href (bidi/path-for client-routes/routes :admin)} "Administration"])
|
||||
[:hr {:class "navbar-divider"}]
|
||||
[:a.navbar-item {"_" (hiccup/raw "on click call localStorage.removeItem(\"jwt\")")
|
||||
:href (bidi/path-for ssr-routes/only-routes :logout)}
|
||||
"Logout"]])
|
||||
[:a.navbar-item {:href (bidi/path-for client-routes/routes :login )} "Login"]))
|
||||
@@ -71,6 +71,11 @@
|
||||
[: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
|
||||
[:svg {:xmlns "http://www.w3.org/2000/svg", :viewbox "0 0 24 24"}
|
||||
[:defs]
|
||||
@@ -307,3 +312,110 @@
|
||||
[:title "arrow-thick-up-4"]
|
||||
[:rect {:y "0.75", :rx "3", :stroke "currentColor", :transform "translate(24 0) rotate(90)", :fill "none", :stroke-linejoin "round", :width "22.5", :stroke-linecap "round", :stroke-width "1.5px", :x "0.75", :ry "3", :height "22.5"}]
|
||||
[:path {:d "M14.25,18V10.5l3.22,3.22a.75.75,0,0,0,1.28-.531V11.121a1.5,1.5,0,0,0-.439-1.06L12.53,4.28a.749.749,0,0,0-1.06,0L5.689,10.061a1.5,1.5,0,0,0-.439,1.06v2.068a.75.75,0,0,0,1.28.531L9.75,10.5V18a.75.75,0,0,0,.75.75h3A.75.75,0,0,0,14.25,18Z", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "1.5px"}]])
|
||||
|
||||
(def dashboard
|
||||
[:svg {:xmlns "http://www.w3.org/2000/svg", :viewbox "0 0 24 24"}
|
||||
[:defs]
|
||||
[:title "gauge-dashboard"]
|
||||
[:circle {:cx "12", :cy "14", :r "1.5", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:line {:x1 "13.06", :y1 "12.939", :x2 "18.011", :y2 "7.99", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:line {:x1 "2.5", :y1 "14.5", :x2 "4.5", :y2 "14.5", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:line {:x1 "21.5", :y1 "14.5", :x2 "19.5", :y2 "14.5", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:line {:x1 "20.776", :y1 "10.365", :x2 "18.929", :y2 "11.13", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:line {:x1 "15.635", :y1 "5.223", :x2 "14.87", :y2 "7.071", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:line {:x1 "12", :y1 "4.5", :x2 "12", :y2 "6.5", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:line {:x1 "8.365", :y1 "5.223", :x2 "9.13", :y2 "7.071", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:line {:x1 "5.283", :y1 "7.282", :x2 "6.695", :y2 "8.697", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:line {:x1 "3.224", :y1 "10.365", :x2 "5.07", :y2 "11.13", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:path {:d "M12,2.5A11.5,11.5,0,0,0,.5,14v3.5a1,1,0,0,0,1,1h21a1,1,0,0,0,1-1V14A11.5,11.5,0,0,0,12,2.5Z", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]])
|
||||
|
||||
(def restaurant
|
||||
[:svg {:xmlns "http://www.w3.org/2000/svg", :viewbox "0 0 24 24"}
|
||||
[:g
|
||||
[:line {:x1 "1", :y1 "14.5", :x2 "23", :y2 "14.5", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:line {:x1 "6", :y1 "17.5", :x2 "11", :y2 "17.5", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:line {:x1 "7.5", :y1 "17.5", :x2 "6", :y2 "23.5", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:line {:x1 "9.5", :y1 "17.5", :x2 "11", :y2 "23.5", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:line {:x1 "6.25", :y1 "22.5", :x2 "10.75", :y2 "22.5", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:line {:x1 "13", :y1 "17.5", :x2 "18", :y2 "17.5", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:line {:x1 "14.5", :y1 "17.5", :x2 "13", :y2 "23.5", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:line {:x1 "16.5", :y1 "17.5", :x2 "18", :y2 "23.5", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:line {:x1 "13.25", :y1 "22.5", :x2 "17.75", :y2 "22.5", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:line {:x1 "2.5", :y1 "23.5", :x2 "2.5", :y2 "7.33", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:line {:x1 "21.5", :y1 "23.5", :x2 "21.5", :y2 "7.33", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:path {:d "M16,14.5V12a.5.5,0,0,1,.5-.5h1a.5.5,0,0,1,.5.5v2.5", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:path {:d "M7,14.5l-.64-1.28a.5.5,0,0,1,.45-.72h4.38a.5.5,0,0,1,.45.72L11,14.5", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:path {:d "M6.5,4H.5V1.5a1,1,0,0,1,1-1h5Z", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:rect {:x "6.5", :y "0.5", :width "5.5", :height "3.5", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:rect {:x "12", :y "0.5", :width "5.5", :height "3.5", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:path {:d "M23.5,4h-6V.5h5a1,1,0,0,1,1,1Z", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:path {:d "M23.5,4v.5a3,3,0,0,1-6,0V4", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:path {:d "M6.5,4v.5a3,3,0,0,1-6,0V4", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:path {:d "M17.5,4v.75a2.75,2.75,0,0,1-5.5,0V4", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:path {:d "M12,4v.75a2.75,2.75,0,0,1-5.5,0V4", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]]])
|
||||
|
||||
(def user
|
||||
[:svg {:id "Light", :xmlns "http://www.w3.org/2000/svg", :viewbox "0 0 24 24"}
|
||||
[:defs]
|
||||
[:title "single-man"]
|
||||
[:path {:d "M3,22.75a9,9,0,0,1,18,0Z", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:circle {:cx "12", :cy "6.75", :r "5.5", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:path {:d "M7.261,3.958A9.124,9.124,0,0,0,13.833,6.75a9.138,9.138,0,0,0,3.617-.744", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]])
|
||||
|
||||
|
||||
(def accounts
|
||||
[:svg {:xmlns "http://www.w3.org/2000/svg", :viewbox "0 0 24 24"}
|
||||
[:defs]
|
||||
[:title "accounting-abacus"]
|
||||
[:rect {:y "0.5", :rx "1", :stroke "currentColor", :fill "none", :stroke-linejoin "round", :width "21", :stroke-linecap "round", :x "1.504", :ry "1", :height "23"}]
|
||||
[:line {:x1 "15.504", :y1 "20.5", :x2 "15.504", :y2 "23.5", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:line {:x1 "8.504", :y1 "17.5", :x2 "8.504", :y2 "23.5", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:path {:d "M10.5,4.5a1,1,0,0,1-1,1h-2a1,1,0,0,1-1-1h0a1,1,0,0,1,1-1h2a1,1,0,0,1,1,1Z", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:path {:d "M10.5,6.5a1,1,0,0,1-1,1h-2a1,1,0,0,1-1-1h0a1,1,0,0,1,1-1h2a1,1,0,0,1,1,1Z", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:path {:d "M17.5,13.5a1,1,0,0,1-1,1h-2a1,1,0,0,1-1-1h0a1,1,0,0,1,1-1h2a1,1,0,0,1,1,1Z", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:path {:d "M17.5,19.5a1,1,0,0,1-1,1h-2a1,1,0,0,1-1-1h0a1,1,0,0,1,1-1h2a1,1,0,0,1,1,1Z", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:path {:d "M10.5,16.5a1,1,0,0,1-1,1h-2a1,1,0,0,1-1-1h0a1,1,0,0,1,1-1h2a1,1,0,0,1,1,1Z", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:path {:d "M17.5,4.5a1,1,0,0,1-1,1h-2a1,1,0,0,1-1-1h0a1,1,0,0,1,1-1h2a1,1,0,0,1,1,1Z", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:line {:x1 "8.504", :y1 "7.5", :x2 "8.504", :y2 "15.5", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:line {:x1 "8.504", :y1 "3.5", :x2 "8.504", :y2 "0.5", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:line {:x1 "15.504", :y1 "14.5", :x2 "15.504", :y2 "18.5", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:line {:x1 "15.504", :y1 "5.5", :x2 "15.504", :y2 "12.5", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:line {:x1 "15.504", :y1 "0.5", :x2 "15.504", :y2 "3.5", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]])
|
||||
|
||||
|
||||
(def cog
|
||||
[:svg {:xmlns "http://www.w3.org/2000/svg", :viewbox "0 0 24 24"}
|
||||
[:defs]
|
||||
[:title "cash-toggle"]
|
||||
[:path {:d "M19.923,7.212a1.874,1.874,0,0,0,1.065,2.571l1.265.45a1.875,1.875,0,0,1,0,3.534l-1.265.45a1.874,1.874,0,0,0-1.065,2.571L20.5,18A1.874,1.874,0,0,1,18,20.5l-1.213-.576a1.874,1.874,0,0,0-2.571,1.065l-.45,1.265a1.875,1.875,0,0,1-3.534,0l-.45-1.265a1.874,1.874,0,0,0-2.571-1.065L6,20.5A1.874,1.874,0,0,1,3.5,18l.576-1.213a1.874,1.874,0,0,0-1.065-2.571l-1.265-.45a1.875,1.875,0,0,1,0-3.534l1.265-.45A1.874,1.874,0,0,0,4.077,7.212L3.5,6A1.874,1.874,0,0,1,6,3.5l1.213.576A1.874,1.874,0,0,0,9.783,3.012l.45-1.265a1.875,1.875,0,0,1,3.534,0l.45,1.265a1.874,1.874,0,0,0,2.571,1.065L18,3.5A1.874,1.874,0,0,1,20.5,6Z", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:path {:d "M13.5,9H11.467a1.342,1.342,0,0,0-.5,2.587l2.064.826a1.342,1.342,0,0,1-.5,2.587H10.5", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:line {:x1 "12", :y1 "16", :x2 "12", :y2 "15", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:line {:x1 "12", :y1 "9", :x2 "12", :y2 "8", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:circle {:cx "12", :cy "12", :r "6.5", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]])
|
||||
|
||||
(def question
|
||||
[: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 "M9.5116 9.51125C9.5116 8.92057 9.68675 8.34316 10.0149 7.85203C10.3431 7.3609 10.8095 6.97811 11.3552 6.75207C11.9009 6.52603 12.5014 6.46689 13.0807 6.58212C13.6601 6.69736 14.1922 6.98179 14.6099 7.39946C15.0276 7.81714 15.312 8.34928 15.4272 8.92861C15.5425 9.50794 15.4833 10.1084 15.2573 10.6541C15.0312 11.1999 14.6485 11.6663 14.1573 11.9944C13.6662 12.3226 13.0888 12.4978 12.4981 12.4978V15.4843"}]
|
||||
[:path {:stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :d "M23.4486 12C23.4488 11.2025 23.2094 10.4233 22.7616 9.76338C22.3138 9.10345 21.6781 8.5932 20.937 8.29872C21.2535 7.56634 21.3428 6.75575 21.1931 5.97204C21.0435 5.18834 20.6619 4.46766 20.0977 3.90343C19.5336 3.3392 18.813 2.95744 18.0293 2.80766C17.2457 2.65787 16.4351 2.74695 15.7026 3.06336C15.4084 2.32189 14.8983 1.68587 14.2383 1.23773C13.5784 0.789596 12.799 0.550003 12.0013 0.550003C11.2036 0.550003 10.4243 0.789596 9.76434 1.23773C9.1044 1.68587 8.59426 2.32189 8.30005 3.06336C7.56756 2.74664 6.75681 2.65732 5.97294 2.80697C5.18907 2.95662 4.46825 3.33834 3.90396 3.90263C3.33967 4.46692 2.95794 5.18774 2.80829 5.97161C2.65864 6.75548 2.74797 7.56623 3.06469 8.29872C2.32321 8.59293 1.68719 9.10307 1.23906 9.76301C0.790923 10.423 0.551331 11.2023 0.551331 12C0.551331 12.7977 0.790923 13.577 1.23906 14.237C1.68719 14.8969 2.32321 15.4071 3.06469 15.7013C2.74814 16.4337 2.6589 17.2443 2.80854 18.028C2.95818 18.8117 3.3398 19.5324 3.90392 20.0966C4.46804 20.6608 5.18865 21.0426 5.97233 21.1924C6.75601 21.3421 7.56661 21.2531 8.29905 20.9366C8.59327 21.6781 9.1034 22.3141 9.76335 22.7623C10.4233 23.2104 11.2026 23.45 12.0003 23.45C12.7981 23.45 13.5774 23.2104 14.2373 22.7623C14.8973 22.3141 15.4074 21.6781 15.7016 20.9366C16.4341 21.2531 17.2447 21.3421 18.0283 21.1924C18.812 21.0426 19.5326 20.6608 20.0968 20.0966C20.6609 19.5324 21.0425 18.8117 21.1921 18.028C21.3418 17.2443 21.2525 16.4337 20.936 15.7013C21.6773 15.407 22.3132 14.8968 22.7612 14.2368C23.2092 13.5769 23.4487 12.7976 23.4486 12Z"}]
|
||||
[:path {:stroke "currentColor", :d "M12.4981 17.973C12.3606 17.973 12.2492 17.8616 12.2492 17.7242C12.2492 17.5867 12.3606 17.4753 12.4981 17.4753"}]
|
||||
[:path {:stroke "currentColor", :d "M12.4981 17.973C12.6356 17.973 12.747 17.8616 12.747 17.7242C12.747 17.5867 12.6356 17.4753 12.4981 17.4753"}]])
|
||||
|
||||
(def rabbit
|
||||
[: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 "M9 21.5H6.5"}]
|
||||
[:path {:stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :d "M6.52169 19.4653H7.51807"}]
|
||||
[:path {:stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :d "M6.52268 21.4561C6.25869 21.4561 6.00551 21.3512 5.81884 21.1646C5.63217 20.9779 5.5273 20.7247 5.5273 20.4607C5.5273 20.1967 5.63217 19.9435 5.81884 19.7569C6.00551 19.5702 6.25869 19.4653 6.52268 19.4653"}]
|
||||
[:path {:stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :d "M7.51806 10.5069C9.50883 10.5069 11.3443 11.9999 12.495 15.4838C12.9927 16.9769 14.4857 21.4561 14.4857 21.4561H18.4673C18.7313 21.4561 18.9844 21.5609 19.1711 21.7476C19.3578 21.9343 19.4626 22.1875 19.4626 22.4515C19.4626 22.7154 19.3578 22.9686 19.1711 23.1553C18.9844 23.342 18.7313 23.4468 18.4673 23.4468H12.495C12.495 23.4468 10.979 20.9733 10.0483 19.4653C8.51345 16.9769 5.65172 15.5246 3.66096 14.1559C1.6702 12.7873 0.324441 9.73645 2.04346 7.52073C4.28208 4.63412 8.59606 5.77881 9.97367 7.37142L13.8049 11.2036C13.9426 11.3462 14.1074 11.46 14.2895 11.5382C14.4717 11.6165 14.6676 11.6577 14.8659 11.6594C15.0641 11.6611 15.2607 11.6233 15.4442 11.5483C15.6277 11.4732 15.7944 11.3623 15.9346 11.2221C16.0748 11.082 16.1857 10.9153 16.2607 10.7318C16.3358 10.5483 16.3736 10.3517 16.3719 10.1534C16.3701 9.95516 16.3289 9.75924 16.2507 9.57708C16.1724 9.39492 16.0587 9.23017 15.9161 9.09244L9.94381 3.10323C9.66371 2.823 9.50641 2.44298 9.5065 2.04677C9.5066 1.65057 9.66408 1.27062 9.94431 0.990528C10.2245 0.710433 10.6046 0.553129 11.0008 0.553223C11.397 0.553316 11.7769 0.710798 12.057 0.991026L22.2617 11.1957C22.6601 11.5395 22.974 11.9705 23.1791 12.4552C23.3841 12.9399 23.4748 13.4653 23.4442 13.9907C23.4442 15.4838 22.4488 16.4792 20.458 16.4792H17.9696L15.4751 18.2271"}]
|
||||
[:path {:stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :d "M1.73291 7.98666C1.46709 7.9302 1.22172 7.80228 1.02324 7.61666C0.824763 7.43105 0.680706 7.19479 0.606596 6.93334C0.532486 6.6719 0.531133 6.39518 0.602684 6.13303C0.674235 5.87087 0.815976 5.63321 1.01263 5.44567C1.20928 5.25812 1.45339 5.1278 1.71864 5.06875C1.9839 5.00971 2.26024 5.02417 2.51788 5.11059C2.77551 5.197 3.00468 5.3521 3.18068 5.55915C3.35668 5.7662 3.47284 6.01735 3.51663 6.28555"}]
|
||||
[:path {:stroke "currentColor", :d "M20.458 13.742C20.3206 13.742 20.2092 13.6305 20.2092 13.4931C20.2092 13.3557 20.3206 13.2443 20.458 13.2443"}]
|
||||
[:path {:stroke "currentColor", :d "M20.458 13.742C20.5955 13.742 20.7069 13.6305 20.7069 13.4931C20.7069 13.3557 20.5955 13.2443 20.458 13.2443"}]])
|
||||
|
||||
|
||||
(def arrow-in
|
||||
[:svg {:xmlns "http://www.w3.org/2000/svg", :viewbox "0 0 24 24"}
|
||||
[:defs]
|
||||
[:title "download-thick-box"]
|
||||
[:polygon {:points "15.5 14.5 15.5 8.5 8.5 8.5 8.5 14.5 5.5 14.5 12 21 18.469 14.5 15.5 14.5", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:path {:d "M22.629,4.572A6.22,6.22,0,0,1,23,6.5v16a1,1,0,0,1-1,1H2a1,1,0,0,1-1-1V6.5a6.22,6.22,0,0,1,.371-1.928L2.629,1.428A1.6,1.6,0,0,1,4,.5H20a1.6,1.6,0,0,1,1.371.928Z", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:line {:x1 "12", :y1 "6", :x2 "12", :y2 "0.5", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]
|
||||
[:line {:x1 "1.034", :y1 "6", :x2 "22.966", :y2 "6", :fill "none", :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]])
|
||||
|
||||
@@ -214,4 +214,4 @@
|
||||
(insight-table* {:selected-client
|
||||
(-> session :client :db/id)
|
||||
:identity identity})]
|
||||
[:div (company-side-bar matched-route)]))
|
||||
"Transaction Insights"))
|
||||
|
||||
@@ -11,57 +11,32 @@
|
||||
{}
|
||||
hiccup))})
|
||||
|
||||
(defn base-page [request contents side-bar-contents]
|
||||
(html-page
|
||||
[:html.has-navbar-fixed-top
|
||||
[:head
|
||||
[:meta {:charset "utf-8"}]
|
||||
[:meta {:http-equiv "X-UA-Compatible", :content "IE=edge"}]
|
||||
[:meta {:name "viewport", :content "width=device-width, initial-scale=1"}]
|
||||
[:title "Integreat"]
|
||||
[:link {:href "/css/font.min.css", :rel "stylesheet"}]
|
||||
[:link {:rel "stylesheet", :href "/css/react-datepicker.min.inc.css"}]
|
||||
[:link {:rel "stylesheet", :href "/output.css"}]
|
||||
[:script {:src "https://unpkg.com/hyperscript.org@0.9.7"}]
|
||||
[:script {:src "https://unpkg.com/@popperjs/core@2"}]
|
||||
#_[:script {:src "https://unpkg.com/htmx.org@1.8.4"
|
||||
:integrity "sha384-wg5Y/JwF7VxGk4zLsJEcAojRtlVp1FKKdGy1qN+OMtdq72WRvX/EdRdqg/LOhYeV"
|
||||
:crossorigin= "anonymous"}]
|
||||
[:script {:src "https://unpkg.com/htmx.org@1.9.0/dist/htmx.js"
|
||||
:crossorigin= "anonymous"}]
|
||||
[:script {:src "/js/htmx-disable.js"}]
|
||||
[:script {:type "text/javascript", :src "https://cdn.yodlee.com/fastlink/v4/initialize.js", :async "async" }]]
|
||||
[:script {:type "text/javascript", :src "https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.7/dist/autoComplete.min.js"}]
|
||||
[:script {:src "https://unpkg.com/dropzone@5/dist/min/dropzone.min.js"}]
|
||||
[:link {:rel "stylesheet" :href "https://unpkg.com/dropzone@5/dist/min/dropzone.min.css" :type "text/css"}]
|
||||
[:body {:hx-ext "disable-submit"
|
||||
}
|
||||
contents
|
||||
[:script {:src "/js/flowbite.min.js"}]
|
||||
[:script {:lang "text/javascript"}
|
||||
(hiccup/raw "
|
||||
const $targetEl = document.getElementById('left-nav');
|
||||
(defn base-page [request contents page-name]
|
||||
(html-page
|
||||
[:html.has-navbar-fixed-top
|
||||
[:head
|
||||
[:meta {:charset "utf-8"}]
|
||||
[:meta {:http-equiv "X-UA-Compatible", :content "IE=edge"}]
|
||||
[:meta {:name "viewport", :content "width=device-width, initial-scale=1"}]
|
||||
[:title (str "Integreat | " page-name)]
|
||||
[:link {:href "/css/font.min.css", :rel "stylesheet"}]
|
||||
[:link {:rel "icon" :type "image/png" :href "/favicon.png"}]
|
||||
[:link {:rel "stylesheet", :href "/css/react-datepicker.min.inc.css"}]
|
||||
[:link {:rel "stylesheet", :href "/output.css"}]
|
||||
|
||||
const $triggerEl = document.getElementById('left-nav-toggle');
|
||||
|
||||
const options = {
|
||||
onCollapse: () => {
|
||||
document.getElementById('main-content').classList.remove('lg:pl-64')
|
||||
},
|
||||
onExpand: () => {
|
||||
document.getElementById('main-content').classList.add('lg:pl-64')
|
||||
},
|
||||
onToggle: () => {
|
||||
}
|
||||
};
|
||||
|
||||
const collapse = new Collapse($targetEl, $triggerEl, options);
|
||||
|
||||
|
||||
;
|
||||
|
||||
"
|
||||
|
||||
|
||||
|
||||
)]]]))
|
||||
[:script {:src "https://unpkg.com/hyperscript.org@0.9.7/dist/_hyperscript.min.js"}]
|
||||
[:script {:src "https://unpkg.com/@popperjs/core@2.11.8/dist/umd/popper.min.js"}]
|
||||
#_[:script {:src "https://unpkg.com/htmx.org@1.8.4"
|
||||
:integrity "sha384-wg5Y/JwF7VxGk4zLsJEcAojRtlVp1FKKdGy1qN+OMtdq72WRvX/EdRdqg/LOhYeV"
|
||||
:crossorigin= "anonymous"}]
|
||||
[:script {:src "https://unpkg.com/htmx.org@1.9.0/dist/htmx.js"
|
||||
:crossorigin= "anonymous"}]
|
||||
[: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"}]]
|
||||
[:script {:type "text/javascript", :src "https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@10.2.7/dist/autoComplete.min.js"}]
|
||||
[:script {:src "https://unpkg.com/dropzone@5.9.3/dist/min/dropzone.min.js"}]
|
||||
[:link {:rel "stylesheet" :href "https://unpkg.com/dropzone@5/dist/min/dropzone.min.css" :type "text/css"}]
|
||||
[:body {:hx-ext "disable-submit"}
|
||||
contents
|
||||
[:script {:src "/js/flowbite.min.js"}]]]))
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"admin" {"/history" {"" :admin-history
|
||||
"/" :admin-history
|
||||
#"/search/?" :admin-history-search
|
||||
["/" [#"\d+" :entity-id] #"/?"] :admin-history-search
|
||||
["/" [#"\d+" :entity-id] #"/?"] :admin-history
|
||||
["/inspect/" [#"\d+" :entity-id] #"/?"] :admin-history-inspect}
|
||||
"/ezcater-xls" :admin-ezcater-xls}
|
||||
"transaction" {"/insights" {"" :transaction-insights
|
||||
|
||||
@@ -37,6 +37,8 @@
|
||||
:owns-state {:single ::page}
|
||||
:query-obj {:venia/queries [[:user
|
||||
[:name
|
||||
:profile_image_url
|
||||
:email
|
||||
:id
|
||||
:role
|
||||
[:clients [:id :name]]]]]}
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
[grid/header
|
||||
[grid/row {}
|
||||
[grid/header-cell {} "User"]
|
||||
[grid/header-cell {} "Email"]
|
||||
[grid/header-cell {} "Role"]
|
||||
[grid/header-cell {} "Clients"]
|
||||
[grid/header-cell {:style {:width (action-cell-width 1)}} ]]]
|
||||
@@ -36,7 +37,16 @@
|
||||
(for [{:keys [id name role clients] :as c} (:data page)]
|
||||
^{:key (str name "-" id )}
|
||||
[grid/row {:class (:class c) :id id}
|
||||
[grid/cell {} name]
|
||||
[grid/cell {}
|
||||
[:div.level
|
||||
[:div.level-left
|
||||
(when-let [url (:profile-image-url c)]
|
||||
[:div.level-item
|
||||
[:figure.image.is-24x24
|
||||
[:img.is-rounded {:src url
|
||||
:referrer-policy= "no-referrer" }]]])
|
||||
[:div.level-item name]]]]
|
||||
[grid/cell {} (:email c)]
|
||||
[grid/cell {} role]
|
||||
[grid/cell {} (str/join ", " (map :name clients))]
|
||||
[grid/cell {}
|
||||
|
||||
@@ -13,7 +13,9 @@
|
||||
[clojure.set :as set]
|
||||
[re-frame.core :as re-frame]
|
||||
[reagent.core :as reagent]
|
||||
[vimsical.re-frame.fx.track :as track]))
|
||||
[vimsical.re-frame.fx.track :as track]
|
||||
[vimsical.re-frame.cofx.inject :as inject]
|
||||
[auto-ap.views.components.buttons :as buttons]))
|
||||
|
||||
(defn data-params->query-params [params]
|
||||
{:start (:start params 0)
|
||||
@@ -61,6 +63,29 @@
|
||||
[::data-page/received ::page (set/rename-keys (:ledger-page result)
|
||||
{:journal-entries :data})])}}))
|
||||
|
||||
(re-frame/reg-sub
|
||||
::csv-content
|
||||
(fn [db]
|
||||
(::csv-content db)))
|
||||
|
||||
(re-frame/reg-event-fx
|
||||
::csv-exported
|
||||
(fn [{:keys [db]} [_ csv]]
|
||||
{:db (assoc db ::csv-content csv)}))
|
||||
|
||||
(re-frame/reg-event-fx
|
||||
::export-csv
|
||||
[with-user (re-frame/inject-cofx ::inject/sub [::data-page/params ::page])]
|
||||
(fn [{:keys [user db] ::data-page/keys [params]}]
|
||||
{:graphql {:token user
|
||||
:owns-state {:single [::data-page/page ::page]}
|
||||
:query-obj {:venia/queries [[:ledger-csv
|
||||
{:filters (data-params->query-params params)}
|
||||
[:csv_content_b64]]]}
|
||||
:on-success (fn [result]
|
||||
[::csv-exported (:csv-content-b64 (:ledger-csv result))])}}))
|
||||
|
||||
|
||||
(re-frame/reg-event-fx
|
||||
::unmounted
|
||||
(fn [_ _]
|
||||
@@ -101,31 +126,51 @@
|
||||
:on-success (fn [result]
|
||||
[::delete-successful result params])}})))
|
||||
|
||||
(defn action-buttons []
|
||||
(println "HERE?")
|
||||
(let [params @(re-frame/subscribe [::data-page/params ::page])
|
||||
csv-content @(re-frame/subscribe [::csv-content])
|
||||
is-admin? @(re-frame/subscribe [::subs/is-admin?])
|
||||
status @(re-frame/subscribe [::status/single [::data-page/page ::page]])
|
||||
checked @(re-frame/subscribe [::data-page/checked ::page])]
|
||||
[:div.buttons
|
||||
(into [:div.tags] (map (fn [[z {:keys [id external-id]}]]
|
||||
(if (= "header" z)
|
||||
[:span.tag.is-medium {:on-click
|
||||
(dispatch-event [::data-page/remove-check ::page "header"])}
|
||||
"All visible ledger entries"]
|
||||
[:span.tag.is-medium external-id
|
||||
[:button.delete.is-small {:on-click
|
||||
(dispatch-event [::data-page/remove-check ::page id])}]]))
|
||||
checked))
|
||||
[:button.button.is-danger {:on-click (dispatch-event [::delete-selected params])
|
||||
:class (status/class-for @(re-frame/subscribe [::status/single ::delete-selected]))
|
||||
:disabled (or (status/disabled-for @(re-frame/subscribe [::status/single ::delete-selected]))
|
||||
(not (seq checked)))}
|
||||
"Delete selected"]
|
||||
(when is-admin?
|
||||
(if csv-content
|
||||
[:a {:href (str "data:attachment/csv;base64," csv-content)
|
||||
:target "_blank"
|
||||
:download (str "ledger.csv")}
|
||||
"Click here to download"]
|
||||
[buttons/event-button {:event [::export-csv]
|
||||
:name "Export"
|
||||
:class (status/class-for status)
|
||||
:disabled (status/disabled-for status)}]))]))
|
||||
|
||||
(defn ledger-content []
|
||||
(let [_ @(re-frame/subscribe [::subs/client])
|
||||
params @(re-frame/subscribe [::data-page/params ::page])
|
||||
checked @(re-frame/subscribe [::data-page/checked ::page])]
|
||||
]
|
||||
[:div
|
||||
[:h1.title "External Ledger"]
|
||||
[status/status-notification {:statuses [[::status/single ::delete-selected]]}]
|
||||
[:div.is-pulled-right
|
||||
[:div.buttons
|
||||
(into [:div.tags ] (map (fn [[z {:keys [id external-id]}]]
|
||||
(if (= "header" z)
|
||||
[:span.tag.is-medium {:on-click
|
||||
(dispatch-event [::data-page/remove-check ::page "header"])}
|
||||
"All visible ledger entries"]
|
||||
[:span.tag.is-medium external-id
|
||||
[:button.delete.is-small {:on-click
|
||||
(dispatch-event [::data-page/remove-check ::page id])}]]))
|
||||
checked))
|
||||
[:button.button.is-danger {:on-click (dispatch-event [::delete-selected params])
|
||||
:class (status/class-for @(re-frame/subscribe [::status/single ::delete-selected]))
|
||||
:disabled (or (status/disabled-for @(re-frame/subscribe [::status/single ::delete-selected]))
|
||||
(not (seq checked)))}
|
||||
"Delete selected"]]]
|
||||
]
|
||||
[table/table {:id :ledger
|
||||
:data-page ::page}]]))
|
||||
:data-page ::page
|
||||
:action-buttons [action-buttons]}]]))
|
||||
|
||||
|
||||
(defn external-ledger-page []
|
||||
|
||||
@@ -42,16 +42,16 @@
|
||||
[grid/cell {:class "has-text-right"} (when debit (nf debit ))]
|
||||
[grid/cell {:class "has-text-right"} (when credit (nf credit ))]])]])
|
||||
|
||||
(defn table [{:keys [data-page]}]
|
||||
(defn table [{:keys [data-page action-buttons]}]
|
||||
(let [{:keys [data params]} @(re-frame/subscribe [::data-page/page data-page])
|
||||
selected-client @(re-frame/subscribe [::subs/client])
|
||||
bank-accounts-by-id @(re-frame/subscribe [::subs/bank-accounts-by-id])]
|
||||
[grid/grid {:data-page data-page
|
||||
:check-boxes? true
|
||||
:column-count (if selected-client 7 8)}
|
||||
[grid/controls data]
|
||||
[grid/controls (assoc data :action-buttons action-buttons)]
|
||||
[grid/table {:fullwidth true :class ["wrappable"]}
|
||||
[grid/header
|
||||
[grid/header
|
||||
[grid/row {:id "header"
|
||||
:entity params}
|
||||
(when-not selected-client
|
||||
@@ -67,6 +67,6 @@
|
||||
(for [{:keys [id] :as i} (:data data)]
|
||||
^{:key id}
|
||||
[external-ledger-row {:row i
|
||||
:selected-client selected-client
|
||||
:bank-accounts-by-id bank-accounts-by-id}])]]]))
|
||||
:selected-client selected-client
|
||||
:bank-accounts-by-id bank-accounts-by-id}])]]]))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user