From 7796e3483496f83cfc157435b9f27b14513fc7c8 Mon Sep 17 00:00:00 2001 From: Bryce Date: Tue, 31 Oct 2023 12:44:10 -0700 Subject: [PATCH] Another admin page --- src/clj/auto_ap/ssr/admin/background_jobs.clj | 5 +- src/clj/auto_ap/ssr/admin/import_batch.clj | 188 ++++++++++++++++++ src/clj/auto_ap/ssr/components/aside.clj | 7 +- src/clj/auto_ap/ssr/components/date_range.clj | 29 +++ src/clj/auto_ap/ssr/components/inputs.clj | 4 +- src/clj/auto_ap/ssr/core.clj | 2 + src/clj/auto_ap/ssr/pos/common.clj | 28 --- src/cljc/auto_ap/client_routes.cljc | 1 - .../auto_ap/routes/admin/import_batch.cljc | 3 + .../auto_ap/shared_views/admin/side_bar.cljc | 8 +- src/cljc/auto_ap/ssr_routes.cljc | 4 +- 11 files changed, 238 insertions(+), 41 deletions(-) create mode 100644 src/clj/auto_ap/ssr/admin/import_batch.clj create mode 100644 src/clj/auto_ap/ssr/components/date_range.clj create mode 100644 src/cljc/auto_ap/routes/admin/import_batch.cljc diff --git a/src/clj/auto_ap/ssr/admin/background_jobs.clj b/src/clj/auto_ap/ssr/admin/background_jobs.clj index de87b4c6..1289b990 100644 --- a/src/clj/auto_ap/ssr/admin/background_jobs.clj +++ b/src/clj/auto_ap/ssr/admin/background_jobs.clj @@ -2,7 +2,6 @@ (:require [amazonica.aws.ecs :as ecs] [auto-ap.logging :as alog] - [clojure.string :as str] [auto-ap.routes.utils :refer [wrap-admin wrap-client-redirect-unauthenticated]] [auto-ap.ssr-routes :as ssr-routes] @@ -22,9 +21,9 @@ [bidi.bidi :as bidi] [clj-time.coerce :as coerce] [clj-time.core :as time] + [clojure.string :as str] [config.core :refer [env]] - [malli.core :as mc] - [auto-ap.ssr.hx :as hx]) + [malli.core :as mc]) (:import (com.amazonaws.services.ecs.model AssignPublicIp))) diff --git a/src/clj/auto_ap/ssr/admin/import_batch.clj b/src/clj/auto_ap/ssr/admin/import_batch.clj new file mode 100644 index 00000000..8bb9a894 --- /dev/null +++ b/src/clj/auto_ap/ssr/admin/import_batch.clj @@ -0,0 +1,188 @@ +(ns auto-ap.ssr.admin.import-batch + (:require + [auto-ap.datomic + :refer [add-sorter-fields + apply-pagination + apply-sort-3 + conn + merge-query + pull-many + query2]] + [auto-ap.routes.admin.import-batch :as route] + [auto-ap.routes.utils + :refer [wrap-admin wrap-client-redirect-unauthenticated]] + [auto-ap.ssr-routes :as ssr-routes] + [auto-ap.ssr.components :as com] + [auto-ap.ssr.components.date-range :refer [date-range-field]] + [auto-ap.ssr.grid-page-helper :as helper] + [auto-ap.ssr.utils + :refer [apply-middleware-to-all-handlers ref->select-options]] + [auto-ap.time :as atime] + [bidi.bidi :as bidi] + [clj-time.coerce :as coerce] + [datomic.api :as dc] + [clj-time.coerce :as c] + [auto-ap.logging :as alog] + [hiccup.util :as hu] + [auto-ap.client-routes :as client-routes] + [auto-ap.ssr.svg :as svg])) + +(defn filters [request] + [:form {"hx-trigger" "change delay:500ms, keyup changed from:.hot-filter delay:1000ms" + "hx-get" (bidi/path-for ssr-routes/only-routes + ::route/table) + "hx-target" "#entity-table" + "hx-indicator" "#entity-table"} + + [:fieldset.space-y-6 + (date-range-field {:value {:start (:start-date (:parsed-query-params request)) + :end (:end-date (:parsed-query-params request))} + :id "date-range"}) + (com/field {:label "Source"} + (com/select {:name "source" + :class "hot-filter w-full" + :value (:source (:parsed-query-params request)) + :placeholder "" + :options (ref->select-options "import-source" :allow-nil? true)})) + + #_(com/field {:label "Code"} + (com/text-input {:name "code" + :id "code" + :class "hot-filter" + :value (:code (:parsed-query-params request)) + :placeholder "11101" + :size :small}))]]) + +(def default-read '[:db/id + [:import-batch/date :xform clj-time.coerce/from-date] + :import-batch/suppressed + :import-batch/extant + :import-batch/user-name + :import-batch/imported + {[:import-batch/source :xform iol-ion.query/ident] [:db/ident] + [:import-batch/status :xform iol-ion.query/ident] [:db/ident]}]) + +(defn fetch-ids [db request] + (let [query-params (:parsed-query-params request) + query (cond-> {:query {:find [] + :in '[$ ] + :where '[]} + :args [db ]} + (:sort query-params) (add-sorter-fields {"source" ['[?e :import-batch/source ?s] + '[?s :db/ident ?s2] + '[(name ?s2) ?sort-source]] + "status" ['[?e :import-batch/status ?st] + '[?st :db/ident ?st2] + '[(name ?st2) ?sort-status]] + "user" ['[?e :import-batch/user-name ?sort-user]] + "date" ['[?e :import-batch/date ?sort-date]] + "type" ['[?e :account/type ?t] + '[?t :db/ident ?ti] + '[(name ?ti) ?sort-type]]} + query-params) + + (or (:start-date query-params) + (:end-date query-params)) + (merge-query {:query '{:where [[?e :import-batch/date ?d]]}}) + + (:start-date query-params) + (merge-query {:query '{:in [?start-date] + :where [[(>= ?d ?start-date)]]} + :args [(-> query-params :start-date c/to-date)]}) + + (:end-date query-params) + (merge-query {:query '{:in [?end-date] + :where [[(< ?d ?end-date)]]} + :args [(-> query-params :end-date c/to-date)]}) + + (not-empty (:source query-params)) + (merge-query {:query '{:in [?source] + :where [[?e :import-batch/source ?source]]} + :args [(keyword "import-source" (:source query-params))]}) + true + (merge-query {:query {:find ['?sort-default '?e] + :where ['[?e :import-batch/date ?sort-default]]}}))] + (cond->> (query2 query) + true (apply-sort-3 query-params) + true (apply-pagination query-params)))) + +(defn hydrate-results [ids db _] + (let [results (->> (pull-many db default-read ids) + (group-by :db/id)) + refunds (->> ids + (map results) + (map first))] + refunds)) + +(defn fetch-page [request] + (let [db (dc/db conn) + {ids-to-retrieve :ids matching-count :count} (fetch-ids db request)] + + [(->> (hydrate-results ids-to-retrieve db request)) + matching-count])) + +(def grid-page + (helper/build {:id "entity-table" + :id-fn :db/id + :nav (com/admin-aside-nav) + :fetch-page fetch-page + :page-specific-nav filters + :row-buttons (fn [_ entity] + [(com/a-icon-button {:href (hu/url (bidi/path-for client-routes/routes :transactions) + {:import-batch-id (:db/id entity)}) + :hx-boost true} + svg/external-link)]) + :oob-render + (fn [request] + [(assoc-in (date-range-field {:value {:start (:start-date (:parsed-query-params request)) + :end (:end-date (:parsed-query-params request))} + :id "date-range"}) [1 :hx-swap-oob] true)]) + :parse-query-params (comp + (helper/default-parse-query-params grid-page)) + :breadcrumbs [[:a {:href (bidi/path-for ssr-routes/only-routes + :admin)} + "Admin"] + + [:a {:href (bidi/path-for ssr-routes/only-routes + ::route/page)} + "Import Batches"]] + :title "Import Batches" + :entity-name "Batch" + :route ::route/table + :headers [{:key "date" + :name "Date" + :sort-key "date" + :render #(some-> % :import-batch/date (atime/unparse-local atime/standard-time))} + {:key "source" + :name "Source" + :sort-key "source" + :render :import-batch/source} + {:key "status" + :name "Status" + :sort-key "status" + :render #(some-> % :import-batch/status name)} + {:key "user" + :name "User" + :sort-key "user" + :render :import-batch/user-name} + {:key "imported" + :name "imported" + :render :import-batch/imported} + {:key "extant" + :name "Pre-existing" + :render :import-batch/extant} + {:key "suppresed" + :name "Suppressed" + :render :import-batch/suppressed}]})) + +(def row* (partial helper/row* grid-page)) +(def table* (partial helper/table* grid-page)) +(def key->handler + (apply-middleware-to-all-handlers + (->> + {::route/page (helper/page-route grid-page) + ::route/table (helper/table-route grid-page)}) + (fn [h] + (-> h + (wrap-admin) + (wrap-client-redirect-unauthenticated))))) diff --git a/src/clj/auto_ap/ssr/components/aside.clj b/src/clj/auto_ap/ssr/components/aside.clj index 91a4f72c..28ea1103 100644 --- a/src/clj/auto_ap/ssr/components/aside.clj +++ b/src/clj/auto_ap/ssr/components/aside.clj @@ -6,7 +6,8 @@ [auto-ap.client-routes :as client-routes] [auto-ap.ssr.hx :as hx] [auto-ap.routes.admin.transaction-rules :as transaction-rules] - [auto-ap.ssr.hiccup-helper :as hh])) + [auto-ap.ssr.hiccup-helper :as hh] + [auto-ap.routes.admin.import-batch :as ib-routes])) (defn menu-button- [params & children] [:div @@ -242,8 +243,8 @@ {:x-show "open"}) (menu-button- {:href (bidi/path-for client-routes/routes :admin-excel-import)} "Excel Invoices") - (menu-button- {:href (bidi/path-for client-routes/routes - :admin-import-batches)} "Import Batches") + (menu-button- {:href (bidi/path-for ssr-routes/only-routes + ::ib-routes/page)} "Import Batches") (menu-button- {:href (bidi/path-for ssr-routes/only-routes :admin-ezcater-xls) :hx-boost "true"} "EZCater XLS Import"))]]) diff --git a/src/clj/auto_ap/ssr/components/date_range.clj b/src/clj/auto_ap/ssr/components/date_range.clj new file mode 100644 index 00000000..6f740e1a --- /dev/null +++ b/src/clj/auto_ap/ssr/components/date_range.clj @@ -0,0 +1,29 @@ +(ns auto-ap.ssr.components.date-range + (:require [auto-ap.ssr.components :as com] + [auto-ap.time :as atime])) + +(defn date-range-field [{:keys [value id] }] + [:div {:id id} + (com/field {:label "Date Range"} + [:div.space-y-4 + [:div + (com/button-group {:name "date-range"} + (com/button-group-button {:size :small :value "all" :hx-trigger "click"} "All") + (com/button-group-button {:size :small :value "week" :hx-trigger "click"} "Week") + (com/button-group-button {:size :small :value "month" :hx-trigger "click"} "Month") + (com/button-group-button {:size :small :value "year" :hx-trigger "click"} "Year")) + ] + [:div.flex.space-x-1.items-baseline.w-full.justify-start + (com/date-input {:name "start-date" + :value (some-> (:start value) + (atime/unparse-local atime/normal-date)) + :placeholder "Date" + :size :small + :class "shrink"}) + + (com/date-input {:name "end-date" + :value (some-> (:end value) + (atime/unparse-local atime/normal-date)) + :placeholder "Date" + :size :small + :class "shrink"})]])]) diff --git a/src/clj/auto_ap/ssr/components/inputs.clj b/src/clj/auto_ap/ssr/components/inputs.clj index 4a67696b..e78e33c1 100644 --- a/src/clj/auto_ap/ssr/components/inputs.clj +++ b/src/clj/auto_ap/ssr/components/inputs.clj @@ -155,7 +155,7 @@ (dissoc :size))]) (defn date-input- [{:keys [size] :as params}] - [:div + [:div.shrink [:input (-> params (update :class (fnil hh/add-class "") default-input-classes) @@ -163,7 +163,7 @@ (assoc "_" (hiccup/raw "init initDatepicker(me)")) (assoc "hx-on" (hiccup/raw "changeDate: htmx.trigger(this, \"change\") htmx:beforeCleanupElement: this.dp.destroy()")) - (update :class #(str % (use-size size))) + (update :class #(str % (use-size size) " w-full")) (dissoc :size))]]) diff --git a/src/clj/auto_ap/ssr/core.clj b/src/clj/auto_ap/ssr/core.clj index d4eeaa9c..08f7f438 100644 --- a/src/clj/auto_ap/ssr/core.clj +++ b/src/clj/auto_ap/ssr/core.clj @@ -11,6 +11,7 @@ [auto-ap.ssr.company.company-1099 :as company-1099] [auto-ap.ssr.company.plaid :as company-plaid] [auto-ap.ssr.admin.accounts :as admin-accounts] + [auto-ap.ssr.admin.import-batch :as import-batch] [auto-ap.ssr.company.reports :as company-reports] [auto-ap.ssr.company.yodlee :as company-yodlee] [auto-ap.ssr.invoice.glimpse :as invoice-glimpse] @@ -73,6 +74,7 @@ :admin-ezcater-xls (wrap-client-redirect-unauthenticated (wrap-admin ezcater-xls/page)) :search (wrap-client-redirect-unauthenticated (wrap-secure search/dialog-contents))} (into company-1099/key->handler) + (into import-batch/key->handler) (into pos-sales/key->handler) (into pos-expected-deposits/key->handler) (into pos-tenders/key->handler) diff --git a/src/clj/auto_ap/ssr/pos/common.clj b/src/clj/auto_ap/ssr/pos/common.clj index 97022a42..6779d846 100644 --- a/src/clj/auto_ap/ssr/pos/common.clj +++ b/src/clj/auto_ap/ssr/pos/common.clj @@ -3,34 +3,6 @@ [auto-ap.time :as atime] [auto-ap.ssr.svg :as svg])) -(defn date-range-field* [request] - [:div#date-range {} - (com/field {:label "Date Range"} - [:div.space-y-4 - [:div - (com/button-group {:name "date-range"} - (com/button-group-button {:size :small :value "all" :hx-trigger "click"} "All") - (com/button-group-button {:size :small :value "week" :hx-trigger "click"} "Week") - (com/button-group-button {:size :small :value "month" :hx-trigger "click"} "Month") - (com/button-group-button {:size :small :value "year" :hx-trigger "click"} "Year")) - ] - [:div.flex.space-x-1.items-baseline - (com/date-input {:name "start-date" - :value (some-> request - :parsed-query-params - :start-date - (atime/unparse-local atime/normal-date)) - :placeholder "Date" - :size :small}) - - (com/date-input {:name "end-date" - :value (some-> request - :parsed-query-params - :end-date - (atime/unparse-local atime/normal-date)) - :placeholder "Date" - :size :small})]])]) - (defn processor-field* [request] (com/field {:label "Processor"} (com/radio {:size :small diff --git a/src/cljc/auto_ap/client_routes.cljc b/src/cljc/auto_ap/client_routes.cljc index c15ccf6b..5ad796cf 100644 --- a/src/cljc/auto_ap/client_routes.cljc +++ b/src/cljc/auto_ap/client_routes.cljc @@ -11,7 +11,6 @@ [:id] {"" :admin-specific-client "/bank-accounts/" {[:bank-account] :admin-specific-bank-account}}} "rules" :admin-rules - "import-batches" :admin-import-batches "vendors" :admin-vendors "excel-import" :admin-excel-import } diff --git a/src/cljc/auto_ap/routes/admin/import_batch.cljc b/src/cljc/auto_ap/routes/admin/import_batch.cljc new file mode 100644 index 00000000..a9d6cbaf --- /dev/null +++ b/src/cljc/auto_ap/routes/admin/import_batch.cljc @@ -0,0 +1,3 @@ +(ns auto-ap.routes.admin.import-batch) +(def routes {"" {:get ::page} + "/table" ::table}) diff --git a/src/cljc/auto_ap/shared_views/admin/side_bar.cljc b/src/cljc/auto_ap/shared_views/admin/side_bar.cljc index 94a4c69b..300c2757 100644 --- a/src/cljc/auto_ap/shared_views/admin/side_bar.cljc +++ b/src/cljc/auto_ap/shared_views/admin/side_bar.cljc @@ -3,7 +3,9 @@ [auto-ap.client-routes :as client-routes] [auto-ap.ssr-routes :as ssr-routes] #?(:cljs [re-frame.core :as re-frame]) - #?(:cljs [reagent.core :as r]))) + #?(:cljs [reagent.core :as r]) + [auto-ap.routes.admin.import-batch :as ib-routes] + [auto-ap.routes.admin.transaction-rules :as tr-routes])) (defn deep-merge [v & vs] (letfn [(rec-merge [v1 v2] @@ -62,7 +64,7 @@ :icon-style {:font-size "25px"}}) (menu-item {:label "Rules" :icon-class "icon icon-cog-play-1" - :test-route #{:admin-transaction-rules} + :test-route #{::tr-routes/page} :active-route active-route :route :admin-transaction-rules :icon-style {:font-size "25px"}}) @@ -88,7 +90,7 @@ :icon-class "fa fa-download" :test-route #{:admin-import-batches} :active-route active-route - :route :admin-import-batches}) + :route ::ib-routes/page}) (menu-item {:label "EZCater XLS Import" :icon-class "icon icon-cog-play-1" :test-route #{:admin-ezcater-xls} diff --git a/src/cljc/auto_ap/ssr_routes.cljc b/src/cljc/auto_ap/ssr_routes.cljc index b926d6d9..ad4b423a 100644 --- a/src/cljc/auto_ap/ssr_routes.cljc +++ b/src/cljc/auto_ap/ssr_routes.cljc @@ -1,5 +1,6 @@ (ns auto-ap.ssr-routes - (:require [auto-ap.routes.admin.transaction-rules :as tr-routes])) + (:require [auto-ap.routes.admin.transaction-rules :as tr-routes] + [auto-ap.routes.admin.import-batch :as ib-routes])) (def routes {"impersonate" :impersonate "logout" :logout @@ -34,6 +35,7 @@ "/new" {:get :admin-job-start-dialog} "/subform" :admin-job-subform} "/ezcater-xls" :admin-ezcater-xls + "/import-batch" ib-routes/routes "/transaction-rule" tr-routes/routes} "transaction" {"/insights" {"" :transaction-insights "/table" :transaction-insight-table