Makes reports page work with new tailwind

This commit is contained in:
2023-05-12 15:17:54 -07:00
parent e78c73e093
commit 8dca622947
13 changed files with 540 additions and 296 deletions

View File

@@ -12,6 +12,8 @@
[clj-time.coerce :as c]
[datomic.api :as dc]))
(def default-read '[:db/id :report/client :report/created :report/url :report/name :report/creator])
(defn raw-graphql-ids [db args]
(let [query (cond-> {:query {:find []
:in ['$ ]
@@ -43,8 +45,7 @@
(apply-pagination args))))
(defn graphql-results [ids db args]
(let [results (->> (pull-many db '[:db/id :report/client :report/created :report/url :report/name :report/creator]
ids)
(let [results (->> (pull-many db default-read ids)
(map #(update % :report/created c/from-date))
(group-by :db/id))]
(->> ids
@@ -63,8 +64,3 @@
[(->> (graphql-results ids-to-retrieve db args))
matching-count]))

View File

@@ -6,7 +6,7 @@
[auto-ap.ssr.components :as com]
[auto-ap.ssr.svg :as svg]
[auto-ap.ssr.ui :refer [base-page]]
[auto-ap.ssr.utils :refer [html-response]]
[auto-ap.ssr.utils :refer [html-response form-data->map path->name]]
[bidi.bidi :as bidi]
[clojure.string :as str]
[datomic.api :as dc]
@@ -53,7 +53,6 @@
[(>= ?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)
@@ -113,7 +112,6 @@
[(>= ?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)
@@ -125,134 +123,97 @@
[(:client/code client ) amount]))
(into []))))
(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
{}
(if (cannot-overwrite? vendor)
[:div (com/link {:href "mailto:ben@integreatconsult.com"} "Contact Integreat")]
(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]}]
(println hx-query-params)
(let [start (or (some-> (or (get query-params "start") (get hx-query-params "start")) not-empty (Long/parseLong ))
(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 ))
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)
total (count companies)
companies (subvec companies (Math/min start total) (Math/min (+ start per-page) total))]
[:div#vendor-table {:hx-get (bidi/path-for ssr-routes/only-routes
:company-1099-vendor-table
:request-method :get)
:hx-trigger "clientSelected from:body"
:hx-swap "outerHTML swap:300ms"}
(com/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 "1099 Vendor Info"]
[:div {:class "flex items-center flex-1 space-x-4"}
[:h5
[:span "Total Vendors:"]
[:span {:class "dark:text-white pl-4"} (count companies)]]]]
[: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"}
(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)]]
[:div {:class "overflow-x-auto"}
(apply com/data-grid {: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 {})]}
(for [[client vendor amount] companies]
(com/data-grid-row
{:class (when (= flash-id
(:db/id vendor))
"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
{}
(if (cannot-overwrite? vendor)
[:div (com/link {:href "mailto:ben@integreatconsult.com"} "Contact Integreat")]
(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))))))]
(com/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
:company-1099-vendor-table
:request-method :get)
"?start=" (* page per-page))
:hx-target "#vendor-table"
:hx-swap "outerHTML show:#app:top"})}))]))
(defn form-data->map [form-data]
(reduce-kv
(fn [acc k v]
(cond (and (string? v)
(empty? v))
acc
:else
(assoc-in acc (->> (str/split k #"_")
(mapv #(apply keyword (str/split % #"/"))))
v)))
{}
form-data))
(defn path->name [k]
(cond (keyword? k)
(str (namespace k) "/" (name k))
(seq k)
(str/join "_" (map path->name k))
:else k))
(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 {})]})))
(defn vendor-save [{:keys [form-params identity route-params] :as request}]
(when-not (cannot-overwrite? (dc/pull (dc/db conn) '[*] (Long/parseLong (:vendor-id route-params))))
@@ -265,8 +226,6 @@
(table* request :flash-id (Long/parseLong (:vendor-id route-params)))
:headers {"hx-trigger" "closeModal"}))
(defn vendor-dialog [request]
(let [vendor (dc/pull (dc/db conn) '[* {:vendor/legal-entity-1099-type [:db/ident]
:vendor/legal-entity-tin-type [:db/ident]}] (Long/parseLong (:vendor-id (:params request))))] ;; TODO perms
@@ -276,86 +235,86 @@
:company-1099-vendor-save
:request-method :post
:vendor-id (Long/parseLong (:vendor-id (:params request)))))
:hx-target "#vendor-table"
:hx-swap "outerHTML swap:300ms"}
[:fieldset {:class "hx-disable"}
(com/modal-card {}
[: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"}
(com/text-input {:name (path->name [:vendor/address :address/street1])
:value (-> vendor :vendor/address :address/street1)
:placeholder "1700 Pennsylvania Ave"
:autofocus true}))]
[:div.col-span-6
(com/field {:label "Street 2"}
(com/text-input {:name (path->name [:vendor/address :address/street2])
:value (-> vendor :vendor/address :address/street2)
:placeholder "Suite 200"}))]
[:div.col-span-3
(com/field {:label "City"}
(com/text-input {:name (path->name [:vendor/address :address/city])
:value (-> vendor :vendor/address :address/city)
:placeholder "Cupertino"}))]
[:div.col-span-1
(com/field {:label "State"}
(com/text-input {:name (path->name [:vendor/address :address/state])
:value (-> vendor :vendor/address :address/state)
:placeholder "CA"}))]
[:div.col-span-2
(com/field {:label "Zip"}
(com/text-input {:name (path->name [:vendor/address :address/zip])
:value (-> vendor :vendor/address :address/zip)
:placeholder "98102"}))]
[:h4.text-xl.border-b.col-span-6 "Legal Entity"]
[:div.col-span-6
(com/field {:label "Legal Entity Name"}
(com/text-input {:name (path->name [:vendor/legal-entity-name])
:value (-> vendor :vendor/legal-entity-name)
:placeholder "Good Restaurant LLC"}))]
[:div.col-span-6.text-center " - OR -"]
[:div.col-span-2
(com/field {:label "First Name"}
(com/text-input {:name (path->name [:vendor/legal-entity-first-name])
:value (-> vendor :vendor/legal-entity-first-name)
:placeholder "John"}))]
[:div.col-span-2
(com/field {:label "Middle Name"}
(com/text-input {:name (path->name [:vendor/legal-entity-middle-name])
:value (-> vendor :vendor/legal-entity-middle-name)
:placeholder "C."}))]
[:div.col-span-2
(com/field {:label "Last Name"}
(com/text-input {:name (path->name [:vendor/legal-entity-last-name])
:value (-> vendor :vendor/legal-entity-last-name)
:placeholder "Riley"}))]
[:div.col-span-2
(com/field {:label "TIN"}
(com/text-input {:name (path->name [:vendor/legal-entity-tin])
:value (-> vendor :vendor/legal-entity-tin)
:placeholder "John"}))]
[:div.col-span-2
(com/field {:label "TIN Type"}
(com/select {:name (path->name [:vendor/legal-entity-tin-type])
:allow-blank? true
:value (some-> vendor :vendor/legal-entity-tin-type :db/ident name)
:options [["ein" "EIN"]
["ssn" "SSN"]]}))]
[:div.col-span-2
(com/field {:label "1099 Type"}
(com/select {:name (path->name [:vendor/legal-entity-1099-type])
:allow-blank? true
:value (some-> vendor :vendor/legal-entity-1099-type :db/ident name)
:options [["none" "None"]
["misc" "Misc"]
["landlord" "Landlord"]]}))]
[:div.col-span-6
(com/button {:color :primary}
"Save")]]]
[:div])]]))))
:hx-target "#vendor-table"
:hx-swap "outerHTML swap:300ms"}
[:fieldset {:class "hx-disable"}
(com/modal-card {}
[: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"}
(com/text-input {:name (path->name [:vendor/address :address/street1])
:value (-> vendor :vendor/address :address/street1)
:placeholder "1700 Pennsylvania Ave"
:autofocus true}))]
[:div.col-span-6
(com/field {:label "Street 2"}
(com/text-input {:name (path->name [:vendor/address :address/street2])
:value (-> vendor :vendor/address :address/street2)
:placeholder "Suite 200"}))]
[:div.col-span-3
(com/field {:label "City"}
(com/text-input {:name (path->name [:vendor/address :address/city])
:value (-> vendor :vendor/address :address/city)
:placeholder "Cupertino"}))]
[:div.col-span-1
(com/field {:label "State"}
(com/text-input {:name (path->name [:vendor/address :address/state])
:value (-> vendor :vendor/address :address/state)
:placeholder "CA"}))]
[:div.col-span-2
(com/field {:label "Zip"}
(com/text-input {:name (path->name [:vendor/address :address/zip])
:value (-> vendor :vendor/address :address/zip)
:placeholder "98102"}))]
[:h4.text-xl.border-b.col-span-6 "Legal Entity"]
[:div.col-span-6
(com/field {:label "Legal Entity Name"}
(com/text-input {:name (path->name [:vendor/legal-entity-name])
:value (-> vendor :vendor/legal-entity-name)
:placeholder "Good Restaurant LLC"}))]
[:div.col-span-6.text-center " - OR -"]
[:div.col-span-2
(com/field {:label "First Name"}
(com/text-input {:name (path->name [:vendor/legal-entity-first-name])
:value (-> vendor :vendor/legal-entity-first-name)
:placeholder "John"}))]
[:div.col-span-2
(com/field {:label "Middle Name"}
(com/text-input {:name (path->name [:vendor/legal-entity-middle-name])
:value (-> vendor :vendor/legal-entity-middle-name)
:placeholder "C."}))]
[:div.col-span-2
(com/field {:label "Last Name"}
(com/text-input {:name (path->name [:vendor/legal-entity-last-name])
:value (-> vendor :vendor/legal-entity-last-name)
:placeholder "Riley"}))]
[:div.col-span-2
(com/field {:label "TIN"}
(com/text-input {:name (path->name [:vendor/legal-entity-tin])
:value (-> vendor :vendor/legal-entity-tin)
:placeholder "John"}))]
[:div.col-span-2
(com/field {:label "TIN Type"}
(com/select {:name (path->name [:vendor/legal-entity-tin-type])
:allow-blank? true
:value (some-> vendor :vendor/legal-entity-tin-type :db/ident name)
:options [["ein" "EIN"]
["ssn" "SSN"]]}))]
[:div.col-span-2
(com/field {:label "1099 Type"}
(com/select {:name (path->name [:vendor/legal-entity-1099-type])
:allow-blank? true
:value (some-> vendor :vendor/legal-entity-1099-type :db/ident name)
:options [["none" "None"]
["misc" "Misc"]
["landlord" "Landlord"]]}))]
[:div.col-span-6
(com/button {:color :primary}
"Save")]]]
[:div])]]))))
(defn vendor-table [request]
(html-response (table* request)

View File

@@ -0,0 +1,120 @@
(ns auto-ap.ssr.company.reports
(:require
[amazonica.aws.s3 :as s3]
[auto-ap.datomic :refer [conn]]
[auto-ap.datomic.reports :as r]
[auto-ap.graphql.utils :refer [assert-admin is-admin?]]
[auto-ap.ssr-routes :as ssr-routes]
[auto-ap.ssr.components :as com]
[auto-ap.ssr.svg :as svg]
[auto-ap.ssr.ui :refer [base-page]]
[auto-ap.ssr.utils :refer [html-response]]
[auto-ap.time :as atime]
[bidi.bidi :as bidi]
[config.core :refer [env]]
[datomic.api :as dc]
[hiccup2.core :as hiccup]))
(defn row* [{:keys [flash? report identity delete-after-settle?]}]
(com/data-grid-row
{:class (when flash?
"live-added")
"_" (hiccup/raw (when delete-after-settle?" on htmx:afterSettle wait 400ms then remove me"))}
(com/data-grid-cell
{}
(:report/name report))
(com/data-grid-cell
{}
(when (:report/creator report)
(com/pill {:color :primary }
(:report/creator report))))
(com/data-grid-cell
{}
(atime/unparse-local (:report/created report)
atime/normal-date))
(com/data-grid-right-stack-cell
{}
(com/a-icon-button {:href (:report/url report)}
svg/download)
(when (is-admin? identity)
[:form
[:input {:type :hidden :name "id" :value (:db/id report)}]
(com/icon-button {:hx-delete (str (bidi/path-for ssr-routes/only-routes
:company-reports-delete
:request-method :delete))
:hx-target "closest tr"}
svg/trash)]))))
(defn table* [{:keys [client start per-page identity session flash-id]}]
(let [start (or start 0)
per-page (or per-page 30)
[reports total] (r/get-graphql {:id identity
:start start
:per-page per-page
:client-id (:db/id client)
:sort nil})]
(com/data-grid-card {:id "report-table"
:title "Reports"
:entity-name "reports"
:route :company-reports-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 [report reports]
(row* {:report report
:flash? (= flash-id
(:db/id report))
:identity identity}))
:headers [(com/data-grid-header {} "Name")
(com/data-grid-header {:class "hidden md:table-cell"} "Created by")
(com/data-grid-header {:class "hidden md:table-cell"} "Created")
(com/data-grid-header {})]})))
(defn delete-report [{:keys [query-params hx-query-params form-params identity session] :as request}]
(let [[id-to-delete key] (first (dc/q '[:find ?i ?k
:in $ ?i
:where [?i :report/key ?k]]
(dc/db conn)
(some-> (get form-params "id") not-empty Long/parseLong)))
report (dc/pull (dc/db conn) r/default-read id-to-delete)]
(when id-to-delete
(s3/delete-object :bucket-name (:data-bucket env)
:key key)
@(dc/transact conn [[:db/retractEntity id-to-delete]]))
(html-response
(row* {:report report
:flash? true
:identity identity
:delete-after-settle? true}))))
(defn reports-table [{:keys [query-params hx-query-params identity session] :as request}]
(html-response (table* {:client (:client (:session request))
:start (some-> (or (get query-params "start") (get hx-query-params "start")) not-empty (Long/parseLong ))
:per-page (some-> (or (get query-params "per-page") (get hx-query-params "per-page")) not-empty (Long/parseLong ))
:identity identity
:session session})
:headers {"hx-push-url" (str "?start=" (get (:query-params request) "start"))}))
(defn page [{:keys [query-params hx-query-params identity session] :as request}]
(base-page
request
(com/page {:nav (com/company-aside-nav)
:active-client (:client (:session request))
:identity (:identity request)}
(com/breadcrumbs {}
[:a {:href "#"} "My Company"]
[:a {:href "#"} "Reports"])
(table* {:client (:client (:session request))
:start (some-> (or (get query-params "start") (get hx-query-params "start")) not-empty (Long/parseLong ))
:per-page (some-> (or (get query-params "per-page") (get hx-query-params "per-page")) not-empty (Long/parseLong ))
:identity identity
:session session}))
nil))

