diff --git a/scratch-sessions/fix-page-performance.repl b/scratch-sessions/fix-page-performance.repl index d6f5dc04..d641f408 100644 --- a/scratch-sessions/fix-page-performance.repl +++ b/scratch-sessions/fix-page-performance.repl @@ -213,4 +213,15 @@ {:user/name "hydrate-tuples"}) +(auto-ap.datomic/audit-transact-batch (->> (dc/q '[:find ?e ?c + :in $ + :where [?e :expected-deposit/client ?c]] + (dc/db conn) + ) + (map (fn [[i c]] + {:db/id i + :expected-deposit/client c}))) + + {:user/name "hydrate-tuples"}) + ) diff --git a/src/clj/auto_ap/datomic/yodlee2.clj b/src/clj/auto_ap/datomic/yodlee2.clj index 28a2a160..4692d37e 100644 --- a/src/clj/auto_ap/datomic/yodlee2.clj +++ b/src/clj/auto_ap/datomic/yodlee2.clj @@ -12,7 +12,7 @@ [clj-time.coerce :as c] [datomic.api :as dc])) -(def default-read '[*]) +(def default-read '[* {:yodlee-provider-account/client [:client/code]}]) (defn <-datomic [x] (-> x (update :yodlee-provider-account/last-updated c/from-date))) diff --git a/src/clj/auto_ap/ssr/company/company_1099.clj b/src/clj/auto_ap/ssr/company/company_1099.clj index 6669999d..b2095d9c 100644 --- a/src/clj/auto_ap/ssr/company/company_1099.clj +++ b/src/clj/auto_ap/ssr/company/company_1099.clj @@ -115,91 +115,93 @@ :per-page (:per-page (:parsed-query-params args))} all)] [(:entries paginated) (:count paginated)])) -(def grid-page {:id "vendor-table" - :nav (com/company-aside-nav) - :id-fn (comp :db/id second) - :fetch-page (fn [user args] - (get-1099-companies user args) - #_(r/get-graphql (into args {:id user}))) - :breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes - :company)} - "My Company"] +(def grid-page + (helper/build + {:id "vendor-table" + :nav (com/company-aside-nav) + :id-fn (comp :db/id second) + :fetch-page (fn [user args] + (get-1099-companies user args) + #_(r/get-graphql (into args {:id user}))) + :breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes + :company)} + "My Company"] - [:a {:href (bidi/path-for ssr-routes/only-routes - :company-1099)} - "1099 Vendor Info"]] - :title "1099 Vendors" - :entity-name "Vendors" - :route :company-1099-vendor-table - :action-buttons (fn [user _] - nil) - :row-buttons (fn [user e] - [(com/icon-button {:hx-get (str (bidi/path-for ssr-routes/only-routes - :company-1099-vendor-dialog - :vendor-id (:db/id (second e))) - "?" - (url/map->query {:client-id (:db/id (first e))})) - :hx-ext "debug" - :hx-target "#modal-holder" - :hx-swap "outerHTML"} - svg/pencil)]) - :headers [{:key "Client" - :name "Client" - :sort-key "client" - :render (comp :client/code first)} - {:key "vendor-name" - :name "Vendor Name" - :sort-key "vendor" - :render (fn [[_ vendor]] - [:div.flex.whitespace-nowrap.items-center.gap-4 - [:div [:div (:vendor/name vendor)] - [:div.text-sm.text-gray-400 - (or (-> vendor :vendor/legal-entity-name not-empty) - (str (-> vendor :vendor/legal-entity-first-name) " " - (-> vendor :vendor/legal-entity-middle-name) " " - (-> vendor :vendor/legal-entity-last-name)))]] - (when-let [t99-type (some-> vendor :vendor/legal-entity-1099-type :db/ident name)] - (com/pill - {:class "text-xs font-medium" - :color :primary} - (str/capitalize t99-type)) - )])} - {:key "tin" - :name "TIN" - :sort-key "tin" - :show-starting "md" - :render (fn [[_ vendor]] - [:div.flex.gap-4 - (when-let [tin (-> vendor :vendor/legal-entity-tin)] - [:span {:class "text-xs font-medium py-0.5 "} - tin]) - (when-let [tin-type (some-> vendor :vendor/legal-entity-tin-type :db/ident name)] - (com/pill {:class "text-xs font-medium" - :color :yellow} - (name tin-type)))] - )} - {:key "address" - :name "Address" - :sort-key "address" - :show-starting "lg" - :render (fn [[_ vendor]] - (if (-> vendor :vendor/address :address/street1) - [:div - [:div (-> vendor :vendor/address :address/street1)] " " - [:div - (-> vendor :vendor/address :address/street2)] " " - [:div - (-> vendor :vendor/address :address/city) " " - (-> vendor :vendor/address :address/state) "," - (-> vendor :vendor/address :address/zip)]] - [:p.text-sm.italic.text-gray-400 "No address"]))} - {:key "paid" - :name "Paid" - :sort-key "paid" - :render (fn [[_ _ paid]] - (com/pill {:class "text-xs font-medium" - :color :primary} - "Paid $" (Math/round paid)))}]}) + [:a {:href (bidi/path-for ssr-routes/only-routes + :company-1099)} + "1099 Vendor Info"]] + :title "1099 Vendors" + :entity-name "Vendors" + :route :company-1099-vendor-table + :action-buttons (fn [user _] + nil) + :row-buttons (fn [user e] + [(com/icon-button {:hx-get (str (bidi/path-for ssr-routes/only-routes + :company-1099-vendor-dialog + :vendor-id (:db/id (second e))) + "?" + (url/map->query {:client-id (:db/id (first e))})) + :hx-ext "debug" + :hx-target "#modal-holder" + :hx-swap "outerHTML"} + svg/pencil)]) + :headers [{:key "Client" + :name "Client" + :sort-key "client" + :render (comp :client/code first)} + {:key "vendor-name" + :name "Vendor Name" + :sort-key "vendor" + :render (fn [[_ vendor]] + [:div.flex.whitespace-nowrap.items-center.gap-4 + [:div [:div (:vendor/name vendor)] + [:div.text-sm.text-gray-400 + (or (-> vendor :vendor/legal-entity-name not-empty) + (str (-> vendor :vendor/legal-entity-first-name) " " + (-> vendor :vendor/legal-entity-middle-name) " " + (-> vendor :vendor/legal-entity-last-name)))]] + (when-let [t99-type (some-> vendor :vendor/legal-entity-1099-type :db/ident name)] + (com/pill + {:class "text-xs font-medium" + :color :primary} + (str/capitalize t99-type)) + )])} + {:key "tin" + :name "TIN" + :sort-key "tin" + :show-starting "md" + :render (fn [[_ vendor]] + [:div.flex.gap-4 + (when-let [tin (-> vendor :vendor/legal-entity-tin)] + [:span {:class "text-xs font-medium py-0.5 "} + tin]) + (when-let [tin-type (some-> vendor :vendor/legal-entity-tin-type :db/ident name)] + (com/pill {:class "text-xs font-medium" + :color :yellow} + (name tin-type)))] + )} + {:key "address" + :name "Address" + :sort-key "address" + :show-starting "lg" + :render (fn [[_ vendor]] + (if (-> vendor :vendor/address :address/street1) + [:div + [:div (-> vendor :vendor/address :address/street1)] " " + [:div + (-> vendor :vendor/address :address/street2)] " " + [:div + (-> vendor :vendor/address :address/city) " " + (-> vendor :vendor/address :address/state) "," + (-> vendor :vendor/address :address/zip)]] + [:p.text-sm.italic.text-gray-400 "No address"]))} + {:key "paid" + :name "Paid" + :sort-key "paid" + :render (fn [[_ _ paid]] + (com/pill {:class "text-xs font-medium" + :color :primary} + "Paid $" (Math/round paid)))}]})) @@ -318,5 +320,5 @@ "Save")]]] [:div])]])))) -(def vendor-table (partial helper/table grid-page)) -(def page (partial helper/page grid-page)) +(def vendor-table (helper/table-route grid-page)) +(def page (helper/page-route grid-page)) diff --git a/src/clj/auto_ap/ssr/company/plaid.clj b/src/clj/auto_ap/ssr/company/plaid.clj index c65fe813..8ab4e736 100644 --- a/src/clj/auto_ap/ssr/company/plaid.clj +++ b/src/clj/auto_ap/ssr/company/plaid.clj @@ -9,8 +9,7 @@ pull-attr pull-many-by-id query2]] - [auto-ap.graphql.utils - :refer [assert-can-see-client extract-client-ids]] + [auto-ap.graphql.utils :refer [assert-can-see-client]] [auto-ap.logging :as alog] [auto-ap.plaid.core :as p] [auto-ap.ssr-routes :as ssr-routes] @@ -36,43 +35,36 @@ :plaid-account/balance :plaid-account/name]}]) -(defn raw-graphql-ids [db args] - (let [valid-clients (extract-client-ids (:clients args) - (:client-id args) - (when (:client-code args) - [:client/code (:client-code args)])) +(defn fetch-ids [db request] + (let [query-params (:parsed-query-params request) query (cond-> {:query {:find [] :in ['$ '[?xx ...]] :where ['[?e :plaid-item/client ?xx]]} - :args [db valid-clients]} + :args [db (:trimmed-clients request)]} - (:sort args) (add-sorter-fields {"external-id" ['[?e :plaid-item/external-id ?sort-external-id]] + (:sort query-params) (add-sorter-fields {"external-id" ['[?e :plaid-item/external-id ?sort-external-id]] "status" ['[?e :plaid-item/status ?sort-status]]} - args) - - (:client-id args) - (merge-query {:query {:in '[?client-id] - :where ['[?e :plaid-item/client ?client-id]]} - :args [(:client-id args)]}) + query-params) true (merge-query {:query {:find ['?e] :where ['[?e :plaid-item/external-id]]}}))] + (clojure.pprint/pprint query-params) (cond->> (query2 query) - true (apply-sort-3 args) - true (apply-pagination args)))) + true (apply-sort-3 query-params) + true (apply-pagination query-params)))) -(defn graphql-results [ids db _] +(defn hydrate-results [ids db _] (let [results (pull-many-by-id db default-read ids)] (->> ids (map results)))) -(defn get-page [args] +(defn fetch-page [request] (let [db (dc/db conn) - {ids-to-retrieve :ids matching-count :count} (raw-graphql-ids db args)] - [(graphql-results ids-to-retrieve db args) + {ids-to-retrieve :ids matching-count :count} (fetch-ids db request)] + [(hydrate-results ids-to-retrieve db request) matching-count])) @@ -138,65 +130,61 @@ "Start relink")]))) -(def grid-page {:id "plaid-table" - :nav (com/company-aside-nav) - :id-fn :db/id - :fetch-page (fn [user args] - (get-page {:client (:client args) - :clients (:clients args) - :start (:start (:parsed-query-params args)) - :sort (:sort (:parsed-query-params args)) - :per-page (:per-page (:parsed-query-params args)) - })) - :breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes - :company)} - "My Company"] +(def grid-page + (helper/build + {:id "plaid-table" + :nav (com/company-aside-nav) + :fetch-page fetch-page + :breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes + :company)} + "My Company"] - [:a {:href (bidi/path-for ssr-routes/only-routes - :company-plaid)} - "Plaid"]] - :title "Plaid Accounts" - :entity-name "Plaid accounts" - :route :company-plaid-table - :action-buttons (fn [user params] - (when-let [client-code (:client/code (:client params))] - [[:div {:hx-post (str (bidi/path-for ssr-routes/only-routes - :company-plaid-link - :request-method :post)) - :hx-vals (hiccup/raw (format "js:{client_code: \"%s\", public_token: event.detail.public_token}", client-code)) - :hx-trigger "linked"} - [:script (hiccup/raw (plaid-link-script (p/get-link-token client-code)))] - (com/button {:color :primary - :id "link-account" - :onClick "window.plaid.open()"} - (com/button-icon {} svg/refresh) - "Link new account")]])) - :row-buttons (fn [user e] - [[:div (com/button {:hx-put (str (bidi/path-for ssr-routes/only-routes - :company-plaid-relink) - "?plaid-item-id=" (:db/id e)) - :color :primary - :hx-target "closest div"} - "Reauthenticate")]]) - :headers [{:key "plaid-item" - :name "Plaid Item" - :sort-key "external-id" - :render :plaid-item/external-id} - {:key "status" - :name "Status" - :sort-key "status" - :render #(when-let [status (:plaid-item/status %)] - [:div [:div (com/pill {:color :primary} - status)] - [:div (atime/unparse-local (coerce/to-date-time (:plaid-item/last-updated %)) atime/normal-date)]])} + [:a {:href (bidi/path-for ssr-routes/only-routes + :company-plaid)} + "Plaid"]] + :title "Plaid Accounts" + :entity-name "Plaid accounts" + :route :company-plaid-table + :action-buttons (fn [request] + (when-let [client-code (:client/code (:client request))] + [[:div {:hx-post (str (bidi/path-for ssr-routes/only-routes + :company-plaid-link + :request-method :post)) + :hx-vals (hiccup/raw (format "js:{client_code: \"%s\", public_token: event.detail.public_token}", client-code)) + :hx-trigger "linked"} + [:script (hiccup/raw (plaid-link-script (p/get-link-token client-code)))] + (com/button {:color :primary + :id "link-account" + :onClick "window.plaid.open()"} + (com/button-icon {} svg/refresh) + (format "Link %s account" client-code))]])) + :row-buttons (fn [request e] + [[:div (com/button {:hx-put (str (bidi/path-for ssr-routes/only-routes + :company-plaid-relink) + "?plaid-item-id=" (:db/id e)) + :color :primary + :hx-target "closest div"} + "Reauthenticate")]]) + :headers [{:key "plaid-item" + :name "Plaid Item" + :sort-key "external-id" + :render :plaid-item/external-id} + {:key "status" + :name "Status" + :sort-key "status" + :render #(when-let [status (:plaid-item/status %)] + [:div [:div (com/pill {:color :primary} + status)] + [:div (atime/unparse-local (coerce/to-date-time (:plaid-item/last-updated %)) atime/normal-date)]])} - {:key "accounts" - :name "Accounts" - :show-starting "md" - :render (fn [e] - [:ul - (for [a (:plaid-item/accounts e)] - [:li (:plaid-account/name a) " - " (:plaid-account/number a)])])}]}) + {:key "accounts" + :name "Accounts" + :show-starting "md" + :render (fn [e] + [:ul + (for [a (:plaid-item/accounts e)] + [:li (:plaid-account/name a) " - " (:plaid-account/number a)])])}]})) -(def page (partial helper/page grid-page)) -(def table (partial helper/table grid-page)) + +(def page (helper/page-route grid-page)) +(def table (helper/table-route grid-page)) diff --git a/src/clj/auto_ap/ssr/company/reports.clj b/src/clj/auto_ap/ssr/company/reports.clj index ebb00243..af41992a 100644 --- a/src/clj/auto_ap/ssr/company/reports.clj +++ b/src/clj/auto_ap/ssr/company/reports.clj @@ -15,59 +15,60 @@ [datomic.api :as dc] [com.brunobonacci.mulog :as mu])) -(def grid-page {:id "report-table" - :nav (com/company-aside-nav) - :id-fn :db/id - :fetch-page (fn [user args] - (prn args) - (r/get-graphql {:client (:client args) - :clients (:clients args) - :start (:start (:parsed-query-params args)) - :per-page (:per-page (:parsed-query-params args)) - :sort (:sort (:parsed-query-params args)) - })) - :breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes - :company)} - "My Company"] +(def grid-page + (helper/build {:id "report-table" + :nav (com/company-aside-nav) + :id-fn :db/id + :fetch-page (fn [user args] + (prn args) + (r/get-graphql {:client (:client args) + :clients (:clients args) + :start (:start (:parsed-query-params args)) + :per-page (:per-page (:parsed-query-params args)) + :sort (:sort (:parsed-query-params args)) + })) + :breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes + :company)} + "My Company"] - [:a {:href (bidi/path-for ssr-routes/only-routes - :company-reports)} - "Reports"]] - :title "Reports" - :entity-name "Reports" - :route :company-reports-table - :action-buttons (fn [user _] - nil) - :row-buttons (fn [user e] - (com/a-icon-button {:href (:report/url e)} - svg/download)[ - (when (is-admin? user) - (com/icon-button {:hx-delete (str (bidi/path-for ssr-routes/only-routes - :company-reports-delete - :request-method :delete)) - :hx-target "closest tr"} - svg/trash))]) - :headers [{:key "name" - :name "Name" - :sort-key "name" - :render :report/name} - {:key "created-by" - :name "Created by" - :sort-key "creator" - :render (fn [report] - (when (:report/creator report) - (com/pill {:color :primary } - (:report/creator report))))} - {:key "created" - :name "Created" - :sort-key "created" - :render #(atime/unparse-local (:report/created %) - atime/normal-date)}]}) + [:a {:href (bidi/path-for ssr-routes/only-routes + :company-reports)} + "Reports"]] + :title "Reports" + :entity-name "Reports" + :route :company-reports-table + :action-buttons (fn [user _] + nil) + :row-buttons (fn [user e] + (com/a-icon-button {:href (:report/url e)} + svg/download)[ + (when (is-admin? user) + (com/icon-button {:hx-delete (str (bidi/path-for ssr-routes/only-routes + :company-reports-delete + :request-method :delete)) + :hx-target "closest tr"} + svg/trash))]) + :headers [{:key "name" + :name "Name" + :sort-key "name" + :render :report/name} + {:key "created-by" + :name "Created by" + :sort-key "creator" + :render (fn [report] + (when (:report/creator report) + (com/pill {:color :primary } + (:report/creator report))))} + {:key "created" + :name "Created" + :sort-key "created" + :render #(atime/unparse-local (:report/created %) + atime/normal-date)}]})) (def row* (partial helper/row* grid-page)) (def table* (partial helper/table* grid-page)) -(def table (partial helper/table grid-page)) -(def page (partial helper/page grid-page)) +(def table (helper/table-route grid-page)) +(def page (helper/page-route grid-page)) (defn delete-report [{:keys [form-params identity]}] @@ -76,7 +77,7 @@ :where [?i :report/key ?k]] (dc/db conn) (some-> (get form-params "id") not-empty Long/parseLong))) - report (dc/pull (dc/db conn) r/default-read id-to-delete)] + report (dc/pull (dc/db conn) r/default-read id-to-delete)] (assert-can-see-client identity (:report/client report)) (when id-to-delete (s3/delete-object :bucket-name (:data-bucket env) diff --git a/src/clj/auto_ap/ssr/company/yodlee.clj b/src/clj/auto_ap/ssr/company/yodlee.clj index e137f176..fb2401e5 100644 --- a/src/clj/auto_ap/ssr/company/yodlee.clj +++ b/src/clj/auto_ap/ssr/company/yodlee.clj @@ -1,21 +1,19 @@ (ns auto-ap.ssr.company.yodlee (:require - [auto-ap.datomic :refer [conn]] + [auto-ap.datomic :refer [conn pull-attr]] [auto-ap.datomic.yodlee2 :as yodlee2] [auto-ap.graphql.utils :refer [is-admin?]] [auto-ap.ssr-routes :as ssr-routes] [auto-ap.ssr.components :as com] - [auto-ap.ssr.svg :as svg] - [auto-ap.ssr.ui :refer [base-page]] [auto-ap.ssr.grid-page-helper :as helper] + [auto-ap.ssr.svg :as svg] [auto-ap.ssr.utils :refer [html-response]] [auto-ap.time :as atime] [auto-ap.yodlee.core2 :as yodlee] [bidi.bidi :as bidi] [config.core :refer [env]] [datomic.api :as dc] - [hiccup2.core :as hiccup] - [auto-ap.datomic :refer [pull-attr]])) + [hiccup2.core :as hiccup])) (def default-read '[:db/id :yodlee-provider-account/last-updated @@ -77,79 +75,88 @@ fastlink.open({fastLinkURL: '%s', (pull-attr (dc/db conn) :yodlee-provider-account/id (Long/parseLong (get form-params "id")))))]] [:div])))) -(def grid-page {:id "yodlee-table" - :nav (com/company-aside-nav) - :id-fn :db/id - :fetch-page (fn [user args] - (yodlee2/get-graphql - {:client (:client args) - :clients (:clients args) - :sort (:sort (:parsed-query-params args)) - :start (:start (:parsed-query-params args)) - :per-page (:per-page (:parsed-query-params args))})) - :breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes - :company)} - "My Company"] +(def grid-page + (helper/build + {:id "yodlee-table" + :nav (com/company-aside-nav) + :id-fn :db/id + :fetch-page (fn [request] + (println (count (:clients request))) + (yodlee2/get-graphql + {:client (:client request) + :clients (:clients request) + :sort (:sort (:parsed-query-params request)) + :start (:start (:parsed-query-params request)) + :per-page (:per-page (:parsed-query-params request))})) + :breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes + :company)} + "My Company"] - [:a {:href (bidi/path-for ssr-routes/only-routes - :company-yodlee)} - "Yodlee"]] - :title "Yodlee Accounts" - :entity-name "Yodlee accounts" - :route :company-yodlee-table - :action-buttons (fn [user _] - [(com/button {:color :primary - :on-click "openFastlink()" - :hx-get (bidi/path-for ssr-routes/only-routes - :company-yodlee-fastlink-dialog) - :hx-target "#modal-holder"} - (com/button-icon {} svg/refresh) - "Link new account")]) - :row-buttons (fn [user e] - [ - (com/button {:hx-put (bidi/path-for ssr-routes/only-routes - :company-yodlee-provider-account-reauthenticate) - :color :primary - :hx-target "#modal-holder"} - "Reauthenticate") - (when (is-admin? user) - (com/icon-button {:hx-put (bidi/path-for ssr-routes/only-routes - :company-yodlee-provider-account-refresh) - :hx-target "closest tr"} - svg/refresh))]) - :headers [{:key "provider-account" - :name "Provider Account" - :sort-key "provider-account" - :render :yodlee-provider-account/id} - {:key "status" - :name "Status" - :sort-key "status" - :render #(when-let [status (:yodlee-provider-account/status %)] - (com/pill {:color (if (not= status "SUCCESS") - :yellow - :primary) } - status))} - {:key "detailed-status" - :name "Detailed Status" - :sort-key "detailed-status" - :render #(when-let [status (:yodlee-provider-account/detailed-status %)] - status)} + [:a {:href (bidi/path-for ssr-routes/only-routes + :company-yodlee)} + "Yodlee"]] + :title "Yodlee Accounts" + :entity-name "Yodlee accounts" + :route :company-yodlee-table + :action-buttons (fn [request] + [(com/button {:color :primary + :on-click "openFastlink()" + :hx-get (bidi/path-for ssr-routes/only-routes + :company-yodlee-fastlink-dialog) + :hx-target "#modal-holder"} + (com/button-icon {} svg/refresh) + "Link new account")]) + :row-buttons (fn [request _] + [ + (com/button {:hx-put (bidi/path-for ssr-routes/only-routes + :company-yodlee-provider-account-reauthenticate) + :color :primary + :hx-target "#modal-holder"} + "Reauthenticate") + (when (is-admin? (:identity request)) + (com/icon-button {:hx-put (bidi/path-for ssr-routes/only-routes + :company-yodlee-provider-account-refresh) + :hx-target "closest tr"} + svg/refresh))]) + :headers [{:key "client" + :name "Client" + :sort-key "client" + :hide? (fn [args] + (= (count (:clients args)) 1)) + :render #(-> % :yodlee-provider-account/client :client/code)} + {:key "provider-account" + :name "Provider Account" + :sort-key "provider-account" + :render :yodlee-provider-account/id} + {:key "status" + :name "Status" + :sort-key "status" + :render #(when-let [status (:yodlee-provider-account/status %)] + (com/pill {:color (if (not= status "SUCCESS") + :yellow + :primary) } + status))} + {:key "detailed-status" + :name "Detailed Status" + :sort-key "detailed-status" + :render #(when-let [status (:yodlee-provider-account/detailed-status %)] + status)} - {:key "last-updated" - :name "Last Updated" - :sort-key "last-updated" - :render #(atime/unparse-local (:yodlee-provider-account/last-updated %) - atime/normal-date)} - {:key "accounts" - :name "Accounts" - :show-starting "md" - :render (fn [e] - [:ul - (for [a (:yodlee-provider-account/accounts e)] - [:li (:yodlee-account/name a) " - " (:yodlee-account/number a)])])}]}) + {:key "last-updated" + :name "Last Updated" + :sort-key "last-updated" + :render #(atime/unparse-local (:yodlee-provider-account/last-updated %) + atime/normal-date)} + {:key "accounts" + :name "Accounts" + :show-starting "md" + :render (fn [e] + [:ul + (for [a (:yodlee-provider-account/accounts e)] + [:li (:yodlee-account/name a) " - " (:yodlee-account/number a)])])}]})) -(def page (partial helper/page grid-page)) -(def table (partial helper/table grid-page)) +(def page (helper/page-route grid-page)) +(def table (helper/table-route grid-page)) ;; TODO delete-after-settle (defn refresh-provider-account [{:keys [form-params identity]}] diff --git a/src/clj/auto_ap/ssr/components/aside.clj b/src/clj/auto_ap/ssr/components/aside.clj index bbbbc761..6b7804ed 100644 --- a/src/clj/auto_ap/ssr/components/aside.clj +++ b/src/clj/auto_ap/ssr/components/aside.clj @@ -12,6 +12,7 @@ (assoc :type "button") (update :class str " cursor-pointer flex items-center p-2 w-full text-xs text-gray-600 rounded-lg transition duration-75 group hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700") (assoc :hx-indicator "find .htmx-indicator") + (assoc :hx-boost "true") (assoc :hx-select "#app-contents") (assoc :hx-target "#app-contents") (assoc :hx-swap "outerHTML")) diff --git a/src/clj/auto_ap/ssr/components/page.clj b/src/clj/auto_ap/ssr/components/page.clj index e6df4eac..ebda28be 100644 --- a/src/clj/auto_ap/ssr/components/page.clj +++ b/src/clj/auto_ap/ssr/components/page.clj @@ -6,7 +6,10 @@ [auto-ap.ssr.svg :as svg])) (defn page- [{:keys [nav page-specific client client-selection identity app-params] :or {app-params {}}} & children] - [:div#app + [:div#app {"_" (hiccup/raw " + on notification put event.detail.value into #notification-details then add .htmx-added to #notification-holder then remove .hidden from #notification-holder then wait 30ms then remove .htmx-added from #notification-holder + on htmx:responseError put event.detail.xhr.response into #error-details then add .htmx-added to #error-holder then remove .hidden from #error-holder then wait 30ms then remove .htmx-added from #error-holder" + )} (navbar- {:client-selection client-selection :client client :identity identity}) @@ -14,10 +17,7 @@ (left-aside- {:nav nav :page-specific page-specific}) [:div#main-content {:class "relative w-full h-full lg:pl-64 overflow-y-auto px-4 bg-gray-100 dark:bg-gray-900 min-h-content " - "_" (hiccup/raw " - on notification put event.detail.value into #notification-details then add .htmx-added to #notification-holder then remove .hidden from #notification-holder then wait 30ms then remove .htmx-added from #notification-holder - on htmx:responseError put event.detail.xhr.response into #error-details then add .htmx-added to #error-holder then remove .hidden from #error-holder then wait 30ms then remove .htmx-added from #error-holder" - )} + } [:div#notification-holder.hidden [:div.fixed.top-0.right-0.left-0.z-30.mx-auto.max-w-screen-lg.w-screen-lg.my-0.pt-8.rounded-lg [:div.relative diff --git a/src/clj/auto_ap/ssr/grid_page_helper.clj b/src/clj/auto_ap/ssr/grid_page_helper.clj index 230362ec..36b1782c 100644 --- a/src/clj/auto_ap/ssr/grid_page_helper.clj +++ b/src/clj/auto_ap/ssr/grid_page_helper.clj @@ -1,17 +1,21 @@ (ns auto-ap.ssr.grid-page-helper (:require + [auto-ap.graphql.utils :refer [extract-client-ids]] + [auto-ap.query-params :as query-params] + [auto-ap.routes.utils + :refer [wrap-client-redirect-unauthenticated wrap-secure]] [auto-ap.ssr-routes :as ssr-routes] [auto-ap.ssr.components :as com] [auto-ap.ssr.svg :as svg] [auto-ap.ssr.ui :refer [base-page]] [auto-ap.ssr.utils :refer [html-response]] + [auto-ap.time :as atime] + [malli.core :as m] [bidi.bidi :as bidi] [cemerick.url :as url] [clojure.string :as str] [hiccup2.core :as hiccup] - [clj-time.core :as time] - [auto-ap.time :as atime] - [auto-ap.query-params :as query-params])) + [malli.transform :as mt2])) (defn row* [gridspec user entity {:keys [flash? delete-after-settle? request] :as options}] (let [cells (->> gridspec @@ -125,23 +129,12 @@ (com/data-grid-header {}))}))) - - - - - (defn sort->query [s] (str/join "," (map (fn [k] (format "%s:%s" (:sort-key k) (if (= true (:asc k)) "asc" "desc"))) s))) -(defn params->query-string [q] - (-> q - :parsed-query-params - (update :sort sort->query) - (url/map->query))) - (defn default-unparse-query-params [query-params] (reduce (fn [query-params [k value]] @@ -169,37 +162,6 @@ query-params query-params)) - - - -(defn table [grid-spec {:keys [identity] :as 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 (unparse-query-params (:parsed-query-params request))))} - :oob (when-let [oob-render (:oob-render grid-spec)] - (oob-render request))))) - - -(defn page [grid-spec {:keys [identity] :as request}] - (base-page - request - (com/page {:nav (:nav grid-spec) - :page-specific (when-let [page-specific-nav (:page-specific-nav grid-spec)] - [:div#page-specific-nav (page-specific-nav request)]) - :client-selection (:client-selection (:session request)) - :clients (:clients request) - :client (:client request) - :identity (:identity request)} - (apply com/breadcrumbs {} (:breadcrumbs grid-spec)) - (table* grid-spec - identity - request)) - (:title grid-spec))) - (defn default-parse-query-params [grid-spec] (comp (query-params/apply-remove-sort) @@ -211,3 +173,116 @@ (query-params/parse-key :start query-params/parse-long) (query-params/parse-key :start-date query-params/parse-date) (query-params/parse-key :end-date query-params/parse-date))) + +(defn wrap-trim-client-ids [handler] + (fn trim-client-ids [request] + (let [valid-clients (extract-client-ids (:clients request) + (:client request) + (:client-id (:parsed-query-params request)) + (when (:client-code (:parsed-query-params request)) + [:client/code (:client-code (:parsed-query-params request))])) + valid-clients (->> valid-clients + (take 20) + set)] + (handler (assoc request :trimmed-clients valid-clients))))) + +(defn table-route [grid-spec] + (-> (fn table [{:keys [identity] :as 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 (unparse-query-params (:parsed-query-params request))))} + :oob (when-let [oob-render (:oob-render grid-spec)] + (oob-render request))))) + (wrap-trim-client-ids) + (query-params/wrap-parse-query-params (or (:parse-query-params grid-spec) + (default-parse-query-params grid-spec))) + (wrap-secure) + (wrap-client-redirect-unauthenticated))) + +(defn page-route [grid-spec ] + (-> (fn page [{:keys [identity] :as request}] + (base-page + request + (com/page {:nav (:nav grid-spec) + :page-specific (when-let [page-specific-nav (:page-specific-nav grid-spec)] + [:div#page-specific-nav (page-specific-nav request)]) + :client-selection (:client-selection (:session request)) + :clients (:clients request) + :client (:client request) + :identity (:identity request)} + (apply com/breadcrumbs {} (:breadcrumbs grid-spec)) + (table* grid-spec + identity + request)) + (:title grid-spec))) + (wrap-trim-client-ids) + (query-params/wrap-parse-query-params (or (:parse-query-params grid-spec) + (default-parse-query-params grid-spec))) + (wrap-secure) + (wrap-client-redirect-unauthenticated))) + +(def request-spec (m/schema [:map])) +(def entity-spec (m/schema [:map])) +(def header-spec (m/schema [:map + [:key :string] + [:name :string] + [:sort-key {:optional true} :string] + [:render [:=> [:cat entity-spec] :any]] + [:hide? {:optional true} [:=> [:cat entity-spec] :boolean]]])) +(def grid-spec (m/schema [:map + [:id :string] + [:nav vector?] + [:page-specific-nav + {:optional true + :default (fn [request])} + [:maybe [:=> + [:cat request-spec] + vector?]]] + [:id-fn {:default :db/id + :optional true} + [:=> + [:cat map?] + nat-int?]] + [:fetch-page [:=> + [:cat request-spec] + [:cat [:vector entity-spec] :int]]] + [:parse-query-params + {:optional true} + [:=> + [:cat [:map-of :keyword :any]] + [:map-of :keyword :any]]] + [:oob-render + {:optional true + :default (fn [request])} + [:=> + [:cat request-spec] + vector?]] + [:breadcrumbs [:vector vector?]] + [:title :string] + [:entity-name :string] + [:route :keyword] + [:action-buttons + {:default (fn [request]) + :optional true} + [:=> + [:cat request-spec] + [:maybe [:vector vector?]]]] + [:row-buttons + {:default (fn [request entity]) + :optional true} + [:=> + [:cat request-spec entity-spec] + [:maybe [:vector vector?]]]] + [:headers [:vector header-spec]]])) + +(defn build [grid-page] + (when-not (m/validate grid-spec grid-page) + (throw (ex-info "Could not validate grid" + (m/explain grid-spec grid-page)))) + (m/decode grid-spec grid-page (mt2/default-value-transformer {::mt2/add-optional-keys true}))) + + diff --git a/src/clj/auto_ap/ssr/pos/cash_drawer_shifts.clj b/src/clj/auto_ap/ssr/pos/cash_drawer_shifts.clj index df5cc696..12cfe3fd 100644 --- a/src/clj/auto_ap/ssr/pos/cash_drawer_shifts.clj +++ b/src/clj/auto_ap/ssr/pos/cash_drawer_shifts.clj @@ -8,10 +8,6 @@ merge-query pull-many query2]] - [auto-ap.graphql.utils :refer [extract-client-ids]] - [auto-ap.query-params :as query-params] - [auto-ap.routes.utils - :refer [wrap-client-redirect-unauthenticated wrap-secure]] [auto-ap.ssr-routes :as ssr-routes] [auto-ap.ssr.components :as com] [auto-ap.ssr.grid-page-helper :as helper] @@ -19,7 +15,8 @@ [auto-ap.time :as atime] [bidi.bidi :as bidi] [clj-time.coerce :as c] - [datomic.api :as dc])) + [datomic.api :as dc] + [malli.core :as m])) ;; always should be fast @@ -42,17 +39,10 @@ (defn fetch-ids [db request] (let [query-params (:parsed-query-params request) - valid-clients (extract-client-ids (:clients request) - (:client-id query-params) - (when (:client-code query-params) - [:client/code (:client-code query-params)])) - valid-clients (->> valid-clients - (take 10) - set) query (cond-> {:query {:find [] :in ['$ '[?clients ?start-date ?end-date]] :where '[[(iol-ion.query/scan-cash-drawer-shifts $ ?clients ?start-date ?end-date) [[?e _ ?sort-default] ...]]]} - :args [db [valid-clients + :args [db [(:trimmed-clients request) (some-> (:start-date query-params) c/to-date) (some-> (:end-date query-params) c/to-date )]]} (:sort query-params) (add-sorter-fields {"client" ['[?e :cash-drawer-shift/client ?c] @@ -93,64 +83,57 @@ [(->> (hydrate-results ids-to-retrieve db request)) matching-count])) -(def grid-page {:id "cash-drawer-shift-table" - :nav (com/main-aside-nav) - :page-specific-nav filters - :id-fn :db/id - :fetch-page fetch-page - :oob-render - (fn [request] - [(assoc-in (date-range-field* request) [1 :hx-swap-oob] true)]) - :breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes - :company)} - "POS"] +(def grid-page + (helper/build + {:id "cash-drawer-shift-table" + :nav (com/main-aside-nav) + :page-specific-nav filters + :fetch-page fetch-page + :oob-render + (fn [request] + [(assoc-in (date-range-field* request) [1 :hx-swap-oob] true)]) + :breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes + :company)} + "POS"] + + [:a {:href (bidi/path-for ssr-routes/only-routes + :pos-cash-drawer-shifts)} + "Cash Drawer Shifts"]] + :title "Cash drawer shifts" + :entity-name "Cash drawer shift" + :route :pos-cash-drawer-shift-table + :headers [{:key "client" + :name "Client" + :sort-key "client" + :hide? (fn [args] + (= (count (:clients args)) 1)) + :render #(-> % :cash-drawer-shift/client :client/code)} + {:key "date" + :name "Date" + :sort-key "date" + :render #(atime/unparse-local (:cash-drawer-shift/date %) atime/standard-time)} + {:key "paid-in" + :name "Paid in" + :sort-key "paid-in" + :render #(some->> % :cash-drawer-shift/paid-in (format "$%.2f"))} + {:key "paid-out" + :name "Paid out" + :sort-key "paid-out" + :render #(some->> % :cash-drawer-shift/paid-out (format "$%.2f"))} + {:key "expected-cash" + :name "Expected cash" + :sort-key "expected-cash" + :render #(some->> % :cash-drawer-shift/expected-cash (format "$%.2f"))} + {:key "opened-cash" + :name "Opened cash" + :sort-key "opened-cash" + :render #(some->> % :cash-drawer-shift/opened-cash (format "$%.2f"))} + ]})) - [:a {:href (bidi/path-for ssr-routes/only-routes - :pos-cash-drawer-shifts)} - "Cash Drawer Shifts"]] - :title "Cash drawer shifts" - :entity-name "Cash drawer shift" - :route :pos-cash-drawer-shift-table - :action-buttons (fn [request]) - :row-buttons (fn [_ e]) - :headers [{:key "client" - :name "Client" - :sort-key "client" - :hide? (fn [args] - (= (count (:clients args)) 1)) - :render #(-> % :cash-drawer-shift/client :client/code)} - {:key "date" - :name "Date" - :sort-key "date" - :render #(atime/unparse-local (:cash-drawer-shift/date %) atime/standard-time)} - {:key "paid-in" - :name "Paid in" - :sort-key "paid-in" - :render #(some->> % :cash-drawer-shift/paid-in (format "$%.2f"))} - {:key "paid-out" - :name "Paid out" - :sort-key "paid-out" - :render #(some->> % :cash-drawer-shift/paid-out (format "$%.2f"))} - {:key "expected-cash" - :name "Expected cash" - :sort-key "expected-cash" - :render #(some->> % :cash-drawer-shift/expected-cash (format "$%.2f"))} - {:key "opened-cash" - :name "Opened cash" - :sort-key "opened-cash" - :render #(some->> % :cash-drawer-shift/opened-cash (format "$%.2f"))} - ]}) (def row* (partial helper/row* grid-page)) (def table* (partial helper/table* grid-page)) -(def table - (query-params/wrap-parse-query-params (partial helper/table grid-page) - (helper/default-parse-query-params grid-page)) - - ) -(def page (query-params/wrap-parse-query-params (partial helper/page grid-page) - (helper/default-parse-query-params grid-page))) (def key->handler - {:pos-cash-drawer-shifts (wrap-client-redirect-unauthenticated (wrap-secure page)) - :pos-cash-drawer-shift-table (wrap-client-redirect-unauthenticated (wrap-secure table))}) + {:pos-cash-drawer-shifts (helper/page-route grid-page) + :pos-cash-drawer-shift-table (helper/table-route grid-page)}) diff --git a/src/clj/auto_ap/ssr/pos/common.clj b/src/clj/auto_ap/ssr/pos/common.clj index 3eeaa63d..ef37e0af 100644 --- a/src/clj/auto_ap/ssr/pos/common.clj +++ b/src/clj/auto_ap/ssr/pos/common.clj @@ -7,9 +7,6 @@ ;; TODO make total fields take decimals (defn date-range-field* [request] - (println "DR" (some-> request - :parsed-query-params - )) [:div#date-range {} (com/field {:label "Date Range"} [:div.space-y-4 diff --git a/src/clj/auto_ap/ssr/pos/expected_deposits.clj b/src/clj/auto_ap/ssr/pos/expected_deposits.clj index 1aebefd0..a11b17bc 100644 --- a/src/clj/auto_ap/ssr/pos/expected_deposits.clj +++ b/src/clj/auto_ap/ssr/pos/expected_deposits.clj @@ -21,7 +21,8 @@ [bidi.bidi :as bidi] [clj-time.coerce :as c] [datomic.api :as dc] - [auto-ap.client-routes :as client-routes])) + [auto-ap.client-routes :as client-routes] + [malli.core :as m])) ;; make params parsing composable @@ -132,75 +133,65 @@ [(->> (hydrate-results ids-to-retrieve db args)) matching-count])) -(def grid-page {:id "expected-deposit-table" - :nav (com/main-aside-nav) - :page-specific-nav filters - :id-fn :db/id - :fetch-page fetch-page - :oob-render - (fn [request] - [(assoc-in (date-range-field* request) [1 :hx-swap-oob] true)]) - :breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes - :company)} - "POS"] +(def grid-page + (helper/build + {:id "expected-deposit-table" + :nav (com/main-aside-nav) + :page-specific-nav filters + :fetch-page fetch-page + :parse-query-params (comp + (query-params/parse-key :total-gte query-params/parse-double) + (query-params/parse-key :total-lte query-params/parse-double) + (helper/default-parse-query-params grid-page)) + :oob-render + (fn [request] + [(assoc-in (date-range-field* request) [1 :hx-swap-oob] true)]) + :breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes + :company)} + "POS"] - [:a {:href (bidi/path-for ssr-routes/only-routes - :pos-expected-deposits)} - "Expected deposits"]] - :title "Expected deposits" - :entity-name "Expected deposit" - :route :pos-expected-deposit-table - :action-buttons (fn [request] - ) - :row-buttons (fn [_ e] - [ - (when (:expected-deposit/reference-link e) - (com/a-icon-button {:href (:expected-deposit/reference-link e)} - svg/external-link)) - (when-let [transaction-id (-> e (:transaction/_expected-deposit) first :db/id)] - (com/a-button {:href (str (bidi/path-for client-routes/routes - :transactions) - "?exact-match-id=" - transaction-id)} "Transaction"))]) - :headers [{:key "client" - :name "Client" - :sort-key "client" - :hide? (fn [args] - (= (count (:clients args)) 1)) - :render #(-> % :expected-deposit/client :client/code)} - {:key "date" - :name "Date" - :sort-key "date" - :render #(atime/unparse-local (:expected-deposit/date %) atime/standard-time)} - {:key "sales-date" - :name "Sales Date" - :sort-key "sales-date" - :render #(atime/unparse-local (:expected-deposit/sales-date %) atime/standard-time)} - {:key "total" - :name "Total" - :sort-key "total" - :render #(some->> % :expected-deposit/total (format "$%.2f"))} - {:key "fee" - :name "Fee" - :sort-key "fee" - :render #(some->> % :expected-deposit/fee (format "$%.2f"))}]}) + [:a {:href (bidi/path-for ssr-routes/only-routes + :pos-expected-deposits)} + "Expected deposits"]] + :title "Expected deposits" + :entity-name "Expected deposit" + :route :pos-expected-deposit-table + :row-buttons (fn [_ e] + [ + (when (:expected-deposit/reference-link e) + (com/a-icon-button {:href (:expected-deposit/reference-link e)} + svg/external-link)) + (when-let [transaction-id (-> e (:transaction/_expected-deposit) first :db/id)] + (com/a-button {:href (str (bidi/path-for client-routes/routes + :transactions) + "?exact-match-id=" + transaction-id)} "Transaction"))]) + :headers [{:key "client" + :name "Client" + :sort-key "client" + :hide? (fn [args] + (= (count (:clients args)) 1)) + :render #(-> % :expected-deposit/client :client/code)} + {:key "date" + :name "Date" + :sort-key "date" + :render #(atime/unparse-local (:expected-deposit/date %) atime/standard-time)} + {:key "sales-date" + :name "Sales Date" + :sort-key "sales-date" + :render #(atime/unparse-local (:expected-deposit/sales-date %) atime/standard-time)} + {:key "total" + :name "Total" + :sort-key "total" + :render #(some->> % :expected-deposit/total (format "$%.2f"))} + {:key "fee" + :name "Fee" + :sort-key "fee" + :render #(some->> % :expected-deposit/fee (format "$%.2f"))}]})) (def row* (partial helper/row* grid-page)) (def table* (partial helper/table* grid-page)) -(def table (-> (partial helper/table grid-page) - (query-params/wrap-parse-query-params - (comp - (query-params/parse-key :total-gte query-params/parse-double) - (query-params/parse-key :total-lte query-params/parse-double) - (helper/default-parse-query-params grid-page))))) - -(def page (-> (partial helper/page grid-page) - (query-params/wrap-parse-query-params - (comp - (query-params/parse-key :total-gte query-params/parse-double) - (query-params/parse-key :total-lte query-params/parse-double) - (helper/default-parse-query-params grid-page))))) (def key->handler - {:pos-expected-deposits (wrap-client-redirect-unauthenticated (wrap-secure page)) - :pos-expected-deposit-table (wrap-client-redirect-unauthenticated (wrap-secure table))}) + {:pos-expected-deposits (helper/page-route grid-page) + :pos-expected-deposit-table (helper/table-route grid-page)}) diff --git a/src/clj/auto_ap/ssr/pos/refunds.clj b/src/clj/auto_ap/ssr/pos/refunds.clj index b774b226..49f741b1 100644 --- a/src/clj/auto_ap/ssr/pos/refunds.clj +++ b/src/clj/auto_ap/ssr/pos/refunds.clj @@ -21,7 +21,8 @@ [clj-time.coerce :as c] [datomic.api :as dc] [clojure.set :as set] - [auto-ap.query-params :as query-params])) + [auto-ap.query-params :as query-params] + [malli.core :as m])) ;; TODO refunds ;; always should be fast @@ -48,17 +49,10 @@ (defn fetch-ids [db request] (let [query-params (:parsed-query-params request) - valid-clients (extract-client-ids (:clients request) - (:client-id query-params) - (when (:client-code query-params) - [:client/code (:client-code query-params)])) - valid-clients (->> valid-clients - (take 10) - set) query (cond-> {:query {:find [] :in ['$ '[?clients ?start-date ?end-date]] :where '[[(iol-ion.query/scan-sales-refunds $ ?clients ?start-date ?end-date) [[?e _ ?sort-default] ...]]]} - :args [db [valid-clients + :args [db [(:trimmed-clients request) (some-> query-params :start-date c/to-date) (some-> query-params :end-date c/to-date )]]} (:sort query-params) (add-sorter-fields {"client" ['[?e :sales-refund/client ?c] @@ -109,64 +103,54 @@ [(->> (hydrate-results ids-to-retrieve db request)) matching-count])) -(def grid-page {:id "refund-table" - :nav (com/main-aside-nav) - :page-specific-nav filters - :id-fn :db/id - :fetch-page fetch-page - :oob-render - (fn [request] - [(assoc-in (date-range-field* request) [1 :hx-swap-oob] true)]) - :breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes - :company)} - "POS"] +(def grid-page + (helper/build {:id "refund-table" + :nav (com/main-aside-nav) + :page-specific-nav filters + :fetch-page fetch-page + :parse-query-params (comp + (query-params/parse-key :total-gte query-params/parse-double) + (query-params/parse-key :total-lte query-params/parse-double) + (helper/default-parse-query-params grid-page)) + :oob-render + (fn [request] + [(assoc-in (date-range-field* request) [1 :hx-swap-oob] true)]) + :breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes + :company)} + "POS"] - [:a {:href (bidi/path-for ssr-routes/only-routes - :pos-refunds)} - "Refunds"]] - :title "Refunds" - :entity-name "Refund" - :route :pos-refund-table - :action-buttons (fn [request]) - :row-buttons (fn [request _]) - :headers [{:key "client" - :name "Client" - :sort-key "client" - :hide? (fn [args] - (= (count (:clients args)) 1)) - :render #(-> % :sales-refund/client :client/code)} - {:key "date" - :name "Date" - :sort-key "date" - :render #(atime/unparse-local (:sales-refund/date %) atime/standard-time)} - {:key "total" - :name "Total" - :sort-key "total" - :render #(some->> % :sales-refund/total (format "$%.2f"))} - {:key "type" - :name "Type" - :sort-key "type" - :render :sales-refund/type} - {:key "fee" - :name "Fee" - :sort-key "fee" - :render #(some->> % :sales-refund/fee (format "$%.2f"))}]}) + [:a {:href (bidi/path-for ssr-routes/only-routes + :pos-refunds)} + "Refunds"]] + :title "Refunds" + :entity-name "Refund" + :route :pos-refund-table + :headers [{:key "client" + :name "Client" + :sort-key "client" + :hide? (fn [args] + (= (count (:clients args)) 1)) + :render #(-> % :sales-refund/client :client/code)} + {:key "date" + :name "Date" + :sort-key "date" + :render #(atime/unparse-local (:sales-refund/date %) atime/standard-time)} + {:key "total" + :name "Total" + :sort-key "total" + :render #(some->> % :sales-refund/total (format "$%.2f"))} + {:key "type" + :name "Type" + :sort-key "type" + :render :sales-refund/type} + {:key "fee" + :name "Fee" + :sort-key "fee" + :render #(some->> % :sales-refund/fee (format "$%.2f"))}]})) (def row* (partial helper/row* grid-page)) (def table* (partial helper/table* grid-page)) -(def table (-> (partial helper/table grid-page) - (query-params/wrap-parse-query-params - (comp - (query-params/parse-key :total-gte query-params/parse-double) - (query-params/parse-key :total-lte query-params/parse-double) - (helper/default-parse-query-params grid-page))))) -(def page (-> (partial helper/page grid-page) - (query-params/wrap-parse-query-params - (comp - (query-params/parse-key :total-gte query-params/parse-double) - (query-params/parse-key :total-lte query-params/parse-double) - (helper/default-parse-query-params grid-page))))) (def key->handler - {:pos-refunds (wrap-client-redirect-unauthenticated (wrap-secure page)) - :pos-refund-table (wrap-client-redirect-unauthenticated (wrap-secure table))}) + {:pos-refunds (helper/page-route grid-page) + :pos-refund-table (helper/table-route grid-page)}) diff --git a/src/clj/auto_ap/ssr/pos/sales_orders.clj b/src/clj/auto_ap/ssr/pos/sales_orders.clj index 54133acc..3ae3dbcd 100644 --- a/src/clj/auto_ap/ssr/pos/sales_orders.clj +++ b/src/clj/auto_ap/ssr/pos/sales_orders.clj @@ -22,12 +22,8 @@ [auto-ap.time :as atime] [bidi.bidi :as bidi] [clj-time.coerce :as c] - [datomic.api :as dc])) - -;; TODO incorporate parsing into spec -;; TODO incorporate permissions into spec -;; TODO incorporate limiting clients into spec - + [datomic.api :as dc] + [malli.core :as m])) (defn filters [request] [:form {"hx-trigger" "change delay:500ms, keyup changed from:.hot-filter delay:1000ms" @@ -98,18 +94,10 @@ (defn fetch-ids [db request] (let [query-params (:parsed-query-params request) - valid-clients (extract-client-ids (:clients request) - (:client request) - (:client-id query-params) - (when (:client-code query-params) - [:client/code (:client-code query-params)])) - valid-clients (->> valid-clients - (take 10) - set) query (cond-> {:query {:find [] :in ['$ '[?clients ?start-date ?end-date]] :where '[[(iol-ion.query/scan-sales-orders $ ?clients ?start-date ?end-date) [[?e _ ?sort-default] ...]]]} - :args [db [valid-clients + :args [db [(:trimmed-clients request) (some-> (:start-date query-params) c/to-date) (some-> (:end-date query-params) c/to-date)]]} (:sort query-params) (add-sorter-fields {"client" ['[?e :sales-order/client ?c] @@ -182,103 +170,93 @@ matching-count])) -(def grid-page {:id "sales-table" - :nav (com/main-aside-nav) - :page-specific-nav filters - :id-fn :db/id - :fetch-page fetch-page - :oob-render - (fn [request] - [(assoc-in (date-range-field* request) [1 :hx-swap-oob] true)]) - :breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes - :company)} - "POS"] +(def grid-page + (helper/build + {:id "sales-table" + :nav (com/main-aside-nav) + :page-specific-nav filters + :fetch-page fetch-page + :parse-query-params (comp + (query-params/parse-key :processor #(query-params/parse-keyword "ccp-processor" %)) + (query-params/parse-key :total-gte query-params/parse-double) + (query-params/parse-key :total-lte query-params/parse-double) + (helper/default-parse-query-params grid-page)) + :oob-render + (fn [request] + [(assoc-in (date-range-field* request) [1 :hx-swap-oob] true)]) + :breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes + :company)} + "POS"] + + [:a {:href (bidi/path-for ssr-routes/only-routes + :pos-sales)} + "Sales"]] + :title "Sales orders" + :entity-name "Sales orders" + :route :pos-sales-table + :action-buttons (fn [request] + (let [{:keys [total tax]} (d-sales/summarize-orders (:ids (fetch-ids (dc/db conn) request)))] + (when (and total tax) + [ + (com/pill {:color :primary} + (format "Total $%.2f" total) + ) + (com/pill {:color :secondary} + (format "Tax $%.2f" tax ) + )]))) + :row-buttons (fn [_ e] + (when (:sales-order/reference-link e) + [(com/a-icon-button {:href (:sales-order/reference-link e)} + svg/external-link)])) + :headers [{:key "client" + :name "Client" + :sort-key "client" + :hide? (fn [args] + (= (count (:clients args)) 1)) + :render #(-> % :sales-order/client :client/code)} + {:key "date" + :name "Date" + :sort-key "date" + :render #(atime/unparse-local (:sales-order/date %) atime/standard-time)} + {:key "source" + :name "Source" + :sort-key "source" + :render (fn [sales-order] + (when (:sales-order/source sales-order) + (com/pill {:color :primary } + (:sales-order/source sales-order))))} + {:key "total" + :name "Total" + :sort-key "total" + :render #(some->> % :sales-order/total (format "$%.2f"))} + {:key "tax" + :name "Tax" + :sort-key "tax" + :render #(some->> % :sales-order/tax (format "$%.2f"))} + {:key "tip" + :name "Tip" + :sort-key "tip" + :render #(some->> % :sales-order/tip (format "$%.2f"))} + {:key "Payment methods" + :name "Payment Methods" + :render (fn [sales-order] + [:div.flex.space-x-2 + (for [payment-method (->> sales-order :sales-order/charges (map :charge/type-name) set)] + (com/pill {:color :primary } + (condp = payment-method + "CASH" "cash" + "" nil + "ALL" nil + "CARD" "card" + "SQUARE_GIFT_CARD" "gift card" + "OTHER" "other" + nil)))])}]} + )) - [:a {:href (bidi/path-for ssr-routes/only-routes - :pos-sales)} - "Sales"]] - :title "Sales orders" - :entity-name "Sales orders" - :route :pos-sales-table - :action-buttons (fn [request] - (let [{:keys [total tax]} (d-sales/summarize-orders (:ids (fetch-ids (dc/db conn) request)))] - (when (and total tax) - [ - (com/pill {:color :primary} - (format "Total $%.2f" total) - ) - (com/pill {:color :secondary} - (format "Tax $%.2f" tax ) - )]))) - :row-buttons (fn [_ e] - (when (:sales-order/reference-link e) - [(com/a-icon-button {:href (:sales-order/reference-link e)} - svg/external-link)])) - :headers [ - {:key "client" - :name "Client" - :sort-key "client" - :hide? (fn [args] - (= (count (:clients args)) 1)) - :render #(-> % :sales-order/client :client/code)} - {:key "date" - :name "Date" - :sort-key "date" - :render #(atime/unparse-local (:sales-order/date %) atime/standard-time)} - {:key "source" - :name "Source" - :sort-key "source" - :render (fn [sales-order] - (when (:sales-order/source sales-order) - (com/pill {:color :primary } - (:sales-order/source sales-order))))} - {:key "total" - :name "Total" - :sort-key "total" - :render #(some->> % :sales-order/total (format "$%.2f"))} - {:key "tax" - :name "Tax" - :sort-key "tax" - :render #(some->> % :sales-order/tax (format "$%.2f"))} - {:key "tip" - :name "Tip" - :sort-key "tip" - :render #(some->> % :sales-order/tip (format "$%.2f"))} - {:key "Payment methods" - :name "Payment Methods" - :render (fn [sales-order] - [:div.flex.space-x-2 - (for [payment-method (->> sales-order :sales-order/charges (map :charge/type-name) set)] - (com/pill {:color :primary } - (condp = payment-method - "CASH" "cash" - "" nil - "ALL" nil - "CARD" "card" - "SQUARE_GIFT_CARD" "gift card" - "OTHER" "other" - nil)))])}]}) (def row* (partial helper/row* grid-page)) (def table* (partial helper/table* grid-page)) - - -(def table (-> (partial helper/table grid-page) - (query-params/wrap-parse-query-params - (comp - (query-params/parse-key :processor #(query-params/parse-keyword "ccp-processor" %)) - (query-params/parse-key :total-gte query-params/parse-double) - (query-params/parse-key :total-lte query-params/parse-double) - (helper/default-parse-query-params grid-page))))) -(def page (-> (partial helper/page grid-page) - (query-params/wrap-parse-query-params - (comp - (query-params/parse-key :processor #(query-params/parse-keyword "ccp-processor" %)) - (query-params/parse-key :total-gte query-params/parse-double) - (query-params/parse-key :total-lte query-params/parse-double) - (helper/default-parse-query-params grid-page))))) - (def key->handler - {:pos-sales (wrap-client-redirect-unauthenticated (wrap-secure page)) - :pos-sales-table (wrap-client-redirect-unauthenticated (wrap-secure table))}) + {:pos-sales (helper/page-route grid-page) + :pos-sales-table (helper/table-route grid-page)}) diff --git a/src/clj/auto_ap/ssr/pos/tenders.clj b/src/clj/auto_ap/ssr/pos/tenders.clj index b615d195..27c4de7e 100644 --- a/src/clj/auto_ap/ssr/pos/tenders.clj +++ b/src/clj/auto_ap/ssr/pos/tenders.clj @@ -8,19 +8,19 @@ merge-query pull-many query2]] - [auto-ap.graphql.utils :refer [extract-client-ids]] - [auto-ap.routes.utils - :refer [wrap-client-redirect-unauthenticated wrap-secure]] - [auto-ap.ssr.pos.common :refer [date-range-field* processor-field* total-field*]] + [auto-ap.query-params :as query-params] [auto-ap.ssr-routes :as ssr-routes] [auto-ap.ssr.components :as com] [auto-ap.ssr.grid-page-helper :as helper] + [auto-ap.ssr.pos.common + :refer [date-range-field* processor-field* total-field*]] [auto-ap.ssr.svg :as svg] [auto-ap.time :as atime] [bidi.bidi :as bidi] [clj-time.coerce :as c] [datomic.api :as dc] - [auto-ap.query-params :as query-params])) + [malli.core :as m] + [malli.transform :as mt2])) ;; always should be fast @@ -48,19 +48,10 @@ (defn fetch-ids [db request] (let [query-params (:parsed-query-params request) - valid-clients (extract-client-ids (:clients request) - (:client request) - (:client-id query-params) - (when (:client-code query-params) - [:client/code (:client-code query-params)])) - valid-clients (->> valid-clients - (take 10) - set) - processor (some-> query-params :processor (#(keyword "ccp-processor" (name %)))) ; TODO parse query (cond-> {:query {:find [] :in ['$ '[?clients ?start-date ?end-date]] :where '[[(iol-ion.query/scan-charges $ ?clients ?start-date ?end-date) [[?e _ ?sort-default] ...]]]} - :args [db [valid-clients + :args [db [(:trimmed-clients request) (some-> (:start-date query-params) c/to-date) (some-> (:end-date query-params) c/to-date)]]} (:sort query-params) (add-sorter-fields {"client" ['[?e :charge/client ?c] @@ -90,10 +81,10 @@ '[(<= ?a ?total-lte)]]} :args [(:total-lte query-params)]}) - processor + (:processor query-params) (merge-query {:query {:in '[?processor] :where ['[?e :charge/processor ?processor]]} - :args [processor]}) + :args [(:processor query-params)]}) true @@ -118,81 +109,73 @@ [(->> (hydrate-results ids-to-retrieve db request)) matching-count])) -(def grid-page {:id "tender-table" - :nav (com/main-aside-nav) - :page-specific-nav filters - :id-fn :db/id - :fetch-page fetch-page - :oob-render - (fn [request] - [(assoc-in (date-range-field* request) [1 :hx-swap-oob] true)]) - :breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes - :company)} - "POS"] +(def grid-page + (helper/build + {:id "tender-table" + :nav (com/main-aside-nav) + :page-specific-nav filters + :fetch-page fetch-page + :parse-query-params (comp + (query-params/parse-key :processor #(query-params/parse-keyword "ccp-processor" %)) + (query-params/parse-key :total-gte query-params/parse-double) + (query-params/parse-key :total-lte query-params/parse-double) + (helper/default-parse-query-params grid-page)) + :oob-render + (fn [request] + [(assoc-in (date-range-field* request) [1 :hx-swap-oob] true)]) + :breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes + :company)} + "POS"] + + [:a {:href (bidi/path-for ssr-routes/only-routes + :pos-tenders)} + "Tenders"]] + :title "Tenders" + :entity-name "Tender" + :route :pos-tender-table + :row-buttons (fn [request e] + (when (:charge/reference-link e) + [(com/a-icon-button {:href (:charge/reference-link e)} + svg/external-link)])) + :headers [{:key "client" + :name "Client" + :sort-key "client" + :hide? (fn [request] + (= (count (:clients request)) 1)) + :render #(-> % :charge/client :client/code)} + {:key "date" + :name "Date" + :sort-key "date" + :render #(atime/unparse-local (:charge/date %) atime/standard-time)} + {:key "total" + :name "Total" + :sort-key "total" + :render #(some->> % :charge/total (format "$%.2f"))} + {:key "processor" + :name "Processor" + :sort-key "processor" + :render (fn [sales-order] + (when (:charge/processor sales-order) + (com/pill {:color :primary } + (name (:charge/processor sales-order)))))} + {:key "tip" + :name "Tip" + :sort-key "tip" + :render #(some->> % :charge/tip (format "$%.2f"))} + {:key "links" + :name "Links" + :render (fn [entity] + (when-let [expected-deposit-id (some->> entity :expected-deposit/_charges first :db/id)] + [:a {:href (str (bidi/path-for ssr-routes/only-routes + :pos-expected-deposits) + "?exact-match-id=" expected-deposit-id) + :hx-boost "true"} + (com/pill {:color :secondary} "expected deposit")]))}]})) - [:a {:href (bidi/path-for ssr-routes/only-routes - :pos-tenders)} - "Tenders"]] - :title "Tenders" - :entity-name "Tender" - :route :pos-tender-table - :action-buttons (fn [request]) - :row-buttons (fn [request e] - (when (:charge/reference-link e) - [(com/a-icon-button {:href (:charge/reference-link e)} - svg/external-link)])) - :headers [{:key "client" - :name "Client" - :sort-key "client" - :hide? (fn [request] - (= (count (:clients request)) 1)) - :render #(-> % :charge/client :client/code)} - {:key "date" - :name "Date" - :sort-key "date" - :render #(atime/unparse-local (:charge/date %) atime/standard-time)} - {:key "total" - :name "Total" - :sort-key "total" - :render #(some->> % :charge/total (format "$%.2f"))} - {:key "processor" - :name "Processor" - :sort-key "processor" - :render (fn [sales-order] - (when (:charge/processor sales-order) - (com/pill {:color :primary } - (name (:charge/processor sales-order)))))} - {:key "tip" - :name "Tip" - :sort-key "tip" - :render #(some->> % :charge/tip (format "$%.2f"))} - {:key "links" - :name "Links" - :render (fn [entity] - (when-let [expected-deposit-id (some->> entity :expected-deposit/_charges first :db/id)] - [:a {:href (str (bidi/path-for ssr-routes/only-routes - :pos-expected-deposits) - "?exact-match-id=" expected-deposit-id) - :hx-boost "true"} - (com/pill {:color :secondary} "expected deposit")]))}]}) (def row* (partial helper/row* grid-page)) (def table* (partial helper/table* grid-page)) -(def table (-> (partial helper/table grid-page) - (query-params/wrap-parse-query-params - (comp - (query-params/parse-key :processor #(query-params/parse-keyword "ccp-processor" %)) - (query-params/parse-key :total-gte query-params/parse-double) - (query-params/parse-key :total-lte query-params/parse-double) - (helper/default-parse-query-params grid-page))))) -(def page (-> (partial helper/page grid-page) - (query-params/wrap-parse-query-params - (comp - (query-params/parse-key :processor #(query-params/parse-keyword "ccp-processor" %)) - (query-params/parse-key :total-gte query-params/parse-double) - (query-params/parse-key :total-lte query-params/parse-double) - (helper/default-parse-query-params grid-page))))) (def key->handler - {:pos-tenders (wrap-client-redirect-unauthenticated (wrap-secure page)) - :pos-tender-table (wrap-client-redirect-unauthenticated (wrap-secure table))}) + {:pos-tenders (helper/page-route grid-page) + :pos-tender-table (helper/table-route grid-page)})