Making investigate improvements.

This commit is contained in:
2024-11-17 22:01:48 -08:00
parent 18172df3f8
commit f634d8fd81
6 changed files with 232 additions and 128 deletions

4
.continueignore Normal file
View File

@@ -0,0 +1,4 @@
./data/**
./resources/**
./target/**
./.calva/**

View File

@@ -75,26 +75,28 @@
thead-params thead-params
start start
per-page per-page
root-params
flash-id flash-id
headers headers
raw? raw?
rows] :as params} & children] rows] :as params} & children]
(let [card (if raw? raw-table-card content-card-)] (let [card (if raw? raw-table-card content-card-)]
(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 route (assoc
:hx-get (bidi/path-for ssr-routes/only-routes :hx-get (bidi/path-for ssr-routes/only-routes
route route
:request-method :get) :request-method :get)
:hx-trigger "clientSelected from:body, invalidated from:body" :hx-trigger "clientSelected from:body, invalidated from:body"
:hx-swap "outerHTML swap:300ms")) :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] [:h1.text-2xl.mb-3.font-bold title]
[:div {:class "flex items-center flex-1 space-x-4"} [:div {:class "flex items-center flex-1 space-x-4"}
[:h5 [:h5
(when subtitle (when subtitle
[:span 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)] action-buttons)]
[:div {:class "overflow-x-auto contents"} [:div {:class "overflow-x-auto contents"}
(data-grid- {:headers headers (data-grid- {:headers headers

View File

@@ -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"}) ]]]])

View File

@@ -106,23 +106,27 @@
"desc"))) "desc")))
s))) s)))
(defn table* [grid-spec user {{:keys [start per-page flash-id sort]} :parsed-query-params :as request}] (defn table* [grid-spec user {{:keys [start per-page flash-id sort]} :parsed-query-params :as request}]
(alog/info ::TABLE-QP (alog/info ::TABLE-QP
:qp (:query-params request) :qp (:query-params request)
:pqp (:parsed-query-params request) :pqp (:parsed-query-params request))
:sort sort) (let [sort (or (and (not (string? (:sort (:parsed-query-params request)))) (:sort (:parsed-query-params request)))
(let [start (or start 0) (:sort (:query-params request)))
start (or start 0)
per-page (or per-page 25) per-page (or per-page 25)
[entities total :as page-results] ((:fetch-page grid-spec) [entities total :as page-results] ((:fetch-page grid-spec)
request) request)
request (assoc request :page-results page-results)] request (assoc request :page-results page-results)]
(com/data-grid-card {:id (:id grid-spec) (com/data-grid-card {:id (:id grid-spec)
:raw? (:raw? grid-spec) :raw? (:raw? grid-spec)
:title [:div.flex.gap-2 (if (string? (:title grid-spec)) :title [:div.flex.gap-2 (if (string? (:title grid-spec))
(:title grid-spec) (:title grid-spec)
((:title grid-spec) request)) ] ((:title grid-spec) request)) ]
:route (:route grid-spec) :route (:route grid-spec)
:root-params {:x-data (hx/json {:sort (sort->query sort)})
"x-hx-val:sort" "sort"}
:start start :start start
:per-page per-page :per-page per-page
:total total :total total
@@ -154,7 +158,7 @@
(let [break-table-fn (some-> grid-spec :break-table ( create-break-table-fn grid-spec))] (let [break-table-fn (some-> grid-spec :break-table ( create-break-table-fn grid-spec))]
(for [entity entities (for [entity entities
row (if-let [break-table-row (when break-table-fn (break-table-fn request entity))] 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})] [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* grid-spec user entity {:flash? (= flash-id ((:id-fn grid-spec) entity)) :request request})])]
row)) row))
@@ -258,24 +262,27 @@
(handler (assoc request :trimmed-clients valid-clients))))) (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}] (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) (let [unparse-query-params (or (:unparse-query grid-spec)
default-unparse-query-params)] default-unparse-query-params)]
(html-response (table* (html-response (table*
grid-spec grid-spec
identity identity
request) request)
:headers {"hx-push-url" (str "?" (url/map->query :headers (when push-url?
(dissoc (if (:query-schema grid-spec) {"hx-push-url" (str "?" (url/map->query
(update (filter-vals #(not (nil? %)) (dissoc (if (:query-schema grid-spec)
(m/encode (:query-schema grid-spec) (update (filter-vals #(not (nil? %))
(:query-params request) (m/encode (:query-schema grid-spec)
main-transformer)) (:query-params request)
"sort" sort->query) main-transformer))
(unparse-query-params (:parsed-query-params request))) "sort" sort->query)
"selected" "all-selected")))} ;; TODO seems hacky to special case selected and all-selected here (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 (when-let [oob-render (:oob-render grid-spec)]
(oob-render request))))) (oob-render request)))))
true (wrap-trim-client-ids) true (wrap-trim-client-ids)

View File

@@ -492,95 +492,100 @@ args
jel (:journal-entry/line-items je)] jel (:journal-entry/line-items je)]
(merge jel je))) (merge jel je)))
:headers [{:key "id" :headers [{:key "id"
:name "Id" :name "Id"
:render-csv :db/id :render-csv :db/id
:render-for #{:csv}} :render-for #{:csv}}
{:key "client" {:key "client"
:name "Client" :name "Client"
:sort-key "client" :sort-key "client"
:hide? (fn [args] :hide? (fn [args]
(and (= (count (:clients args)) 1) (and (= (count (:clients args)) 1)
(= 1 (count (:client/locations (:client args)))))) (= 1 (count (:client/locations (:client args))))))
:render (fn [x] [:div.flex.items-center.gap-2 (-> x :journal-entry/client :client/name)]) :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))} :render-csv (fn [x] (-> x :journal-entry/client :client/name))}
{:key "vendor" {:key "vendor"
:name "Vendor" :name "Vendor"
:sort-key "vendor" :sort-key "vendor"
:render (fn [e] (or (-> e :journal-entry/vendor :vendor/name) :render (fn [e] (or (-> e :journal-entry/vendor :vendor/name)
[:span.italic.text-gray-400 (-> e :journal-entry/alternate-description)])) [:span.italic.text-gray-400 (-> e :journal-entry/alternate-description)]))
:render-csv (fn [e] (or (-> e :journal-entry/vendor :vendor/name) :render-csv (fn [e] (or (-> e :journal-entry/vendor :vendor/name)
(-> e :journal-entry/alternate-description)))} (-> e :journal-entry/alternate-description)))}
{:key "source" {:key "source"
:name "Source" :name "Source"
:sort-key "source" :sort-key "source"
:hide? (fn [args] :hide? (fn [args]
(not (:external? (:route-params args)))) (not (:external? (:route-params args))))
:render :journal-entry/source :render :journal-entry/source
:render-csv :journal-entry/source} :render-csv :journal-entry/source}
{:key "external-id" {:key "external-id"
:name "External Id" :name "External Id"
:sort-key "external-id" :sort-key "external-id"
:class "max-w-[12rem]" :class "max-w-[12rem]"
:hide? (fn [args] :hide? (fn [args]
(not (:external? (:route-params args)))) (not (:external? (:route-params args))))
:render (fn [x] [:p.truncate (:journal-entry/external-id x)]) :render (fn [x] [:p.truncate (:journal-entry/external-id x)])
:render-csv :journal-entry/external-id} :render-csv :journal-entry/external-id}
{:key "date" {:key "date"
:sort-key "date" :sort-key "date"
:name "Date" :name "Date"
:show-starting "lg" :show-starting "lg"
:render (fn [{:journal-entry/keys [date]}] :render (fn [{:journal-entry/keys [date]}]
(some-> date (atime/unparse-local atime/normal-date)))} (some-> date (atime/unparse-local atime/normal-date)))}
{:key "account" {:key "amount"
:name "Account" :sort-key "amount"
:sort-key "account" :name "Amount"
:class "text-right" :show-starting "lg"
:render-csv #(or (-> % :journal-entry-line/account :account/name) :render (fn [{:journal-entry/keys [amount]}]
(-> % :journal-entry-line/account :bank-account/name)) (some->> amount
:render-for #{:csv}} (format "$%,.2f")))}
{:key "debit" {:key "account"
:name "Debit" :name "Account"
:sort-key "debit" :sort-key "account"
:class "text-right" :class "text-right"
:render (partial render-lines :journal-entry-line/debit) :render-csv #(or (-> % :journal-entry-line/account :account/name)
:render-csv :journal-entry-line/debit } (-> % :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 {:key "credit"
{:link (hu/url (bidi/path-for client-routes/routes :name "Credit"
:transactions) :class "text-right"
{:exact-match-id (:db/id (:journal-entry/original-entity i))}) :render (partial render-lines :journal-entry-line/credit)
:color :primary :render-csv :journal-entry-line/credit}
:content (format "Transaction '%s'" (-> i :journal-entry/original-entity :transaction/description-original))}))))
:render-for #{:html}}]})) {: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)) (def row* (partial helper/row* grid-page))

View File

@@ -1,16 +1,18 @@
(ns auto-ap.ssr.ledger.investigate (ns auto-ap.ssr.ledger.investigate
(:require [auto-ap.permissions :refer [wrap-must]] (:require
[auto-ap.query-params :refer [wrap-copy-qp-pqp]] [auto-ap.permissions :refer [wrap-must]]
[auto-ap.routes.ledger :as route] [auto-ap.query-params :refer [wrap-copy-qp-pqp]]
[auto-ap.routes.utils :refer [wrap-client-redirect-unauthenticated]] [auto-ap.routes.ledger :as route]
[auto-ap.ssr.components :as com] [auto-ap.routes.utils :refer [wrap-client-redirect-unauthenticated]]
[auto-ap.ssr.grid-page-helper :refer [table*]] [auto-ap.ssr.components :as com]
[auto-ap.ssr.hx :as hx] [auto-ap.ssr.grid-page-helper :refer [table* wrap-apply-sort]]
[auto-ap.ssr.ledger.common :refer [grid-page query-schema]] [auto-ap.ssr.grid-page-helper :as helper]
[auto-ap.ssr.utils :refer [apply-middleware-to-all-handlers [auto-ap.ssr.hx :as hx]
html-response modal-response [auto-ap.ssr.ledger.common :refer [grid-page query-schema]]
wrap-merge-prior-hx wrap-schema-enforce]] [auto-ap.ssr.utils :refer [apply-middleware-to-all-handlers html-response
[auto-ap.time :as atime])) modal-response wrap-merge-prior-hx
wrap-schema-enforce]]
[auto-ap.time :as atime]))
(def altered-grid-page (def altered-grid-page
(assoc grid-page (assoc grid-page
@@ -20,24 +22,27 @@
:route ::route/investigate-results)) :route ::route/investigate-results))
(defn investigate-results [request] (defn investigate-results [request]
(clojure.pprint/pprint (:query-params request))
(html-response (html-response
(table* (table*
altered-grid-page altered-grid-page
identity 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] (defn investigate [request]
(modal-response (modal-response
(com/modal {:class "max-h-[600px]"} (com/modal {:class "max-h-[600px]"}
(com/modal-card {:hx-vals (hx/json (-> (:query-params request) (com/modal-card {:hx-vals (hx/json (cond-> (:query-params request)
(update :numeric-code pr-str) true (update :numeric-code pr-str)
(update :end-date #(some-> (atime/unparse-local % atime/normal-date))))) (: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"] [:div "Ledger entries"]
(table* (table*
altered-grid-page altered-grid-page
identity 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) nil)
))) )))
@@ -45,13 +50,17 @@
(apply-middleware-to-all-handlers (apply-middleware-to-all-handlers
(-> (->
{::route/investigate investigate {::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] (fn [h]
(-> h (-> h
(wrap-apply-sort grid-page)
(wrap-copy-qp-pqp) (wrap-copy-qp-pqp)
#_(wrap-merge-prior-hx) (wrap-merge-prior-hx)
(wrap-schema-enforce :query-schema query-schema) (wrap-schema-enforce :query-schema query-schema)
(wrap-schema-enforce :hx-schema query-schema) (wrap-schema-enforce :hx-schema query-schema)
(wrap-must {:activity :read :subject :ledger}) (wrap-must {:activity :read :subject :ledger})
(wrap-client-redirect-unauthenticated))))) (wrap-client-redirect-unauthenticated)))))