View File

@@ -12,14 +12,14 @@
(defn dropdown-search-results* [{:keys [options]}]
[:ul
(for [option options]
(for [[id company-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 (get option "key")}
(get option "value")]]])])
:data-value id}
company-name]]])])
(defn get-clients [identity query]
(dc/q '[:find ?c ?n
@@ -33,10 +33,7 @@
(defn dropdown-search-results [{:keys [identity] :as request}]
(html-response
(dropdown-search-results* {:options (->> (get-clients identity (get (:query-params request) "search-text"))
(map (fn [[k v]]
{"key" k
"value" v})))
(dropdown-search-results* {:options (get-clients identity (get (:query-params request) "search-text"))
:client (:client (:session request))})))
(defn dropdown [{:keys [client]}]
@@ -79,14 +76,7 @@
:hx-target "#company-search-results"
:hx-swap "innerHTML"} ]]
[:input#company-search-value {:type "hidden"
:autocomplete "off"
:name "search-client"
:hx-put (bidi/path-for ssr-routes/only-routes
:active-client
:request-method :put)
:hx-target "#company-dropdown"
:hx-swap "outerHTML"
:hx-trigger "change changed"} ]]
:name "search-client"}]]
[:div.divide-y.divide-gray-100
[:div#company-search-results {:class "h-48 px-3 pb-3 overflow-y-auto text-sm text-gray-700 dark:text-gray-200"}]
[:div {:class "flex items-center pl-2 rounded hover:bg-green-100 dark:hover:bg-green-600"}

View File

@@ -8,13 +8,15 @@
[auto-ap.ssr.components.navbar :as navbar]
[auto-ap.ssr.components.page :as page]
[auto-ap.ssr.components.data-grid :as data-grid]
[auto-ap.ssr.components.tags :as tags]))
[auto-ap.ssr.components.tags :as tags]
[auto-ap.ssr.components.paginator :as paginator]))
(def breadcrumbs breadcrumbs/breadcrumbs-)
(def button buttons/button-)
(def button-icon buttons/button-icon-)
(def icon-button buttons/icon-button-)
(def a-icon-button buttons/a-icon-button-)
(def modal dialog/modal-)
(def modal-card dialog/modal-card-)
@@ -44,62 +46,9 @@
:class (str "font-medium text-blue-600 dark:text-blue-500 hover:underline " class)}]
children))
(defn bound [x y z]
(cond
(< z x)
x
(< y x)
x
(> y z)
z
:else
y))
(def elipsis-button
[:p {:href "#", :class "flex items-center justify-center px-3 py-2 text-sm leading-tight text-gray-500 bg-white border border-gray-300 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400"} "..."])
(defn paginator- [{:keys [start per-page end total a-params]}]
(let [per-page (or per-page 20)
max-buttons 5
buttons-before (Math/floor (/ max-buttons 2))
total-pages (long (Math/max (long 1) (long (Math/ceil (/ total per-page)))))
current-page (long (Math/floor (/ start per-page)))
first-page-button (bound 0 (- current-page buttons-before) (- total-pages max-buttons))
all-buttons (into [] (for [x (range total-pages)]
[:li
[:a (-> (a-params x)
(update
:class #(cond-> %
true (str " flex items-center justify-center px-3 py-2 text-sm leading-tight border ")
(= current-page x)
(str " text-primary-600 bg-primary-50 border-primary-300 hover:bg-primary-100 hover:text-primary-700 dark:border-gray-700 dark:bg-gray-700 dark:text-white")
(not= current-page x)
(str " text-gray-500 bg-white border-gray-300 hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white")))
(assoc :href "#"))
(inc x)]]))
last-page-button (Math/min (long total-pages) (long (+ max-buttons first-page-button)))
extended-last-page-button (when (not= last-page-button total-pages)
(list
elipsis-button
(last all-buttons)))
(def paginator paginator/paginator-)
(def data-grid-card data-grid/data-grid-card-)
extended-first-page-button (when (not= first-page-button 0)
(list
(first all-buttons)
elipsis-button))]
[:nav
[:ul {:class "inline-flex items-stretch -space-x-px"}
extended-first-page-button
(apply list (subvec all-buttons first-page-button last-page-button))
extended-last-page-button]]))
(defn paginator [{:keys [start per-page end total a-params] :as params}]
[:nav {:class "flex flex-col items-start justify-between p-4 space-y-3 md:flex-row md:items-center md:space-y-0", :aria-label "Table navigation"}
[:span {:class "text-sm font-normal text-gray-500 dark:text-gray-400"}
[:span {:class "font-semibold text-gray-900 dark:text-white"} (str (inc start)) "-" (str end) " of " (str total)]]
(paginator- params)])

