diff --git a/src/clj/auto_ap/server.clj b/src/clj/auto_ap/server.clj index cc12b7ec..c331cb37 100644 --- a/src/clj/auto_ap/server.clj +++ b/src/clj/auto_ap/server.clj @@ -108,7 +108,7 @@ (add-shutdown-hook! shutdown-mount) (start-server :port 9000 :bind "0.0.0.0" #_#_:handler (cider-nrepl-handler)) - (alter-var-root #'nrepl.middleware.print/*print-fn* (constantly clojure.pprint/pprint)) + #_(alter-var-root #'nrepl.middleware.print/*print-fn* (constantly clojure.pprint/pprint)) (apply mount/start-without without))) (comment diff --git a/src/cljs/auto_ap/forms.cljs b/src/cljs/auto_ap/forms.cljs index 8fdeeff6..36e79bb4 100644 --- a/src/cljs/auto_ap/forms.cljs +++ b/src/cljs/auto_ap/forms.cljs @@ -83,6 +83,10 @@ (fn [db [_ form & paths]] (update-in db [::forms form :visited] (fn [v] (set (into v paths)))))) +(re-frame/reg-event-db + ::attempted-submit + (fn [db [_ form & paths]] + (assoc-in db [::forms form :attempted-submit?] true))) (defn change-handler [form customize-fn] (fn [db [_ & path-pairs]] diff --git a/src/cljs/auto_ap/forms/builder.cljs b/src/cljs/auto_ap/forms/builder.cljs index d4fa039a..8c165a6a 100644 --- a/src/cljs/auto_ap/forms/builder.cljs +++ b/src/cljs/auto_ap/forms/builder.cljs @@ -34,45 +34,49 @@ (get-in field-path) first)) -(defn builder [{:keys [value on-change can-submit data-sub error-messages change-event submit-event id fullwidth? schema] :as z}] +(defn builder [{:keys [value on-change can-submit data-sub error-messages change-event submit-event id fullwidth? schema validation-error-string]}] (when (and change-event on-change) (throw "Either the form is to be managed by ::forms, or it should have value and on-change passed in")) - (let [data-sub (or data-sub [::forms/form id]) - change-event (when-not on-change + (let [data-sub (or data-sub [::forms/form id]) + change-event (when-not on-change (or change-event [::forms/change id])) - {:keys [data error visited] form-key :id} @(re-frame/subscribe data-sub) - data (or value data) - status @(re-frame/subscribe [::status/single id]) - can-submit (if can-submit @(re-frame/subscribe can-submit) + {:keys [data error visited attempted-submit?] form-key :id} @(re-frame/subscribe data-sub) + data (or value data) + status @(re-frame/subscribe [::status/single id]) + can-submit (if can-submit @(re-frame/subscribe can-submit) true) - problems (when schema + problems (when schema (m/explain schema data))] - - - (r/create-element Provider #js {:value #js {:can-submit can-submit - :error-messages (or error-messages + (r/create-element Provider #js {:value #js {:can-submit can-submit + :error-messages (or error-messages nil) - :on-change on-change - :change-event change-event - :blur-event [::forms/visited id] - :visited visited - :submit-event submit-event - :problems problems - :error error - :status status - :id id - :data data - :fullwidth? fullwidth?}} + :on-change on-change + :change-event change-event + :blur-event [::forms/visited id] + :visited visited + :submit-event submit-event + :problems problems + :attempted-submit? attempted-submit? + :error error + :status status + :id id + :data data + :fullwidth? fullwidth?}} (r/as-element ^{:key form-key} [:form {:on-submit (fn [e] (when (.-stopPropagation e) (.stopPropagation e) (.preventDefault e)) - (when can-submit - (re-frame/dispatch-sync (vec (conj submit-event {})))))} + (if (and schema (not (m/validate schema data))) + (do + (re-frame/dispatch-sync [::status/dispose-single id]) + (re-frame/dispatch [::status/error id [{:message (or validation-error-string "Please fix the errors and try again.")}]]) + (re-frame/dispatch [::forms/attempted-submit id])) + (when can-submit + (re-frame/dispatch-sync (vec (conj submit-event {}))))))} (into [:fieldset {:disabled (boolean (= :loading (:state status)))}] - (r/children (r/current-component)))] + (r/children (r/current-component)))] )))) (defn virtual-builder [] @@ -155,9 +159,10 @@ :else nil) - visited? (get (aget consume-form "visited") full-field-path)] + visited? (get (aget consume-form "visited") full-field-path) + attempted-submit? (aget consume-form "attempted-submit?")] (when-let [error-message (and - visited? + (or visited? attempted-submit?) (spec-error-message (aget consume-form "problems") full-field-path (aget consume-form "error-messages")))] [:div [:p.help.has-text-danger error-message]]))))]))]) @@ -184,9 +189,10 @@ :else nil) visited? (get (aget consume-form "visited") full-field-path) + attempted-submit? (aget consume-form "attempted-submit?") value (get-in (aget consume-form "data") full-field-path) on-change (aget consume-form "on-change")] - (-> child-props + (-> child-props (assoc :on-change (if on-change (partial form-change-handler (aget consume-form "data") full-field-path (aget consume-form "on-change")) @@ -196,7 +202,7 @@ (update :class (fn [class] (str class (cond - (not visited?) + (and (not visited?) (not attempted-submit?)) "" (not (valid-field? (aget consume-form "problems") full-field-path)) " is-danger" @@ -317,5 +323,3 @@ (when-let [error (aget consume "error")] ^{:key error} [:div.has-text-danger.animated.fadeInUp {} error])))])) - - diff --git a/src/cljs/auto_ap/status.cljs b/src/cljs/auto_ap/status.cljs index 6e1c78ed..d468b8e9 100644 --- a/src/cljs/auto_ap/status.cljs +++ b/src/cljs/auto_ap/status.cljs @@ -117,6 +117,7 @@ ::error [(re-frame/path [::status]) ] (fn [db [_ single error]] + (println "STATUS" single error) (assoc db single {:state :error :info nil :error error}))) diff --git a/src/cljs/auto_ap/views/components/expense_accounts_field.cljs b/src/cljs/auto_ap/views/components/expense_accounts_field.cljs index 74e359ee..73daa9b5 100644 --- a/src/cljs/auto_ap/views/components/expense_accounts_field.cljs +++ b/src/cljs/auto_ap/views/components/expense_accounts_field.cljs @@ -3,6 +3,7 @@ [auto-ap.forms.builder :as form-builder] [auto-ap.schema :as schema] [auto-ap.utils :refer [dollars-0?]] + [auto-ap.views.components :as com] [auto-ap.views.components.button-radio :as button-radio] [auto-ap.views.components.level :refer [left-stack]] [auto-ap.views.components.money-field :refer [money-field]] @@ -232,18 +233,7 @@ [:location schema/not-empty-string] [:amount schema/money]]])) -(defn select-field [{:keys [options allow-nil? class] :as props}] - [:div.select {:class class} - [:select (-> props - (dissoc :allow-nil? :class :options) - (update :value (fn [v] (if (str/blank? v) - "" - v)))) - [:<> - (when allow-nil? - [:option {:value nil}]) - (for [[k v] options] - ^{:key k} [:option {:value k} v])]]]) + (defn expense-accounts-field-v2 [{value :value on-change :on-change expense-accounts :value client :client max-value :max locations :locations disabled :disabled percentage-only? :percentage-only? :or {percentage-only? false}}] [form-builder/virtual-builder {:value value @@ -306,7 +296,7 @@ [form-builder/field-v2 {:required? true :field [index :location]} "Location" - [select-field {:options (if (:location account) + [com/select-field {:options (if (:location account) [[(:location account) (:location account)]] (map (fn [l] [l l]) locations)) diff --git a/src/cljs/auto_ap/views/components/vendor_dialog.cljs b/src/cljs/auto_ap/views/components/vendor_dialog.cljs index ecd6581c..c79eff99 100644 --- a/src/cljs/auto_ap/views/components/vendor_dialog.cljs +++ b/src/cljs/auto_ap/views/components/vendor_dialog.cljs @@ -72,13 +72,6 @@ [:or [:maybe :string] [:maybe keyword?]]]])) -(re-frame/reg-sub - ::can-submit - :<- [::forms/form ::vendor-form] - (fn [form] - (m/validate schema (:data form)))) - - (re-frame/reg-event-fx ::save-complete [(forms/triggers-stop ::vendor-form)] @@ -89,55 +82,58 @@ ::save [with-user with-is-admin? (forms/triggers-loading ::vendor-form) (forms/in-form ::vendor-form)] (fn [{:keys [user is-admin?] {{:keys [name hidden print-as terms invoice-reminder-schedule primary-contact automatically-paid-when-due schedule-payment-dom secondary-contact address default-account terms-overrides account-overrides id legal-entity-tin legal-entity-tin-type legal-entity-first-name legal-entity-last-name legal-entity-middle-name legal-entity-1099-type] :as data} :data} :db} _] - (let [query [:upsert-vendor - {:vendor (cond-> {:id id - :name name - :print-as print-as - :terms (or terms - nil) - :default-account-id (:id default-account) - :address address - :primary-contact primary-contact - :secondary-contact secondary-contact - :invoice-reminder-schedule invoice-reminder-schedule} - is-admin? (assoc :hidden hidden - :terms-overrides (mapv - (fn [{:keys [client terms id]}] - {:id id - :client-id (:id client) - :terms (or (str->int terms) 0)}) - terms-overrides) - :account-overrides (mapv - (fn [{:keys [client account id]}] - {:id id - :client-id (:id client) - :account-id (:id account)}) - account-overrides) - :schedule-payment-dom (mapv - (fn [{:keys [client dom id]}] - {:id id - :client-id (:id client) - :dom (or (str->int dom) - 0)}) - schedule-payment-dom) - :automatically-paid-when-due (mapv - (comp :id :client) - automatically-paid-when-due) - :legal-entity-first-name legal-entity-first-name - :legal-entity-middle-name legal-entity-middle-name - :legal-entity-last-name legal-entity-last-name - :legal-entity-tin legal-entity-tin - :legal-entity-tin-type (some-> legal-entity-tin-type clojure.core/name not-empty keyword) - :legal-entity-1099-type (some-> legal-entity-1099-type clojure.core/name not-empty keyword) - ))} - common/default-read]] - { :graphql - {:token user - :owns-state {:single ::vendor-form} - :query-obj {:venia/operation - {:operation/type :mutation - :operation/name "UpsertVendor"} :venia/queries [{:query/data query}]} - :on-success [::save-complete]}}))) + (if (m/validate schema data) + (let [query [:upsert-vendor + {:vendor (cond-> {:id id + :name name + :print-as print-as + :terms (or terms + nil) + :default-account-id (:id default-account) + :address address + :primary-contact primary-contact + :secondary-contact secondary-contact + :invoice-reminder-schedule invoice-reminder-schedule} + is-admin? (assoc :hidden hidden + :terms-overrides (mapv + (fn [{:keys [client terms id]}] + {:id id + :client-id (:id client) + :terms (or (str->int terms) 0)}) + terms-overrides) + :account-overrides (mapv + (fn [{:keys [client account id]}] + {:id id + :client-id (:id client) + :account-id (:id account)}) + account-overrides) + :schedule-payment-dom (mapv + (fn [{:keys [client dom id]}] + {:id id + :client-id (:id client) + :dom (or (str->int dom) + 0)}) + schedule-payment-dom) + :automatically-paid-when-due (mapv + (comp :id :client) + automatically-paid-when-due) + :legal-entity-first-name legal-entity-first-name + :legal-entity-middle-name legal-entity-middle-name + :legal-entity-last-name legal-entity-last-name + :legal-entity-tin legal-entity-tin + :legal-entity-tin-type (some-> legal-entity-tin-type clojure.core/name not-empty keyword) + :legal-entity-1099-type (some-> legal-entity-1099-type clojure.core/name not-empty keyword)))} + common/default-read]] + { :graphql + {:token user + :owns-state {:single ::vendor-form} + :query-obj {:venia/operation + {:operation/type :mutation + :operation/name "UpsertVendor"} :venia/queries [{:query/data query}]} + :on-success [::save-complete]}}) + + {:dispatch-n [[::forms/attempted-submit ::vendor-form] + [::status/error ::vendor-form [{:message "Please fix the errors and try again."}]]]}))) (defn pull-left [] (into [:div {:style {:position "relative" @@ -178,7 +174,6 @@ (let [is-admin? @(re-frame/subscribe [::subs/is-admin?]) clients @(re-frame/subscribe [::subs/client-refs])] [form-builder/builder {:submit-event [::save] - :can-submit [::can-submit] :id ::vendor-form :schema schema} [form-builder/field-v2 {:field :name @@ -275,19 +270,18 @@ [:div {:style {:width "30em"}} [form-builder/raw-field-v2 {:field :address} [address2-field]]]] - + [form-builder/section {:title "Contact"} [contact-field {:name "Primary" :field [:primary-contact]}] [contact-field {:name "Secondary" :field [:secondary-contact]}]] - (when is-admin? [form-builder/section {:title "Legal Entity"} [form-builder/vertical-control "Name" - [left-stack + [left-stack [:div.control [form-builder/raw-field-v2 {:field :legal-entity-first-name} [:input.input {:type "text" @@ -304,7 +298,7 @@ :placeholder "Last Name"}]]]]] [form-builder/vertical-control "TIN" - [left-stack + [left-stack [form-builder/raw-field-v2 {:field :legal-entity-tin} [:input.input {:type "text" :placeholder "SSN or EIN" @@ -334,27 +328,23 @@ ::vendor-selected [with-user (forms/in-form ::select-vendor-form)] (fn [{{:keys [data]} :db :keys [user]} _] - {:graphql {:token user - :query-obj {:venia/queries [[:vendor-by-id - {:id (:id (:vendor data))} - common/default-read]]} - :owns-state {:single ::select-vendor-form} - :on-success (fn [r] - [::started (:vendor-by-id r)])}})) - -(re-frame/reg-sub - ::can-submit-select-vendor-form - :<- [::forms/field ::select-vendor-form [:vendor]] - (fn [vendor] - (if vendor - true - false))) - + (if (:vendor data) + {:graphql {:token user + :query-obj {:venia/queries [[:vendor-by-id + {:id (:id (:vendor data))} + common/default-read]]} + :owns-state {:single ::select-vendor-form} + :on-success (fn [r] + [::started (:vendor-by-id r)])}} + {:dispatch-n [[::forms/attempted-submit ::select-vendor-form] + [::status/error ::select-vendor-form [{:message "Please select a vendor."}]]]}))) (defn select-vendor-form-content [] [form-builder/builder {:submit-event [::vendor-selected] - :can-submit [::can-submit-select-vendor-form] - :id ::select-vendor-form} + :id ::select-vendor-form + :validation-error-string "Please select a vendor." + :schema [:map + [:vendor schema/reference]]} [form-builder/field-v2 {:field :vendor :required? true} "Vendor to edit" @@ -384,11 +374,11 @@ {:title "Vendor" :body [vendor-dialog] :class "semi-wide" + :status-from [::status/single ::vendor-form] :confirm {:value "Save Vendor" :status-from [::status/single ::vendor-form] :class "is-primary" :on-click (dispatch-event [::save]) - :can-submit [::can-submit] :close-event [::status/completed ::vendor-form]}}]})) (re-frame/reg-event-fx @@ -402,5 +392,4 @@ :status-from [::status/single ::select-vendor-form] :class "is-primary" :on-click (dispatch-event [::vendor-selected]) - :can-submit [::can-submit-select-vendor-form] :close-event [::status/completed ::select-vendor-form]}}]}))