Implements the ability to dig into history

This commit is contained in:
2023-01-11 12:54:48 -08:00
parent 2facb0c64f
commit 3a2dc07227
7 changed files with 114 additions and 150 deletions

View File

@@ -10,7 +10,6 @@
"@popperjs/core": "^2.11.5",
"downshift": "^6.1.3",
"dropzone": "^4.3.0",
"htmx.org": "^1.8.4",
"minisearch": "^3.0.2",
"prop-types": "^15.7.2",
"react": "^17.0.1",

View File

@@ -1,27 +1,17 @@
(ns auto-ap.ssr.admin
(:require
[auto-ap.datomic :refer [conn]]
[auto-ap.graphql.utils :refer [assert-admin]]
[auto-ap.logging :as alog]
[auto-ap.time :as atime]
[clj-time.coerce :as coerce]
[clojure.string :as str]
[clojure.tools.logging :as log]
#_{:clj-kondo/ignore [:refer-all]}
[compojure.core :refer [defroutes GET POST context ANY wrap-routes]]
[compojure.route :as route]
[config.core :refer [env]]
[mount.core :as mount]
[ring.middleware.edn :refer [wrap-edn-params]]
[ring.middleware.multipart-params :as mp]
[ring.middleware.params :refer [wrap-params]]
[ring.middleware.reload :refer [wrap-reload]]
[ring.util.response :as response]
[compojure.core :refer [context defroutes GET POST routes]]
[datomic.api :as d]
[auto-ap.datomic :refer [conn]]
[clj-time.coerce :as coerce]
[auto-ap.time :as atime]
[hiccup.core :refer [html]]
[hiccup.page :refer [html5]]
[unilog.context :as lc]))
[hiccup2.core :as hiccup]))
(defn base-page [contents]
(defn base-page [request contents]
[:html.has-navbar-fixed-top
[:head
[:meta {:charset "utf-8"}]
@@ -43,37 +33,57 @@
:integrity "sha384-wg5Y/JwF7VxGk4zLsJEcAojRtlVp1FKKdGy1qN+OMtdq72WRvX/EdRdqg/LOhYeV"
:crossorigin="anonymous"}]
[:script {:type "text/javascript", :src "https://cdn.yodlee.com/fastlink/v4/initialize.js", :async "async" }]]
[:script {:type "text/javascript"}
(hiccup/raw
(str
"
window.onload = function () {
document.body.addEventListener(\"htmx:configRequest\", function(event) {
event.detail.headers[\"Authorization\"] = \"Token " (get (:query-params request) "jwt")" \" ;
});
}"))]
[:body
[:div {:id "app"}
[:div
[:nav {:class "navbar has-shadow is-fixed-top"}
[:div {:class "container"}
[:div {:class "navbar-brand"}
[:a {:class "navbar-item", :href "../"}
[:img {:src "/img/logo.png"}]]]
[:div {:class "navbar-menu"}
[:div {:class "navbar-burger burger", :data-target "navMenu"}
[:span]
[:span]
[:span]]]]]
[:nav {:class "navbar has-shadow is-fixed-top is-grey"}
[:div {:class "container"}
[:div {:class "navbar-brand"}
[:a {:class "navbar-item", :href "../"}
[:img {:src "/img/logo.png"}]]]
[:div.navbar-menu {:id "navMenu"}
[:div.navbar-start
[:a.navbar-item {:href "/"}
"Home" ]
[:a.navbar-item {:href "/invoices/"}
"Invoices" ]
[:a.navbar-item {:href "/payments/"}
"Payments" ]
[:a.navbar-item {:href "/pos/sales-orders/"}
"POS" ]
[:a.navbar-item {:href "/transactions/"}
"Transactions" ]
[:a.navbar-item {:href "/ledger/"}
"Ledger" ]]]]]
[:div {:class "columns has-shadow", :id "mail-app", :style "margin-bottom: 0px; height: calc(100vh - 46px);"}
[:aside {:class "column aside menu is-2 "}
[:div {:class "main left-nav"}
[:div]]]
[:div {:class "column messages hero ", :id "message-feed", :style "overflow: auto;"}
(into [:div {:class "inbox-messages"}]
contents)]]
[:div {:class "inbox-messages"}
contents]]]
[:div]
[:div {:id "dz-hidden"}]]]]])
(defn wrap-html [handler]
(fn [request]
(doto
{:status 200
:headers {"Content-Type" "text/html"}
:body (handler request)
}
println)))
(defn html-response [hiccup]
{:status 200
:headers {"Content-Type" "text/html"}
:body (str
(hiccup/html
{}
hiccup))})
(defn inline-add-deletes [history]
@@ -121,26 +131,29 @@
(pr-str v)))
(comment "_" )
(defn page-template [& {:keys [table entity-id]}]
[:div
[:div.columns
[:div.column.is-4
[:form.hello {"hx-target" "#history-table"
"hx-post" "/admin/history/search"
"hx-swap" "innerHTML"
"_" (hiccup2.core/raw "on submit toggle @disabled on me then toggle .is-loading on <#dig/> end
"hx-post" "/admin/history/search"
"hx-swap" "innerHTML"
"_" (hiccup/raw "on htmx:beforeRequest toggle @disabled on me then toggle .is-loading on <#dig/> end
on htmx:afterRequest toggle @disabled on me then toggle .is-loading on <#dig /> end")
}
[:div.field.is-grouped
[:p.control {}
[:input.input {:type "text" :name "entity-id" :placeholder "Entity id" }]]
[:input.input {:type "text" :name "entity-id" :placeholder "Entity id" :value entity-id}]]
[:p.control
[:button#dig.button.is-primary {}
"Dig"]]]]]]
[:div#history-table
table]])
(defn history-search [{:keys [form-params params] :as request}]
(defn history-search [{:keys [form-params params] identity :identity :as request}]
(assert-admin identity)
(log/info ::request
request)
(try
@@ -158,17 +171,18 @@
inline-add-deletes
(sort-by (comp :db/txInstant :tx))
vec)
best-guess-entity (->> history
(group-by
(comp
namespace
second)
)
(map (fn [[k v]]
[k v]))
(sort-by second)
last
first)
best-guess-entity (or (->> history
(group-by
(comp
namespace
second)
)
(map (fn [[k v]]
[k v]))
(sort-by second)
last
first)
"?")
table [:div [:h1.title "History for "
(str/capitalize best-guess-entity)
" "
@@ -181,7 +195,7 @@
[:td {:style "width: 12em"} "Transaction" ]
[:td {:style "width: 12em"} "Date"]
[:td {:style "width: 12em"} "User"]
[:td {:style "width: 15em"} "Field"]
[:td {:style "width: 10em"} "Field"]
[:td "Change"]]]
[:tbody
(for [[tx a c] history]
@@ -193,9 +207,16 @@
(atime/unparse atime/standard-time))]
[:td (str (:audit/user tx))]
[:td (namespace a) ": " (name a)]
[:td (format-value (:removed c))
" => "
(format-value (:added c))]])]
[:td
[:div.tags
[:div.tag.is-danger.is-light
[:span
"FROM "
(format-value (:removed c))]]
[:div.tag.is-primary.is-light
[:span
"TO "
(format-value (:added c))]]]]])]
]]
[:div.column.is-3
[:div#inspector.card]]]]]
@@ -203,23 +224,24 @@
(alog/info ::trace
:bge best-guess-entity
:headers (:headers request))
(html (if (= "history-table"
(get (:headers request) "hx-target"))
table
(do
(println "WHOLE PAGOE")
(page-template :table table
:entity-id entity-id)))))
(html-response
(if (get (:headers request) "hx-request")
table
(base-page request
(page-template :table table
:entity-id entity-id)))))
(catch NumberFormatException e
(html [:div.notification.is-danger.is-light
"Cannot parse the entity-id " (or (:entity-id form-params)
(:entity-id params))
(html-response
(str [:div.notification.is-danger.is-light
"Cannot parse the entity-id " (or (:entity-id form-params)
(:entity-id params))
". It should be a number."]))))
". It should be a number."])))))
(defn inspect [{{:keys [entity-id]} :params :as request}]
(defn inspect [{{:keys [entity-id]} :params identity :identity :as request}]
(alog/info ::inspect
:request request)
(assert-admin identity)
(try
(let [entity-id (Long/parseLong entity-id)
data (d/pull (d/db conn)
@@ -227,34 +249,27 @@
entity-id
) ]
(html [:div.box [:h1.title "Snapshot of "
entity-id]
[:ul
(for [[k v] data]
[:li [:strong k] ":" v]
)]]))
(html-response
[:div.box [:h1.title "Snapshot of "
entity-id]
[:ul
(for [[k v] data]
[:li [:strong k] ":" v]
)]]))
(catch NumberFormatException e
(html [:div.notification.is-danger.is-light
"Cannot parse the entity-id " entity-id ". It should be a number."]))))
(html-response
[:div.notification.is-danger.is-light
"Cannot parse the entity-id " entity-id ". It should be a number."]))))
(defn history [request]
(str "<!DOCTYPE html>"
(html
(base-page (page-template )))))
(defn- with-ignore-trailing-slash [handler]
(fn [request]
(let [uri (request :uri)
clean-uri (str/replace uri #"^(.+?)/+$" "$1")]
(handler (assoc request :uri clean-uri)))))
(defn history [{:keys [identity] :as request}]
(html-response
(base-page request (page-template ))))
(defroutes admin-routes
(wrap-routes
(context "/admin" []
(GET "/history" [] history)
(GET "/history/" [] history)
(POST "/history/search" [] history-search)
#_(GET "/history/:entity-id" [entity-id] history-search)
#_(GET "/history/inspect/:entity-id" [entity-id] inspect))
with-ignore-trailing-slash))
(routes
(context "/admin" []
(GET "/history" [] history)
(GET "/history/" [] history)
(POST "/history/search" [] history-search)
(GET "/history/:entity-id" [entity-id] history-search)
(GET "/history/inspect/:entity-id" [entity-id] inspect))))

View File

@@ -381,7 +381,7 @@
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
(defn tx-detail [i]
(map (juxt :e #(d/ident (d/db (d/connect uri)) (:a %)) :v)
(map (juxt :e #(d/ident (d/db (d/connect uri)) (:a %)) :v :added?)
(:data (first
(d/tx-range (d/log (d/connect uri))
i

View File

@@ -27,13 +27,7 @@
(defn ^:export init []
(dev-setup)
(.addEventListener (.-body js/document)
"htmx:configRequest"
(fn [event]
(aset (.-headers (.-detail event))
"Authorization"
(some->> (.getItem js/localStorage "jwt")
(str "Token ")))))
;; document.body.addEventListener('htmx:configRequest', function(evt) {

View File

@@ -46,7 +46,7 @@
[:span {:class "icon icon-cog-play-1" :style {:font-size "25px"}}]
[:span {:class "name"} "Rules"]]]
[:li.menu-item
[:a {:href "/admin/history", :class (str "item" (active-when ap = :admin-history))}
[:a {:href (str "/admin/history?jwt=" (.getItem js/localStorage "jwt")) :class (str "item" (active-when ap = :admin-history))}
[:span {:class "icon icon-cog-play-1" :style {:font-size "25px"}}]
[:span {:class "name"} "History"]]]
[:ul ]]

View File

@@ -12,7 +12,6 @@
[auto-ap.views.pages.error :refer [error-page]]
[auto-ap.views.pages.ledger.balance-sheet :refer [balance-sheet-page]]
[auto-ap.views.pages.admin.jobs :refer [jobs-page]]
[auto-ap.views.pages.admin.history :refer [history-page]]
[auto-ap.views.pages.ledger.external-import :refer [external-import-page]]
[auto-ap.views.pages.ledger.external-ledger :refer [external-ledger-page]]
[auto-ap.views.pages.ledger.profit-and-loss :refer [profit-and-loss-page]]
@@ -155,9 +154,6 @@
[admin-excel-import-page])
(defmethod page :admin-history [_]
[history-page])
(defmethod page :initial-error [_]
[error-page])

View File

@@ -1,40 +0,0 @@
(ns auto-ap.views.pages.admin.history
(:require
[auto-ap.status :as status]
[auto-ap.subs :as subs]
[auto-ap.views.components.admin.side-bar :refer [admin-side-bar]]
[auto-ap.views.components.layouts :refer [side-bar-layout]]
[auto-ap.views.pages.admin.jobs.table :as table]
[auto-ap.views.components.modal :as modal]
[auto-ap.views.components :as com]
[auto-ap.views.pages.data-page :as data-page]
[auto-ap.views.utils :refer [dispatch-event with-user]]
[clojure.set :as set]
[re-frame.core :as re-frame]
[vimsical.re-frame.fx.track :as track]
[auto-ap.forms.builder :as form-builder]
[vimsical.re-frame.cofx.inject :as inject]
[auto-ap.forms :as forms]
[clojure.string :as str]
["htmx.org" :as htmx]
))
;; VIEWS
(def history-content
(let [c (atom nil)]
(with-meta
(fn []
(println "bothered?")
[:div {"hx-get" (.-pathname (.-location js/document))
"hx-trigger" "load"
"hx-swap" "outerHTML"
:ref (fn [i] (reset! c i))}
"test"])
{:should-component-update (fn [] false)
:component-did-mount (fn []
(.process htmx @c))})))
(defn history-page []
[side-bar-layout {:side-bar [admin-side-bar {}]
:main [history-content]}])