View File

@@ -1,6 +1,8 @@
(ns auto-ap.ssr.components.aside
(:require [auto-ap.ssr.svg :as svg]
[hiccup2.core :as hiccup]))
[hiccup2.core :as hiccup]
[bidi.bidi :as bidi]
[auto-ap.ssr-routes :as ssr-routes]))
(defn menu-button- [params & children]
[:a (-> params
@@ -188,7 +190,9 @@
[:ul {:class "space-y-2"}
[:li
(menu-button- {:icon svg/report}
(menu-button- {:icon svg/report
:href (bidi/path-for ssr-routes/only-routes
:company-reports)}
"Reports")]
[:li
(menu-button- {:icon svg/bank}
@@ -198,5 +202,8 @@
"Vendors")]
[:li
(menu-button- {:icon svg/government-building}
"1099 Vendor Info")]])
(menu-button- {:icon svg/government-building
:href (bidi/path-for ssr-routes/only-routes
:company-1099)}
"1099 Vendor Info"
)]])

View File

@@ -16,6 +16,13 @@
(defn icon-button- [params & children]
(into
[:button (update params :class str " inline-flex items-center justify-center bg-white dark:bg-gray-600 items-center p-3 text-sm font-medium border border-gray-300 dark:border-gray-700 text-center text-gray-500 hover:text-gray-800 rounded-lg dark:text-gray-400 dark:hover:text-gray-100")
[:div.htmx-indicator.flex.items-center
(svg/spinner {:class "inline w-4 h-4 text-white"})]
[:div.htmx-indicator-hidden.inline-flex.gap-2.items-center.justify-center (into [:div.h-4.w-4] children)]]))
(defn a-icon-button- [params & children]
(into
[:a (update params :class str " inline-flex items-center justify-center bg-white dark:bg-gray-600 items-center p-3 text-sm font-medium border border-gray-300 dark:border-gray-700 text-center text-gray-500 hover:text-gray-800 rounded-lg dark:text-gray-400 dark:hover:text-gray-100")
[:div.h-4.w-4 children]]))
(defn save-button- [params & children]

View File

@@ -1,13 +1,17 @@
(ns auto-ap.ssr.components.data-grid)
(ns auto-ap.ssr.components.data-grid
(:require
[auto-ap.ssr-routes :as ssr-routes]
[auto-ap.ssr.components.card :refer [content-card-]]
[auto-ap.ssr.components.paginator :refer [paginator-]]
[bidi.bidi :as bidi]))
(defn header- [params & rest]
(into [:th.px-4.py-3 {:scope "col" :class (:class params)} ] rest))
(defn row- [params & rest]
(into [:tr {:class (cond-> "border-b dark:border-gray-600 hover:bg-gray-100 dark:hover:bg-gray-700"
(:class params) (str " " (:class params)))}] rest))
(into [:tr (update params
:class str " border-b dark:border-gray-600 hover:bg-gray-100 dark:hover:bg-gray-700")] rest))
(defn cell- [params & rest]
(into [:td.px-4.py-2 {:class (:class params)}] rest))
@@ -32,3 +36,50 @@
(into
[:tbody]
rest)])
(defn data-grid-card- [{:keys [id
route
title
entity-name
action-buttons
total
start
per-page
flash-id
headers
rows] :as params}]
[:div {:hx-get (bidi/path-for ssr-routes/only-routes
route
: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
[:span (format "Total %s:" entity-name)]
[:span {:class "dark:text-white pl-4"} total]]]]
(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
}
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"})}))])

View File

@@ -0,0 +1,61 @@
(ns auto-ap.ssr.components.paginator)
(defn bound [x y z]
(cond
(< z x)
x
(< y x)
x
(> y z)
z
:else
y))
(def elipsis-button
[:p {:href "#", :class "flex items-center justify-center px-3 py-2 text-sm leading-tight text-gray-500 bg-white border border-gray-300 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400"} "..."])
(defn paginator-internal- [{:keys [start per-page end total a-params]}]
(let [per-page (or per-page 20)
max-buttons 5
buttons-before (Math/floor (/ max-buttons 2))
total-pages (long (Math/max (long 1) (long (Math/ceil (/ total per-page)))))
current-page (long (Math/floor (/ start per-page)))
first-page-button (bound 0 (- current-page buttons-before) (- total-pages max-buttons))
all-buttons (into [] (for [x (range total-pages)]
[:li
[:a (-> (a-params x)
(update
:class #(cond-> %
true (str " flex items-center justify-center px-3 py-2 text-sm leading-tight border ")
(= current-page x)
(str " text-primary-600 bg-primary-50 border-primary-300 hover:bg-primary-100 hover:text-primary-700 dark:border-gray-700 dark:bg-gray-700 dark:text-white")
(not= current-page x)
(str " text-gray-500 bg-white border-gray-300 hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white")))
(assoc :href "#"))
(inc x)]]))
last-page-button (Math/min (long total-pages) (long (+ max-buttons first-page-button)))
extended-last-page-button (when (not= last-page-button total-pages)
(list
elipsis-button
(last all-buttons)))
extended-first-page-button (when (not= first-page-button 0)
(list
(first all-buttons)
elipsis-button))]
[:nav
[:ul {:class "inline-flex items-stretch -space-x-px"}
extended-first-page-button
(apply list (subvec all-buttons first-page-button last-page-button))
extended-last-page-button]]))
(defn paginator- [{:keys [start per-page end total a-params] :as params}]
[:nav {:class "flex flex-col items-start justify-between p-4 space-y-3 md:flex-row md:items-center md:space-y-0", :aria-label "Table navigation"}
[:span {:class "text-sm font-normal text-gray-500 dark:text-gray-400"}
[:span {:class "font-semibold text-gray-900 dark:text-white"} (str (inc start)) "-" (str end) " of " (str total)]]
(paginator-internal- params)])

