diff --git a/resources/public/js/alpine-vals.js b/resources/public/js/alpine-vals.js index 55af6cbf..f83be960 100644 --- a/resources/public/js/alpine-vals.js +++ b/resources/public/js/alpine-vals.js @@ -14,6 +14,7 @@ document.addEventListener('alpine:init', () => { effect(() => { dependent_properties(props => { + console.log("CHANGED PROPS", expression, props) el.dispatchEvent( new CustomEvent(value, { props, diff --git a/src/clj/auto_ap/ssr/invoice/new_invoice_wizard.clj b/src/clj/auto_ap/ssr/invoice/new_invoice_wizard.clj index b7bba26b..55101784 100644 --- a/src/clj/auto_ap/ssr/invoice/new_invoice_wizard.clj +++ b/src/clj/auto_ap/ssr/invoice/new_invoice_wizard.clj @@ -12,10 +12,12 @@ [auto-ap.ssr.components.multi-modal :as mm] [auto-ap.ssr.form-cursor :as fc] [auto-ap.ssr.hx :as hx] + [auto-ap.ssr.nested-form-params :refer [wrap-nested-form-params]] [auto-ap.ssr.svg :as svg] [auto-ap.ssr.utils :refer [apply-middleware-to-all-handlers clj-date-schema - entity-id html-response money wrap-schema-enforce]] + entity-id html-response money wrap-schema-decode + wrap-schema-enforce]] [auto-ap.time :as atime] [bidi.bidi :as bidi] [clj-time.core :as time] @@ -27,6 +29,7 @@ [:map [:invoice/client entity-id] [:invoice/date clj-date-schema] + [:invoice/due {:optional true} [:maybe clj-date-schema]] [:invoice/vendor entity-id] [:invoice/expense-accounts [:vector {:coerce? true} @@ -46,99 +49,115 @@ []) (step-schema [_] - (mut/select-keys (mm/form-schema linear-wizard) #{:invoice/client :invoice/vendor :invoice/date})) + (mut/select-keys (mm/form-schema linear-wizard) #{:invoice/client :invoice/vendor :invoice/date :invoice/due})) (render-step [this request] - (alog/peek ::check (:multi-form-state request)) - (mm/default-render-step - linear-wizard this - :head [:div.p-2 "New invoice"] - :body (mm/default-step-body - {} - [:div {} - (fc/with-field :invoice/client - (if (:client request) - (com/hidden {:name (fc/field-name) - :value (:db/id (:client request))}) - (com/validated-field - {:label "Client" - :errors (fc/field-errors)} - [:div.w-96 - (com/typeahead {:name (fc/field-name) - :error? (fc/error?) - :class "w-96" - :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 :invoice/vendor - (com/validated-field - {:label "Vendor" - :errors (fc/field-errors)} - [:div.w-96 - (com/typeahead {:name (fc/field-name) - :error? (fc/error?) - :class "w-96" - :placeholder "Search..." - :url (bidi/path-for ssr-routes/only-routes :vendor-search) - :value (fc/field-value) - :content-fn (fn [c] (pull-attr (dc/db conn) :vendor/name c))})])) + (alog/peek ::check (:multi-form-state request)) + (mm/default-render-step + linear-wizard this + :head [:div.p-2 "New invoice"] + :body (mm/default-step-body + {} + [:div {:x-data (hx/json {:clientId (or (:db/id (:client request)) + (fc/field-value (:invoice/client fc/*current*))) + :vendorId (fc/field-value (:invoice/vendor fc/*current*)) + :date (-> (fc/field-value (:invoice/date fc/*current*)) + (atime/unparse-local atime/normal-date))})} + (fc/with-field :invoice/client + (if (:client request) + (com/hidden {:name (fc/field-name) + :value (:db/id (:client request))}) + (com/validated-field + {:label "Client" + :errors (fc/field-errors)} + [:div.w-96 + (com/typeahead {:name (fc/field-name) + :error? (fc/error?) + :class "w-96" + :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)) + :x-model "clientId"})]))) + (fc/with-field :invoice/vendor + (com/validated-field + {:label "Vendor" + :errors (fc/field-errors)} + [:div.w-96 + (com/typeahead {:name (fc/field-name) + :error? (fc/error?) + :class "w-96" + :placeholder "Search..." + :url (bidi/path-for ssr-routes/only-routes :vendor-search) + :value (fc/field-value) + :content-fn (fn [c] (pull-attr (dc/db conn) :vendor/name c)) + :x-model "vendorId"})])) - [:div.flex.space-x-8 - (fc/with-field :invoice/date - (com/validated-field - {:label "Date" - :errors (fc/field-errors)} - [:div {:class "w-24"} - (com/date-input {:value (some-> (fc/field-value) - (atime/unparse-local atime/normal-date)) - :name (fc/field-name) - :error? (fc/field-errors) - :placeholder "1/1/2024"})])) - (fc/with-field :invoice/due - (com/validated-field - {:label "Due (optional)" - :errors (fc/field-errors)} - [:div {:class "w-24"} - (com/date-input {:value (some-> (fc/field-value) - (atime/unparse-local atime/normal-date)) - :name (fc/field-name) - :error? (fc/field-errors) - :placeholder "1/1/2024"})])) - (fc/with-field :invoice/scheduled-payment - (com/validated-field - {:label "Scheduled payment (optional)" - :errors (fc/field-errors)} - [:div {:class "w-24"} - (com/date-input {:value (some-> (fc/field-value) - (atime/unparse-local atime/normal-date)) - :name (fc/field-name) - :error? (fc/field-errors) - :placeholder "1/1/2024"})]))] + [:div.flex.space-x-8 + (fc/with-field :invoice/date + (com/validated-field + {:label "Date" + :errors (fc/field-errors)} + [:div {:class "w-24"} + (com/date-input {:value (some-> (fc/field-value) + (atime/unparse-local atime/normal-date)) + :name (fc/field-name) + :error? (fc/field-errors) + "@change" "date=$event.target.value" ;; TODO x-model does not work + :placeholder "1/1/2024"})])) + (fc/with-field :invoice/due + (com/validated-field + {:label "Due (optional)" + :errors (fc/field-errors)} + [:div {:class "w-24" + :hx-put (bidi.bidi/path-for ssr-routes/only-routes ::route/due-date) + :x-hx-val:client-id "clientId" ;; TODO maybe just use the whole form + :x-hx-val:vendor-id "vendorId" + :x-hx-val:date "date" + :x-dispatch:changed "[clientId, vendorId, date]" + :x-effect "console.log([clientId, vendorId, date])" + :hx-trigger "changed" + :hx-target "this" + :hx-swap "innerHTML"} + (com/date-input {:value (some-> (fc/field-value) + (atime/unparse-local atime/normal-date)) + :name (fc/field-name) + :error? (fc/field-errors) + :placeholder "1/1/2024"})])) + (fc/with-field :invoice/scheduled-payment + (com/validated-field + {:label "Scheduled payment (optional)" + :errors (fc/field-errors)} + [:div {:class "w-24"} + (com/date-input {:value (some-> (fc/field-value) + (atime/unparse-local atime/normal-date)) + :name (fc/field-name) + :error? (fc/field-errors) + :placeholder "1/1/2024"})]))] - (fc/with-field :invoice/invoice-number - (com/validated-field - {:label "Invoice Number" - :errors (fc/field-errors)} - [:div {:class "w-24"} - (com/text-input {:value (-> (fc/field-value)) - :name (fc/field-name) - :error? (fc/field-errors) - :placeholder "HA-123"})])) - (fc/with-field :invoice/total - (com/validated-field - {:label "Total" - :errors (fc/field-errors)} - [:div {:class "w-16"} - (com/money-input {:value (-> (fc/field-value)) - :name (fc/field-name) - :class "w-24" - :error? (fc/field-errors) - :placeholder "212.44"})]))]) + (fc/with-field :invoice/invoice-number + (com/validated-field + {:label "Invoice Number" + :errors (fc/field-errors)} + [:div {:class "w-24"} + (com/text-input {:value (-> (fc/field-value)) + :name (fc/field-name) + :error? (fc/field-errors) + :placeholder "HA-123"})])) + (fc/with-field :invoice/total + (com/validated-field + {:label "Total" + :errors (fc/field-errors)} + [:div {:class "w-16"} + (com/money-input {:value (-> (fc/field-value)) + :name (fc/field-name) + :class "w-24" + :error? (fc/field-errors) + :placeholder "212.44"})]))]) - :footer - (mm/default-step-footer linear-wizard this :validation-route ::route/new-wizard-navigate) - :validation-route ::route/new-wizard-navigate))) + :footer + (mm/default-step-footer linear-wizard this :validation-route ::route/new-wizard-navigate) + :validation-route ::route/new-wizard-navigate))) (defn- location-select* [{:keys [name account-location client-locations value]}] @@ -173,7 +192,7 @@ [{:keys [value client-id]}] (com/data-grid-row (-> {:x-data (hx/json {:show (boolean (not (fc/field-value (:new? value)))) - :accountId (:db/id (fc/field-value (:invoice-expense-account/account value)))}) + :accountId (fc/field-value (:invoice-expense-account/account value))}) :data-key "show" :x-ref "p"} hx/alpine-mount-then-appear) @@ -205,10 +224,10 @@ (location-select* {:name (fc/field-name) :account-location (:account/location (cond->> (:invoice-expense-account/account @value) (nat-int? (:invoice-expense-account/account @value)) (dc/pull (dc/db conn) - '[:account/location]))) + '[:account/location]))) :client-locations (pull-attr (dc/db conn) :client/locations client-id) :value (fc/field-value)})))) - (fc/with-field :invoice-expense-account/amount + (fc/with-field :invoice-expense-account/amount (com/data-grid-cell {} (com/validated-field @@ -233,31 +252,32 @@ (mut/select-keys (mm/form-schema linear-wizard) #{:invoice/expense-accounts})) (render-step [this {{:keys [snapshot]} :multi-form-state :as request}] - (mm/default-render-step - linear-wizard this - :head [:div.p-2 "Invoice accounts "] - :body (mm/default-step-body - {} - [:div {} - (pull-attr (dc/db conn) :client/name (:invoice/client snapshot)) - (fc/with-field :invoice/expense-accounts - (com/validated-field - {:errors (fc/field-errors)} - (com/data-grid {:headers [(com/data-grid-header {} "Account") - (com/data-grid-header {:class "w-32"} "Location") - (com/data-grid-header {:class "w-16"} "%") - (com/data-grid-header {:class "w-16"})]} - (fc/cursor-map #(invoice-expense-account-row* {:value % - :client-id (:invoice/client snapshot)})) - (com/data-grid-new-row {:colspan 4 - :hx-get (bidi/path-for ssr-routes/only-routes - ::route/new-wizard-new-account) - :index (count (fc/field-value)) - :tr-params {:hx-vals (hx/json {:client-id (:invoice/client snapshot)})}} - "New account"))))]) - :footer - (mm/default-step-footer linear-wizard this :validation-route ::route/new-wizard-navigate) - :validation-route ::route/new-wizard-navigate))) + (alog/peek ::mfs (:step-params (:multi-form-state request))) + (mm/default-render-step + linear-wizard this + :head [:div.p-2 "Invoice accounts "] + :body (mm/default-step-body + {} + [:div {} + (pull-attr (dc/db conn) :client/name (:invoice/client snapshot)) + (fc/with-field :invoice/expense-accounts + (com/validated-field + {:errors (fc/field-errors)} + (com/data-grid {:headers [(com/data-grid-header {} "Account") + (com/data-grid-header {:class "w-32"} "Location") + (com/data-grid-header {:class "w-16"} "%") + (com/data-grid-header {:class "w-16"})]} + (fc/cursor-map #(invoice-expense-account-row* {:value % + :client-id (:invoice/client snapshot)})) + (com/data-grid-new-row {:colspan 4 + :hx-get (bidi/path-for ssr-routes/only-routes + ::route/new-wizard-new-account) + :index (count (fc/field-value)) + :tr-params {:hx-vals (hx/json {:client-id (:invoice/client snapshot)})}} + "New account"))))]) + :footer + (mm/default-step-footer linear-wizard this :validation-route ::route/new-wizard-navigate) + :validation-route ::route/new-wizard-navigate))) @@ -291,8 +311,8 @@ step-key))) (form-schema [_] new-form-schema) (submit [this {:keys [multi-form-state request-method identity] :as request}] - (html-response [:div] - :headers {"hx-trigger" "modalclose"}))) + (html-response [:div] + :headers {"hx-trigger" "modalclose"}))) (def new-wizard (->NewWizard2 nil nil)) @@ -310,12 +330,46 @@ (pull-attr (dc/db conn) :account/location)) :client-locations (some->> client-id (pull-attr (dc/db conn) :client/locations))}))) +(defn due-date [{:keys [multi-form-state]}] + (alog/peek ::date multi-form-state) + (let [vendor (dc/pull (dc/db conn) + [:vendor/terms {:vendor/terms-overrides + [:vendor-terms-override/client :vendor-terms-override/terms]}] + (:invoice/vendor (:step-params multi-form-state))) + good-date + (or (when (and (:invoice/date (:step-params multi-form-state)) + (-> vendor :vendor/terms)) ;; TODO get specific terms + (time/plus (:invoice/date (:step-params multi-form-state)) + (time/days (-> vendor :vendor/terms)))) + (:invoice/due (:step-params multi-form-state)))] + (alog/peek ::good-date multi-form-state) + + (html-response + (com/date-input {:value (some-> good-date + (atime/unparse-local atime/normal-date)) + :name "step-params[invoice/due]" + :error? false + :placeholder "1/1/2024"})))) + (def key->handler (apply-middleware-to-all-handlers {::route/new-wizard (-> mm/open-wizard-handler (mm/wrap-wizard new-wizard) (mm/wrap-init-multi-form-state initial-new-wizard-state)) + ::route/due-date (-> due-date + #_(wrap-schema-decode :form-schema [:map + [:step-params {:optional true} + [:maybe + [:map + [:invoice/due {:optional true} [:maybe clj-date-schema]]]]] + [:date {:optional true} [:maybe clj-date-schema]] + [:due-date {:optional true} [:maybe clj-date-schema]] + [:client-id {:optional true} [:maybe entity-id]] + [:vendor-id {:optional true} [:maybe entity-id]]]) + (mm/wrap-wizard new-wizard) + (mm/wrap-decode-multi-form-state) + (wrap-nested-form-params)) ::route/location-select (-> location-select (wrap-schema-enforce :query-schema [:map [:name :string] diff --git a/src/cljc/auto_ap/routes/invoice.cljc b/src/cljc/auto_ap/routes/invoice.cljc index b410a0a9..55277d01 100644 --- a/src/cljc/auto_ap/routes/invoice.cljc +++ b/src/cljc/auto_ap/routes/invoice.cljc @@ -5,6 +5,7 @@ "/voided" ::voided-page} "/new" {:get ::new-wizard :post ::new-invoice-submit + "/due-date" ::due-date "/navigate" ::new-wizard-navigate "/account/new" ::new-wizard-new-account "/account/location-select" ::location-select}