Vendors form progress

This commit is contained in:
2023-11-02 23:40:24 -07:00
parent 99e4f05769
commit eef1cfb919
16 changed files with 846 additions and 425 deletions

File diff suppressed because one or more lines are too long

View File

@@ -5,7 +5,7 @@
:refer [assert-can-see-client can-see-client? cleanse-query is-admin?]] :refer [assert-can-see-client can-see-client? cleanse-query is-admin?]]
[auto-ap.solr :as solr] [auto-ap.solr :as solr]
[auto-ap.ssr.utils [auto-ap.ssr.utils
:refer [entity-id ref->enum-schema wrap-schema-decode]] :refer [entity-id ref->enum-schema wrap-schema-enforce]]
[com.brunobonacci.mulog :as mu] [com.brunobonacci.mulog :as mu]
[datomic.api :as dc] [datomic.api :as dc]
[ring.middleware.json :refer [wrap-json-response]])) [ring.middleware.json :refer [wrap-json-response]]))
@@ -91,7 +91,7 @@
xform)))) xform))))
[]))})) []))}))
(def account-search (wrap-json-response (wrap-schema-decode account-search (def account-search (wrap-json-response (wrap-schema-enforce account-search
:query-schema [:map :query-schema [:map
[:q :string] [:q :string]
[:client-id {:optional true [:client-id {:optional true

View File

@@ -36,7 +36,7 @@
temp-id temp-id
wrap-entity wrap-entity
wrap-form-4xx-2 wrap-form-4xx-2
wrap-schema-decode]] wrap-schema-enforce]]
[bidi.bidi :as bidi] [bidi.bidi :as bidi]
[clojure.string :as str] [clojure.string :as str]
[datomic.api :as dc] [datomic.api :as dc]
@@ -412,18 +412,18 @@
{:admin-accounts (helper/page-route grid-page) {:admin-accounts (helper/page-route grid-page)
:admin-account-table (helper/table-route grid-page) :admin-account-table (helper/table-route grid-page)
:admin-account-client-override-new (-> new-client-override :admin-account-client-override-new (-> new-client-override
(wrap-schema-decode :query-schema [:map (wrap-schema-enforce :query-schema [:map
[:index {:optional true [:index {:optional true
:default 0} [nat-int? {:default 0}]]]) :default 0} [nat-int? {:default 0}]]])
wrap-admin wrap-client-redirect-unauthenticated) wrap-admin wrap-client-redirect-unauthenticated)
:admin-account-save (-> account-save :admin-account-save (-> account-save
(wrap-entity [:form-params :db/id] default-read) (wrap-entity [:form-params :db/id] default-read)
(wrap-schema-decode :form-schema form-schema) (wrap-schema-enforce :form-schema form-schema)
(wrap-nested-form-params) (wrap-nested-form-params)
(wrap-form-4xx-2 (wrap-entity account-dialog [:form-params :db/id] default-read))) (wrap-form-4xx-2 (wrap-entity account-dialog [:form-params :db/id] default-read)))
:admin-account-edit-dialog (-> account-dialog :admin-account-edit-dialog (-> account-dialog
(wrap-entity [:route-params :db/id] default-read) (wrap-entity [:route-params :db/id] default-read)
(wrap-schema-decode :route-schema [:map [:db/id entity-id]])) (wrap-schema-enforce :route-schema [:map [:db/id entity-id]]))
:admin-account-new-dialog account-dialog}) :admin-account-new-dialog account-dialog})
(fn [h] (fn [h]
(-> h (-> h

View File

@@ -16,7 +16,7 @@
html-response html-response
modal-response modal-response
wrap-form-4xx-2 wrap-form-4xx-2
wrap-schema-decode]] wrap-schema-enforce]]
[auto-ap.time :as atime] [auto-ap.time :as atime]
[bidi.bidi :as bidi] [bidi.bidi :as bidi]
[clj-time.coerce :as coerce] [clj-time.coerce :as coerce]
@@ -256,9 +256,9 @@
(->> (->>
{:admin-jobs (helper/page-route grid-page) {:admin-jobs (helper/page-route grid-page)
:admin-job-table (helper/table-route grid-page) :admin-job-table (helper/table-route grid-page)
:admin-job-subform (-> subform (wrap-schema-decode :query-schema [:map [:name {:optional true} [:maybe :string]]])) :admin-job-subform (-> subform (wrap-schema-enforce :query-schema [:map [:name {:optional true} [:maybe :string]]]))
:admin-job-start (-> job-start :admin-job-start (-> job-start
(wrap-schema-decode :form-schema form-schema) (wrap-schema-enforce :form-schema form-schema)
(wrap-nested-form-params) (wrap-nested-form-params)
(wrap-form-4xx-2 job-start-dialog)) (wrap-form-4xx-2 job-start-dialog))
:admin-job-start-dialog job-start-dialog}) :admin-job-start-dialog job-start-dialog})

View File

@@ -19,7 +19,7 @@
:refer [apply-middleware-to-all-handlers :refer [apply-middleware-to-all-handlers
html-response html-response
wrap-form-4xx-2 wrap-form-4xx-2
wrap-schema-decode]] wrap-schema-enforce]]
[auto-ap.utils :refer [by]] [auto-ap.utils :refer [by]]
[bidi.bidi :as bidi] [bidi.bidi :as bidi]
[clj-time.coerce :as coerce] [clj-time.coerce :as coerce]
@@ -311,7 +311,7 @@
(->> (->>
{::route/page page {::route/page page
::route/import (-> import ::route/import (-> import
(wrap-schema-decode :form-schema [:map [:tsv :string]]) (wrap-schema-enforce :form-schema [:map [:tsv :string]])
(wrap-nested-form-params) (wrap-nested-form-params)
(wrap-form-4xx-2 form)) (wrap-form-4xx-2 form))
}) })

View File

@@ -45,7 +45,7 @@
temp-id temp-id
wrap-entity wrap-entity
wrap-form-4xx-2 wrap-form-4xx-2
wrap-schema-decode]] wrap-schema-enforce]]
[auto-ap.time :as atime] [auto-ap.time :as atime]
[auto-ap.utils :refer [dollars=]] [auto-ap.utils :refer [dollars=]]
[bidi.bidi :as bidi] [bidi.bidi :as bidi]
@@ -832,6 +832,7 @@
{}) {})
:form-errors form-errors}))) :form-errors form-errors})))
(defn check-badges [{query-params :query-params}] (defn check-badges [{query-params :query-params}]
(html-response (html-response
[:div (if (not-empty (:all query-params)) [:div (if (not-empty (:all query-params))
@@ -875,21 +876,21 @@
{::route/page (helper/page-route grid-page) {::route/page (helper/page-route grid-page)
::route/table (helper/table-route grid-page) ::route/table (helper/table-route grid-page)
::route/new-account (-> new-account ::route/new-account (-> new-account
(wrap-schema-decode :query-schema [:map (wrap-schema-enforce :query-schema [:map
[:client-id {:optional true} [:client-id {:optional true}
[:maybe entity-id]] [:maybe entity-id]]
[:index {:optional true [:index {:optional true
:default 0} [nat-int? {:default 0}]]]) :default 0} [nat-int? {:default 0}]]])
wrap-admin wrap-client-redirect-unauthenticated) wrap-admin wrap-client-redirect-unauthenticated)
::route/location-select (-> location-select ::route/location-select (-> location-select
(wrap-schema-decode :query-schema [:map (wrap-schema-enforce :query-schema [:map
[:name :string] [:name :string]
[:client-id {:optional true} [:client-id {:optional true}
[:maybe entity-id]] [:maybe entity-id]]
[:account-id {:optional true} [:account-id {:optional true}
[:maybe entity-id]]])) [:maybe entity-id]]]))
::route/account-typeahead (-> account-typeahead ::route/account-typeahead (-> account-typeahead
(wrap-schema-decode :query-schema [:map (wrap-schema-enforce :query-schema [:map
[:name :string] [:name :string]
[:client-id {:optional true} [:client-id {:optional true}
[:maybe entity-id]] [:maybe entity-id]]
@@ -897,7 +898,7 @@
[:maybe entity-id]]])) [:maybe entity-id]]]))
::route/save (-> save ::route/save (-> save
(wrap-entity [:form-params :db/id] default-read) (wrap-entity [:form-params :db/id] default-read)
(wrap-schema-decode :form-schema form-schema) (wrap-schema-enforce :form-schema form-schema)
(wrap-nested-form-params) (wrap-nested-form-params)
(wrap-form-4xx-2 (-> edit-dialog (wrap-form-4xx-2 (-> edit-dialog
(wrap-entity [:form-params :db/id] default-read)))) (wrap-entity [:form-params :db/id] default-read))))
@@ -905,8 +906,8 @@
::route/execute (-> execute ::route/execute (-> execute
(wrap-entity [:route-params :db/id] default-read) (wrap-entity [:route-params :db/id] default-read)
(wrap-entity [:route-params :db/id] default-read) (wrap-entity [:route-params :db/id] default-read)
(wrap-schema-decode :route-schema [:map [:db/id entity-id]]) (wrap-schema-enforce :route-schema [:map [:db/id entity-id]])
(wrap-schema-decode :form-schema (wrap-schema-enforce :form-schema
[:map [:map
[:transaction-id {:optional true} [:transaction-id {:optional true}
[:maybe [:vector {:decode/arbitrary (fn [x] ;; TODO make this easier [:maybe [:vector {:decode/arbitrary (fn [x] ;; TODO make this easier
@@ -920,13 +921,13 @@
::route/test (-> test ::route/test (-> test
(wrap-entity [:form-params :db/id] default-read) (wrap-entity [:form-params :db/id] default-read)
(wrap-schema-decode :form-schema form-schema) (wrap-schema-enforce :form-schema form-schema)
(wrap-nested-form-params) (wrap-nested-form-params)
(wrap-form-4xx-2 (-> edit-dialog (wrap-form-4xx-2 (-> edit-dialog
(wrap-entity [:form-params :db/id] default-read)))) (wrap-entity [:form-params :db/id] default-read))))
::route/check-badges (-> check-badges ::route/check-badges (-> check-badges
(wrap-schema-decode :query-schema [:map (wrap-schema-enforce :query-schema [:map
[:transaction-id {:optional true} [:transaction-id {:optional true}
[:maybe [:vector {:decode/arbitrary (fn [x] [:maybe [:vector {:decode/arbitrary (fn [x]
(if (sequential? x) (if (sequential? x)
@@ -936,11 +937,11 @@
[:all {:optional true} [:maybe :string]]])) [:all {:optional true} [:maybe :string]]]))
::route/execute-dialog (-> execute-dialog ::route/execute-dialog (-> execute-dialog
(wrap-entity [:route-params :db/id] default-read) (wrap-entity [:route-params :db/id] default-read)
(wrap-schema-decode :route-schema [:map [:db/id entity-id]])) (wrap-schema-enforce :route-schema [:map [:db/id entity-id]]))
::route/edit-dialog (-> edit-dialog ::route/edit-dialog (-> edit-dialog
(wrap-entity [:route-params :db/id] default-read) (wrap-entity [:route-params :db/id] default-read)
(wrap-schema-decode :route-schema [:map [:db/id entity-id]])) (wrap-schema-enforce :route-schema [:map [:db/id entity-id]]))
::route/new-dialog edit-dialog}) ::route/new-dialog edit-dialog})
(fn [h] (fn [h]
(-> h (-> h

View File

@@ -1,5 +1,6 @@
(ns auto-ap.ssr.admin.vendors (ns auto-ap.ssr.admin.vendors
(:require (:require
[auto-ap.cursor :as cursor]
[auto-ap.datomic [auto-ap.datomic
:refer [add-sorter-fields :refer [add-sorter-fields
apply-pagination apply-pagination
@@ -10,13 +11,15 @@
pull-attr pull-attr
pull-many pull-many
query2]] query2]]
[auto-ap.routes.admin.vendors :as route] [auto-ap.datomic.accounts :as d-accounts]
[auto-ap.logging :as alog]
[auto-ap.query-params :as query-params] [auto-ap.query-params :as query-params]
[auto-ap.routes.admin.vendors :as route]
[auto-ap.routes.utils [auto-ap.routes.utils
:refer [wrap-admin wrap-client-redirect-unauthenticated]] :refer [wrap-admin wrap-client-redirect-unauthenticated]]
[auto-ap.solr :as solr]
[auto-ap.ssr-routes :as ssr-routes] [auto-ap.ssr-routes :as ssr-routes]
[auto-ap.ssr.components :as com] [auto-ap.ssr.components :as com]
[auto-ap.ssr.components.timeline :as timeline]
[auto-ap.ssr.form-cursor :as fc] [auto-ap.ssr.form-cursor :as fc]
[auto-ap.ssr.grid-page-helper :as helper] [auto-ap.ssr.grid-page-helper :as helper]
[auto-ap.ssr.hx :as hx] [auto-ap.ssr.hx :as hx]
@@ -25,26 +28,24 @@
[auto-ap.ssr.utils [auto-ap.ssr.utils
:refer [apply-middleware-to-all-handlers :refer [apply-middleware-to-all-handlers
entity-id entity-id
field-validation-error
form-validation-error
html-response html-response
main-transformer main-transformer
many-entity
modal-response modal-response
ref->enum-schema ref->enum-schema
ref->select-options ref->select-options
schema-enforce-request
strip strip
temp-id temp-id
wrap-entity wrap-entity
wrap-form-4xx-2 wrap-form-4xx-2
wrap-schema-decode]] wrap-schema-enforce]]
[bidi.bidi :as bidi] [bidi.bidi :as bidi]
[clojure.string :as str] [clojure.string :as str]
[datomic.api :as dc] [datomic.api :as dc]
[malli.core :as mc] [malli.core :as mc]
[auto-ap.ssr.hiccup-helper :as hh] [malli.util :as mut]
[auto-ap.logging :as alog] [com.brunobonacci.mulog :as mu]
[auto-ap.cursor :as cursor])) [hiccup.util :as hu]))
(defn filters [request] (defn filters [request]
[:form {"hx-trigger" "change delay:500ms, keyup changed from:.hot-filter delay:1000ms" [:form {"hx-trigger" "change delay:500ms, keyup changed from:.hot-filter delay:1000ms"
@@ -85,10 +86,12 @@
:vendor/legal-entity-middle-name :vendor/legal-entity-middle-name
:vendor/legal-entity-last-name :vendor/legal-entity-last-name
{:vendor/primary-contact [:contact/email :contact/first-name :contact/middle-name :contact/last-name] {:vendor/primary-contact [:contact/email :contact/first-name :contact/middle-name :contact/last-name]
:vendor/address [:address/street1 :address/street2 :address/city :address/state :address/zip] :vendor/address [:address/street1 :address/street2 :address/city :address/state :address/zip :db/id]
:vendor/terms-overrides [:vendor-terms-override/terms {:vendor-terms-override/client [:client/name :db/id]}] :vendor/terms-overrides [:vendor-terms-override/terms {:vendor-terms-override/client [:client/name :db/id]} :db/id]
:vendor/account-overrides [{:vendor-terms-override/account [:account/name :db/id] :vendor/automatically-paid-when-due [:db/id :client/name]
:vendor-terms-override/client [:client/name :db/id]}] :vendor/account-overrides [{:vendor-account-override/account [:account/name :db/id]
:vendor-account-override/client [:client/name :db/id]}
:db/id]
:vendor/default-account [:account/name :account/numeric-code :db/id] :vendor/default-account [:account/name :account/numeric-code :db/id]
[:vendor-usage/_vendor :as :vendor/usage] [:vendor-usage/client :vendor-usage/count] [:vendor-usage/_vendor :as :vendor/usage] [:vendor-usage/client :vendor-usage/count]
[:vendor/legal-entity-1099-type :xform iol-ion.query/ident] [:db/ident] [:vendor/legal-entity-1099-type :xform iol-ion.query/ident] [:db/ident]
@@ -191,7 +194,6 @@
(when (> client-usage 0) (when (> client-usage 0)
[:div (com/pill {:color :secondary} [:div (com/pill {:color :secondary}
(format "Used %d times" total-usage))])]))} (format "Used %d times" total-usage))])]))}
{:key "email" {:key "email"
:name "Email" :name "Email"
:sort-key "email" :sort-key "email"
@@ -206,9 +208,8 @@
(defn save [{:keys [form-params request-method] :as request}] (defn save [{:keys [form-params request-method] :as request}]
(let [entity (alog/peek (cond-> form-params (let [entity (alog/peek (cond-> form-params
(= :post request-method) (assoc :db/id "new"))) (= :post request-method) (assoc :db/id "new")
(not (some identity (vals (dissoc (:vendor/address form-params) :db/id)))) (assoc :vendor/address nil)))
{:keys [tempids]} (audit-transact [[:upsert-entity entity]] {:keys [tempids]} (audit-transact [[:upsert-entity entity]]
(:identity request)) (:identity request))
updated-vendor (dc/pull (dc/db conn) updated-vendor (dc/pull (dc/db conn)
@@ -238,64 +239,43 @@
"hx-reswap" "afterbegin") "hx-reswap" "afterbegin")
(= :put request-method) (assoc "hx-retarget" (format "#entity-table tr[data-id=\"%d\"]" (:db/id updated-vendor))))))) (= :put request-method) (assoc "hx-retarget" (format "#entity-table tr[data-id=\"%d\"]" (:db/id updated-vendor)))))))
#_(defn client-override* [override]
(com/data-grid-row (-> {:x-ref "p"
:data-key "show"
:x-data (hx/json {:show (boolean (doto (not (fc/field-value (:new? override)))
println))})}
hx/alpine-mount-then-appear)
(fc/with-field :db/id
(com/hidden {:name (fc/field-name)
:value (fc/field-value)}))
(fc/with-field :account-client-override/client
(com/data-grid-cell {}
(com/validated-field {:errors (fc/field-errors)}
(com/typeahead {:name (fc/field-name)
:placeholder "Search..."
:class "w-96"
:url (bidi/path-for ssr-routes/only-routes
:company-search)
:value (fc/field-value)
:content-fn #(pull-attr (dc/db conn) :client/name %)}))))
(fc/with-field :account-client-override/name
(com/data-grid-cell
{}
(com/validated-field {:errors (fc/field-errors)}
(com/text-input {:name (fc/field-name)
:class "w-96"
:value (fc/field-value)}))))
(com/data-grid-cell {:class "align-top"}
(com/a-icon-button {"@click.prevent.stop" "show=false; setTimeout(() => $refs.p.remove(), 500)"} svg/x))))
(defn back-button [] (defn back-button []
[:a {"@click" "$dispatch('modalprevious')" [:a {"@click" "$dispatch('modalprevious')"
"class" "text-sm font-medium text-gray-700 cursor-pointer"} "class" "text-sm font-medium text-gray-700 cursor-pointer"}
"Back"]) "Back"])
(defn dialog* [{:keys [entity form-params form-errors]}] ;; TODO -
(alog/peek ::dialog-entity form-params) ;; make new vendor work
(fc/start-form form-params form-errors ;; generalize
[:div {:x-data (hx/json {"vendorName" (or (:vendor/name form-params) (:vendor/name entity))
"showPrintAs" (boolean (not-empty (:vendor/print-as form-params)))
"printAs" (or (:vendor/print-as form-params) (:vendor/print-as entity))})
:class "w-full h-full"} ;; TODO warn about usage on the vendor based on the thing
[:form#my-form (-> {:hx-ext "response-targets"
:hx-swap "outerHTML swap:300ms" (defn timeline [{:keys [active]}]
:hx-target-400 "#form-errors .error-content" (let [steps ["Info" "Terms" "Account" "Address" "Legal"]
:hx-trigger "submit" active-index (.indexOf steps active )]
:class "h-full w-full"} (timeline/timeline
(assoc (if (:db/id entity)
:hx-put
:hx-post)
(str (bidi/path-for ssr-routes/only-routes ::route/save))))
(com/modal
{} {}
(com/stacked-modal-card (for [[n i] (map vector steps (range))]
(timeline/timeline-step (cond-> {}
(= i active-index) (assoc :active? true)
(< i active-index) (assoc :visited? true)
(= i (dec (count steps))) (assoc :last? true))
n)))))
(defn info-modal [{:keys [form-params form-errors entity]}]
(com/stacked-modal-card-2
0 0
{"@keydown.enter.prevent.stop" "$refs.next.click()"} {"@keydown.enter.prevent.stop" "$refs.next.click()"}
(com/modal-header {}
[:div.flex [:div.p-2 "Basic Info"] [:p.ml-2.rounded.bg-gray-200.p-2.dark:bg-gray-600 [:div.flex [:div.p-2 "Basic Info"] [:p.ml-2.rounded.bg-gray-200.p-2.dark:bg-gray-600
[:span {:x-text "vendorName"}]]] [:span {:x-text "vendorName"}]]])
[:div.space-y-1 (com/modal-header-attachment
{}
(timeline {:active "Info"}))
(com/modal-body {}
[:div.space-y-1.mt-4 {:class "w-[600px] h-[350px]"}
(when-let [id (:db/id entity)] (when-let [id (:db/id entity)]
(com/hidden {:name "db/id" (com/hidden {:name "db/id"
:value id})) :value id}))
@@ -304,13 +284,16 @@
(com/validated-field {:label "Name" (com/validated-field {:label "Name"
:errors (fc/field-errors) :errors (fc/field-errors)
} }
[:div.flex.items-baseline.space-x-2
(com/text-input {:name (fc/field-name) (com/text-input {:name (fc/field-name)
:value (fc/field-value) :value (fc/field-value)
:x-model "vendorName" :x-model "vendorName"
:autofocus true :autofocus true
:class "w-64"}) :class "w-96"})))
(com/checkbox {:x-model "showPrintAs" "@change" "if (!showPrintAs) { printAs = ''; } "} "Use different name for checks")])) (com/validated-field
{}
[:div (com/checkbox
{:x-model "showPrintAs" "@change" "if (!showPrintAs) { printAs = ''; } "}
"Use different name for checks")])
(fc/with-field :vendor/print-as (fc/with-field :vendor/print-as
(com/validated-field (-> {:label "Print as" (com/validated-field (-> {:label "Print as"
@@ -321,42 +304,145 @@
(com/text-input {:name (fc/field-name) (com/text-input {:name (fc/field-name)
:x-model "printAs" :x-model "printAs"
:value (fc/field-value) :value (fc/field-value)
:class "w-64"}))) :class "w-96"})))
(fc/with-field :vendor/hidden (fc/with-field :vendor/hidden
(alog/peek (cursor/path fc/*current*)) (alog/peek (cursor/path fc/*current*))
(com/checkbox {:name (fc/field-name) (com/checkbox {:name (fc/field-name)
:value (boolean (fc/field-value)) :value (boolean (fc/field-value)) ;
:checked (alog/peek :checked (fc/field-value))} :checked (alog/peek :checked (fc/field-value))}
"Hidden"))] "Admin-only"))])
(com/modal-footer
{}
[:div.flex.justify-end [:div.flex.justify-end
(com/form-errors {:errors (:errors fc/*form-errors*)}) (com/form-errors {:errors (:errors fc/*form-errors*)})
[:div.flex.items-baseline.gap-x-4 [:div.flex.items-baseline.gap-x-4
(com/validated-save-button {:errors (seq form-errors) (com/validated-save-button {:errors (seq form-errors)
:x-ref "next" :x-ref "next"
:class "w-48" :class "w-48"
"@click.prevent" "$dispatch('modalnext'); console.log($el)"} :hx-put (hu/url (bidi/path-for ssr-routes/only-routes ::route/validate)
{:step "Info"})
}
"Terms" "Terms"
[:div.w-5.h-5 svg/arrow-right])]]) [:div.w-5.h-5 svg/arrow-right])]])))
(com/stacked-modal-card ;; TODO add plaid merchant
;; TODO each client only used once
(defn terms-override-row [terms-override-cursor]
(com/data-grid-row
(-> {:x-data (hx/json {:show (boolean (not (fc/field-value (:new? terms-override-cursor))))})
:data-key "show"
:x-ref "p"}
hx/alpine-mount-then-appear)
(list
(fc/with-field :db/id
(com/hidden {:name (fc/field-name)
:value (fc/field-value)}))
(fc/with-field :vendor-terms-override/client
(com/data-grid-cell
{}
(com/validated-field
{:errors (fc/field-errors)}
(com/typeahead {:name (fc/field-name)
:error? (fc/error?)
:autofocuse true
:class "w-full grow shrink"
:placeholder "Search..."
:url (bidi/path-for ssr-routes/only-routes :company-search)
:value (fc/field-value)
:content-fn (fn [c] (pull-attr (dc/db conn) :client/name c))}))))
(fc/with-field :vendor-terms-override/terms
(com/data-grid-cell
{}
(com/validated-field
{:errors (fc/field-errors)}
[:div.flex.items-baseline.gap-x-4
(com/int-input {:name (fc/field-name)
:value (fc/field-value)
:class "w-16"})
"days"])))
(com/data-grid-cell {:class "align-top"}
(com/a-icon-button {"@click.prevent.stop" "show=false; setTimeout(() => $refs.p.remove(), 500)"} svg/x)))))
(defn automatically-paid-when-due-row [terms-override-cursor]
(com/data-grid-row
(-> {:x-data (hx/json {:show (boolean (not (fc/field-value (:new? terms-override-cursor))))})
:data-key "show"
:x-ref "p"}
hx/alpine-mount-then-appear)
(list
(com/data-grid-cell
{}
(com/validated-field {:errors (fc/field-errors (:db/id fc/*current*))}
(com/typeahead {:name (fc/field-name (:db/id fc/*current*))
:class "w-full"
:url (bidi/path-for ssr-routes/only-routes
:company-search)
:value (fc/field-value)
:value-fn :db/id
:content-fn #(pull-attr (dc/db conn) :client/name (:db/id %))
:size :small})))
(com/data-grid-cell {:class "align-top"}
(com/a-icon-button {"@click.prevent.stop" "show=false; setTimeout(() => $refs.p.remove(), 500)"} svg/x)))))
;; TODO when a validation fails, only re render that page
(defn terms-modal [{:keys [form-errors entity form-params]}]
(com/stacked-modal-card-2
1 1
{"@keydown.enter.prevent.stop" "$refs.next.click()"} {"@keydown.enter.prevent.stop" "$refs.next.click()"}
(com/modal-header {}
[:div.flex [:div.flex
[:div.p-2 "Vendor Terms"] [:div.p-2 "Vendor Terms"]
[:p.ml-2.rounded.bg-gray-200.p-2.dark:bg-gray-600 [:p.ml-2.rounded.bg-gray-200.p-2.dark:bg-gray-600
[:span {:x-text "vendorName"}]]] [:span {:x-text "vendorName"}]]])
[:div (com/modal-header-attachment {}
[:div.space-y-1 (timeline {:active "Terms"}))
(com/modal-body {}
[:div.space-y-1 {:class "w-[600px] h-[350px]"}
(fc/with-field :vendor/terms (fc/with-field :vendor/terms
(com/validated-field {:label "Terms" (com/validated-field {:label "Terms"
:errors (fc/field-errors)} :errors (fc/field-errors)}
[:div.flex.items-baseline.gap-x-4
(com/int-input {:name (fc/field-name) (com/int-input {:name (fc/field-name)
:value (fc/field-value) :value (fc/field-value)
:autofocus true })
:class "w-24"})))]] "days"]))
(fc/with-field :vendor/terms-overrides
(com/validated-field
{:errors (fc/field-errors)
:label "Terms Overrides"}
(com/data-grid {:headers [(com/data-grid-header {} "Client")
(com/data-grid-header {:class "w-16"} "Terms")
(com/data-grid-header {:class "w-16"})]}
(fc/cursor-map #(terms-override-row %))
(com/data-grid-new-row {:colspan 3
:hx-get (bidi/path-for ssr-routes/only-routes
::route/new-terms-override)
:index (count (fc/field-value))}
"New override"))))
(fc/with-field :vendor/automatically-paid-when-due
(com/validated-field
{:errors (fc/field-errors)
:label "Automatically pay when due"}
(com/data-grid {:headers [(com/data-grid-header {} "Client")
(com/data-grid-header {:class "w-16"})]}
(fc/cursor-map #(automatically-paid-when-due-row %))
(com/data-grid-new-row {:colspan 2
:hx-get (bidi/path-for ssr-routes/only-routes ::route/new-automatic-payment)
:index (count (fc/field-value))}
"New automatic payment for client"))))])
(com/modal-footer
{}
[:div.flex.justify-end [:div.flex.justify-end
(com/form-errors {:errors (:errors fc/*form-errors*)}) (com/form-errors {:errors (:errors fc/*form-errors*)})
[:div.flex.items-baseline.gap-x-4 [:div.flex.items-baseline.gap-x-4
@@ -364,20 +450,85 @@
(com/validated-save-button {:errors (seq form-errors) (com/validated-save-button {:errors (seq form-errors)
:x-ref "next" :x-ref "next"
:class "w-48" :class "w-48"
"@click.prevent" "$dispatch('modalnext'); console.log($el)"} :hx-put (hu/url (bidi/path-for ssr-routes/only-routes ::route/validate)
{:step "Terms"})}
"Account assignments" "Account assignments"
[:div.w-5.h-5 svg/arrow-right])]]) [:div.w-5.h-5 svg/arrow-right])]])))
(com/stacked-modal-card (defn- account-typeahead*
[{:keys [name value client-id x-model]}]
[:div.flex.flex-col
(com/typeahead {:name name
:placeholder "Search..."
:url (str (bidi/path-for ssr-routes/only-routes :account-search) "?client-id=" client-id)
:id name
:x-model x-model
:value value
:content-fn (fn [value]
(:account/name (d-accounts/clientize (dc/pull (dc/db conn) d-accounts/default-read value)
client-id)))})])
(defn account-override-row [terms-override-cursor]
(alog/peek @terms-override-cursor)
(let [client-id (fc/field-value (:vendor-account-override/client terms-override-cursor))]
(com/data-grid-row
(-> {:x-data (hx/json {:show (boolean (not (fc/field-value (:new? terms-override-cursor))))
:clientId client-id
:accountId (fc/field-value (:vendor-account-override/account terms-override-cursor))})
:data-key "show"
:x-ref "p"}
hx/alpine-mount-then-appear)
(list
(fc/with-field :db/id
(com/hidden {:name (fc/field-name)
:value (fc/field-value)}))
(fc/with-field :vendor-account-override/client
(com/data-grid-cell
{}
(com/validated-field
{:errors (fc/field-errors)}
(com/typeahead {:name (fc/field-name)
:error? (fc/error?)
:x-model "clientId"
:autofocuse true
:class "w-full grow shrink"
:placeholder "Search..."
:url (bidi/path-for ssr-routes/only-routes :company-search)
:value (fc/field-value)
:content-fn (fn [c] (pull-attr (dc/db conn) :client/name c))}))))
(fc/with-field :vendor-account-override/account
(com/data-grid-cell
{}
(com/validated-field
{:errors (fc/field-errors)}
[:div {:hx-trigger "changed"
:hx-target "next div"
:hx-vals (format "js:{name: '%s', 'client-id': event.detail.clientId, value: event.detail.accountId || ''}" (fc/field-name))
:hx-get (str (bidi/path-for ssr-routes/only-routes ::route/account-typeahead))
:x-init "$watch('clientId', cid => $dispatch('changed', $data));"}]
(account-typeahead* {:value (fc/field-value)
:client-id client-id
:name (fc/field-name)
:x-model "accountId"}))))
(com/data-grid-cell {:class "align-top"}
(com/a-icon-button {"@click.prevent.stop" "show=false; setTimeout(() => $refs.p.remove(), 500)"} svg/x))))))
(defn account-modal [{:keys [form-errors entity form-params]}]
(com/stacked-modal-card-2
2 2
{"@keydown.enter.prevent.stop" "$refs.next.click()"} {"@keydown.enter.prevent.stop" "$refs.next.click()"}
(com/modal-header
{}
[:div.flex [:div.flex
[:div.p-2 "Vendor Account"] [:div.p-2 "Vendor Account"]
[:p.ml-2.rounded.bg-gray-200.p-2.dark:bg-gray-600 [:p.ml-2.rounded.bg-gray-200.p-2.dark:bg-gray-600
[:span {:x-text "vendorName"}]]] [:span {:x-text "vendorName"}]]])
[:div (com/modal-header-attachment {}
[:div.space-y-1 (timeline {:active "Account"}))
(com/modal-body
{}
[:div.space-y-1 {:class "w-[600px] h-[350px] "}
(fc/with-field :vendor/default-account (fc/with-field :vendor/default-account
(alog/info ::acount-check :a (fc/field-value)) (alog/info ::acount-check :a (fc/field-value))
(com/validated-field {:label "Default Account" (com/validated-field {:label "Default Account"
@@ -388,28 +539,52 @@
:placeholder "Search..." :placeholder "Search..."
:url (bidi/path-for ssr-routes/only-routes :account-search) :url (bidi/path-for ssr-routes/only-routes :account-search)
:value (fc/field-value) :value (fc/field-value)
:content-fn (fn [c] (pull-attr (dc/db conn) :account/name c))})))]] :content-fn (fn [c] (pull-attr (dc/db conn) :account/name c))})))
(fc/with-field :vendor/account-overrides
(com/validated-field
{:errors (fc/field-errors)
:label "Account Overrides"}
(com/data-grid {:headers [(com/data-grid-header {} "Client")
(com/data-grid-header {} "Account")
(com/data-grid-header {:class "w-16"})]}
(fc/cursor-map #(account-override-row %))
(com/data-grid-new-row {:colspan 3
:hx-get (bidi/path-for ssr-routes/only-routes ::route/new-account-override)
:index (count (fc/field-value))}
"New override"))))])
(com/modal-footer
{}
[:div.flex.justify-end [:div.flex.justify-end
(com/form-errors {:errors (:errors fc/*form-errors*)}) (com/form-errors {:errors (:errors fc/*form-errors*)})
[:div.flex.items-baseline.gap-x-4 [:div.flex.items-baseline.gap-x-4
(back-button) (back-button)
(com/validated-save-button {:errors (seq form-errors) (com/validated-save-button {:errors (seq form-errors)
:x-ref "next" :x-ref "next"
"@click.prevent" "$dispatch('modalnext'); console.log($el)"} :hx-put (hu/url (bidi/path-for ssr-routes/only-routes ::route/validate)
{:step "Account"})}
"Address" "Address"
[:div.w-5.h-5 svg/arrow-right])]]) [:div.w-5.h-5 svg/arrow-right])]])))
(com/stacked-modal-card
(defn address-modal [{:keys [form-errors entity form-params]}]
(com/stacked-modal-card-2
3 3
{"@keydown.enter.prevent.stop" "$refs.next.click()"} {"@keydown.enter.prevent.stop" "$refs.next.click()"}
(com/modal-header
{}
[:div.flex ;; TODO standardize how these headers are built [:div.flex ;; TODO standardize how these headers are built
[:div.p-2 "Address"] [:div.p-2 "Address"]
[:p.ml-2.rounded.bg-gray-200.p-2.dark:bg-gray-600 [:p.ml-2.rounded.bg-gray-200.p-2.dark:bg-gray-600
[:span {:x-text "vendorName"}]]] [:span {:x-text "vendorName"}]]])
[:div.mt-2 (com/modal-header-attachment {}
[:div.space-y-1 (timeline {:active "Address"}))
(com/modal-body
{}
[:div.space-y-1 {:class "w-[600px] h-[350px]"}
(fc/with-field :vendor/address (fc/with-field :vendor/address
[:div.flex.flex-col.w-full [:div.flex.flex-col.w-full
(fc/with-field :db/id
(com/hidden {:name (fc/field-name)
:value (fc/field-value)}))
(fc/with-field :address/street1 (fc/with-field :address/street1
(com/validated-field {:label "Street" (com/validated-field {:label "Street"
:errors (fc/field-errors)} :errors (fc/field-errors)}
@@ -450,25 +625,36 @@
:error? (fc/error?) :error? (fc/error?)
:placeholder "Suite 300" :placeholder "Suite 300"
:class "w-full" :class "w-full"
:value (fc/field-value)})))]])]] :value (fc/field-value)})))]])])
(com/modal-footer
{}
[:div.flex.justify-end [:div.flex.justify-end
(com/form-errors {:errors (:errors fc/*form-errors*)}) (com/form-errors {:errors (:errors fc/*form-errors*)})
[:div.flex.items-baseline.gap-x-4 [:div.flex.items-baseline.gap-x-4
(back-button) (back-button)
(com/validated-save-button {:errors (seq form-errors) (com/validated-save-button {:errors (seq form-errors)
:x-ref "next" :x-ref "next"
"@click.prevent" "$dispatch('modalnext'); console.log($el)"} :hx-put (hu/url (bidi/path-for ssr-routes/only-routes ::route/validate)
{:step "Address"})}
"Legal Entity" "Legal Entity"
[:div.w-5.h-5 svg/arrow-right])]]) [:div.w-5.h-5 svg/arrow-right])]])))
(com/stacked-modal-card
(defn legal-modal [{:keys [form-params entity form-errors]}]
(com/stacked-modal-card-2
4 4
{"@keydown.enter.prevent.stop" "$refs.next.click()"} {"@keydown.enter.prevent.stop" "$refs.next.click()"}
(com/modal-header
{}
[:div.flex [:div.flex
[:div.p-2 "Legal Entity Info"] [:div.p-2 "Legal Entity Info"]
[:p.ml-2.rounded.bg-gray-200.p-2.dark:bg-gray-600 [:p.ml-2.rounded.bg-gray-200.p-2.dark:bg-gray-600
[:span {:x-text "vendorName"}]]] [:span {:x-text "vendorName"}]]])
[:div (com/modal-header-attachment {}
(timeline {:active "Legal"}))
(com/modal-body
{}
[:div {:class "w-[600px] h-[350px]"}
[:div.grid.grid-cols-6.gap-x-4.gap-y-2 [:div.grid.grid-cols-6.gap-x-4.gap-y-2
[:div.col-span-6 [:div.col-span-6
(fc/with-field :vendor/legal-entity-name (fc/with-field :vendor/legal-entity-name
@@ -523,23 +709,40 @@
(com/select {:name (fc/field-name) (com/select {:name (fc/field-name)
:allow-blank? true :allow-blank? true
:value (some-> (fc/field-value) name) :value (some-> (fc/field-value) name)
:options (ref->select-options "legal-entity-1099-type")})))]]] :options (ref->select-options "legal-entity-1099-type")})))]]])
(com/modal-footer
{}
[:div.flex.justify-end [:div.flex.justify-end
(com/form-errors {:errors (:errors fc/*form-errors*)}) (com/form-errors {:errors (:errors fc/*form-errors*)})
[:div.flex.items-baseline.gap-x-4 [:div.flex.items-baseline.gap-x-4
(back-button) (back-button)
(com/validated-save-button {:errors (seq form-errors) (com/validated-save-button {:errors (seq form-errors)
:x-ref "next"} :x-ref "next"}
"Save vendor")]]))]])) "Save vendor")]])))
#_(defn new-client-override [{ {:keys [index]} :query-params}] (defn dialog* [{:keys [entity form-params form-errors] :as params}]
(html-response (alog/peek ::dialog-entity form-params)
(fc/start-form-with-prefix (fc/start-form form-params form-errors
[:account/client-overrides (or index 0)] [:div {:x-data (hx/json {"vendorName" (:vendor/name form-params)
{:db/id (str (java.util.UUID/randomUUID)) "showPrintAs" (boolean (not-empty (:vendor/print-as form-params)))
:new? true} "printAs" (:vendor/print-as form-params)})
[] :class "w-full h-full"}
(client-override* fc/*current*)))) [:form#my-form (-> {:hx-ext "response-targets"
:hx-swap "outerHTML swap:300ms"
:hx-target-400 "#form-errors .error-content"
:hx-trigger "submit"
:class "h-full w-full"}
(assoc (if (:db/id entity)
:hx-put
:hx-post)
(str (bidi/path-for ssr-routes/only-routes ::route/save))))
(com/modal
{}
(info-modal params)
(terms-modal params)
(account-modal params)
(address-modal params)
(legal-modal params))]]))
(def form-schema (mc/schema (def form-schema (mc/schema
[:map [:map
@@ -548,15 +751,31 @@
[:vendor/print-as {:optional true} [:maybe [:string {:min 3 :decode/string strip}]]] [:vendor/print-as {:optional true} [:maybe [:string {:min 3 :decode/string strip}]]]
[:vendor/default-account entity-id] [:vendor/default-account entity-id]
[:vendor/terms {:optional true} [:maybe :int]] [:vendor/terms {:optional true} [:maybe :int]]
[:vendor/hidden {:optional true :default false} [:vendor/automatically-paid-when-due {:optional true}
[:boolean {:decode/string {:enter #(if (= % "on") true (boolean %))}}]] ;; TODO cant turn off [:maybe
[:vendor/address {:optional true} (many-entity {} [:db/id entity-id])]]
[:maybe [:map [:vendor/terms-overrides {:optional true}
(many-entity {}
[:db/id [:or entity-id temp-id]]
[:vendor-terms-override/terms :int]
[:vendor-terms-override/client entity-id])]
[:vendor/account-overrides {:optional true}
(many-entity {}
[:db/id [:or entity-id temp-id]]
[:vendor-account-override/account entity-id]
[:vendor-account-override/client entity-id])]
[:vendor/hidden {:default false}
[:boolean {:decode/string {:enter #(if (= % "on") true
(boolean %))}}]]
[:vendor/address {:default {}}
[:map
[:db/id {:default "new-account"} [:or entity-id temp-id]]
[:address/street1 {:optional true} [:maybe [:string {:min 3 :decode/string strip}]]] [:address/street1 {:optional true} [:maybe [:string {:min 3 :decode/string strip}]]]
[:address/street2 {:optional true} [:maybe [:string {:min 3 :decode/string strip}]]] [:address/street2 {:optional true} [:maybe [:string {:min 3 :decode/string strip}]]]
[:address/city {:optional true} [:maybe [:string {:min 2 :decode/string strip}]]] [:address/city {:optional true} [:maybe [:string {:min 2 :decode/string strip}]]]
[:address/state {:optional true} [:maybe [:string {:min 2 :max 2 :decode/string strip}]]] [:address/state {:optional true} [:maybe [:string {:min 2 :max 2 :decode/string strip}]]]
[:address/zip {:optional true} [:maybe [:string {:min 5 :max 5 :decode/string strip}]] ]]]] [:address/zip {:optional true} [:maybe [:string {:min 5 :max 5 :decode/string strip}]]]]]
[:vendor/legal-entity-tin {:optional true} [:maybe [:string {:min 3 :decode/string strip}]]] [:vendor/legal-entity-tin {:optional true} [:maybe [:string {:min 3 :decode/string strip}]]]
[:vendor/legal-entity-name {:optional true} [:maybe [:string {:min 3 :decode/string strip}]]] [:vendor/legal-entity-name {:optional true} [:maybe [:string {:min 3 :decode/string strip}]]]
[:vendor/legal-entity-first-name {:optional true} [:maybe [:string {:min 3 :decode/string strip}]]] [:vendor/legal-entity-first-name {:optional true} [:maybe [:string {:min 3 :decode/string strip}]]]
@@ -566,55 +785,131 @@
[:vendor/legal-entity-1099-type {:optional true} [:maybe (ref->enum-schema "legal-entity-1099-type")]]])) [:vendor/legal-entity-1099-type {:optional true} [:maybe (ref->enum-schema "legal-entity-1099-type")]]]))
(defn dialog [{:keys [entity form-params form-errors]}] (defn dialog [{:keys [entity form-params form-errors]}]
(alog/info ::here :fp entity)
(modal-response (dialog* {:entity entity (modal-response (dialog* {:entity entity
:form-params (or (when (seq form-params) :form-params (or (when (seq form-params)
form-params) form-params)
(when entity (when entity
(mc/decode form-schema entity main-transformer)) (mc/decode form-schema entity main-transformer))
{}) (mc/decode form-schema {} main-transformer))
:form-errors form-errors}))) :form-errors form-errors})))
(defn terms-dialog [{:keys [entity form-params form-errors]}]
(let [form-params (merge {}
(when entity (defn new-terms-override [{{:keys [index]} :query-params}]
(mc/decode form-schema entity main-transformer))
(when (seq form-params)
form-params))]
(html-response (html-response
(fc/start-form-with-prefix
[:vendor/terms-overrides (or index 0)]
{:db/id (str (java.util.UUID/randomUUID))
:new? true}
[]
(terms-override-row fc/*current*))))
:headers (-> {} (defn new-automatic-payment [{{:keys [index]} :query-params}]
(assoc "hx-trigger-after-settle" "modalnext") (html-response
(assoc "hx-retarget" ".modal-stack") (fc/start-form-with-prefix
(assoc "hx-reswap" "beforeend"))))) [:vendor/automatically-paid-when-due (or index 0)]
{:db/id nil
:new? true}
[]
(automatically-paid-when-due-row fc/*current*))))
(defn new-account-override [{{:keys [index]} :query-params}]
(html-response
(fc/start-form-with-prefix
[:vendor/account-overrides (or index 0)]
{:db/id (str (java.util.UUID/randomUUID))
:new? true}
[]
(account-override-row fc/*current*))))
(defn validate [request]
(cond (= (:step (:query-params request))
"Info")
(schema-enforce-request request
:form-schema (mut/select-keys form-schema #{:vendor/name :vendor/print-as}))
(= (:step (:query-params request))
"Terms")
(schema-enforce-request request
:form-schema (mut/select-keys form-schema #{:vendor/terms :vendor/terms-overrides :vendor/automatically-paid-when-due}))
(= (:step (:query-params request))
"Account")
(schema-enforce-request request
:form-schema (mut/select-keys form-schema #{:vendor/default-account :vendor/account-overrides}))
(= (:step (:query-params request))
"Address")
(schema-enforce-request request
:form-schema (mut/select-keys form-schema #{:vendor/address}))
(= (:step (:query-params request))
"Legal")
(schema-enforce-request request
:form-schema form-schema)
:else nil)
(html-response [:div]
:headers {"hx-trigger-after-settle" "modalnext"
"hx-reswap" "none"}))
#_(defn validate-failed [request]
(d)
(html-response ()
:headers {"hx-trigger-after-settle" "modalnext"
"hx-reswap" "none"}))
(defn account-typeahead [{{:keys [name value client-id] :as qp} :query-params}]
(html-response (account-typeahead* {:name name
:value value
:client-id client-id
:x-model "accountId"})))
(def key->handler (def key->handler
(apply-middleware-to-all-handlers (apply-middleware-to-all-handlers
(->> (->>
{::route/page (helper/page-route grid-page) {::route/page (helper/page-route grid-page)
::route/table (helper/table-route grid-page) ::route/table (helper/table-route grid-page)
::route/new nil #_ (-> new-client-override ::route/new dialog
(wrap-schema-decode :query-schema [:map ::route/validate (-> validate
[:index {:optional true (wrap-schema-enforce :query-schema [:map [:step :string]])
:default 0} [nat-int? {:default 0}]]]) (wrap-nested-form-params)
wrap-admin wrap-client-redirect-unauthenticated) (wrap-form-4xx-2 (-> dialog
(wrap-schema-decode :form-schema form-schema)
(wrap-entity [:form-params :db/id] default-read))))
::route/save (-> save ::route/save (-> save
(wrap-entity [:form-params :db/id] default-read) (wrap-entity [:form-params :db/id] default-read)
(wrap-schema-decode :form-schema form-schema) (wrap-schema-enforce :form-schema form-schema)
(wrap-nested-form-params) (wrap-nested-form-params)
(wrap-form-4xx-2 (wrap-entity dialog [:form-params :db/id] default-read))) (wrap-form-4xx-2 (wrap-entity dialog [:form-params :db/id] default-read)))
::route/edit (-> dialog ::route/edit (-> dialog
(wrap-entity [:route-params :db/id] default-read) (wrap-entity [:route-params :db/id] default-read)
(wrap-schema-decode :route-schema [:map [:db/id entity-id]])) (wrap-schema-enforce :route-schema [:map [:db/id entity-id]]))
::route/edit-terms (-> terms-dialog ::route/edit-terms (-> terms-dialog
(wrap-entity [:form-params :db/id] default-read) (wrap-entity [:form-params :db/id] default-read)
(wrap-schema-decode :form-schema form-schema) (wrap-schema-enforce :form-schema form-schema)
(wrap-nested-form-params) (wrap-nested-form-params)
(wrap-form-4xx-2 (-> dialog (wrap-form-4xx-2 (-> dialog
(wrap-entity [:form-params :db/id] default-read)))) (wrap-entity [:form-params :db/id] default-read))))
#_#_:admin-account-new-dialog account-dialog}) ::route/new-terms-override (-> new-terms-override
(wrap-schema-enforce :query-schema [:map
[:index {:optional true
:default 0} [nat-int? {:default 0}]]]))
::route/account-typeahead (-> account-typeahead
(wrap-schema-enforce :query-schema [:map
[:name :string]
[:client-id {:optional true}
[:maybe entity-id]]
[:value {:optional true}
[:maybe entity-id]]]))
::route/new-automatic-payment (-> new-automatic-payment
(wrap-schema-enforce :query-schema [:map
[:index {:optional true
:default 0} [nat-int? {:default 0}]]]))
::route/new-account-override (-> new-account-override
(wrap-schema-enforce :query-schema [:map
[:index {:optional true
:default 0} [nat-int? {:default 0}]]]))})
(fn [h] (fn [h]
(-> h (-> h
(wrap-admin) (wrap-admin)

View File

@@ -21,7 +21,7 @@
strip strip
wrap-entity wrap-entity
wrap-form-4xx-2 wrap-form-4xx-2
wrap-schema-decode]] wrap-schema-enforce]]
[bidi.bidi :as bidi] [bidi.bidi :as bidi]
[clojure.string :as str] [clojure.string :as str]
[datomic.api :as dc] [datomic.api :as dc]
@@ -365,18 +365,18 @@
:company-1099-vendor-table vendor-table :company-1099-vendor-table vendor-table
:company-1099-vendor-dialog (-> vendor-dialog :company-1099-vendor-dialog (-> vendor-dialog
(wrap-entity [:route-params :vendor-id] default-vendor-read) (wrap-entity [:route-params :vendor-id] default-vendor-read)
(wrap-schema-decode :route-schema [:map [:vendor-id entity-id]] (wrap-schema-enforce :route-schema [:map [:vendor-id entity-id]]
:query-schema [:map [:client-id entity-id]])) :query-schema [:map [:client-id entity-id]]))
:company-1099-vendor-save (-> vendor-save :company-1099-vendor-save (-> vendor-save
(wrap-entity [:form-params :db/id] default-vendor-read) (wrap-entity [:form-params :db/id] default-vendor-read)
(wrap-schema-decode :form-schema form-schema (wrap-schema-enforce :form-schema form-schema
:route-schema [:map [:vendor-id entity-id]] :route-schema [:map [:vendor-id entity-id]]
:query-schema [:map [:client-id entity-id]]) :query-schema [:map [:client-id entity-id]])
(wrap-nested-form-params) (wrap-nested-form-params)
(wrap-form-4xx-2 (-> vendor-dialog (wrap-form-4xx-2 (-> vendor-dialog
(wrap-entity [:form-params :db/id] default-vendor-read) (wrap-entity [:form-params :db/id] default-vendor-read)
(wrap-entity [:route-params :vendor-id] default-vendor-read) (wrap-entity [:route-params :vendor-id] default-vendor-read)
(wrap-schema-decode :route-schema [:map [:vendor-id entity-id]] (wrap-schema-enforce :route-schema [:map [:vendor-id entity-id]]
:query-schema [:map [:client-id entity-id]]))))}) :query-schema [:map [:client-id entity-id]]))))})
(fn [h] (fn [h]
(-> h (-> h

View File

@@ -25,6 +25,11 @@
(def modal dialog/modal-) (def modal dialog/modal-)
(def modal-card dialog/modal-card-) (def modal-card dialog/modal-card-)
(def stacked-modal-card dialog/stacked-modal-card-) (def stacked-modal-card dialog/stacked-modal-card-)
(def stacked-modal-card-2 dialog/stacked-modal-card-2-)
(def modal-header dialog/modal-header-)
(def modal-header-attachment dialog/modal-header-attachment-)
(def modal-body dialog/modal-body-)
(def modal-footer dialog/modal-footer-)
(def text-input inputs/text-input-) (def text-input inputs/text-input-)
(def checkbox inputs/checkbox-) (def checkbox inputs/checkbox-)

View File

@@ -58,7 +58,7 @@
(defn stacked-modal-card- [index params header content footer] (defn stacked-modal-card- [index params header content footer]
[:div (merge params [:div (merge params
{:class "bg-white rounded-lg shadow dark:bg-gray-700 dark:text-white modal-content flex flex-col h-full" {:class (hh/add-class "bg-white rounded-lg shadow dark:bg-gray-700 dark:text-white modal-content flex flex-col h-full" (:class params ""))
:x-data (hx/json {:i index}) :x-data (hx/json {:i index})
:x-show "index == i && hidingIndex != i" :x-show "index == i && hidingIndex != i"
"x-trap" "index == i && hidingIndex == -1 && !transitioning" "x-trap" "index == i && hidingIndex == -1 && !transitioning"
@@ -67,12 +67,46 @@
"x-transition:leave" "transition duration-150", "x-transition:leave" "transition duration-150",
"x-transition:leave-start" "translate-x-0 scale-100 opacity-100", "x-transition:leave-start" "translate-x-0 scale-100 opacity-100",
}) })
[:div {:class "flex items-start justify-between p-4 border-b rounded-t dark:border-gray-600 shrink-0"} header] [:div {:class "flex items-start justify-between p-4 border-b rounded-t dark:border-gray-600 shrink-0"} header] ;; todo componentize these
[:div {:class "px-6 space-y-6 overflow-y-scroll w-full shrink"} [:div {:class "px-6 space-y-6 overflow-y-scroll w-full shrink"}
content] content] ;; TODO componentize
(when footer [:div {:class "p-4"} (when footer [:div {:class "p-4"}
[:span.items-center.bg-red-100.text-red-800.text-xs.font-medium.mb-2.p-1.rounded-full.inline-flex (hx/alpine-appear {:x-show "unexpectedError" :class "dark:bg-red-900 dark:text-red-300"}) [:span.items-center.bg-red-100.text-red-800.text-xs.font-medium.mb-2.p-1.rounded-full.inline-flex (hx/alpine-appear {:x-show "unexpectedError" :class "dark:bg-red-900 dark:text-red-300"})
[:span {:class "w-2 h-2 bg-red-500 rounded-full"}] [:span {:class "w-2 h-2 bg-red-500 rounded-full"}]
[:span.px-2.py-0.5 "An unexpected error has occured. Integreat staff have been notified."]] [:span.px-2.py-0.5 "An unexpected error has occured. Integreat staff have been notified."]]
[:div {:class "shrink-0"}]footer])]) [:div {:class "shrink-0"}]footer])])
(defn modal-header- [params & children]
[:div {:class "flex items-start justify-between p-4 border-b rounded-t dark:border-gray-600 shrink-0"}
children])
(defn modal-header-attachment- [params & children]
[:div {:class "flex items-start justify-between p-4 border-b shrink-0"}
children])
(defn modal-body- [params & children]
[:div {:class "px-6 py-2 space-y-6 overflow-y-scroll w-full shrink"}
children])
(defn modal-footer- [params & children]
[:div {:class "p-4"}
[:span.items-center.bg-red-100.text-red-800.text-xs.font-medium.mb-2.p-1.rounded-full.inline-flex (hx/alpine-appear {:x-show "unexpectedError" :class "dark:bg-red-900 dark:text-red-300"})
[:span {:class "w-2 h-2 bg-red-500 rounded-full"}]
[:span.px-2.py-0.5 "An unexpected error has occured. Integreat staff have been notified."]]
[:div {:class "shrink-0"}]
children])
(defn stacked-modal-card-2- [index params & children]
[:div (merge params
{:class (hh/add-class "bg-white rounded-lg shadow dark:bg-gray-700 dark:text-white modal-content flex flex-col h-full" (:class params ""))
:x-data (hx/json {:i index})
:x-show "index == i && hidingIndex != i"
"x-trap" "index == i && hidingIndex == -1 && !transitioning"
"x-transition:enter" "transition duration-150",
"x-transition:enter-end" "translate-x-0 scale-100 opacity-100",
"x-transition:leave" "transition duration-150",
"x-transition:leave-start" "translate-x-0 scale-100 opacity-100",
})
children])

View File

@@ -128,7 +128,7 @@
[:input [:input
(-> params (-> params
(dissoc :error?) (dissoc :error?)
(assoc :type "text") (assoc :type "text" :autocomplete "off")
(update (update
:class #(-> "" :class #(-> ""
(hh/add-class default-input-classes) (hh/add-class default-input-classes)

View File

@@ -0,0 +1,28 @@
(ns auto-ap.ssr.components.timeline
(:require [auto-ap.ssr.hiccup-helper :as hh]))
(defn timeline-step [{:keys [active? visited? last?]} & children]
(if active?
[:li {:class "flex items-center text-primary-600 font-medium dark:text-primary-500"}
[:span {:class "flex items-center justify-center w-5 h-5 mr-2 text-xs border-2 border-primary-600 rounded-full shrink-0 dark:border-primary-500"} ]
children
(when-not last?
[:svg {:class "w-3 h-3 ml-2 sm:ml-4", :aria-hidden "true", :xmlns "http://www.w3.org/2000/svg", :fill "none", :viewbox "0 0 12 10"}
[:path {:stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "2", :d "m7 9 4-4-4-4M1 9l4-4-4-4"}]])]
[:li {:class (cond-> "flex items-center"
(not visited?) (hh/add-class "text-gray-400"))}
[:span {:class "flex items-center justify-center w-5 h-5 mr-2 text-xs border border-gray-500 rounded-full shrink-0 dark:border-gray-400"}
(when visited?
[:svg {:class "w-3 h-3 text-primary-600 dark:text-primary-500", :aria-hidden "true", :xmlns "http://www.w3.org/2000/svg", :fill "none", :viewbox "0 0 16 12"}
[:path {:stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "2", :d "M1 5.917 5.724 10.5 15 1.5"}]])]
children
(when-not last?
[:svg {:class "w-3 h-3 ml-2 sm:ml-4", :aria-hidden "true", :xmlns "http://www.w3.org/2000/svg", :fill "none", :viewbox "0 0 12 10"}
[:path {:stroke "currentColor", :stroke-linecap "round", :stroke-linejoin "round", :stroke-width "2", :d "m7 9 4-4-4-4M1 9l4-4-4-4"}]])]))
(defn timeline [params & children]
[:ol {:class "flex items-center w-full space-x-2 text-xs text-center text-gray-500 bg-white dark:text-gray-400 sm:text-base dark:bg-gray-800 sm:space-x-4 px-2"}
children
#_[:li {:class "flex items-center"}
[:span {:class "flex items-center justify-center w-5 h-5 mr-2 text-xs border border-gray-500 rounded-full shrink-0 dark:border-gray-400"} ]]])

View File

@@ -31,7 +31,7 @@
ref->select-options ref->select-options
wrap-entity wrap-entity
wrap-form-4xx-2 wrap-form-4xx-2
wrap-schema-decode]] wrap-schema-enforce]]
[auto-ap.time :as atime] [auto-ap.time :as atime]
[bidi.bidi :as bidi] [bidi.bidi :as bidi]
[buddy.sign.jwt :as jwt] [buddy.sign.jwt :as jwt]
@@ -375,20 +375,20 @@
:user-table (helper/table-route grid-page) :user-table (helper/table-route grid-page)
:user-edit-save (-> user-edit-save :user-edit-save (-> user-edit-save
(wrap-entity [:form-params :db/id] default-read) (wrap-entity [:form-params :db/id] default-read)
(wrap-schema-decode :form-schema form-schema) (wrap-schema-enforce :form-schema form-schema)
(wrap-nested-form-params) (wrap-nested-form-params)
(wrap-form-4xx-2 (wrap-entity user-dialog [:form-params :db/id] default-read))) (wrap-form-4xx-2 (wrap-entity user-dialog [:form-params :db/id] default-read)))
:user-client-new (-> new-client :user-client-new (-> new-client
(wrap-schema-decode :query-schema [:map (wrap-schema-enforce :query-schema [:map
[:index {:optional true [:index {:optional true
:default 0} [nat-int? {:default 0}]]])) :default 0} [nat-int? {:default 0}]]]))
:user-edit-dialog (-> user-dialog :user-edit-dialog (-> user-dialog
(wrap-entity [:route-params :db/id] default-read) (wrap-entity [:route-params :db/id] default-read)
(wrap-schema-decode (wrap-schema-enforce
:route-schema (mc/schema [:map [:db/id entity-id]]))) :route-schema (mc/schema [:map [:db/id entity-id]])))
:user-impersonate (-> impersonate :user-impersonate (-> impersonate
(wrap-entity [:params :db/id] default-read) (wrap-entity [:params :db/id] default-read)
(wrap-schema-decode (wrap-schema-enforce
:params-schema (mc/schema [:map [:db/id entity-id]])))} :params-schema (mc/schema [:map [:db/id entity-id]])))}
(fn [h] (fn [h]
(-> h (-> h

View File

@@ -174,8 +174,7 @@
:else :else
s)) s))
(defn wrap-schema-decode [handler & {:keys [form-schema query-schema route-schema params-schema]}] (defn schema-enforce-request [{:keys [form-params query-params params] :as request} & {:keys [form-schema query-schema route-schema params-schema]}]
(fn [{:keys [form-params query-params params] :as request}]
(let [request (try (let [request (try
(cond-> request (cond-> request
(and (:params request) params-schema) (and (:params request) params-schema)
@@ -222,7 +221,55 @@
{:type :schema-validation {:type :schema-validation
:decoded (:value (:data (ex-data e))) :decoded (:value (:data (ex-data e)))
:error (:data (ex-data e))}))))] :error (:data (ex-data e))}))))]
(handler request)))) request))
(defn wrap-schema-enforce [handler & {:keys [form-schema query-schema route-schema params-schema]}]
(fn [request]
(handler (schema-enforce-request request
:form-schema form-schema
:query-schema query-schema
:route-schema route-schema
:params-schema params-schema))))
(defn schema-decode-request [{:keys [form-params query-params params] :as request} & {:keys [form-schema query-schema route-schema params-schema]}]
(let [request (cond-> request
(and (:params request) params-schema)
(assoc :params
(mc/decode
params-schema
(:params request)
main-transformer))
(and (:route-params request) route-schema)
(assoc :route-params
(mc/decode
route-schema
(:route-params request)
main-transformer))
(and form-schema form-params)
(assoc :form-params
(mc/decode
form-schema
form-params
main-transformer))
(and query-schema query-params)
(assoc :query-params
(mc/decode
query-schema
query-params
main-transformer)))]
request))
(defn wrap-schema-decode [handler & {:keys [form-schema query-schema route-schema params-schema]}]
(fn [request]
(handler (schema-decode-request request
:form-schema form-schema
:query-schema query-schema
:route-schema route-schema
:params-schema params-schema))))
(defn ref->enum-schema [n] (defn ref->enum-schema [n]
(into [:enum {:decode/string #(if (keyword? %) (into [:enum {:decode/string #(if (keyword? %)

View File

@@ -38,7 +38,14 @@
"") "")
(:user-name item) (:user-name item)
) )
(puget/cprint (reduce (println (reduce
(fn [acc [k v]]
(assoc acc k v))
{}
(dissoc
item
:user)))
#_(puget/cprint (reduce
(fn [acc [k v]] (fn [acc [k v]]
(assoc acc k v)) (assoc acc k v))
{} {}

View File

@@ -4,7 +4,11 @@
:put ::save :put ::save
:post ::save} :post ::save}
"/table" ::table "/table" ::table
"/terms" ::edit-terms "/terms/override" ::new-terms-override
"/automatic-payment" ::new-automatic-payment
"/account-override" ::new-account-override
"/account-typeahead" ::account-typeahead
"/validate" ::validate
"/new" {:get ::new} "/new" {:get ::new}
["/" [#"\d+" :db/id] "/edit"] {:get ::edit ["/" [#"\d+" :db/id] "/edit"] {:get ::edit
}}) }})