View File

@@ -8,6 +8,7 @@
[auto-ap.ssr.company.company-1099 :as company-1099]
[auto-ap.ssr.search :as search]
[auto-ap.ssr.company-dropdown :as company-dropdown]
[auto-ap.ssr.company.reports :as company-reports]
[auto-ap.routes.ezcater-xls :as ezcater-xls]))
;; from auto-ap.ssr-routes, because they're shared
@@ -24,6 +25,9 @@
:company-1099-vendor-table (wrap-client-redirect-unauthenticated (wrap-secure company-1099/vendor-table))
:company-1099-vendor-dialog (wrap-client-redirect-unauthenticated (wrap-secure company-1099/vendor-dialog))
:company-1099-vendor-save (wrap-client-redirect-unauthenticated (wrap-secure company-1099/vendor-save))
:company-reports (wrap-client-redirect-unauthenticated (wrap-secure company-reports/page))
:company-reports-table (wrap-client-redirect-unauthenticated (wrap-secure company-reports/reports-table))
:company-reports-delete (wrap-client-redirect-unauthenticated (wrap-admin company-reports/delete-report))
:transaction-insights (wrap-client-redirect-unauthenticated (wrap-secure insights/page))
:transaction-insight-table (wrap-client-redirect-unauthenticated (wrap-secure insights/insight-table))
:transaction-insight-rows (wrap-client-redirect-unauthenticated (wrap-secure insights/transaction-rows))

