From 9e9eda343ac1cbf5f7aaeda515a36cb690ded9e8 Mon Sep 17 00:00:00 2001 From: Bryce Date: Mon, 11 Mar 2024 08:20:33 -0700 Subject: [PATCH] progress on pay button. --- src/clj/auto_ap/ssr/components/buttons.clj | 3 +- src/clj/auto_ap/ssr/grid_page_helper.clj | 21 +++-- src/clj/auto_ap/ssr/invoices.clj | 104 +++++++++++++++++---- src/clj/auto_ap/ssr/utils.clj | 15 +-- src/cljc/auto_ap/routes/invoice.cljc | 1 + 5 files changed, 109 insertions(+), 35 deletions(-) diff --git a/src/clj/auto_ap/ssr/components/buttons.clj b/src/clj/auto_ap/ssr/components/buttons.clj index 93171657..b53ba26d 100644 --- a/src/clj/auto_ap/ssr/components/buttons.clj +++ b/src/clj/auto_ap/ssr/components/buttons.clj @@ -98,7 +98,8 @@ (str " bg-white dark:bg-gray-600 border-gray-300 dark:border-gray-700 text-gray-500 hover:text-gray-800 dark:text-gray-400 dark:hover:text-gray-100 font-medium border border-gray-300 dark:border-gray-700"))) [:div.htmx-indicator.flex.items-center (svg/spinner {:class "inline w-4 h-4 text-white"}) - [:div.ml-3 "Loading..."]] + (when (not (:minimal-loading? params)) + [:div.ml-3 "Loading..."])] (into [:div.htmx-indicator-hidden.inline-flex.gap-2.items-center.justify-center] children)]) (defn a-button- [params & children] diff --git a/src/clj/auto_ap/ssr/grid_page_helper.clj b/src/clj/auto_ap/ssr/grid_page_helper.clj index 37567129..7283cf5c 100644 --- a/src/clj/auto_ap/ssr/grid_page_helper.clj +++ b/src/clj/auto_ap/ssr/grid_page_helper.clj @@ -215,13 +215,14 @@ identity request) :headers {"hx-push-url" (str "?" (url/map->query - (if (:query-schema grid-spec) - (update (filter-vals #(not (nil? %)) - (m/encode (:query-schema grid-spec) - (:query-params request) - main-transformer)) - "sort" sort->query) - (unparse-query-params (:parsed-query-params request)))))} + (dissoc (if (:query-schema grid-spec) + (update (filter-vals #(not (nil? %)) + (m/encode (:query-schema grid-spec) + (:query-params request) + main-transformer)) + "sort" sort->query) + (unparse-query-params (:parsed-query-params request))) + "selected" "all-selected")))} ;; TODO seems hacky to special case selected and all-selected here :oob (when-let [oob-render (:oob-render grid-spec)] (oob-render request))))) (wrap-trim-client-ids) @@ -242,7 +243,11 @@ :client (:client request) :identity (:identity request)} (apply com/breadcrumbs {} (:breadcrumbs grid-spec)) - [:div {:x-data (hx/json {:selected [] :all_selected false})} + [:div {:x-data (hx/json {:selected [] :all_selected false}) + "x-bind:hx-vals" "JSON.stringify({selected: $data.selected, 'all-selected': $data.all_selected})" + :x-init "$watch('selected', s=> $dispatch('selectedChanged', {selected: s, all_selected: all_selected}) ); + $watch('all_selected', a=>$dispatch('selectedChanged', {selected: selected, all_selected: a}))"} + (table* grid-spec identity request)]) diff --git a/src/clj/auto_ap/ssr/invoices.clj b/src/clj/auto_ap/ssr/invoices.clj index 568995b8..9b6180a7 100644 --- a/src/clj/auto_ap/ssr/invoices.clj +++ b/src/clj/auto_ap/ssr/invoices.clj @@ -50,7 +50,7 @@ [:div {:id "exact-match-id-tag"}])) (defn filters [request] - [:form#payment-filters {"hx-trigger" "change delay:500ms, keyup changed from:.hot-filter delay:1000ms" + [:form#invoice-filters {"hx-trigger" "change delay:500ms, keyup changed from:.hot-filter delay:1000ms" "hx-get" (bidi/path-for ssr-routes/only-routes ::route/table) "hx-target" "#entity-table" @@ -264,11 +264,10 @@ [:start {:optional true :default 0} [:maybe :int]] [:amount-gte {:optional true} [:maybe :double]] [:amount-lte {:optional true} [:maybe :double]] - [:payment-type {:optional true} [:maybe (ref->enum-schema "payment-type")]] [:vendor {:optional true :default nil} [:maybe [:entity-map {:pull [:db/id :vendor/name]}]]] [:check-number {:optional true} [:maybe [:string {:decode/string strip}]]] [:invoice-number {:optional true} [:maybe [:string {:decode/string strip}]]] - [:status {:optional true} [:maybe (ref->enum-schema "payment-status")]] + [:status {:optional true} [:maybe (ref->enum-schema "invoice-status")]] [:exact-match-id {:optional true} [:maybe entity-id]] [:all-selected {:optional true :default nil} [:maybe :boolean]] [:selected {:optional true :default nil} [:maybe [:vector {:coerce? true} @@ -283,6 +282,75 @@ {:start " "} main-transformer)) +(defn selected->ids [request params] + (let [all-selected (:all-selected params) + selected (:selected params) + ids (cond + all-selected + (:ids (fetch-ids (dc/db conn) (-> request + (assoc :query-params params) + (assoc-in [:query-params :start] 0) + (assoc-in [:query-params :per-page] 250)))) + + + :else + selected)] + ids)) + +(defn pay-button* [params] + (let [ids (:ids params) + selected-client-count (if (seq ids) + (ffirst + (dc/q '[:find (count ?c) + :in $ [?i ...] + :where [?i :invoice/client ?c]] + (dc/db conn) + ids)) + + 0)] + + [:div {:hx-target "this" + :x-data (hx/json {:popper nil + :hovering false}) + "x-init" "popper = Popper.createPopper($refs.button, $refs.tooltip, {placement: 'bottom', strategy: 'fixed', modifiers: [{name: 'preventOverflow'}, {name: 'offset', options: {offset: [0, 10]}}]});"} + (com/button {:color :primary + :disabled (or (= (count (:ids params)) 0) + (not= 1 selected-client-count)) + "x-bind:hx-vals" "JSON.stringify({selected: $data.selected, 'all-selected': $data.all_selected})" + "hx-include" "#invoice-filters" + :hx-get (bidi/path-for ssr-routes/only-routes ::route/pay-button) + :hx-swap "outerHTML" + :hx-trigger "selectedChanged from:body, htmx:afterSwap from:#entity-table" + "@mouseover" "hovering=true; $nextTick(() => popper.update())" + "@mouseout" "hovering=false;" + :x-ref "button" + :minimal-loading? true + :class "relative"} + (if (> (count (:ids params)) 0) + + (str "Pay " (count (:ids params)) " invoices") + "Pay") + (when (or (= 0 (count ids)) + (> selected-client-count 1)) + (com/badge {} "!"))) + [:div (hx/alpine-appear {:x-ref "tooltip" + + :x-show "hovering" + :class "bg-gray-100 dark:bg-gray-600 rounded-lg shadow-2xl w-max z-50 p-4"}) + (cond + (= 0 (count ids)) + [:div "Please select some invoices to pay"] + (> selected-client-count 1) + [:div "Can only pay for one client at a time"] + :else + [:div "Click to choose a bank account"])]])) + + +(defn pay-button [request] + (html-response + (pay-button* {:ids (selected->ids request + (:query-params request))}))) + ;; TODO fix parsing of query params (def grid-page (helper/build {:id "entity-table" @@ -298,12 +366,15 @@ :parse-query-params (fn [p] (mc/decode query-schema p main-transformer)) :action-buttons (fn [request] - [(when (can? (:identity request) {:subject :payment :activity :bulk-delete}) + [(when (can? (:identity request) {:subject :invoice :activity :bulk-delete}) (com/button {:hx-get (str (bidi/path-for ssr-routes/only-routes ::route/bulk-delete)) "x-bind:hx-vals" "JSON.stringify({selected: $data.selected, 'all-selected': $data.all_selected})" - "hx-include" "#payment-filters" + "hx-include" "#invoice-filters" :color :red} - "Void selected"))]) + "Void selected")) + (when (can? (:identity request) {:subject :invoice :activity :pay}) + (pay-button* {:ids (selected->ids request + (:query-params request))}))]) :row-buttons (fn [_ entity] [(when (= :invoice-status/unpaid (:invoice/status entity)) (com/icon-button {:hx-delete (bidi/path-for ssr-routes/only-routes @@ -470,7 +541,7 @@ (assoc "hx-retarget" ".modal-stack") (assoc "hx-reswap" "beforeend"))))) -(defn void-payments-internal [all-ids id] +(defn void-invoices-internal [all-ids id] (let [all-ids (->> all-ids (dc/q '[:find (pull ?i [:db/id :invoice/date {:invoice/expense-accounts [:db/id]}]) :in $ [?i ...] @@ -512,21 +583,12 @@ id) (count all-ids))) + + (defn bulk-delete-dialog-confirm [request] (alog/peek (:form-params request)) - (let [all-selected (:all-selected (:form-params request)) - selected (:selected (:form-params request)) - ids (cond - all-selected - (:ids (fetch-ids (dc/db conn) (-> request - (assoc :query-params (:form-params request)) - (assoc-in [:query-params :start] 0) - (assoc-in [:query-params :per-page] 250)))) - - - :else - selected) - updated-count (void-payments-internal ids (:identity request))] + (let [ids (selected->ids request (:form-params request)) + updated-count (void-invoices-internal ids (:identity request))] (html-response [:div] :headers {"hx-trigger" (hx/json {:modalclose "" @@ -539,6 +601,8 @@ (def key->handler (apply-middleware-to-all-handlers {::route/page (helper/page-route grid-page) + ::route/pay-button (-> pay-button + (wrap-schema-enforce :query-schema query-schema)) ::route/delete (-> delete (wrap-entity [:route-params :db/id] default-read) (wrap-schema-enforce :route-params [:map [:db/id entity-id]])) diff --git a/src/clj/auto_ap/ssr/utils.clj b/src/clj/auto_ap/ssr/utils.clj index f02a3651..8bf27ae3 100644 --- a/src/clj/auto_ap/ssr/utils.clj +++ b/src/clj/auto_ap/ssr/utils.clj @@ -259,12 +259,15 @@ (mt2/transformer {:decoders {:vector {:compile (fn [schema _] (when (:coerce? (m/properties schema)) (fn [data] - (cond (sequential? data) - data - (nil? data) - nil - :else - [data]))))}}})) + (cond + (vector? data) + data + (sequential? data) + data + (nil? data) + nil + :else + [data]))))}}})) (defn wrap-merge-prior-hx [handler] ;; TODO this should just be automatic diff --git a/src/cljc/auto_ap/routes/invoice.cljc b/src/cljc/auto_ap/routes/invoice.cljc index 282abee4..4c26c726 100644 --- a/src/cljc/auto_ap/routes/invoice.cljc +++ b/src/cljc/auto_ap/routes/invoice.cljc @@ -1,5 +1,6 @@ (ns auto-ap.routes.invoice) (def routes {"" {:get ::page} + "/pay-button" ::pay-button "/bulk-delete" {:get ::bulk-delete :delete ::bulk-delete-confirm} ["/" [#"\d+" :db/id]] {:delete ::delete}