From 0baab4eaf0f78687a36f7ea2baf36d2c95c76629 Mon Sep 17 00:00:00 2001 From: Bryce Covert Date: Tue, 19 Jul 2022 15:10:33 -0700 Subject: [PATCH] improved two major forms. --- src/cljs/auto_ap/events.cljs | 4 - src/cljs/auto_ap/forms/builder.cljs | 14 +- src/cljs/auto_ap/schema.cljs | 2 + src/cljs/auto_ap/subs.cljs | 6 + src/cljs/auto_ap/views/components.cljs | 34 ++ .../auto_ap/views/components/address.cljs | 130 ++---- .../views/components/bank_account_filter.cljs | 1 + src/cljs/auto_ap/views/components/multi.cljs | 68 +++ src/cljs/auto_ap/views/components/number.cljs | 43 ++ .../views/components/vendor_dialog.cljs | 388 +++++++++--------- .../views/pages/admin/clients/form.cljs | 11 +- src/cljs/auto_ap/views/pages/admin/users.cljs | 32 +- .../auto_ap/views/pages/admin/yodlee2.cljs | 33 +- src/cljs/auto_ap/views/utils.cljs | 20 +- 14 files changed, 431 insertions(+), 355 deletions(-) create mode 100644 src/cljs/auto_ap/views/components.cljs create mode 100644 src/cljs/auto_ap/views/components/multi.cljs create mode 100644 src/cljs/auto_ap/views/components/number.cljs diff --git a/src/cljs/auto_ap/events.cljs b/src/cljs/auto_ap/events.cljs index d33fc69e..cc8c19bc 100644 --- a/src/cljs/auto_ap/events.cljs +++ b/src/cljs/auto_ap/events.cljs @@ -142,10 +142,6 @@ (re-frame/reg-event-fx ::set-active-route (fn [{:keys [db]} [_ handler params route-params]] - (println "HANDELR IS" handler (:user db)) - - - (cond (and (not= :login handler) (not (:user db))) {:redirect (bidi/path-for routes/routes :login) diff --git a/src/cljs/auto_ap/forms/builder.cljs b/src/cljs/auto_ap/forms/builder.cljs index 36576100..d4fa039a 100644 --- a/src/cljs/auto_ap/forms/builder.cljs +++ b/src/cljs/auto_ap/forms/builder.cljs @@ -7,7 +7,8 @@ [auto-ap.forms :as forms] [auto-ap.status :as status] [malli.core :as m] - [malli.error :as me])) + [malli.error :as me] + [clojure.string :as str])) (defonce ^js/React.Context form-context (react/createContext "default")) (def ^js/React.Provider Provider (. form-context -Provider)) @@ -161,7 +162,9 @@ [:div [:p.help.has-text-danger error-message]]))))]))]) -(defn raw-field-v2 [{:keys [field]}] +(defn raw-field-v2 [{:keys [field] :as props}] + (when-not field + (throw (ex-info (str "Missing field") (clj->js {:props props})))) (let [[child] (r/children (r/current-component))] [:> Consumer {} (fn [consume-form] @@ -188,7 +191,6 @@ (if on-change (partial form-change-handler (aget consume-form "data") full-field-path (aget consume-form "on-change")) (partial change-handler full-field-path (aget consume-form "change-event"))) - :on-blur (partial blur-handler full-field-path (aget consume-form "blur-event")) :value value) (update :class (fn [class] @@ -198,8 +200,12 @@ "" (not (valid-field? (aget consume-form "problems") full-field-path)) " is-danger" + + value + "is-success" + :else - "is-success"))))))))))]))])) + ""))))))))))]))])) (defn with-scope [{:keys [scope]}] (r/create-element FormScopeProvider #js {:value scope} diff --git a/src/cljs/auto_ap/schema.cljs b/src/cljs/auto_ap/schema.cljs index cbfeb806..279868d9 100644 --- a/src/cljs/auto_ap/schema.cljs +++ b/src/cljs/auto_ap/schema.cljs @@ -11,6 +11,8 @@ (def money (m/schema [float? {:error/message "Invalid money"}])) (def not-empty-string (m/schema [:re {:error/message "Required"} #"\S+"])) +(def keyword (m/schema [:fn (fn [d] + (keyword? d))])) (def expense-account (m/schema [:map [:id :string] diff --git a/src/cljs/auto_ap/subs.cljs b/src/cljs/auto_ap/subs.cljs index a7ebbca4..4adc529e 100644 --- a/src/cljs/auto_ap/subs.cljs +++ b/src/cljs/auto_ap/subs.cljs @@ -22,6 +22,12 @@ (when (:user db) (sort-by :name (vals (:clients db)))))) +(re-frame/reg-sub + ::client-refs + :<- [::clients] + (fn [c] + (map #(select-keys % [:id :name]) c))) + (re-frame/reg-sub ::all-accounts (fn [db] diff --git a/src/cljs/auto_ap/views/components.cljs b/src/cljs/auto_ap/views/components.cljs new file mode 100644 index 00000000..df95d2fb --- /dev/null +++ b/src/cljs/auto_ap/views/components.cljs @@ -0,0 +1,34 @@ +(ns auto-ap.views.components + (:require [reagent.core :as r] + [clojure.string :as str])) + + +(defn checkbox [{:keys [on-change + value + label] + :as props}] + (into [:label.checkbox + [:input (-> props + (assoc + :type "checkbox" + :on-change (fn [] + (on-change (not value))) + :checked value) + (dissoc :value))] + " " label + ] + (r/children (r/current-component)))) + +(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])]]]) diff --git a/src/cljs/auto_ap/views/components/address.cljs b/src/cljs/auto_ap/views/components/address.cljs index f0ebc02a..ffe0980b 100644 --- a/src/cljs/auto_ap/views/components/address.cljs +++ b/src/cljs/auto_ap/views/components/address.cljs @@ -1,108 +1,34 @@ (ns auto-ap.views.components.address (:require [auto-ap.entities.address :as address] [auto-ap.views.utils :refer [dispatch-value-change dispatch-event bind-field horizontal-field]] - [auto-ap.forms.builder :as form-builder])) + [auto-ap.forms.builder :as form-builder] + [auto-ap.views.components.level :as level])) -(defn address-field [{:keys [event field subscription]}] - [:span - [horizontal-field - nil - [:div.control - [:p.help "Address"] - [bind-field - [:input.input.is-expanded {:type "text" - :placeholder "1700 Pennsylvania Ave" - :field (conj field :street1) - :spec ::address/street1 - :event event - :subscription subscription}]]]] - - [horizontal-field - nil - [:div.control - [bind-field - [:input.input.is-expanded {:type "text" - :placeholder "Suite 400" - :field (conj field :street2) - :spec ::address/street2 - :event event - :subscription subscription}]]]] - - [horizontal-field - nil - [:div.control - [:p.help "City"] - [bind-field - [:input.input.is-expanded {:type "text" - :placeholder "Cupertino" - :field (conj field :city) - :spec ::address/city - :event event - :subscription subscription}]]] - [:div.control - [:p.help "State"] - [bind-field - [:input.input {:type "text" - :placeholder "CA" - :field (conj field :state) - :spec ::address/state - :size 2 - :max-length "2" - :event event - :subscription subscription}]]] - [:div.control - [:p.help "Zip"] - [bind-field - [:input.input {:type "text" - :field (conj field :zip) - :spec ::address/zip - :event event - :subscription subscription - :placeholder "95014"}]]]]]) - -(defn address2-field [] - [:span - [horizontal-field - nil - [:div.control +(defn address2-field [{:keys [value on-change]}] + [form-builder/virtual-builder {:value (or value {}) + :on-change on-change} + [:div + [form-builder/field-v2 {:field :street1} [:p.help "Street Address"] - [form-builder/raw-field - [:input.input.is-expanded {:type "text" - :placeholder "1700 Pennsylvania Ave" - :field [:street1] - :spec ::address/street1}]]]] - - [horizontal-field - nil - [:div.control - [form-builder/raw-field - [:input.input.is-expanded {:type "text" - :placeholder "Suite 400" - :field [:street2] - :spec ::address/street2}]]]] - - [horizontal-field - nil - [:div.control - [:p.help "City"] - [form-builder/raw-field - [:input.input.is-expanded {:type "text" + [:input.input.is-expanded {:type "text" + :placeholder "1700 Pennsylvania Ave"}]] + [form-builder/raw-field-v2 {:field :street2} + [:input.input.is-expanded {:type "text" + :placeholder "Suite 400"}]] + [level/left-stack + [form-builder/field-v2 {:field :city} + [:p.help "City"] + [:input.input.is-expanded {:type "text" :placeholder "Cupertino" - :field [:city] - :spec ::address/city}]]] - [:div.control - [:p.help "State"] - [form-builder/raw-field - [:input.input {:type "text" - :placeholder "CA" - :field [:state] - :spec ::address/state - :size 2 - :max-length "2"}]]] - [:div.control - [:p.help "Zip"] - [form-builder/raw-field - [:input.input {:type "text" - :field [:zip] - :spec ::address/zip - :placeholder "95014"}]]]]]) + :field [:city] + :spec ::address/city}]] + [form-builder/field-v2 {:field :state} + [:p.help "State"] + [:input.input {:type "text" + :placeholder "CA" + :size 2 + :max-length "2"}]] + [form-builder/field-v2 {:field :zip} + [:p.help "Zip"] + [:input.input {:type "text" + :placeholder "95014"}]]]]]) diff --git a/src/cljs/auto_ap/views/components/bank_account_filter.cljs b/src/cljs/auto_ap/views/components/bank_account_filter.cljs index 28b5b09f..6c73ff15 100644 --- a/src/cljs/auto_ap/views/components/bank_account_filter.cljs +++ b/src/cljs/auto_ap/views/components/bank_account_filter.cljs @@ -1,5 +1,6 @@ (ns auto-ap.views.components.bank-account-filter (:require + [auto-ap.views.utils :refer [->$]] [auto-ap.subs :as subs] [re-frame.core :as re-frame])) diff --git a/src/cljs/auto_ap/views/components/multi.cljs b/src/cljs/auto_ap/views/components/multi.cljs new file mode 100644 index 00000000..dbf56f89 --- /dev/null +++ b/src/cljs/auto_ap/views/components/multi.cljs @@ -0,0 +1,68 @@ +(ns auto-ap.views.components.multi + (:require + [cemerick.url] + #_{:clj-kondo/ignore [:unused-namespace]} + [reagent.core :as reagent] + [react :as react] + [auto-ap.entities.shared :as shared] + [auto-ap.views.utils :refer [appearing-group]] + [auto-ap.forms.builder :as form-builder])) + + +(defn multi-field-v2-internal [{:keys [template key-fn next-key allow-change? disable-new? disable-remove? schema on-change disabled new-text] prop-value :value :as props} ] + (let [prop-value (if (seq prop-value) + prop-value + [])] + [form-builder/virtual-builder {:value prop-value + :schema schema + :on-change on-change} + [:fieldset {:disabled disabled} + [:div {:style {:margin-bottom "0.25em"}} + (into [appearing-group] + (for [[i override] (map vector (range) prop-value) + :let [is-disabled? (if (= false allow-change?) + (not (boolean (::new? override))) + nil)]] + + ^{:key (or (key-fn override) + (::key override))} + [form-builder/with-scope {:scope [i]} + ^{:key (or (key-fn override) + (::key override))} + [:div.level {:style {:margin-bottom "0.25em"}} + [:div.level-left {:style {:padding "0.5em 1em"} + :class (when-not (key-fn override) + "has-background-info-light")} + (let [template (if (fn? template) + (template override) + template)] + (for [[idx template] (map vector (range ) template)] + ^{:key idx} + [:div.level-item + template])) + (when-not disable-remove? + [:div.level-item + [:a.button.level-item + {:disabled is-disabled? + :on-click (fn [] + (on-change (into [] + (for [[idx item] (map vector (range) prop-value) + :when (not= idx i)] + item))))} + [:span.icon [:span.icon-remove]]]])]]])) + (when-not disable-new? + [:button.button.is-outline + {:type "button" + :on-click (fn [e] + (on-change (conj prop-value {::key next-key})) + (.stopPropagation e) + (.preventDefault e))} + [:span.icon [:i.fa.fa-plus]] + [:span (or new-text "New")]])]]])) + + +(defn multi-field-v2 [] + (into + [:f> multi-field-v2-internal + (reagent/props (reagent/current-component))] + (reagent/children (reagent/current-component)))) diff --git a/src/cljs/auto_ap/views/components/number.cljs b/src/cljs/auto_ap/views/components/number.cljs new file mode 100644 index 00000000..2aeadf6a --- /dev/null +++ b/src/cljs/auto_ap/views/components/number.cljs @@ -0,0 +1,43 @@ +(ns auto-ap.views.components.number + (:require [react :as react] + [reagent.core :as r])) + + +(defn number-internal [props] + + (let [[text set-text ] (react/useState (some-> props :value str)) + [value set-value ] (react/useState (some-> props :value)) + coerce-value (fn [new-value] + (let [new-value (js/parseInt new-value)] + (cond + (nil? new-value) + nil + + (js/Number.isNaN new-value) + nil + + :else + new-value)))] + (react/useEffect (fn [] + (let [prop-value (:value props)] + (when (not (= prop-value value)) + (set-value prop-value) + (if prop-value + (set-text (str prop-value)) + (set-text "")))))) + [:div.field.has-addons + [:div.control + [:input.input (assoc props + :on-change (fn [e] + ((:on-change props) + (coerce-value (.. e -target -value)))) + :value text + :type "number" + :step "1" + :style {:width "5em"} + :size 3)]]])) + +(defn number-input [] + [:f> number-internal + (r/props (r/current-component))]) + diff --git a/src/cljs/auto_ap/views/components/vendor_dialog.cljs b/src/cljs/auto_ap/views/components/vendor_dialog.cljs index d4ecd63b..ecd6581c 100644 --- a/src/cljs/auto_ap/views/components/vendor_dialog.cljs +++ b/src/cljs/auto_ap/views/components/vendor_dialog.cljs @@ -7,26 +7,76 @@ [auto-ap.status :as status] [auto-ap.views.components.level :refer [left-stack]] [auto-ap.subs :as subs] + [auto-ap.views.components :as com] [auto-ap.views.components.address :refer [address2-field]] [auto-ap.views.components.modal :as modal] + [auto-ap.views.components.number :refer [number-input]] [auto-ap.views.components.typeahead :refer [typeahead-v3]] [auto-ap.views.components.typeahead.vendor :refer [search-backed-typeahead]] [auto-ap.views.pages.admin.vendors.common :as common] + [auto-ap.views.components.multi :refer [multi-field-v2]] [auto-ap.views.utils :refer [dispatch-event multi-field str->int with-is-admin? with-user]] [clojure.spec.alpha :as s] [re-frame.core :as re-frame] - [reagent.core :as r])) + [reagent.core :as r] + [malli.core :as m] + [auto-ap.schema :as schema] + [malli.error :as me])) ;; Remaining cleanup todos: ;; test minification +(def terms-override-schema (m/schema [:map + [:client schema/reference] + [:terms :int]])) + +(def automatically-paid-schema (m/schema [:map + [:client schema/reference]])) + +(def schedule-payment-dom-schema (m/schema [:map + [:client schema/reference] + [:dom [:int {:max 30}]]])) + +(def account-override-schema (m/schema [:map + [:client schema/reference] + [:account schema/reference]])) + +(def schema (m/schema [:map [:name schema/not-empty-string] + [:print-as {:optional true} + [:maybe :string]] + [:hidden {:optional true} + [:maybe :boolean]] + [:terms {:optional true} + [:maybe :int]] + [:terms-overrides {:optional true} + [:maybe [:sequential terms-override-schema]]] + [:schedule-payment-dom {:optional true} + [:maybe [:sequential schedule-payment-dom-schema]]] + [:default-account schema/reference] + [:account-overrides {:optional true} + [:sequential account-override-schema]] + [:legal-entity-first-name {:optional true} + [:maybe :string]] + [:legal-entity-middle-name {:optional true} + [:maybe :string]] + [:legal-entity-last-name {:optional true} + [:maybe :string]] + [:legal-entity-tin {:optional true} + [:maybe :string]] + [:legal-entity-tin-type {:optional true} + [:or [:maybe :string] + [:maybe keyword?]]] + [:legal-entity-1099-type {:optional true} + [:or [:maybe :string] + [:maybe keyword?]]]])) + (re-frame/reg-sub ::can-submit :<- [::forms/form ::vendor-form] (fn [form] - (s/valid? ::entity/vendor (:data form)))) + (m/validate schema (:data form)))) (re-frame/reg-event-fx @@ -39,56 +89,55 @@ ::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} _] - (when (s/valid? ::entity/vendor data) - (let [query [:upsert-vendor - {:vendor (cond-> {:id id - :name name - :print-as print-as - :terms (or (str->int terms) + (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]}})))) + :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]}}))) (defn pull-left [] (into [:div {:style {:position "relative" @@ -103,10 +152,8 @@ [form-builder/vertical-control {:is-small? true} "Name" [:div.control.has-icons-left - [form-builder/raw-field - [:input.input.is-expanded {:type "text" - :field :name - :spec ::contact/name}]] + [form-builder/raw-field-v2 {:field :name} + [:input.input.is-expanded {:type "text"}]] [:span.icon.is-small.is-left [:i.fa.fa-user]]]] [form-builder/vertical-control {:is-small? true} @@ -115,137 +162,118 @@ [:div.control.has-icons-left [:span.icon.is-small.is-left [:i.fa.fa-envelope]] - [form-builder/raw-field - [:input.input {:type "email" - :field :email - :spec ::contact/email}]]]] + [form-builder/raw-field-v2 {:field :email} + [:input.input {:type "email"}]]]] [form-builder/vertical-control {:is-small? true} "Phone" [:div.control.has-icons-left - [form-builder/raw-field - [:input.input {:type "phone" - :field :phone - :spec ::contact/phone}]] + [form-builder/raw-field-v2 {:field :phone} + [:input.input {:type "phone"}]] [:span.icon.is-small.is-left [:i.fa.fa-phone]]]]]]]) + (defn form-content [{:keys [data]}] (let [is-admin? @(re-frame/subscribe [::subs/is-admin?]) - clients @(re-frame/subscribe [::subs/clients])] + clients @(re-frame/subscribe [::subs/client-refs])] [form-builder/builder {:submit-event [::save] :can-submit [::can-submit] - :id ::vendor-form} - [form-builder/field - [:span "Name " [:span.has-text-danger "*"]] - [:input.input {:type "text" - :auto-focus true - :field :name - :spec ::entity/name}]] + :id ::vendor-form + :schema schema} + [form-builder/field-v2 {:field :name + :required true} + "Name" + [:input.input {:auto-focus true}]] - [form-builder/field + [form-builder/field-v2 {:field :print-as} "Print Checks As" - [:input.input {:type "text" - :field :print-as - :spec ::entity/print-as}]] + [:input.input]] (when is-admin? - [:div.field - [:label.checkbox - [form-builder/raw-field - [:input {:type "checkbox" - :field [:hidden] - :spec ::entity/hidden}]] - " Hidden"]]) + [form-builder/raw-field-v2 {:field :hidden} + [com/checkbox {:label "Hidden"}]]) [form-builder/section {:title "Terms"} - [form-builder/field + [form-builder/field-v2 {:field :terms} "Terms" - [:input.input {:type "number" - :step "1" - :style {:width "4em"} - :field [:terms] - :size 3 - :spec ::entity/terms}]] + [number-input ]] (when is-admin? - [form-builder/field + [form-builder/field-v2 {:field [:terms-overrides]} "Overrides" - [multi-field {:type "multi-field" - :field [:terms-overrides] - :template [[typeahead-v3 {:entities clients - :entity->text :name - :style {:width "13em"} - :type "typeahead-v3" - :field [:client]}] - [:input.input {:type "number" - :step "1" - :style {:width "4em"} - :field [:terms] - :size 3 - :spec ::entity/terms}]]}]]) - ] + [multi-field-v2 {:template [[form-builder/raw-field-v2 {:field [:client]} + [typeahead-v3 {:entities clients + :entity->text :name + :style {:width "13em"} + :type "typeahead-v3" + }]] + [form-builder/raw-field-v2 {:field :terms} + [number-input]]] + :schema [:sequential terms-override-schema] + :key-fn :id + :next-key (random-uuid) + :new-text "New Terms Override"}]])] + (when is-admin? [form-builder/section {:title "Schedule payment when due"} - [form-builder/field + [form-builder/field-v2 {:field [:automatically-paid-when-due]} "Client" - [multi-field {:type "multi-field" - :field [:automatically-paid-when-due] - :template [[typeahead-v3 {:entities clients - :entity->text :name - :style {:width "13em"} - :type "typeahead-v3" - :field [:client]}]]}]]]) + [multi-field-v2 {:template [[form-builder/raw-field-v2 {:field :client} + [typeahead-v3 {:entities clients + :entity->text :name + :style {:width "13em"}}]]] + :schema [:sequential automatically-paid-schema] + :key-fn :id + :next-key (random-uuid) + :new-text "Schedule another client"}]]]) (when is-admin? [form-builder/section {:title "Schedule payment on day of month"} - [form-builder/field + [form-builder/field-v2 {:field [:schedule-payment-dom]} "Overrides" - [multi-field {:type "multi-field" - :field [:schedule-payment-dom] - :template [[typeahead-v3 {:entities clients - :entity->text :name - :style {:width "13em"} - :type "typeahead-v3" - :field [:client]}] - [:input.input {:type "number" - :step "1" - :style {:width "4em"} - :field [:dom] - :size 3 - :spec ::entity/terms}]]}]]]) + [multi-field-v2 {:template [[form-builder/raw-field-v2 {:field :client} + [typeahead-v3 {:entities clients + :entity->text :name + :style {:width "13em"}}]] + [form-builder/raw-field-v2 {:field :dom} + [number-input]]] + :schema [:sequential schedule-payment-dom-schema] + :key-fn :id + :next-key (random-uuid) + :new-text "Schedule another client"}]]]) [form-builder/section {:title "Expense Accounts"} - [form-builder/field - "Default *" + [form-builder/field-v2 {:field :default-account + :required? true} + "Default" [search-backed-typeahead {:search-query (fn [i] [:search_account {:query i} [:name :id]]) - :type "typeahead-v3" - :style {:width "19em"} - :field [:default-account]}]] + :style {:width "19em"}}]] (when is-admin? - [form-builder/field + [form-builder/field-v2 {:field [:account-overrides]} "Overrides" - [multi-field {:type "multi-field" - :field [:account-overrides] - :template (fn [entity] - [[typeahead-v3 {:entities clients - :entity->text :name - :style {:width "19em"} - :type "typeahead-v3" - :field [:client]}] - [search-backed-typeahead {:search-query (fn [i] - [:search_account - {:query i - :client_id (:id (:client entity))} - [:name :id]]) - :type "typeahead-v3" - :style {:width "15em"} - :field [:account]}]])}]])] + [multi-field-v2 {:template (fn [entity] + [[form-builder/raw-field-v2 {:field :client} + [typeahead-v3 {:entities clients + :entity->text :name + :style {:width "19em"} + }]] + [form-builder/raw-field-v2 {:field :account} + [search-backed-typeahead {:search-query (fn [i] + [:search_account + {:query i + :client_id (:id (:client entity))} + [:name :id]]) + :style {:width "15em"}}]]]) + :schema [:sequential account-override-schema] + :key-fn :id + :next-key (random-uuid) + :new-text "Add override"}]])] - [form-builder/with-scope {:scope [:address ]} - [form-builder/section {:title "Address"} - [:div {:style {:width "30em"}} + [form-builder/section {:title "Address"} + [:div {:style {:width "30em"}} + [form-builder/raw-field-v2 {:field :address} [address2-field]]]] [form-builder/section {:title "Contact"} @@ -261,55 +289,40 @@ "Name" [left-stack [:div.control - [form-builder/raw-field + [form-builder/raw-field-v2 {:field :legal-entity-first-name} [:input.input {:type "text" - :placeholder "First Name" - :field [:legal-entity-first-name] - :spec ::contact/name}]]] + :placeholder "First Name"}]]] [:div.control - [form-builder/raw-field + [form-builder/raw-field-v2 {:field :legal-entity-middle-name} [:input.input {:type "text" - :placeholder "Middle Name" - :field [:legal-entity-middle-name] - :spec ::contact/name}]]] + :placeholder "Middle Name"}]]] [:div.control - [form-builder/raw-field + [form-builder/raw-field-v2 {:field :legal-entity-last-name} [:input.input {:type "text" - :placeholder "Last Name" - :field [:legal-entity-last-name] - :spec ::contact/name}]]]]] + :placeholder "Last Name"}]]]]] [form-builder/vertical-control "TIN" [left-stack - [form-builder/raw-field + [form-builder/raw-field-v2 {:field :legal-entity-tin} [:input.input {:type "text" :placeholder "SSN or EIN" - :field [:legal-entity-tin] :size "12" - :spec ::contact/name}]] + }]] [:div.control - [:div.select - [form-builder/raw-field - [:select {:type "select" - :field [:legal-entity-tin-type]} - [:option {:value nil} ""] - [:option {:value "ein"} "EIN"] - [:option {:value "ssn"} "SSN"]]]]]]] + [form-builder/raw-field-v2 {:field :legal-entity-tin-type} + [com/select-field {:options [["ein" "EIN"] + ["ssn" "SSN"]] + :allow-nil? true}]]]]] - [form-builder/vertical-control + [form-builder/field-v2 {:field :legal-entity-1099-type} "1099 Type" - [:div.control - [:div.select - [form-builder/raw-field - [:select {:type "select" - :field [:legal-entity-1099-type]} - [:option {:value nil} ""] - [:option {:value "none"} "Don't 1099"] - [:option {:value "misc"} "Misc"] - [:option {:value "landlord"} "Landlord"]]]]]]]) + [com/select-field {:options [["none" "Don't 1099"] + ["misc" "Misc"] + ["landlord" "Landlord"]] + :allow-nil? true}]]]) [form-builder/hidden-submit-button]])) (defn vendor-dialog [ ] @@ -342,15 +355,15 @@ [form-builder/builder {:submit-event [::vendor-selected] :can-submit [::can-submit-select-vendor-form] :id ::select-vendor-form} - [form-builder/field + [form-builder/field-v2 {:field :vendor + :required? true} "Vendor to edit" [search-backed-typeahead {:search-query (fn [i] [:search_vendor {:query i} [:name :id]]) - :type "typeahead-v3" - :auto-focus true - :field [:vendor]}]] + :style {:width "20em"} + :auto-focus true}]] [form-builder/hidden-submit-button]]) @@ -360,6 +373,7 @@ (fn [{:keys [db]} [_ vendor]] {:db (-> db (forms/start-form ::vendor-form (-> vendor (update :automatically-paid-when-due #(mapv (fn [apwd] + apwd {:id (:id apwd) :client apwd}) %)) 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 1f1133be..4bc15327 100644 --- a/src/cljs/auto_ap/views/pages/admin/clients/form.cljs +++ b/src/cljs/auto_ap/views/pages/admin/clients/form.cljs @@ -595,12 +595,11 @@ :placeholder "Manager" :field [:description]}]]}]] - [form-builder/with-scope {:scope [:address ]} - [:div.field - [:label.label "Address"] - [:div.control - [:div {:style {:width "30em"}} - [address2-field]]]]]]) + [form-builder/vertical-control + "Address" + [:div {:style {:width "30em"}} + [form-builder/raw-field-v2 {:field :address} + [address2-field]]]]]) (defn matching-section [] [form-builder/section {:title "Matching"} diff --git a/src/cljs/auto_ap/views/pages/admin/users.cljs b/src/cljs/auto_ap/views/pages/admin/users.cljs index c7489f83..4c925c47 100644 --- a/src/cljs/auto_ap/views/pages/admin/users.cljs +++ b/src/cljs/auto_ap/views/pages/admin/users.cljs @@ -1,25 +1,15 @@ (ns auto-ap.views.pages.admin.users - (:require [re-frame.core :as re-frame] - [reagent.core :as reagent] - [clojure.string :as str] - [auto-ap.subs :as subs] - [auto-ap.events :as events] - [auto-ap.entities.clients :as entity] - [auto-ap.views.components.address :refer [address-field]] - [auto-ap.views.components.admin.side-bar :refer [admin-side-bar]] - [auto-ap.views.pages.admin.users.table :as table] - [auto-ap.views.components.layouts :refer [side-bar-layout]] - [auto-ap.views.utils :refer [login-url dispatch-value-change bind-field horizontal-field dispatch-event]] - [auto-ap.views.components.grid :as grid] - [auto-ap.utils :refer [by replace-by]] - [cljs.reader :as edn] - [auto-ap.routes :as routes] - [bidi.bidi :as bidi] - [auto-ap.status :as status] - [auto-ap.views.pages.admin.users.form :as form] - [auto-ap.effects.forward :as forward])) - - + (:require + [auto-ap.effects.forward :as forward] + [auto-ap.status :as status] + [auto-ap.utils :refer [replace-by]] + [auto-ap.views.components.admin.side-bar :refer [admin-side-bar]] + [auto-ap.views.components.grid :as grid] + [auto-ap.views.components.layouts :refer [side-bar-layout]] + [auto-ap.views.pages.admin.users.form :as form] + [auto-ap.views.pages.admin.users.table :as table] + [re-frame.core :as re-frame] + [reagent.core :as reagent])) (re-frame/reg-sub ::params diff --git a/src/cljs/auto_ap/views/pages/admin/yodlee2.cljs b/src/cljs/auto_ap/views/pages/admin/yodlee2.cljs index ab7f19d1..29d27fef 100644 --- a/src/cljs/auto_ap/views/pages/admin/yodlee2.cljs +++ b/src/cljs/auto_ap/views/pages/admin/yodlee2.cljs @@ -1,26 +1,15 @@ (ns auto-ap.views.pages.admin.yodlee2 - (:require [re-frame.core :as re-frame] - [auto-ap.forms :as forms] - [reagent.core :as reagent] - [clojure.string :as str] - [cljs-time.format :as f] - [cljs-time.core :as time] - [auto-ap.subs :as subs] - [auto-ap.events :as events] - [auto-ap.entities.clients :as entity] - [auto-ap.views.components.layouts :refer [side-bar-layout]] - [auto-ap.views.components.admin.side-bar :refer [admin-side-bar]] - [auto-ap.views.components.address :refer [address-field]] - [auto-ap.views.utils :refer [login-url dispatch-event dispatch-value-change bind-field horizontal-field str->date date->str with-user]] - [auto-ap.views.components.modal :as modal] - [auto-ap.status :as status] - [cljs.reader :as edn] - [auto-ap.routes :as routes] - [bidi.bidi :as bidi] - [auto-ap.views.pages.admin.yodlee2.table :as table] - [auto-ap.views.pages.admin.yodlee2.form :as form] - [auto-ap.views.components.grid :as grid] - [auto-ap.effects.forward :as forward])) + (:require + [auto-ap.effects.forward :as forward] + [auto-ap.status :as status] + [auto-ap.subs :as subs] + [auto-ap.views.components.admin.side-bar :refer [admin-side-bar]] + [auto-ap.views.components.grid :as grid] + [auto-ap.views.components.layouts :refer [side-bar-layout]] + [auto-ap.views.pages.admin.yodlee2.table :as table] + [auto-ap.views.utils :refer [dispatch-event]] + [re-frame.core :as re-frame] + [reagent.core :as reagent])) (re-frame/reg-sub ::authentication diff --git a/src/cljs/auto_ap/views/utils.cljs b/src/cljs/auto_ap/views/utils.cljs index fc426f86..0e0d15c9 100644 --- a/src/cljs/auto_ap/views/utils.cljs +++ b/src/cljs/auto_ap/views/utils.cljs @@ -157,13 +157,13 @@ (for [child children] ^{:key (:key (meta child))} [transition - {:timeout 300 + {:timeout 200 :exit true :in true #_ (= current-stack- (:key (meta child)))} (clj->js (fn [state] (r/as-element [:div {:style { - :transition "opacity 300ms ease-in-out" + :transition "opacity 150ms ease-in-out" :opacity (cond (= "entered" state) 1.0 @@ -264,17 +264,19 @@ [:span.icon [:span.icon-remove]]]]) ]])])))) + + (defmethod do-bind "select" [dom {:keys [field allow-nil? subscription event class spec] :as keys} & rest] (let [field (if (keyword? field) [field] field) event (if (keyword? event) [event] event) - keys (assoc keys - :on-change (dispatch-value-change (conj event field)) + keys (assoc keys + :on-change (dispatch-value-change (conj event field)) - :value (or (get-in subscription field) "") - :class (str class - (when (and spec (not (s/valid? spec (get-in subscription field)))) - " is-danger"))) - keys (dissoc keys :field :subscription :event :spec) + :value (or (get-in subscription field) "") + :class (str class + (when (and spec (not (s/valid? spec (get-in subscription field)))) + " is-danger"))) + keys (dissoc keys :field :subscription :event :spec) options (if allow-nil? (with-keys (conj rest [:option {:value nil}])) (with-keys rest))]