View File

@@ -197,3 +197,77 @@
(def drop-down
[:svg {:class "w-4 h-4 ml-2", :aria-hidden "true", :fill "none", :stroke "currentColor", :viewbox "0 0 24 24", :xmlns "http://www.w3.org/2000/svg"}
[:path {:stroke-linecap "round", :stroke-linejoin "round", :stroke-width "2", :d "M19 9l-7 7-7-7"}]])
(def download
[:svg
{:xmlns "http://www.w3.org/2000/svg", :viewBox "0 0 24 24"}
[:defs]
[:title "download-thick-bottom"]
[:path
{:d
"M5.5,11.5c-.275,0-.341.159-.146.354l6.292,6.293a.5.5,0,0,0,.709,0l6.311-6.275c.2-.193.13-.353-.145-.355L15.5,11.5V1.5a1,1,0,0,0-1-1h-5a1,1,0,0,0-1,1V11a.5.5,0,0,1-.5.5Z",
:fill "none",
:stroke "currentColor",
:stroke-linecap "round",
:stroke-linejoin "round"}]
[:path
{:d "M23.5,18.5v4a1,1,0,0,1-1,1H1.5a1,1,0,0,1-1-1v-4",
:fill "none",
:stroke "currentColor",
:stroke-linecap "round",
:stroke-linejoin "round"}]])
(def trash
[:svg
{:xmlns "http://www.w3.org/2000/svg", :viewBox "0 0 24 24"}
[:defs]
[:title "bin-1"]
[:path
{:d
"M21,4.5,19.188,21.709A2,2,0,0,1,17.2,23.5H6.8a2,2,0,0,1-1.989-1.791L3,4.5",
:fill "none",
:stroke "currentColor",
:stroke-linecap "round",
:stroke-linejoin "round"}]
[:line
{:x1 "0.5",
:y1 "4.5",
:x2 "23.5",
:y2 "4.5",
:fill "none",
:stroke "currentColor",
:stroke-linecap "round",
:stroke-linejoin "round"}]
[:path
{:d "M7.5,4.5v-3a1,1,0,0,1,1-1h7a1,1,0,0,1,1,1v3",
:fill "none",
:stroke "currentColor",
:stroke-linecap "round",
:stroke-linejoin "round"}]
[:line
{:x1 "12",
:y1 "9",
:x2 "12",
:y2 "19.5",
:fill "none",
:stroke "currentColor",
:stroke-linecap "round",
:stroke-linejoin "round"}]
[:line
{:x1 "16.5",
:y1 "9",
:x2 "16",
:y2 "19.5",
:fill "none",
:stroke "currentColor",
:stroke-linecap "round",
:stroke-linejoin "round"}]
[:line
{:x1 "7.5",
:y1 "9",
:x2 "8",
:y2 "19.5",
:fill "none",
:stroke "currentColor",
:stroke-linecap "round",
:stroke-linejoin "round"}]])

