From f634d8fd8100bc99a56837fb2fc61a7bcf5044dc Mon Sep 17 00:00:00 2001 From: Bryce Date: Sun, 17 Nov 2024 22:01:48 -0800 Subject: [PATCH] Making investigate improvements. --- .continueignore | 4 + src/clj/auto_ap/ssr/components/data_grid.clj | 8 +- src/clj/auto_ap/ssr/components/periods.clj | 77 ++++++++ src/clj/auto_ap/ssr/grid_page_helper.clj | 43 +++-- src/clj/auto_ap/ssr/ledger/common.clj | 179 ++++++++++--------- src/clj/auto_ap/ssr/ledger/investigate.clj | 49 ++--- 6 files changed, 232 insertions(+), 128 deletions(-) create mode 100644 .continueignore create mode 100644 src/clj/auto_ap/ssr/components/periods.clj diff --git a/.continueignore b/.continueignore new file mode 100644 index 00000000..4139befe --- /dev/null +++ b/.continueignore @@ -0,0 +1,4 @@ +./data/** +./resources/** +./target/** +./.calva/** diff --git a/src/clj/auto_ap/ssr/components/data_grid.clj b/src/clj/auto_ap/ssr/components/data_grid.clj index 32822fe3..df30bcbb 100644 --- a/src/clj/auto_ap/ssr/components/data_grid.clj +++ b/src/clj/auto_ap/ssr/components/data_grid.clj @@ -75,26 +75,28 @@ thead-params start per-page + root-params flash-id headers raw? rows] :as params} & children] (let [card (if raw? raw-table-card content-card-)] (card - (cond-> { :id id :class (cond-> "group" raw? (hh/add-class "raw h-full flex flex-col overflow-hidden"))} + (cond-> { :id id :class (cond-> "group" raw? (hh/add-class "raw h-full flex flex-col overflow-hidden")) } + root-params (merge root-params) route (assoc :hx-get (bidi/path-for ssr-routes/only-routes route :request-method :get) :hx-trigger "clientSelected from:body, invalidated from:body" :hx-swap "outerHTML swap:300ms")) - [:div {:class " group-[.raw]:hidden flex flex-col px-4 py-3 space-y-3 lg:flex-row lg:items-baseline lg:justify-between lg:space-y-0 lg:space-x-4 text-gray-800 dark:text-gray-100"} + [:div {:class " flex flex-col px-4 py-3 space-y-3 lg:flex-row lg:items-baseline lg:justify-between lg:space-y-0 lg:space-x-4 text-gray-800 dark:text-gray-100"} [:h1.text-2xl.mb-3.font-bold title] [:div {:class "flex items-center flex-1 space-x-4"} [:h5 (when subtitle [:span subtitle])]] - (into [:div {:class "flex flex-col flex-shrink-0 space-y-3 md:flex-row md:items-center lg:justify-end md:space-y-0 md:space-x-3"}] + (into [:div {:class "group-[.raw]:hidden 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 contents"} (data-grid- {:headers headers diff --git a/src/clj/auto_ap/ssr/components/periods.clj b/src/clj/auto_ap/ssr/components/periods.clj new file mode 100644 index 00000000..ca68cc48 --- /dev/null +++ b/src/clj/auto_ap/ssr/components/periods.clj @@ -0,0 +1,77 @@ +(ns auto-ap.ssr.components.periods + (:require + [auto-ap.ssr.components.buttons :as buttons] + [auto-ap.ssr.components.inputs :as inputs] + [auto-ap.ssr.components.tabs :as tabs] + [auto-ap.ssr.components.tags :as tags] + [auto-ap.ssr.hx :as hx] + [auto-ap.ssr.svg :as svg] + [auto-ap.time :as atime])) + +(defn periods-dropdown- [{:keys [value]}] + [:div {:x-data (hx/json {:periods (map (fn [p] + {:start (atime/unparse-local (:start p) atime/normal-date) + :end (atime/unparse-local (:end p) atime/normal-date)}) + value) + :source_date (-> value + first + :end + (atime/unparse-local atime/normal-date))}) + + :x-init "$watch('periods', ds => source_date= ds.length > 0 ? ds[0].end : null)"} + [:template {:x-for "(v,n) in periods"} + [:div + [:input {:type "hidden" + ":name" "'periods[' + n + '][start]'" + :x-model "v.start"}] + [:input {:type "hidden" + ":name" "'periods[' + n + '][end]'" + :x-model "v.end"}]]] + (buttons/a-button- {"x-tooltip.on.click.theme.dropdown.placement.bottom.interactive" "{content: ()=> $refs.tooltip.innerHTML, allowHTML: true, appendTo: $root}" + :indicator? false} + [:template {:x-if "periods.length == 0"} + [:span.text-left.text-gray-400 "None selected"]] + [:template {:x-if "periods.length < 3 && periods.length > 0"} + [:span.inline-flex.gap-2 + [:template {:x-for "p in periods"} + (tags/pill- {:color :secondary} + [:span {:x-text "p.start"}] + " - " + [:span {:x-text "p.end"}])]]] + [:template {:x-if "periods.length >= 3"} + (tags/pill- {:color :secondary} + [:span {:x-text "periods.length"}] + " periods selected")] + [:div {:class "w-3 h-3 m-1 inline ml-1 justify-self-end text-gray-500 self-center"} + svg/drop-down]) + [:template {:x-ref "tooltip"} + [:div.p-4.gap-2 {:class "bg-gray-100 dark:bg-gray-600 rounded-lg shadow-2xl w-max z-50 ring-1 p-4 w-[700px] "} + [:div.flex.flex-col.gap-2 + (tabs/tabs- + {:tabs [{:name "Quick" + :content [:div.flex.flex.gap-2 + (inputs/calendar-input- {:placeholder "12/21/2020" :x-model "source_date"}) + [:div.flex.flex-col.gap-2 + (buttons/a-button- {"@click" "periods=getFourWeekPeriodsPeriods(source_date)"} "13 periods") + (buttons/a-button- {"@click" "periods=[calendarYearPeriod(source_date)]"} "Calendar year") + (buttons/a-button- {"@click" "periods=getLastMonthPeriods()"} "Last Month") + (buttons/a-button- {"@click" "periods=[]"} "Clear")]]} + {:name "Advanced" + :content [:div.flex.gap-4 {:class "overflow-hidden max-h-[300px]" + :x-data (hx/json {:calendarTarget "0" + :calendarWhich "start"}) } + (inputs/calendar-input- {:x-model "periods[calendarTarget][calendarWhich]"}) + [:div.flex.flex-col.gap-4.p-2 + [:div.overflow-y-scroll.flex.flex-col.gap-4 + [:template {:x-for "(p, i) in periods" ":key" "i"} + [:div.flex.gap-4. + (inputs/text-input- { :x-model "p.start" "@focus" "calendarTarget =i; calendarWhich='start'" }) + (inputs/text-input- { :x-model "p.end" "@focus" "calendarTarget =i; calendarWhich='end'"}) + (buttons/a-icon-button- {"@click.prevent.stop" "periods=periods.filter((_, i2) => i !== i2); calendarTarget=0"} svg/x)] + #_(com/pill {:color :secondary} + [:span {:x-text "p.start"}] + " - " + [:span {:x-text "p.end"}])]] + (buttons/button- {"@click.prevent.stop" "periods.push({start: '', end: ''}); calendarTarget=0" :class "w-32"} "Add new period")] + ]}] + :active "Quick"}) ]]]]) \ No newline at end of file diff --git a/src/clj/auto_ap/ssr/grid_page_helper.clj b/src/clj/auto_ap/ssr/grid_page_helper.clj index 6715ce42..ccec7635 100644 --- a/src/clj/auto_ap/ssr/grid_page_helper.clj +++ b/src/clj/auto_ap/ssr/grid_page_helper.clj @@ -106,23 +106,27 @@ "desc"))) s))) + (defn table* [grid-spec user {{:keys [start per-page flash-id sort]} :parsed-query-params :as request}] (alog/info ::TABLE-QP :qp (:query-params request) - :pqp (:parsed-query-params request) - :sort sort) - (let [start (or start 0) + :pqp (:parsed-query-params request)) + (let [sort (or (and (not (string? (:sort (:parsed-query-params request)))) (:sort (:parsed-query-params request))) + (:sort (:query-params request))) + start (or start 0) per-page (or per-page 25) [entities total :as page-results] ((:fetch-page grid-spec) - request) + request) request (assoc request :page-results page-results)] (com/data-grid-card {:id (:id grid-spec) :raw? (:raw? grid-spec) :title [:div.flex.gap-2 (if (string? (:title grid-spec)) - (:title grid-spec) - ((:title grid-spec) request)) ] + (:title grid-spec) + ((:title grid-spec) request)) ] :route (:route grid-spec) + :root-params {:x-data (hx/json {:sort (sort->query sort)}) + "x-hx-val:sort" "sort"} :start start :per-page per-page :total total @@ -154,7 +158,7 @@ (let [break-table-fn (some-> grid-spec :break-table ( create-break-table-fn grid-spec))] (for [entity entities row (if-let [break-table-row (when break-table-fn (break-table-fn request entity))] - + [break-table-row (row* grid-spec user entity {:flash? (= flash-id ((:id-fn grid-spec) entity)) :request request})] [(row* grid-spec user entity {:flash? (= flash-id ((:id-fn grid-spec) entity)) :request request})])] row)) @@ -258,24 +262,27 @@ (handler (assoc request :trimmed-clients valid-clients))))) -(defn table-route [grid-spec & {:keys [parse-query-params?] :or {parse-query-params? true}}] +(defn table-route [grid-spec & {:keys [parse-query-params? push-url?] :or {parse-query-params? true push-url? true}}] (cond-> (fn table [{:keys [identity] :as request}] - + + (println "SORT IS") + (clojure.pprint/pprint (sort->query (:sort (:query-params request)))) (let [unparse-query-params (or (:unparse-query grid-spec) default-unparse-query-params)] (html-response (table* grid-spec identity request) - :headers {"hx-push-url" (str "?" (url/map->query - (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 + :headers (when push-url? + {"hx-push-url" (str "?" (url/map->query + (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))))) true (wrap-trim-client-ids) diff --git a/src/clj/auto_ap/ssr/ledger/common.clj b/src/clj/auto_ap/ssr/ledger/common.clj index a1502b27..d64ffb06 100644 --- a/src/clj/auto_ap/ssr/ledger/common.clj +++ b/src/clj/auto_ap/ssr/ledger/common.clj @@ -492,95 +492,100 @@ args jel (:journal-entry/line-items je)] (merge jel je))) :headers [{:key "id" - :name "Id" - :render-csv :db/id - :render-for #{:csv}} - {:key "client" - :name "Client" - :sort-key "client" - :hide? (fn [args] - (and (= (count (:clients args)) 1) - (= 1 (count (:client/locations (:client args)))))) - :render (fn [x] [:div.flex.items-center.gap-2 (-> x :journal-entry/client :client/name)]) - :render-csv (fn [x] (-> x :journal-entry/client :client/name))} + :name "Id" + :render-csv :db/id + :render-for #{:csv}} + {:key "client" + :name "Client" + :sort-key "client" + :hide? (fn [args] + (and (= (count (:clients args)) 1) + (= 1 (count (:client/locations (:client args)))))) + :render (fn [x] [:div.flex.items-center.gap-2 (-> x :journal-entry/client :client/name)]) + :render-csv (fn [x] (-> x :journal-entry/client :client/name))} - {:key "vendor" - :name "Vendor" - :sort-key "vendor" - :render (fn [e] (or (-> e :journal-entry/vendor :vendor/name) - [:span.italic.text-gray-400 (-> e :journal-entry/alternate-description)])) - :render-csv (fn [e] (or (-> e :journal-entry/vendor :vendor/name) - (-> e :journal-entry/alternate-description)))} - {:key "source" - :name "Source" - :sort-key "source" - :hide? (fn [args] - (not (:external? (:route-params args)))) - :render :journal-entry/source - :render-csv :journal-entry/source} - {:key "external-id" - :name "External Id" - :sort-key "external-id" - :class "max-w-[12rem]" - :hide? (fn [args] - (not (:external? (:route-params args)))) - :render (fn [x] [:p.truncate (:journal-entry/external-id x)]) - :render-csv :journal-entry/external-id} - {:key "date" - :sort-key "date" - :name "Date" - :show-starting "lg" - :render (fn [{:journal-entry/keys [date]}] - (some-> date (atime/unparse-local atime/normal-date)))} -{:key "account" - :name "Account" - :sort-key "account" - :class "text-right" - :render-csv #(or (-> % :journal-entry-line/account :account/name) - (-> % :journal-entry-line/account :bank-account/name)) - :render-for #{:csv}} - {:key "debit" - :name "Debit" - :sort-key "debit" - :class "text-right" - :render (partial render-lines :journal-entry-line/debit) - :render-csv :journal-entry-line/debit } - - + {:key "vendor" + :name "Vendor" + :sort-key "vendor" + :render (fn [e] (or (-> e :journal-entry/vendor :vendor/name) + [:span.italic.text-gray-400 (-> e :journal-entry/alternate-description)])) + :render-csv (fn [e] (or (-> e :journal-entry/vendor :vendor/name) + (-> e :journal-entry/alternate-description)))} + {:key "source" + :name "Source" + :sort-key "source" + :hide? (fn [args] + (not (:external? (:route-params args)))) + :render :journal-entry/source + :render-csv :journal-entry/source} + {:key "external-id" + :name "External Id" + :sort-key "external-id" + :class "max-w-[12rem]" + :hide? (fn [args] + (not (:external? (:route-params args)))) + :render (fn [x] [:p.truncate (:journal-entry/external-id x)]) + :render-csv :journal-entry/external-id} + {:key "date" + :sort-key "date" + :name "Date" + :show-starting "lg" + :render (fn [{:journal-entry/keys [date]}] + (some-> date (atime/unparse-local atime/normal-date)))} + {:key "amount" + :sort-key "amount" + :name "Amount" + :show-starting "lg" + :render (fn [{:journal-entry/keys [amount]}] + (some->> amount + (format "$%,.2f")))} + {:key "account" + :name "Account" + :sort-key "account" + :class "text-right" + :render-csv #(or (-> % :journal-entry-line/account :account/name) + (-> % :journal-entry-line/account :bank-account/name)) + :render-for #{:csv}} + {:key "debit" + :name "Debit" + :class "text-right" + :render (partial render-lines :journal-entry-line/debit) + :render-csv :journal-entry-line/debit} - {:key "credit" - :name "Credit" - :sort-key "credit" - :class "text-right" - :render (partial render-lines :journal-entry-line/credit) - :render-csv :journal-entry-line/credit } - - {:key "links" - :name "Links" - :show-starting "lg" - :class "w-8" - :render (fn [i] - (link-dropdown - (cond-> [] - (-> i :journal-entry/original-entity :invoice/invoice-number) - (conj - {:link (hu/url (bidi/path-for ssr-routes/only-routes - ::invoice-route/all-page) - {:exact-match-id (:db/id (:journal-entry/original-entity i))}) - :color :primary - :content (format "Invoice '%s'" (-> i :journal-entry/original-entity :invoice/invoice-number))}) - (-> i :journal-entry/original-entity :invoice/source-url) - {:link (-> i :journal-entry/original-entity :invoice/source-url) - :color :secondary - :content (str "File")} - (-> i :journal-entry/original-entity :transaction/description-original) - (conj - {:link (hu/url (bidi/path-for client-routes/routes - :transactions) - {:exact-match-id (:db/id (:journal-entry/original-entity i))}) - :color :primary - :content (format "Transaction '%s'" (-> i :journal-entry/original-entity :transaction/description-original))})))) - :render-for #{:html}}]})) + + {:key "credit" + :name "Credit" + :class "text-right" + :render (partial render-lines :journal-entry-line/credit) + :render-csv :journal-entry-line/credit} + + {:key "links" + :name "Links" + :show-starting "lg" + :class "w-8" + :render (fn [i] + (link-dropdown + (cond-> [] + (-> i :journal-entry/original-entity :invoice/invoice-number) + (conj + {:link (hu/url (bidi/path-for ssr-routes/only-routes + ::invoice-route/all-page) + {:exact-match-id (:db/id (:journal-entry/original-entity i))}) + :color :primary + :content (format "Invoice '%s'" (-> i :journal-entry/original-entity :invoice/invoice-number))}) + (-> i :journal-entry/original-entity :invoice/source-url) + {:link (-> i :journal-entry/original-entity :invoice/source-url) + :color :secondary + :content (str "File")} + + (-> i :journal-entry/original-entity :transaction/description-original) + (conj + {:link (hu/url (bidi/path-for client-routes/routes + :transactions) + {:exact-match-id (:db/id (:journal-entry/original-entity i))}) + :color :primary + :content (format "Transaction '%s'" (-> i :journal-entry/original-entity :transaction/description-original))})))) + :render-for #{:html}}]})) (def row* (partial helper/row* grid-page)) \ No newline at end of file diff --git a/src/clj/auto_ap/ssr/ledger/investigate.clj b/src/clj/auto_ap/ssr/ledger/investigate.clj index 69fc8629..35b0ae15 100644 --- a/src/clj/auto_ap/ssr/ledger/investigate.clj +++ b/src/clj/auto_ap/ssr/ledger/investigate.clj @@ -1,16 +1,18 @@ (ns auto-ap.ssr.ledger.investigate - (:require [auto-ap.permissions :refer [wrap-must]] - [auto-ap.query-params :refer [wrap-copy-qp-pqp]] - [auto-ap.routes.ledger :as route] - [auto-ap.routes.utils :refer [wrap-client-redirect-unauthenticated]] - [auto-ap.ssr.components :as com] - [auto-ap.ssr.grid-page-helper :refer [table*]] - [auto-ap.ssr.hx :as hx] - [auto-ap.ssr.ledger.common :refer [grid-page query-schema]] - [auto-ap.ssr.utils :refer [apply-middleware-to-all-handlers - html-response modal-response - wrap-merge-prior-hx wrap-schema-enforce]] - [auto-ap.time :as atime])) + (:require + [auto-ap.permissions :refer [wrap-must]] + [auto-ap.query-params :refer [wrap-copy-qp-pqp]] + [auto-ap.routes.ledger :as route] + [auto-ap.routes.utils :refer [wrap-client-redirect-unauthenticated]] + [auto-ap.ssr.components :as com] + [auto-ap.ssr.grid-page-helper :refer [table* wrap-apply-sort]] + [auto-ap.ssr.grid-page-helper :as helper] + [auto-ap.ssr.hx :as hx] + [auto-ap.ssr.ledger.common :refer [grid-page query-schema]] + [auto-ap.ssr.utils :refer [apply-middleware-to-all-handlers html-response + modal-response wrap-merge-prior-hx + wrap-schema-enforce]] + [auto-ap.time :as atime])) (def altered-grid-page (assoc grid-page @@ -20,24 +22,27 @@ :route ::route/investigate-results)) (defn investigate-results [request] + (clojure.pprint/pprint (:query-params request)) (html-response (table* altered-grid-page identity - (assoc-in request [:query-params :sort] [{:sort-key "date" :asc? false}])))) + request + #_(assoc-in request [:query-params :sort] [{:sort-key "date" :asc? false}])))) (defn investigate [request] (modal-response (com/modal {:class "max-h-[600px]"} - (com/modal-card {:hx-vals (hx/json (-> (:query-params request) - (update :numeric-code pr-str) - (update :end-date #(some-> (atime/unparse-local % atime/normal-date))))) - } + (com/modal-card {:hx-vals (hx/json (cond-> (:query-params request) + true (update :numeric-code pr-str) + (:start-date (:query-params request)) (update :start-date #(some-> (atime/unparse-local % atime/normal-date))) + (:end-date (:query-params request)) (update :end-date #(some-> (atime/unparse-local % atime/normal-date))))) } [:div "Ledger entries"] (table* altered-grid-page identity - (assoc-in request [:query-params :sort] [{:sort-key "date" :asc? false}])) + request + #_(assoc-in request [:query-params :sort] [{:sort-key "date" :asc? false :name "Date"}])) nil) ))) @@ -45,13 +50,17 @@ (apply-middleware-to-all-handlers (-> {::route/investigate investigate - ::route/investigate-results investigate-results} + ::route/investigate-results (helper/table-route altered-grid-page :parse-query-params? false + :push-url? false)} ) (fn [h] (-> h + (wrap-apply-sort grid-page) (wrap-copy-qp-pqp) - #_(wrap-merge-prior-hx) + (wrap-merge-prior-hx) (wrap-schema-enforce :query-schema query-schema) (wrap-schema-enforce :hx-schema query-schema) (wrap-must {:activity :read :subject :ledger}) (wrap-client-redirect-unauthenticated))))) + +