diff --git a/src/cljs/auto_ap/forms.cljs b/src/cljs/auto_ap/forms.cljs index 84d3b2ee..c13fd09a 100644 --- a/src/cljs/auto_ap/forms.cljs +++ b/src/cljs/auto_ap/forms.cljs @@ -2,7 +2,8 @@ (:require [re-frame.core :as re-frame] [re-frame.interceptor :as i] [auto-ap.status :as status] - [auto-ap.views.utils :refer [dispatch-event bind-field]])) + [auto-ap.views.utils :refer [dispatch-event bind-field]] + [malli.core :as m])) (re-frame/reg-sub @@ -85,6 +86,14 @@ (fn [db [_ form & paths]] (update-in db [::forms form :visited] (fn [v] (set (into v paths)))))) + +(re-frame/reg-event-db + ::check-problems + (fn [db [_ form schema]] + (println "Checking problems") + (assoc-in db [::forms form :problems] + (when schema (m/explain schema (get-in db [::forms form :data])))))) + (re-frame/reg-event-db ::attempted-submit (fn [db [_ form & paths]] diff --git a/src/cljs/auto_ap/forms/builder.cljs b/src/cljs/auto_ap/forms/builder.cljs index e76286de..f1ee63a4 100644 --- a/src/cljs/auto_ap/forms/builder.cljs +++ b/src/cljs/auto_ap/forms/builder.cljs @@ -41,27 +41,29 @@ (apply f (for [field fields] (aget consumed field)))))]) +(re-frame/reg-event-fx + ::blurred + (fn [_ [_ schema id field]] + {:dispatch-n [[::forms/check-problems id schema] + [::forms/visited id]]})) + (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 (or change-event [::forms/change id])) - {:keys [data visited attempted-submit?] form-key :id} @(re-frame/subscribe data-sub) + {:keys [data visited attempted-submit? problems] 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) - ;; TODO ONLY validate on blur - problems (when schema - - (m/explain schema data))] + true)] (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] + :blur-event [::blurred schema id] :visited visited :submit-event submit-event :problems problems @@ -92,17 +94,14 @@ (let [key (r/atom (random-uuid))] (fn [{:keys [value on-change can-submit error-messages fullwidth? schema]}] (let [data-sub [::forms/form @key] - {:keys [data error visited]} @(re-frame/subscribe data-sub) - data (or value data) - problems (when schema - (m/explain schema data))] + {:keys [data error problems visited]} @(re-frame/subscribe data-sub) + data (or value data)] (r/create-element Provider #js {:value #js {:can-submit can-submit :error-messages (or error-messages nil) :on-change on-change - :blur-event [::forms/visited @key] + :blur-event [::blurred schema @key] :visited visited - :problems problems :error error :id @key :data data @@ -148,7 +147,10 @@ event-or-value)) data)) -(defn blur-handler [path re-frame-blur-event _] +(defn blur-handler [path re-frame-blur-event original-on-blur e] + (when original-on-blur + (original-on-blur e)) + (re-frame/dispatch (-> re-frame-blur-event (conj path)))) @@ -159,7 +161,8 @@ (consume FormScopeConsumer ["scope"] (fn [scope] - (let [full-field-path (cond + (let [scope (or scope []) + full-field-path (cond (sequential? field) (into scope field) @@ -186,7 +189,7 @@ ["scope"] (fn [scope] (update child 1 (fn [child-props] - (let [ + (let [scope (or scope []) full-field-path (cond (sequential? field) (into scope field) @@ -203,7 +206,7 @@ (if on-change (partial form-change-handler data full-field-path on-change) (partial change-handler full-field-path change-event)) - :on-blur (partial blur-handler full-field-path blur-event) + :on-blur (partial blur-handler full-field-path blur-event (:on-blur child-props)) :value value) (update :class (fn [class] (str class diff --git a/src/cljs/auto_ap/views/components/expense_accounts_dialog.cljs b/src/cljs/auto_ap/views/components/expense_accounts_dialog.cljs index 7a426894..13338f8d 100644 --- a/src/cljs/auto_ap/views/components/expense_accounts_dialog.cljs +++ b/src/cljs/auto_ap/views/components/expense_accounts_dialog.cljs @@ -10,7 +10,8 @@ [clojure.string :as str] [goog.string :as gstring] [re-frame.core :as re-frame] - [auto-ap.forms.builder :as form-builder])) + [auto-ap.forms.builder :as form-builder] + [auto-ap.views.components :as com])) (re-frame/reg-event-fx ::try-save @@ -28,6 +29,7 @@ %)) (reduce + 0)) does-add-up? (< (Math/abs (- expense-accounts-total (js/parseFloat total))) 0.001)] + (if (and does-add-up? (every? :new-amount expense-accounts)) @@ -113,14 +115,12 @@ ^{:key id} [:tr [:td.expandable [:div.control - [form-builder/raw-field + [form-builder/raw-field-v2 {:field [:expense-accounts id :account]} [search-backed-typeahead {:search-query (fn [i] [:search_account {:query i :client-id (:id client)} - [:name :id :location]]) - :type "typeahead-v3" - :field [:expense-accounts id :account]}]]]] + [:name :id :location]])}]]]] (when multi-location? [:td @@ -129,11 +129,10 @@ [:select {:disabled "disabled" :value forced-location} [:option {:value forced-location} forced-location]]] [:div.select - [form-builder/raw-field - [:select {:type "select" - :field [:expense-accounts id :location] - :spec (set locations)} - (map (fn [l] ^{:key l} [:option {:value l} l]) locations)]]])]) + [form-builder/raw-field-v2 {:field [:expense-accounts id :location]} + [com/select-field {:options (map (fn [l] [l l]) + locations) + :allow-nil? true}]]])]) [:td (str "$" (get-in expense-accounts [id :amount]))] @@ -142,9 +141,8 @@ [:div.field.has-addons.is-extended [:p.control [:a.button.is-static "$"]] [:p.control - [form-builder/raw-field + [form-builder/raw-field-v2 {:field [:expense-accounts id :new-amount-temp]} [:input.input {:type "number" - :field [:expense-accounts id :new-amount-temp] :style {:text-align "right"} :on-blur (dispatch-event [::forms/change ::form [:expense-accounts id :new-amount] (get-in expense-accounts [id :new-amount-temp])]) :on-key-down (fn [e ] diff --git a/src/cljs/auto_ap/views/components/multi.cljs b/src/cljs/auto_ap/views/components/multi.cljs index 1d1760eb..fb71622b 100644 --- a/src/cljs/auto_ap/views/components/multi.cljs +++ b/src/cljs/auto_ap/views/components/multi.cljs @@ -13,7 +13,6 @@ (let [prop-value (if (seq prop-value) prop-value [])] - (println "PROP VALUE IS") [form-builder/virtual-builder {:value prop-value :schema schema :on-change on-change} @@ -69,7 +68,7 @@ (defn multi-field-v2 [] - (into + (into [:f> multi-field-v2-internal (reagent/props (reagent/current-component))] (reagent/children (reagent/current-component)))) diff --git a/src/cljs/auto_ap/views/pages/admin/clients/form.cljs b/src/cljs/auto_ap/views/pages/admin/clients/form.cljs index ad97d69f..3a7ef1c3 100644 --- a/src/cljs/auto_ap/views/pages/admin/clients/form.cljs +++ b/src/cljs/auto_ap/views/pages/admin/clients/form.cljs @@ -34,6 +34,14 @@ (def location-schema (m/schema [:map [:location schema/not-empty-string]])) +(def square-location-schema (m/schema [:map + [:square-location schema/reference] + [:client-location schema/not-empty-string]])) + +(def ezcater-schema (m/schema [:map + [:caterer schema/reference] + [:client-location schema/not-empty-string]])) + (def name-match-schema (m/schema [:map [:match schema/not-empty-string]])) (def location-match-schema (m/schema [:map @@ -52,7 +60,9 @@ [:matches {:optional true} [:maybe [:sequential name-match-schema]]] [:location-matches {:optional true} - [:maybe [:sequential location-match-schema]]]]) + [:maybe [:sequential location-match-schema]]] + [:selected-square-locations {:optional true} + [:maybe [:sequential square-location-schema]]]]) (defn upload-replacement-button [{:keys [on-change]} text] (let [button (atom nil)] @@ -486,7 +496,7 @@ [:div.control [:p.help "If this account is location-specific, add the valid locations"] [form-builder/raw-field-v2 {:field :locations} - [com/multi-field-v2 {:template [[form-builder/raw-field {:key :location} + [com/multi-field-v2 {:template [[form-builder/raw-field-v2 {:field :location} [com/select-field {:options (map (fn [l] [(:location l) (:location l)]) (get-in new-client [:locations])) @@ -650,45 +660,49 @@ (defn square-section [] (let [{new-client :data} @(re-frame/subscribe [::forms/form ::form])] [form-builder/section {:title "Square Integration"} - [form-builder/field "Square Authentication Token" + [form-builder/field-v2 {:field :square-auth-token} + "Square Authentication Token" [:input.input {:type "text" - :style {:width "40em"} - :field [:square-auth-token]}]] - [form-builder/field + :style {:width "40em"}}]] + [form-builder/field-v2 {:field :selected-square-locations} "Square Locations" - [multi-field {:type "multi-field" - :field :selected-square-locations - :template [[typeahead-v3 {:entities (:square-locations new-client) - :entity->text :name - :type "typeahead-v3" - :style {:width "15em"} - :field [:square-location]}] - [:input.input {:type "text" - :style {:width "4em"} - :field [:client-location] - :step "0.01"}]] - :disable-remove? true}]]])) + [com/multi-field-v2 {:template [[form-builder/raw-field-v2 {:field :square-location} + [typeahead-v3 {:entities (:square-locations new-client) + :entity->text :name + :style {:width "15em"}}]] + [form-builder/raw-field-v2 {:field :client-location} + [com/select-field {:options (map (fn [l] + [(:location l) (:location l)]) + (get-in new-client [:locations])) + :allow-nil? true + :style {:width "7em"} + }]]] + :disable-remove? true + :key-fn :id + :schema [:sequential square-location-schema]}]]])) (defn ezcater-section [] - [form-builder/section {:title "EZCater integration"} + (let [{new-client :data} @(re-frame/subscribe [::forms/form ::form])] + [form-builder/section {:title "EZCater integration"} - [form-builder/field - "EZCater Locations" - [multi-field {:type "multi-field" - :field :ezcater-locations - :template [[search-backed-typeahead {:search-query (fn [i] - [:search_ezcater_caterer - {:query i} - [:name :id]]) - :entity->text :name - :type "typeahead-v3" - :field [:caterer] - :style {:width "20em"}}] - [:input.input {:type "text" - :style {:width "4em"} - :field [:location] - :step "0.01"}]] - :disable-remove? true}]]]) + [form-builder/field-v2 {:field :ezcater-locations} + "EZCater Locations" + [com/multi-field-v2 {:template [[form-builder/raw-field-v2 {:field :caterer} + [search-backed-typeahead {:search-query (fn [i] + [:search_ezcater_caterer + {:query i} + [:name :id]]) + :entity->text :name + :style {:width "20em"}}]] + [form-builder/raw-field-v2 {:field [:location]} + [com/select-field {:options (map (fn [l] + [(:location l) (:location l)]) + (get-in new-client [:locations])) + :allow-nil? true + :style {:width "7em"}}]]] + :key-fn :id + :schema [:sequential ezcater-schema] + :disable-remove? true}]]])) (defn form-content [] diff --git a/src/cljs/auto_ap/views/pages/invoices/advanced_print_checks.cljs b/src/cljs/auto_ap/views/pages/invoices/advanced_print_checks.cljs index 4856a1ee..1f0d052a 100644 --- a/src/cljs/auto_ap/views/pages/invoices/advanced_print_checks.cljs +++ b/src/cljs/auto_ap/views/pages/invoices/advanced_print_checks.cljs @@ -9,7 +9,10 @@ :refer [does-amount-exceed-outstanding? invoice-read]] [auto-ap.views.components.money-field :refer [money-field]] [auto-ap.views.utils :refer [dispatch-event horizontal-field with-user]] - [re-frame.core :as re-frame])) + [re-frame.core :as re-frame] + [auto-ap.views.components :as com] + [malli.core :as m] + [auto-ap.schema :as schema])) (re-frame/reg-sub ::can-submit @@ -32,22 +35,26 @@ :else true))) +(def advanced-print-schema (m/schema + [:map + [:bank-account-id schema/not-empty-string]])) + (defn form [] (let [real-bank-accounts @(re-frame/subscribe [::subs/real-bank-accounts]) {:keys [data]} @(re-frame/subscribe [::forms/form ::form])] - [form-builder/builder {:submit-event [::save] + [form-builder/builder {:submit-event [::try-save] :can-submit [::can-submit] - :id ::form} + :id ::form + :schema advanced-print-schema} [:div.field [:label.label "Pay using"] [:div.control [:span.select - [form-builder/raw-field - [:select {:type "select" - :field :bank-account-id} - (for [{:keys [id name]} real-bank-accounts] - ^{:key id} [:option {:value id} name])]]]]] + [form-builder/raw-field-v2 {:field :bank-account-id} + [com/select-field {:options (for [{:keys [id name]} real-bank-accounts] + [id name]) + :allow-nil? true}]]]]] [:table.table.is-fullwidth [:thead @@ -64,10 +71,8 @@ [:td invoice-number] [:td - [form-builder/raw-field - [money-field {:type "money" - :field [:invoice-amounts id :amount] - :step "0.01"}]]]]))]]])) + [form-builder/raw-field-v2 {:field [:invoice-amounts :id :amount]} + [money-field]]]]))]]])) (re-frame/reg-event-fx ::show @@ -77,13 +82,12 @@ :confirm {:value "Print checks" :status-from [::status/single ::form] :class "is-primary" - :on-click (dispatch-event [::save]) + :on-click (dispatch-event [::try-save]) :can-submit [::can-submit] :close-event [::status/completed ::form]}}] :db (-> db (forms/start-form ::form - {:bank-account-id (:id (first @(re-frame/subscribe [::subs/real-bank-accounts]))) - :invoices invoices + {:invoices invoices :invoice-amounts (into {} (map (fn [i] [(:id i) {:amount (:outstanding-balance i)}]) @@ -91,13 +95,23 @@ +(re-frame/reg-event-fx + ::try-save + [(forms/in-form ::form)] + (fn [{:keys [db]}] + (if (not (m/validate advanced-print-schema (:data db))) + {:dispatch-n [[::status/error ::form [{:message "Please correct any errors and try again"}]] + [::forms/attempted-submit ::form]]} + {:dispatch [::save]}))) + (re-frame/reg-event-fx ::save [with-user (forms/in-form ::form) ] - (fn [{:keys [db user]} [_ bank-account-id]] + (fn [{:keys [db user]} _] + (let [type (or (->> @(re-frame/subscribe [::subs/client]) :bank-accounts - (filter #(= bank-account-id (:id %))) + (filter #(= (:bank-account-id (:data db)) (:id %))) first :type) :check)