View File

@@ -2,7 +2,8 @@
(:require
[auto-ap.logging :as alog]
[config.core :refer [env]]
[hiccup2.core :as hiccup]))
[hiccup2.core :as hiccup]
[clojure.string :as str]))
(defn html-response [hiccup & {:keys [status headers] :or {status 200 headers {}}}]
{:status status
@@ -39,3 +40,24 @@
(ex-message e)]
:status 500)))))))
(defn form-data->map [form-data]
(reduce-kv
(fn [acc k v]
(cond (and (string? v)
(empty? v))
acc
:else
(assoc-in acc (->> (str/split k #"_")
(mapv #(apply keyword (str/split % #"/"))))
v)))
{}
form-data))
(defn path->name [k]
(cond (keyword? k)
(str (namespace k) "/" (name k))
(seq k)
(str/join "_" (map path->name k))
:else k))

View File

@@ -18,7 +18,11 @@
"/1099" :company-1099
"/1099/table" {:get :company-1099-vendor-table}
"/1099/vendor-dialog" {["/" [#"\d+" :vendor-id]] {:get :company-1099-vendor-dialog
:post :company-1099-vendor-save}}}})
:post :company-1099-vendor-save}}
"/reports" {"" {:get :company-reports
:delete :company-reports-delete}}
"/reports/table" :company-reports-table
}})
(def only-routes ["/" routes])