From e78c73e0936932fa2f65b2e68f27b942c1eb9978 Mon Sep 17 00:00:00 2001 From: Bryce Covert Date: Fri, 12 May 2023 12:27:48 -0700 Subject: [PATCH] Makes company 1099 page work better and faster --- resources/input.css | 3 + resources/public/js/htmx-disable.js | 16 ++ resources/public/output.css | 267 +++++++------------ src/clj/auto_ap/handler.clj | 11 +- src/clj/auto_ap/ssr/company/company_1099.clj | 213 ++++++++------- src/clj/auto_ap/ssr/company_dropdown.clj | 206 +++++++------- src/clj/auto_ap/ssr/components.clj | 84 ++++-- src/clj/auto_ap/ssr/components/dialog.clj | 43 ++- src/clj/auto_ap/ssr/components/navbar.clj | 34 ++- src/clj/auto_ap/ssr/components/page.clj | 19 +- src/clj/auto_ap/ssr/core.clj | 3 +- src/clj/auto_ap/ssr/search.clj | 44 +-- src/clj/auto_ap/ssr/svg.clj | 4 + src/clj/auto_ap/ssr/ui.clj | 14 +- src/cljc/auto_ap/ssr_routes.cljc | 2 +- 15 files changed, 519 insertions(+), 444 deletions(-) create mode 100644 resources/public/js/htmx-disable.js diff --git a/resources/input.css b/resources/input.css index cb8a07a5..9027f220 100644 --- a/resources/input.css +++ b/resources/input.css @@ -12,6 +12,9 @@ .htmx-added .slide-up { @apply translate-y-5 !important; } +.hidden .slide-up { + @apply translate-y-5 !important; +} .slide-up { @apply translate-y-0; } diff --git a/resources/public/js/htmx-disable.js b/resources/public/js/htmx-disable.js new file mode 100644 index 00000000..b1bbc156 --- /dev/null +++ b/resources/public/js/htmx-disable.js @@ -0,0 +1,16 @@ +htmx.defineExtension('disable-submit', { + onEvent: function (name, evt, data) { + let elt = evt.detail.elt; + let result = elt.querySelectorAll('.hx-disable'); + + if (name === 'htmx:beforeRequest') { + result.forEach(element => element.disabled = true); + if (elt.classList.contains('hx-disable')) { + elt.disabled = true;} + + } else if(name == 'htmx:afterRequest') { + result.forEach(element => element.disabled = false); + if (elt.classList.contains('hx-disable')) elt.disabled = false; + } + } +}) diff --git a/resources/public/output.css b/resources/public/output.css index 0d584506..ed0087b4 100644 --- a/resources/public/output.css +++ b/resources/public/output.css @@ -1149,10 +1149,6 @@ input:checked + .toggle-bg { margin-bottom: 1rem; } -.ml-0 { - margin-left: 0px; -} - .ml-1 { margin-left: 0.25rem; } @@ -1165,10 +1161,6 @@ input:checked + .toggle-bg { margin-left: 0.75rem; } -.ml-8 { - margin-left: 2rem; -} - .mr-16 { margin-right: 4rem; } @@ -1253,6 +1245,10 @@ input:checked + .toggle-bg { height: 1rem; } +.h-48 { + height: 12rem; +} + .h-5 { height: 1.25rem; } @@ -1269,6 +1265,10 @@ input:checked + .toggle-bg { height: 2rem; } +.h-96 { + height: 24rem; +} + .h-\[calc\(100\%-1rem\)\] { height: calc(100% - 1rem); } @@ -1281,30 +1281,6 @@ input:checked + .toggle-bg { height: 100vh; } -.h-2 { - height: 0.5rem; -} - -.h-48 { - height: 12rem; -} - -.h-64 { - height: 16rem; -} - -.h-80 { - height: 20rem; -} - -.h-1\/2 { - height: 50%; -} - -.h-96 { - height: 24rem; -} - .max-h-full { max-height: 100%; } @@ -1345,23 +1321,10 @@ input:checked + .toggle-bg { width: 2rem; } -.w-auto { - width: auto; -} - .w-full { width: 100%; } -.w-max { - width: -moz-max-content; - width: max-content; -} - -.w-2 { - width: 0.5rem; -} - .max-w-2xl { max-width: 42rem; } @@ -1510,16 +1473,12 @@ input:checked + .toggle-bg { justify-content: space-between; } -.gap-4 { - gap: 1rem; -} - .gap-2 { gap: 0.5rem; } -.gap-6 { - gap: 1.5rem; +.gap-4 { + gap: 1rem; } .gap-8 { @@ -1568,18 +1527,6 @@ input:checked + .toggle-bg { margin-bottom: calc(1.5rem * var(--tw-space-y-reverse)); } -.space-y-4 > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(1rem * var(--tw-space-y-reverse)); -} - -.space-y-8 > :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(2rem * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(2rem * var(--tw-space-y-reverse)); -} - .divide-y > :not([hidden]) ~ :not([hidden]) { --tw-divide-y-reverse: 0; border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse))); @@ -1633,10 +1580,6 @@ input:checked + .toggle-bg { border-radius: 0.5rem; } -.rounded-xl { - border-radius: 0.75rem; -} - .rounded-l-lg { border-top-left-radius: 0.5rem; border-bottom-left-radius: 0.5rem; @@ -1746,6 +1689,11 @@ input:checked + .toggle-bg { background-color: rgb(17 24 39 / var(--tw-bg-opacity)); } +.bg-green-100 { + --tw-bg-opacity: 1; + background-color: rgb(228 240 213 / var(--tw-bg-opacity)); +} + .bg-green-500 { --tw-bg-opacity: 1; background-color: rgb(121 181 46 / var(--tw-bg-opacity)); @@ -1756,16 +1704,6 @@ input:checked + .toggle-bg { background-color: rgb(242 248 234 / var(--tw-bg-opacity)); } -.bg-red-700 { - --tw-bg-opacity: 1; - background-color: rgb(153 2 2 / var(--tw-bg-opacity)); -} - -.bg-slate-300 { - --tw-bg-opacity: 1; - background-color: rgb(203 213 225 / var(--tw-bg-opacity)); -} - .bg-white { --tw-bg-opacity: 1; background-color: rgb(255 255 255 / var(--tw-bg-opacity)); @@ -1780,21 +1718,6 @@ input:checked + .toggle-bg { background-color: rgb(253 246 178 / var(--tw-bg-opacity)); } -.bg-blue-200 { - --tw-bg-opacity: 1; - background-color: rgb(153 215 247 / var(--tw-bg-opacity)); -} - -.bg-blue-300 { - --tw-bg-opacity: 1; - background-color: rgb(102 196 242 / var(--tw-bg-opacity)); -} - -.bg-green-100 { - --tw-bg-opacity: 1; - background-color: rgb(228 240 213 / var(--tw-bg-opacity)); -} - .bg-opacity-50 { --tw-bg-opacity: 0.5; } @@ -1823,14 +1746,6 @@ input:checked + .toggle-bg { padding: 1.5rem; } -.p-8 { - padding: 2rem; -} - -.p-5 { - padding: 1.25rem; -} - .px-2 { padding-left: 0.5rem; padding-right: 0.5rem; @@ -1866,11 +1781,6 @@ input:checked + .toggle-bg { padding-bottom: 0.25rem; } -.py-1\.5 { - padding-top: 0.375rem; - padding-bottom: 0.375rem; -} - .py-2 { padding-top: 0.5rem; padding-bottom: 0.5rem; @@ -1896,6 +1806,14 @@ input:checked + .toggle-bg { padding-bottom: 1.25rem; } +.pb-2 { + padding-bottom: 0.5rem; +} + +.pb-3 { + padding-bottom: 0.75rem; +} + .pl-10 { padding-left: 2.5rem; } @@ -1904,6 +1822,10 @@ input:checked + .toggle-bg { padding-left: 2.75rem; } +.pl-2 { + padding-left: 0.5rem; +} + .pl-3 { padding-left: 0.75rem; } @@ -1912,6 +1834,14 @@ input:checked + .toggle-bg { padding-left: 1rem; } +.pr-2 { + padding-right: 0.5rem; +} + +.pr-2\.5 { + padding-right: 0.625rem; +} + .pt-16 { padding-top: 4rem; } @@ -1924,18 +1854,6 @@ input:checked + .toggle-bg { padding-top: 1.25rem; } -.pb-2 { - padding-bottom: 0.5rem; -} - -.pr-2 { - padding-right: 0.5rem; -} - -.pr-2\.5 { - padding-right: 0.625rem; -} - .text-left { text-align: left; } @@ -1978,10 +1896,6 @@ input:checked + .toggle-bg { font-weight: 700; } -.font-light { - font-weight: 300; -} - .font-medium { font-weight: 500; } @@ -2054,6 +1968,11 @@ input:checked + .toggle-bg { color: rgb(17 24 39 / var(--tw-text-opacity)); } +.text-green-800 { + --tw-text-opacity: 1; + color: rgb(48 72 18 / var(--tw-text-opacity)); +} + .text-primary-600 { --tw-text-opacity: 1; color: rgb(97 145 37 / var(--tw-text-opacity)); @@ -2064,26 +1983,11 @@ input:checked + .toggle-bg { color: rgb(255 255 255 / var(--tw-text-opacity)); } -.text-yellow-400 { - --tw-text-opacity: 1; - color: rgb(227 160 8 / var(--tw-text-opacity)); -} - .text-yellow-800 { --tw-text-opacity: 1; color: rgb(114 59 19 / var(--tw-text-opacity)); } -.text-gray-200 { - --tw-text-opacity: 1; - color: rgb(229 231 235 / var(--tw-text-opacity)); -} - -.text-green-800 { - --tw-text-opacity: 1; - color: rgb(48 72 18 / var(--tw-text-opacity)); -} - .opacity-0 { opacity: 0; } @@ -2169,6 +2073,10 @@ input:checked + .toggle-bg { transition-duration: 300ms; } +.duration-500 { + transition-duration: 500ms; +} + .duration-75 { transition-duration: 75ms; } @@ -2194,6 +2102,11 @@ input:checked + .toggle-bg { transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)) !important; } +.hidden .slide-up { + --tw-translate-y: 1.25rem !important; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)) !important; +} + .slide-up { --tw-translate-y: 0px; transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); @@ -2296,6 +2209,16 @@ input:checked + .toggle-bg { background-color: rgb(243 244 246 / var(--tw-bg-opacity)); } +.hover\:bg-gray-200:hover { + --tw-bg-opacity: 1; + background-color: rgb(229 231 235 / var(--tw-bg-opacity)); +} + +.hover\:bg-green-100:hover { + --tw-bg-opacity: 1; + background-color: rgb(228 240 213 / var(--tw-bg-opacity)); +} + .hover\:bg-green-600:hover { --tw-bg-opacity: 1; background-color: rgb(97 145 37 / var(--tw-bg-opacity)); @@ -2316,11 +2239,6 @@ input:checked + .toggle-bg { background-color: rgb(255 255 255 / var(--tw-bg-opacity)); } -.hover\:bg-gray-200:hover { - --tw-bg-opacity: 1; - background-color: rgb(229 231 235 / var(--tw-bg-opacity)); -} - .hover\:text-blue-600:hover { --tw-text-opacity: 1; color: rgb(0 125 187 / var(--tw-text-opacity)); @@ -2446,6 +2364,11 @@ input:checked + .toggle-bg { border-color: rgb(0 156 234 / var(--tw-border-opacity)); } +.dark .dark\:border-gray-500 { + --tw-border-opacity: 1; + border-color: rgb(107 114 128 / var(--tw-border-opacity)); +} + .dark .dark\:border-gray-600 { --tw-border-opacity: 1; border-color: rgb(75 85 99 / var(--tw-border-opacity)); @@ -2504,21 +2427,16 @@ input:checked + .toggle-bg { background-color: rgb(97 145 37 / var(--tw-bg-opacity)); } -.dark .dark\:bg-yellow-900 { - --tw-bg-opacity: 1; - background-color: rgb(99 49 18 / var(--tw-bg-opacity)); -} - -.dark .dark\:bg-blue-700 { - --tw-bg-opacity: 1; - background-color: rgb(0 94 140 / var(--tw-bg-opacity)); -} - .dark .dark\:bg-green-900 { --tw-bg-opacity: 1; background-color: rgb(24 36 9 / var(--tw-bg-opacity)); } +.dark .dark\:bg-yellow-900 { + --tw-bg-opacity: 1; + background-color: rgb(99 49 18 / var(--tw-bg-opacity)); +} + .dark .dark\:bg-opacity-80 { --tw-bg-opacity: 0.8; } @@ -2538,6 +2456,11 @@ input:checked + .toggle-bg { color: rgb(243 244 246 / var(--tw-text-opacity)); } +.dark .dark\:text-gray-200 { + --tw-text-opacity: 1; + color: rgb(229 231 235 / var(--tw-text-opacity)); +} + .dark .dark\:text-gray-300 { --tw-text-opacity: 1; color: rgb(209 213 219 / var(--tw-text-opacity)); @@ -2548,6 +2471,16 @@ input:checked + .toggle-bg { color: rgb(156 163 175 / var(--tw-text-opacity)); } +.dark .dark\:text-gray-50 { + --tw-text-opacity: 1; + color: rgb(249 250 251 / var(--tw-text-opacity)); +} + +.dark .dark\:text-green-300 { + --tw-text-opacity: 1; + color: rgb(175 211 130 / var(--tw-text-opacity)); +} + .dark .dark\:text-white { --tw-text-opacity: 1; color: rgb(255 255 255 / var(--tw-text-opacity)); @@ -2558,16 +2491,6 @@ input:checked + .toggle-bg { color: rgb(250 202 21 / var(--tw-text-opacity)); } -.dark .dark\:text-green-300 { - --tw-text-opacity: 1; - color: rgb(175 211 130 / var(--tw-text-opacity)); -} - -.dark .dark\:text-gray-50 { - --tw-text-opacity: 1; - color: rgb(249 250 251 / var(--tw-text-opacity)); -} - .dark .dark\:placeholder-gray-400::-moz-placeholder { --tw-placeholder-opacity: 1; color: rgb(156 163 175 / var(--tw-placeholder-opacity)); @@ -2602,6 +2525,11 @@ input:checked + .toggle-bg { background-color: rgb(31 41 55 / var(--tw-bg-opacity)); } +.dark .dark\:hover\:bg-green-600:hover { + --tw-bg-opacity: 1; + background-color: rgb(97 145 37 / var(--tw-bg-opacity)); +} + .dark .dark\:hover\:bg-green-700:hover { --tw-bg-opacity: 1; background-color: rgb(73 109 28 / var(--tw-bg-opacity)); @@ -2642,6 +2570,11 @@ input:checked + .toggle-bg { --tw-ring-color: rgb(0 156 234 / var(--tw-ring-opacity)); } +.dark .dark\:focus\:ring-blue-800:focus { + --tw-ring-opacity: 1; + --tw-ring-color: rgb(0 62 94 / var(--tw-ring-opacity)); +} + .dark .dark\:focus\:ring-gray-600:focus { --tw-ring-opacity: 1; --tw-ring-color: rgb(75 85 99 / var(--tw-ring-opacity)); @@ -2677,10 +2610,6 @@ input:checked + .toggle-bg { display: block; } - .sm\:flex { - display: flex; - } - .sm\:rounded-lg { border-radius: 0.5rem; } @@ -2742,10 +2671,6 @@ input:checked + .toggle-bg { display: block; } - .lg\:inline { - display: inline; - } - .lg\:flex { display: flex; } @@ -2804,10 +2729,6 @@ input:checked + .toggle-bg { padding-left: 0.75rem; } - .lg\:pl-3\.5 { - padding-left: 0.875rem; - } - .lg\:pl-64 { padding-left: 16rem; } diff --git a/src/clj/auto_ap/handler.clj b/src/clj/auto_ap/handler.clj index 54643aa9..da43b0df 100644 --- a/src/clj/auto_ap/handler.clj +++ b/src/clj/auto_ap/handler.clj @@ -30,7 +30,8 @@ [ring.util.response :as response] [unilog.context :as lc] [clj-time.coerce :as coerce] - [clj-time.core :as time])) + [clj-time.core :as time] + [cemerick.url :as url])) (when (:aws-access-key-id env) (defcredential (:aws-access-key-id env) (:aws-secret-access-key env) (:aws-region env))) @@ -161,9 +162,17 @@ (let [end-time (time/plus (time/now) (time/days 14))] (assoc response :session (assoc session ::idle-timeout (coerce/to-date end-time))))))))))) +(defn wrap-hx-current-url-params + [handler ] + (fn [request] + (let [query-params (some-> (get-in request [:headers "hx-current-url"]) (url/url ) :query) + request (assoc request :hx-query-params query-params)] + (handler request)))) + #_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]} (def app (-> route-handler + (wrap-hx-current-url-params) (wrap-guess-route) (wrap-authorization auth-backend ) diff --git a/src/clj/auto_ap/ssr/company/company_1099.clj b/src/clj/auto_ap/ssr/company/company_1099.clj index b4b81d2b..e487cde3 100644 --- a/src/clj/auto_ap/ssr/company/company_1099.clj +++ b/src/clj/auto_ap/ssr/company/company_1099.clj @@ -121,12 +121,19 @@ (->> results (filter (fn [[_ _ a]] (>= (or a 0.0) 600.0))) - (take 200) (sort-by (fn [[client _ amount]] - [(:client/code client ) amount]))))) + [(:client/code client ) amount])) + (into [])))) -(defn table* [{:keys [identity session]} & {:keys [flash-id]}] - (let [companies (take 30 (get-1099-companies identity session))] +(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 )) + 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))] [:div#vendor-table {:hx-get (bidi/path-for ssr-routes/only-routes :company-1099-vendor-table :request-method :get) @@ -210,10 +217,20 @@ (com/icon-button {:hx-get (bidi/path-for ssr-routes/only-routes :company-1099-vendor-dialog :vendor-id (:db/id vendor)) - :hx-target "#modal-content" + :hx-target "#modal-holder" :hx-swap "outerHTML"} svg/pencil))))))] - (com/paginator))])) + (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 @@ -246,7 +263,7 @@ (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" "closeDialog"})) + :headers {"hx-trigger" "closeModal"})) @@ -254,104 +271,102 @@ (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 (html-response - [:form {:hx-post (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" - :hx-swap "outerHTML swap:300ms"} - (com/dialog - [: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])] - :headers {"hx-trigger" "openDialog"}))) + (com/modal {} + [: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" + :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 (com/page - {:nav - (com/company-aside-nav)} - (com/breadcrumbs {} - [:a {:href "#"} "My Company"] - [:a {:href "#"} "1099 Vendor Info"]) - (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)} + (com/page {:nav (com/company-aside-nav) + :active-client (:client (:session request)) + :identity (:identity request)} (com/breadcrumbs {} [:a {:href "#"} "My Company"] [:a {:href "#"} "1099 Vendor Info"]) diff --git a/src/clj/auto_ap/ssr/company_dropdown.clj b/src/clj/auto_ap/ssr/company_dropdown.clj index 4f73a8ea..8e60eaba 100644 --- a/src/clj/auto_ap/ssr/company_dropdown.clj +++ b/src/clj/auto_ap/ssr/company_dropdown.clj @@ -2,103 +2,130 @@ (:require [auto-ap.datomic :refer [conn]] [auto-ap.graphql.utils :refer [assert-can-see-client]] - [iol-ion.query :refer [can-see-client?]] [auto-ap.ssr-routes :as ssr-routes] - [auto-ap.ssr.components.navbar-dropdown :refer [navbar-dropdown]] + [auto-ap.ssr.svg :as svg] [auto-ap.ssr.utils :refer [html-response]] [bidi.bidi :as bidi] [datomic.api :as dc] - [hiccup2.core :as hiccup])) + [hiccup2.core :as hiccup] + [iol-ion.query :refer [can-see-client?]])) -(defn dropdown-contents [{:keys [identity]}] - (let [options (->> (dc/q '[:find ?c ?n - :in $ ?user - :where [?c :client/name ?n] - [(iol-ion.query/can-see-client? ?user ?c)]] - (dc/db conn) - identity) - (map (fn [[k v]] - {"key" k - "value" v})))] - (html-response - [:div.navbar-dropdown {:style {:width "20em"}} - [:a.navbar-item {:hx-put (bidi/path-for ssr-routes/only-routes - :active-client - :request-method :put) - :hx-target "#company-dropdown" - :hx-swap "outerHTML" - :hx-trigger "click"} - "All"] - [:hr.navbar-divider] - [:input#company-search.input.navbar-item {:placeholder "Company name" - :name "search-text" - :autoFocus true} ] - [:input#company-search-value {:type "hidden" +(defn dropdown-search-results* [{:keys [options]}] + [:ul + (for [option 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")]]])]) + +(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 ""))) + +(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}))) + :client (:client (:session request))}))) + +(defn dropdown [{:keys [client]}] + [:div#company-dropdown + {:hx-put (bidi/path-for ssr-routes/only-routes + :active-client + :request-method :put) + :hx-target "#company-dropdown" + :hx-include "#company-search-value" + :hx-swap "outerHTML" + :hx-trigger "selected"} + [:script + (hiccup/raw + "localStorage.setItem(\"last-client-id\", \""(:db/id client)"\")")] + [:div + [:button#company-dropdown-button { :class "text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-4 py-2.5 text-center inline-flex items-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800" + :type "button"} + (if client + (:client/name client) + "All Companies") + [:div.w-4.h-4.ml-2 + svg/drop-down]] + [:div#company-dropdown-list.hidden {"_" (hiccup/raw "init call initCompanyDropdown()") + } + [:div {:class "z-10 bg-white rounded-lg shadow w-64 dark:bg-gray-700 slide-up duration-500 transition-all"} + [:div {:class "p-3"} + [:label {:for "input-group-search", :class "sr-only"} "Search"] + [:div {:class "relative"} + [:div {:class "absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none"} + [:div.w-5.h-5.text-gray-500.dark:text-gray-400 + svg/search]] + [:input#company-search {:placeholder "Company name" + :name "search-text" + :class "block w-full p-2 pl-10 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" + :autoFocus true + :tab-index -1 + :hx-trigger "keyup changed delay:500ms, search" + :hx-get (bidi/path-for ssr-routes/only-routes + :company-dropdown-search-results) + :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"} ] - [:script - (hiccup/raw - (str " -var z = new autoComplete({ - selector:\"#company-search\", - placeholder: \"Company Name....\", - data: { - keys: [\"value\"], - src: " (cheshire.core/encode - options) + :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"} ]] + [: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"} - " - }, - resultItem: { - highlight:true, - class: \"autocomplete-suggestion\", - selected: \"highlighted\" + [:button {:class "w-full py-2 ml-2 text-sm font-medium text-gray-900 rounded dark:text-gray-300" + :hx-put (bidi/path-for ssr-routes/only-routes + :active-client + :request-method :put) + :hx-target "#company-dropdown" + :hx-swap "outerHTML" + :hx-trigger "click"} + "All"]]] + ]] + [:script {:lang "text/javascript"} + (hiccup/raw + " +function initCompanyDropdown() { + var $dropdownTargetEl = document.getElementById('company-dropdown-list'); - }, - resultsList: { - tabSelect: true - }, - submit: true + // set the element that trigger the dropdown menu on click + var $dropdownTriggerEl = document.getElementById('company-dropdown-button'); -}); -z.input.addEventListener(\"selection\", function (event) { - z.input.blur(); - z.input.value = event.detail.selection.value.value; -document.getElementById(\"company-search-value\").value= event.detail.selection.value.key; -document.getElementById(\"company-search-value\").dispatchEvent(new Event('change')); - -}); -"))]]))) - -(defn dropdown [request] - - (let [client (get-in request [:session :client])] - - (navbar-dropdown - "company-dropdown" - [:span - (if client - (str "Company: " (:client/name client)) - "Company") - [:script - (hiccup/raw - "localStorage.setItem(\"last-client-id\", \""(:db/id client)"\")")]] - [:div {:hx-get - (bidi/path-for ssr-routes/only-routes - :company-dropdown-contents) - :hx-swap "outerHTML" - :hx-trigger "intersect delay:150ms" - :hx-target "closest .navbar-dropdown" - :style {:width "20em" - :height "80px"} - } - [:div.loader.is-loading.is-active.is-centered]]))) + var dropdownOptions = { + placement: 'bottom', + triggerType: 'click', + offsetSkidding: 0, + offsetDistance: 10, + delay: 300, + onHide: () => { + }, + onShow: () => { + document.getElementById('company-search').focus() + }, + onToggle: () => { + } + }; + var companyDrowdown = new Dropdown($dropdownTargetEl, $dropdownTriggerEl, dropdownOptions); +} +")]]]) (defn active-client [{:keys [identity params] :as request}] (let [client-id (some-> (or (:search-client params) (get params "search-client")) not-empty Long/parseLong)] @@ -109,7 +136,8 @@ document.getElementById(\"company-search-value\").dispatchEvent(new Event('chang (dc/pull (dc/db conn) [:db/id :client/name :client/code] client-id)))] (assoc (html-response - (dropdown (assoc request :session new-session))) + (dropdown {:client (:client new-session) + :identity identity})) :session new-session :headers diff --git a/src/clj/auto_ap/ssr/components.clj b/src/clj/auto_ap/ssr/components.clj index f4a7777f..f7d3fae5 100644 --- a/src/clj/auto_ap/ssr/components.clj +++ b/src/clj/auto_ap/ssr/components.clj @@ -15,7 +15,8 @@ (def button buttons/button-) (def button-icon buttons/button-icon-) (def icon-button buttons/icon-button-) -(def dialog dialog/dialog-) +(def modal dialog/modal-) +(def modal-card dialog/modal-card-) (def text-input inputs/text-input-) (def select inputs/select-) @@ -43,29 +44,62 @@ :class (str "font-medium text-blue-600 dark:text-blue-500 hover:underline " class)}] children)) -(defn 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- [{: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"} "1-10"] - [:span {:class "font-semibold text-gray-900 dark:text-white"} "1000"]] - [:ul {:class "inline-flex items-stretch -space-x-px"} - [:li - [:a {:href "#", :class "flex items-center justify-center h-full py-1.5 px-3 ml-0 text-gray-500 bg-white rounded-l-lg border 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"} - [:span {:class "sr-only"} "Previous"] - [:svg {:class "w-5 h-5", :aria-hidden "true", :fill "currentColor", :viewbox "0 0 20 20", :xmlns "http://www.w3.org/2000/svg"} - [:path {:fill-rule "evenodd", :d "M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z", :clip-rule "evenodd"}]]]] - [:li - [:a {:href "#", :class "flex items-center justify-center px-3 py-2 text-sm leading-tight text-gray-500 bg-white border 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"} "1"]] - [:li - [:a {:href "#", :class "flex items-center justify-center px-3 py-2 text-sm leading-tight text-gray-500 bg-white border 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"} "2"]] - [:li - [:a {:href "#", :aria-current "page", :class "z-10 flex items-center justify-center px-3 py-2 text-sm leading-tight border 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"} "3"]] - [:li - [:a {:href "#", :class "flex items-center justify-center px-3 py-2 text-sm leading-tight text-gray-500 bg-white border 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"} "..."]] - [:li - [:a {:href "#", :class "flex items-center justify-center px-3 py-2 text-sm leading-tight text-gray-500 bg-white border 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"} "100"]] - [:li - [:a {:href "#", :class "flex items-center justify-center h-full py-1.5 px-3 leading-tight text-gray-500 bg-white rounded-r-lg border 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"} - [:span {:class "sr-only"} "Next"] - [:svg {:class "w-5 h-5", :aria-hidden "true", :fill "currentColor", :viewbox "0 0 20 20", :xmlns "http://www.w3.org/2000/svg"} - [:path {:fill-rule "evenodd", :d "M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z", :clip-rule "evenodd"}]]]]]]) + [:span {:class "font-semibold text-gray-900 dark:text-white"} (str (inc start)) "-" (str end) " of " (str total)]] + (paginator- params)]) diff --git a/src/clj/auto_ap/ssr/components/dialog.clj b/src/clj/auto_ap/ssr/components/dialog.clj index ed56d0b8..dc8e9635 100644 --- a/src/clj/auto_ap/ssr/components/dialog.clj +++ b/src/clj/auto_ap/ssr/components/dialog.clj @@ -1,9 +1,44 @@ -(ns auto-ap.ssr.components.dialog) +(ns auto-ap.ssr.components.dialog + (:require [hiccup2.core :as hiccup])) -(defn dialog- [header content footer] - [:div#modal-content +(defn modal- [params & children] + [:div + [:div#modal-holder { :tabindex "-1", :class "fixed top-0 left-0 right-0 z-50 w-full p-4 overflow-x-hidden overflow-y-auto md:inset-0 h-[calc(100%-1rem)] max-h-full flex justify-center hidden" :aria-hidden true + "_" (hiccup/raw "on closeModal transition <#modal-holder .modal-content /> opacity to 0.0 over 300ms then call hideModal()")} + [:div {:class "relative w-full max-w-2xl max-h-full"} + (into [:div#modal-content] + children)] + ] + [:script {:lang "text/javascript"} + (hiccup/raw " + var modal_element = document.getElementById('modal-holder'); + var modal_options = { + placement: 'center', + backdrop: 'dynamic', + backdropClasess: 'bg-gray-900 bg-opacity-50 dark:bg-opacity-80 fixed inset-0 z-40', + closable: true, + onOpen: function() { + modal_element.dispatchEvent('openModal'); + + }, + onHide: function() { + modal_element.outerHTML='
'; + }, + }; + var curModal = new Modal(modal_element, modal_options); +curModal.show(); +function hideModal() { +curModal.hide(); +} +") + + ]]) + +(defn modal-card- [params header content footer] + [:div#modal-card [:div {:class "relative bg-white rounded-lg shadow dark:bg-gray-700 dark:text-white fade-in slide-up duration-300 transition-all modal-content"} [:div {:class "flex items-start justify-between p-4 border-b rounded-t dark:border-gray-600"} header] [:div {:class "p-6 space-y-6"} content] - [:div footer]]]) + [:div footer]] + ]) diff --git a/src/clj/auto_ap/ssr/components/navbar.clj b/src/clj/auto_ap/ssr/components/navbar.clj index 964924b6..21024527 100644 --- a/src/clj/auto_ap/ssr/components/navbar.clj +++ b/src/clj/auto_ap/ssr/components/navbar.clj @@ -3,9 +3,10 @@ [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.ssr.company-dropdown :as cd])) -(defn navbar- [] +(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"} [:div {:class "px-3 py-3 lg:px-5 lg:pl-3"} [:div {:class "flex items-center justify-between"} @@ -17,36 +18,31 @@ [:a {:href "https://flowbite-admin-dashboard.vercel.app/", :class "flex ml-2 md:mr-24"} [:img {:src "/img/logo-big2.png", :class "h-10 mr-16", :alt "Integreat logo"}] ] - [:button.mt-1.lg:w-96.relative.hidden.lg:block {:class "bg-gray-50 hover:bg-gray-200 dark:hover:bg-gray-700 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-primary-500 focus:border-primary-500 w-full pl-10 py-4 pr-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500 gap-4 " - :hx-get (bidi/path-for ssr-routes/only-routes - :search) - :hx-target "#modal-content" - :hx-swap "innerHTML"} + ] + + [:div {:class "flex items-center gap-4"} + [:button.mt-1.lg:w-96.relative.hidden.lg:block {:class "bg-gray-50 hover:bg-gray-200 dark:hover:bg-gray-700 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-primary-500 focus:border-primary-500 w-full pl-10 py-4 pr-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500 gap-4 " + :hx-get (bidi/path-for ssr-routes/only-routes + :search) + :hx-target "#modal-holder" + :hx-swap "outerHTML"} [:div {:class "absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none text-gray-500"} [:div.w-4.h-4 svg/search] - [:span.ml-2 "Search"]]]] - - [:div {:class "flex items-center gap-4"} + [:span.ml-2 "Search"]]] [:div {:class "hidden mr-3 -mb-1 sm:block"} [:span]] (icon-button- {:id "toggleSidebarMobileSearch", :type "button", :class "p-2 text-gray-500 rounded-lg lg:hidden hover:text-gray-900 hover:bg-gray-100 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white" :hx-get (bidi/path-for ssr-routes/only-routes :search) - :hx-target "#modal-content" - :hx-swap "innerHTML"} + :hx-target "#modal-holder" + :hx-swap "outerHTML"} svg/search) #_[:button [:div.w-4.h-4 svg/search]] + (cd/dropdown {:client client :identity identity}) - - #_[:button {:type "button", :data-dropdown-toggle "apps-dropdown", :class "hidden p-2 text-gray-500 rounded-lg sm:flex hover:text-gray-900 hover:bg-gray-100 dark:text-gray-400 dark:hover:text-white dark:hover:bg-gray-700"} - [:span {:class "sr-only"} "View notifications"] - [:svg {:class "w-6 h-6", :fill "currentColor", :viewbox "0 0 20 20", :xmlns "http://www.w3.org/2000/svg"} - [:path {:d "M5 3a2 2 0 00-2 2v2a2 2 0 002 2h2a2 2 0 002-2V5a2 2 0 00-2-2H5zM5 11a2 2 0 00-2 2v2a2 2 0 002 2h2a2 2 0 002-2v-2a2 2 0 00-2-2H5zM11 5a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V5zM11 13a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z"}]]] - #_(com/icon-button {} - [:div.w-4.h-4 svg/search]) [: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"} diff --git a/src/clj/auto_ap/ssr/components/page.clj b/src/clj/auto_ap/ssr/components/page.clj index c1037992..1f312c4d 100644 --- a/src/clj/auto_ap/ssr/components/page.clj +++ b/src/clj/auto_ap/ssr/components/page.clj @@ -3,9 +3,10 @@ [auto-ap.ssr.components.aside :refer [left-aside-]] [hiccup2.core :as hiccup])) -(defn page- [{:keys [nav page-specific]} & children] +(defn page- [{:keys [nav page-specific active-client identity]} & children] [:div#app - (navbar-) + (navbar- {:client active-client + :identity identity}) [:div.flex.pt-16.overflow-hidden (left-aside- {:nav nav :page-specific page-specific}) @@ -14,11 +15,13 @@ (into [:div.p-4] children)]] - [:div#modal-holder.hidden + + + + + #_[:div#modal-holder.hidden {"_" (hiccup/raw "on click trigger closeDialog")} - [:div { :tabindex "-1", :class "fixed top-0 left-0 right-0 z-50 w-full p-4 overflow-x-hidden overflow-y-auto md:inset-0 h-[calc(100%-1rem)] max-h-full flex justify-center "} - [:div {:class "relative w-full max-w-2xl max-h-full" - "_" (hiccup/raw "on click halt the event")} - [:div#modal-content ]]] + [:div {:class "bg-gray-900 bg-opacity-50 dark:bg-opacity-80 fixed inset-0 z-40" - }]]]) + }]] + [:div#modal-holder]]) diff --git a/src/clj/auto_ap/ssr/core.clj b/src/clj/auto_ap/ssr/core.clj index e5d344d4..32f1277b 100644 --- a/src/clj/auto_ap/ssr/core.clj +++ b/src/clj/auto_ap/ssr/core.clj @@ -18,7 +18,8 @@ :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))) :active-client (wrap-client-redirect-unauthenticated (wrap-secure (wrap-admin company-dropdown/active-client))) - :company-dropdown-contents (wrap-client-redirect-unauthenticated (wrap-secure company-dropdown/dropdown-contents)) + :company-dropdown-search-results + (wrap-client-redirect-unauthenticated (wrap-secure company-dropdown/dropdown-search-results)) :company-1099 (wrap-client-redirect-unauthenticated (wrap-secure company-1099/page)) :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)) diff --git a/src/clj/auto_ap/ssr/search.clj b/src/clj/auto_ap/ssr/search.clj index f50e34ee..cf8ea376 100644 --- a/src/clj/auto_ap/ssr/search.clj +++ b/src/clj/auto_ap/ssr/search.clj @@ -130,26 +130,26 @@ (if-let [q (get (:form-params request) "q")] (html-response (search-results* q (:identity request))) (html-response - (com/dialog - [:div.p-2 "Search"] - [:div#search.overflow-auto.space-y-6.p-2.h-96 - - (com/text-input {:id "search-input" - :type "search" - :placeholder "5/5/2034 Magheritas" - :name "q" - :hx-post "/search" - :hx-trigger "keyup changed delay:300ms, search" - :hx-target "#search-results" - :hx-indicator "#search" - :value (:q (:params request)) - :autofocus true}) - [:i.text-sm.text-gray-600.dark:text-gray-50 "Try dates, numbers, vendors. To filter to specific type, use 'invoice', 'transaction', 'journal-entry', 'payment'."] - #_[:style - ".htmx-request #search-results {display: none} .htmx-request .htmx-indicator { display: block !important; }"] - [:div#search-results - ] - [:div.loader.is-loading.big.htmx-indicator ]] - nil) - :headers {"hx-trigger" "openDialog"}))) + (com/modal {} + (com/modal-card {} + [:div.p-2 "Search"] + [:div#search.overflow-auto.space-y-6.p-2.h-96 + + (com/text-input {:id "search-input" + :type "search" + :placeholder "5/5/2034 Magheritas" + :name "q" + :hx-post "/search" + :hx-trigger "keyup changed delay:300ms, search" + :hx-target "#search-results" + :hx-indicator "#search" + :value (:q (:params request)) + :autofocus true}) + [:i.text-sm.text-gray-600.dark:text-gray-50 "Try dates, numbers, vendors. To filter to specific type, use 'invoice', 'transaction', 'journal-entry', 'payment'."] + #_[:style + ".htmx-request #search-results {display: none} .htmx-request .htmx-indicator { display: block !important; }"] + [:div#search-results + ] + [:div.loader.is-loading.big.htmx-indicator ]] + nil))))) diff --git a/src/clj/auto_ap/ssr/svg.clj b/src/clj/auto_ap/ssr/svg.clj index 9cd986b7..be39d49b 100644 --- a/src/clj/auto_ap/ssr/svg.clj +++ b/src/clj/auto_ap/ssr/svg.clj @@ -193,3 +193,7 @@ :stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round"}]]) + +(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"}]]) diff --git a/src/clj/auto_ap/ssr/ui.clj b/src/clj/auto_ap/ssr/ui.clj index 619fc952..bf96c347 100644 --- a/src/clj/auto_ap/ssr/ui.clj +++ b/src/clj/auto_ap/ssr/ui.clj @@ -29,11 +29,12 @@ :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 {"_" (hiccup/raw "on closeDialog transition <#modal-holder .modal-content /> opacity to 0.0 over 300ms then add .hidden to <#modal-holder /> on openDialog remove .hidden from #modal-holder")} + [:body {:hx-ext "disable-submit"} contents [:script {:src "/js/flowbite.min.js"}] [:script {:lang "text/javascript"} @@ -53,4 +54,13 @@ } }; - const collapse = new Collapse($targetEl, $triggerEl, options); ")]]])) + const collapse = new Collapse($targetEl, $triggerEl, options); + + + ; + +" + + + + )]]])) diff --git a/src/cljc/auto_ap/ssr_routes.cljc b/src/cljc/auto_ap/ssr_routes.cljc index 9f4cdfba..07aa8b7d 100644 --- a/src/cljc/auto_ap/ssr_routes.cljc +++ b/src/cljc/auto_ap/ssr_routes.cljc @@ -13,7 +13,7 @@ ["/approve/" [#"\d+" :transaction-id]] {:post :transaction-insight-approve} ["/rows/" [#"\d+" :after]] {:get :transaction-insight-rows} ["/explain/" [#"\d+" :transaction-id]] {:get :transaction-insight-explain}}} - "company" {"/dropdown" :company-dropdown-contents + "company" {"/dropdown" :company-dropdown-search-results "/active" {:put :active-client} "/1099" :company-1099 "/1099/table" {:get :company-1099-vendor-table}