From 283000409275cd59e668dd8828b22ac6bedf9bb6 Mon Sep 17 00:00:00 2001 From: Bryce Covert Date: Sat, 16 Jul 2022 15:53:27 -0700 Subject: [PATCH] making main form better. --- src/cljc/auto_ap/entities/invoice.cljc | 1 - src/cljc/auto_ap/entities/shared.cljc | 5 +- src/cljs/auto_ap/forms/builder.cljs | 22 +- .../components/expense_accounts_field.cljs | 2 - .../auto_ap/views/components/money_field.cljs | 88 +++-- .../views/components/typeahead/vendor.cljs | 58 ++-- .../views/components/vendor_dialog.cljs | 2 +- .../auto_ap/views/pages/invoices/form.cljs | 320 ++++++++---------- src/cljs/auto_ap/views/utils.cljs | 13 + 9 files changed, 284 insertions(+), 227 deletions(-) diff --git a/src/cljc/auto_ap/entities/invoice.cljc b/src/cljc/auto_ap/entities/invoice.cljc index 04d4e148..6f9c57b8 100644 --- a/src/cljc/auto_ap/entities/invoice.cljc +++ b/src/cljc/auto_ap/entities/invoice.cljc @@ -10,7 +10,6 @@ (s/def ::due (s/nilable ::shared/date)) (s/def ::scheduled-payment (s/nilable ::shared/date)) (s/def ::total ::shared/money) -(s/def ::vendor-id ::shared/identifier) (s/def ::invoice (s/keys :req-un [::client ::invoice-number diff --git a/src/cljc/auto_ap/entities/shared.cljc b/src/cljc/auto_ap/entities/shared.cljc index f3377268..70b6fd93 100644 --- a/src/cljc/auto_ap/entities/shared.cljc +++ b/src/cljc/auto_ap/entities/shared.cljc @@ -3,7 +3,7 @@ [clojure.string :as str])) (def date-regex #"[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}") -(def money-regex #"\-?[0-9]+(\.[0-9]{1,2})?$") +(def money-regex #"\-?[0-9]+(\.[0-9]{2})?$") (def numeric-regex #"^[0-9]+$") (def only-upper-case #"^[A-Z]+$") @@ -14,4 +14,5 @@ (s/def ::money (s/or :string (s/and string? #(re-matches money-regex %)) - :float float?)) + :float float? + :int int?)) diff --git a/src/cljs/auto_ap/forms/builder.cljs b/src/cljs/auto_ap/forms/builder.cljs index 86cabaff..39e1c434 100644 --- a/src/cljs/auto_ap/forms/builder.cljs +++ b/src/cljs/auto_ap/forms/builder.cljs @@ -18,7 +18,7 @@ (defn builder [{:keys [can-submit data-sub change-event submit-event id fullwidth?] :as z}] (let [data-sub (or data-sub [::forms/form id]) change-event (or change-event [::forms/change id]) - {:keys [data error]} @(re-frame/subscribe data-sub) + {:keys [data error] form-key :id} @(re-frame/subscribe data-sub) status @(re-frame/subscribe [::status/single id])] (r/create-element Provider #js {:value #js {:can-submit @(re-frame/subscribe can-submit) :change-event change-event @@ -29,6 +29,7 @@ :data data :fullwidth? fullwidth?}} (r/as-element + ^{:key form-key} [:form {:on-submit (fn [e] (when (.-stopPropagation e) (.stopPropagation e) @@ -78,13 +79,19 @@ (into [:div.control ] children)]))])) (defn field [] - (let [[label child] (r/children (r/current-component))] + (let [props (r/props (r/current-component)) + [label child] (r/children (r/current-component))] [:> Consumer {} (fn [consume] (r/as-element [:div.field - (when label (if (aget consume "fullwidth?") [:p.help label] - [:label.label label])) + (when label + (if (aget consume "fullwidth?") + [:p.help label] + [:label.label + (if (:required? props) + [:span label [:span.has-text-danger " *"]] + label)])) [:div.control [raw-field {} child]]]))])) (defn horizontal-control [] @@ -111,7 +118,7 @@ (into [:div {:style {:margin-bottom "5em"}}] (r/children (r/current-component)))]) -(defn submit-button [] +(defn submit-button [{:keys [class]}] (let [[child] (r/children (r/current-component))] [:> Consumer {} (fn [consume] @@ -121,8 +128,9 @@ (r/as-element [:button.button.is-medium.is-primary {:disabled (or (status/disabled-for status) (not can-submit)) - :class (cond-> (status/class-for status) - fullwidth? (conj "is-fullwidth")) } + :class (cond-> (or class []) + (status/class-for status) (conj (status/class-for status)) + fullwidth? (conj "is-fullwidth")) } child])))])) (defn hidden-submit-button [] 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 06e02d4e..855dc645 100644 --- a/src/cljs/auto_ap/views/components/expense_accounts_field.cljs +++ b/src/cljs/auto_ap/views/components/expense_accounts_field.cljs @@ -103,7 +103,6 @@ updated-accounts (if-let [location (get-in updated-accounts [(first field) :account :location])] (assoc-in updated-accounts [(first field) :location] location) updated-accounts)] - (println updated-accounts) {:dispatch (into event [updated-accounts])}))) @@ -163,7 +162,6 @@ [:div.column.is-narrow [:p.help "Location"] [:div.control - (println account) (if-let [forced-location (:location account)] [:div.select [:select {:disabled "disabled" :style {:width "5em"} :value forced-location} [:option {:value forced-location} forced-location]]] diff --git a/src/cljs/auto_ap/views/components/money_field.cljs b/src/cljs/auto_ap/views/components/money_field.cljs index 0cfdd2d7..954371df 100644 --- a/src/cljs/auto_ap/views/components/money_field.cljs +++ b/src/cljs/auto_ap/views/components/money_field.cljs @@ -1,23 +1,71 @@ (ns auto-ap.views.components.money-field (:require [reagent.core :as r] - [clojure.string :as str])) + [auto-ap.views.utils :refer [->short$]] + [clojure.string :as str] + [react :as react])) +(def good-$ #"^\-?[0-9]+(\.[0-9][0-9])?$") -(defn money-field [{:keys [min max disabled on-change value]}] - (let [parsed-amount (r/atom {:parsed value - :raw (str value)})] - (fn [{:keys [min max disabled on-change value]}] - [:input.input {:type "number" - :disabled disabled - :on-change (fn [e] - (let [raw (.. e -target -value) - new-value (when (and raw (not (str/blank? raw))) - (js/parseFloat raw))] - (swap! parsed-amount assoc - :raw raw - :parsed new-value) - (when (not= value new-value) - (on-change new-value)))) - :value (:raw @parsed-amount) - :min min - :max max - :step "0.01"}]))) +(defn -money-field [{:keys [min max disabled on-change value class style]}] + (let [[ parsed-amount set-parsed-amount] (react/useState {:parsed value + :raw (cond + (str/blank? value) + "" + + (js/Number.isNaN (js/parseFloat value)) + "" + + :else + (->short$ (js/parseFloat value)))})] + (react/useEffect (fn [] + ;; allow the controlling field to change the raw representation + ;; when the raw amount is a valid representation, so that 33. + ;; doesn't get unset + (when (or + (and (:raw parsed-amount) + (re-find good-$ (:raw parsed-amount))) + (str/blank? (:raw parsed-amount))) + (set-parsed-amount + (assoc parsed-amount + :parsed value + :raw (cond + (str/blank? value) + "" + + (js/Number.isNaN (js/parseFloat value)) + "" + + :else + (->short$ (js/parseFloat value)))))) + nil)) + [:div.control.has-icons-left + [:input.input {:type "text" + :disabled disabled + :class class + :on-change (fn [e] + (let [raw (.. e -target -value) + new-value (when (and raw + (not (str/blank? raw)) + (re-find good-$ raw)) + (js/parseFloat raw))] + (set-parsed-amount {:raw raw + :parsed new-value}) + (when (not= value new-value) + (on-change new-value)))) + :value (or (:raw parsed-amount) + "") + :on-blur (fn [] + (when-not (re-find good-$ (:raw parsed-amount)) + + (set-parsed-amount {:raw "" + :parsed nil}) + (on-change nil))) + :min min + :max max + :step "0.01" + :style (or style + {:width "8em"})}] + [:span.icon.is-left + [:i.fa.fa-usd]]])) + +(defn money-field [] + [:f> -money-field (r/props (r/current-component))]) diff --git a/src/cljs/auto_ap/views/components/typeahead/vendor.cljs b/src/cljs/auto_ap/views/components/typeahead/vendor.cljs index 9991939e..3768e867 100644 --- a/src/cljs/auto_ap/views/components/typeahead/vendor.cljs +++ b/src/cljs/auto_ap/views/components/typeahead/vendor.cljs @@ -13,7 +13,7 @@ ::search-completed (fn [_ [_ set-items set-loading-status result]] (set-loading-status nil) - (set-items (:search-results result)) + (set-items (into-array (:search-results result))) {})) (re-frame/reg-event-fx ::search-failed @@ -54,31 +54,40 @@ (re-frame/reg-event-fx ::input-value-changed (fn [_ [_ input-value search-query set-items set-loading-status]] - (set-items []) + (set-items #js []) (when (> (count input-value) 2) (set-loading-status :loading) {:dispatch-debounce {:event [::input-value-settled input-value search-query set-items set-loading-status] :time 250 :key ::input-value-settled}}))) -(defn typeahead-v3-internal [{:keys [class entity->text entities on-input-change style ^js on-change disabled value name auto-focus] :or {disabled false} :as i}] - (let [[items set-items] (react/useState (or (clj->js entities) +(defn typeahead-v3-internal [{:keys [class entity->text entities on-input-change style ^js on-change disabled value name auto-focus] :or {disabled false} + prop-value :value + :as i}] + (let [[items set-items] (react/useState (or entities [])) [focused set-focus] (react/useState (boolean auto-focus)) [loading-status set-loading-status] (react/useState false) + [value set-value] (react/useState value) + + ;; resets internal representation of value when props change + _ (react/useEffect (fn [] + (set-value prop-value))) [getMenuProps getComboboxProps getInputProps getItemProps isOpen highlightedIndex selectItem selectedItem setInputValue] - (as-> (useCombobox (clj->js {:items items - :defaultHighlightedIndex 0 - :defaultSelectedItem value - :itemToString (fn [] - ;; once an item is selected, you just use empty text - "") - :onInputValueChange (fn [input] - (on-input-change input set-items set-loading-status)) - :stateReducer state-reducer - :onSelectedItemChange (fn [z] - (when on-change - (on-change (js->clj (aget z "selectedItem") :keywordize-keys true))))})) $ + (as-> (useCombobox #js {:items (into-array items) + :defaultHighlightedIndex 0 + :defaultSelectedItem value + :itemToString (fn [] + ;; once an item is selected, you just use empty text + "") + :onInputValueChange (fn [input] + (on-input-change input set-items set-loading-status)) + :stateReducer state-reducer + :selectedItem value + :onSelectedItemChange (fn [z] + (set-value (aget z "selectedItem")) + (when on-change + (on-change (aget z "selectedItem"))))}) $ (map #(aget $ %) ["getMenuProps" "getComboboxProps" "getInputProps" "getItemProps" "isOpen" "highlightedIndex" "selectItem" "selectedItem" "setInputValue"]))] [:<> [:div.typeahead (assoc (js->clj (getComboboxProps)) @@ -110,7 +119,7 @@ [:div.level-item [:div.control [:div.tags.has-addons - [:span.tag (:name (js->clj selectedItem :keywordize-keys true))] + [:span.tag (:name selectedItem)] (when name [:input {:type "hidden" :name name :value (:id (js->clj selectedItem :keywordize-keys true))}]) (when-not disabled @@ -166,11 +175,12 @@ (fn [input set-items] (if entities-by-id (do - (->> (.search entity-index (or (aget input "inputValue") "") #js {:fuzzy 0.2} ) - clj->js - (take 10) - (set-items))) + (set-items + (into-array + (->> (.search entity-index (or (aget input "inputValue") "") #js {:fuzzy 0.2} ) + (take 10))))) - (set-items (map clj->js (take 10 (filter (fn [x] (str/includes? (or (some-> (entity->text x) str/lower-case) "") - (or (some-> (aget input "inputValue") str/lower-case) ""))) - entities)))))))]]) + (set-items (into-array + (take 10 (filter (fn [x] (str/includes? (or (some-> (entity->text x) str/lower-case) "") + (or (some-> (aget input "inputValue") str/lower-case) ""))) + entities)))))))]]) diff --git a/src/cljs/auto_ap/views/components/vendor_dialog.cljs b/src/cljs/auto_ap/views/components/vendor_dialog.cljs index 6d2ee36e..d4ecd63b 100644 --- a/src/cljs/auto_ap/views/components/vendor_dialog.cljs +++ b/src/cljs/auto_ap/views/components/vendor_dialog.cljs @@ -45,7 +45,7 @@ :name name :print-as print-as :terms (or (str->int terms) - 0) + nil) :default-account-id (:id default-account) :address address :primary-contact primary-contact diff --git a/src/cljs/auto_ap/views/pages/invoices/form.cljs b/src/cljs/auto_ap/views/pages/invoices/form.cljs index def5a8fb..8f72b000 100644 --- a/src/cljs/auto_ap/views/pages/invoices/form.cljs +++ b/src/cljs/auto_ap/views/pages/invoices/form.cljs @@ -1,34 +1,41 @@ (ns auto-ap.views.pages.invoices.form - (:require [auto-ap.entities.invoice :as invoice] - [auto-ap.events :as events] - [auto-ap.forms :as forms] - [auto-ap.status :as status] - [auto-ap.subs :as subs] - [auto-ap.time-utils :refer [next-dom]] - [auto-ap.utils :refer [dollars=]] - [auto-ap.views.components.dropdown :refer [drop-down]] - [auto-ap.views.components.expense-accounts-field - :as - expense-accounts-field - :refer - [expense-accounts-field recalculate-amounts]] - [auto-ap.views.components.layouts :as layouts] - [auto-ap.views.components.modal :as modal] - [auto-ap.views.components.money-field :refer [money-field]] - [auto-ap.views.components.switch-field :refer [switch-field]] - [auto-ap.views.components.typeahead :refer [typeahead-v3]] - [auto-ap.views.components.typeahead.vendor :refer [search-backed-typeahead]] - [auto-ap.views.pages.invoices.common :refer [invoice-read]] - [auto-ap.views.utils - :refer - [date->str date-picker dispatch-event standard str->date with-user]] - [cljs-time.core :as c] - [clojure.spec.alpha :as s] - [clojure.string :as str] - [re-frame.core :as re-frame] - [reagent.core :as r] - [vimsical.re-frame.fx.track :as track] - [vimsical.re-frame.cofx.inject :as inject])) + (:require + [auto-ap.entities.invoice :as invoice] + [auto-ap.events :as events] + [auto-ap.forms :as forms] + [auto-ap.forms.builder :as form-builder] + [auto-ap.status :as status] + [auto-ap.subs :as subs] + [auto-ap.time-utils :refer [next-dom]] + [auto-ap.utils :refer [dollars=]] + [auto-ap.views.components.expense-accounts-field + :as expense-accounts-field + :refer [recalculate-amounts]] + [auto-ap.views.components.layouts :as layouts] + [auto-ap.views.components.level :refer [left-stack]] + [auto-ap.views.components.modal :as modal] + [auto-ap.views.components.expense-accounts-field :refer [expense-accounts-field]] + [auto-ap.views.components.dropdown :refer [drop-down]] + [auto-ap.views.components.money-field :refer [money-field]] + [auto-ap.views.components.switch-field :refer [switch-field]] + [auto-ap.views.components.typeahead :refer [typeahead-v3]] + [auto-ap.views.components.typeahead.vendor + :refer [search-backed-typeahead]] + [auto-ap.views.pages.invoices.common :refer [invoice-read]] + [auto-ap.views.utils + :refer [date->str + date-picker-friendly + dispatch-event + standard + str->date + with-user]] + [cljs-time.core :as c] + [clojure.spec.alpha :as s] + [clojure.string :as str] + [re-frame.core :as re-frame] + [reagent.core :as r] + [vimsical.re-frame.cofx.inject :as inject] + [vimsical.re-frame.fx.track :as track])) ;; SUBS (re-frame/reg-sub @@ -134,7 +141,8 @@ :vendor-preferences vendor-preferences :scheduled-payment (:scheduled-payment edit-invoice) :invoice-number (:invoice-number edit-invoice) - :total (:total edit-invoice) + :total (cond-> (:total edit-invoice) + (not (str/blank? (:total edit-invoice))) (js/parseFloat )) :original edit-invoice :vendor (:vendor edit-invoice) :client (:client edit-invoice) @@ -320,153 +328,125 @@ min-total (if (= (:total (:original data)) (:outstanding-balance (:original data))) nil (- (:total (:original data)) (:outstanding-balance (:original data))))] - (with-meta - (form-inline (assoc params :title [:div "New Invoice " - (cond - (#{:unpaid ":unpaid"} (:status data)) - nil + [form-builder/builder {:can-submit [::can-submit] + :change-event [::changed] + :submit-event [::save-requested [::saving ]] + :id ::form} - (#{:voided ":voided"} (:status data)) - [:div.tag.is-info.is-light "Voided"] + [form-builder/section {:title [:div "New Invoice " + (cond + (#{:unpaid ":unpaid"} (:status data)) + nil - (and (#{:paid ":paid"} (:status data)) - (not (seq (:payments data)))) - [:div.tag.is-info.is-light "Automatically paid"] + (#{:voided ":voided"} (:status data)) + [:div.tag.is-info.is-light "Voided"] - (#{:paid ":paid"} (:status data)) - (if-let [check-number (:check-number (:payment (first (:payments data))))] - [:div.tag.is-info.is-light "Paid by check #" check-number ] - [:div.tag.is-info.is-light "Paid"]) + (and (#{:paid ":paid"} (:status data)) + (not (seq (:payments data)))) + [:div.tag.is-info.is-light "Automatically paid"] - :else - nil)]) - [:<> - (when-not active-client - (field [:span "Client" - [:span.has-text-danger " *"]] - [typeahead-v3 {:entities @(re-frame/subscribe [::subs/clients]) - :entity->text :name - :type "typeahead-v3" - :auto-focus (if active-client false true) - :field [:client] - :disabled exists? - :spec ::invoice/client}])) - (field [:span "Vendor" - [:span.has-text-danger " *"]] - [search-backed-typeahead {:disabled exists? - :search-query (fn [i] - [:search_vendor - {:query i} - [:name :id]]) - :type "typeahead-v3" - :auto-focus (if active-client true false) - :field [:vendor]}]) + (#{:paid ":paid"} (:status data)) + (if-let [check-number (:check-number (:payment (first (:payments data))))] + [:div.tag.is-info.is-light "Paid by check #" check-number ] + [:div.tag.is-info.is-light "Paid"]) - (field [:span "Date" - [:span.has-text-danger " *"]] - [date-picker {:class-name "input" - :class "input" - :format-week-number (fn [] "") - :previous-month-button-label "" - :placeholder "mm/dd/yyyy" - :disable-keyboard-navigation true - :next-month-button-label "" - :next-month-label "" - :type "date" - :field [:date] - :spec ::invoice/date}]) + :else + nil)]} - (field "Due (optional)" - [date-picker {:class-name "input" - :class "input" - :format-week-number (fn [] "") - :previous-month-button-label "" - :placeholder "mm/dd/yyyy" - :disable-keyboard-navigation true - :next-month-button-label "" - :next-month-label "" - :type "date" - :field [:due] - :spec ::invoice/due}]) + (when-not active-client + [form-builder/field {:required? true} + "Client" + [typeahead-v3 {:entities @(re-frame/subscribe [::subs/clients]) + :entity->text :name + :type "typeahead-v3" + :auto-focus (if active-client false true) + :field [:client] + :disabled exists? + :spec ::invoice/client}]]) + [form-builder/field {:required? true} + "Vendor" + [search-backed-typeahead {:disabled exists? + :search-query (fn [i] + [:search_vendor + {:query i} + [:name :id]]) + :type "typeahead-v3" + :auto-focus (if active-client true false) + :field [:vendor]}]] + [form-builder/field {:required? true} + "Date" + [date-picker-friendly {:type "date" + :field [:date] + :spec ::invoice/date}]] - [:p.help "Scheduled payment (optional)"] - [:div.level - [:div.level-left - [:div.level-item - [:div.control - (raw-field - [date-picker {:class-name "input" - :class "input" - :disabled (boolean (:schedule-when-due data)) - :format-week-number (fn [] "") - :previous-month-button-label "" - :placeholder "mm/dd/yyyy" - :next-month-button-label "" - :next-month-label "" - :type "date" - :field [:scheduled-payment] - :spec ::invoice/scheduled-payment}])]] - [:div.level-item [:div.control - (raw-field - [switch-field {:id "schedule-when-due" - :field [:schedule-when-due] - :label "Same as due date" - :type "checkbox"}])]]]] + [form-builder/field + "Due (optional)" + [date-picker-friendly {:type "date" + :field [:due] + :spec ::invoice/due}]] + [form-builder/vertical-control + "Scheduled payment (optional)" + [left-stack + [:div.control + [form-builder/raw-field + [date-picker-friendly {:type "date" + :field [:scheduled-payment] + :spec ::invoice/scheduled-payment}]]] + [:div.control + [form-builder/raw-field - (field [:span "Invoice #" - [:span.has-text-danger " *"]] - [:input.input {:type "text" - :field [:invoice-number] - :spec ::invoice/invoice-number}]) - - - (field [:span "Total" - [:span.has-text-danger " *"]] - [money-field {:type "money" - :field [:total] - :disabled (if can-change-amount? "" "disabled") - :min min-total - :spec ::invoice/total - :step "0.01"}]) - - (with-meta - (field nil - [expense-accounts-field {:type "expense-accounts" - :descriptor "expense account" - :locations (:locations (:client data)) - :max (:total data) - :client (or (:client data) active-client) - :field [:expense-accounts]}]) - {:key (str (:id (:vendor data) "none") "-" (:id (:client data) "none") )}) - - (error-notification) - - [:div.columns - (when-not exists? - [:div.column - [drop-down {:header [:button.button.is-primary-two.is-medium.is-fullwidth {:aria-haspopup true - :type "button" - :on-click (dispatch-event [::events/toggle-menu ::add-and-print-invoice ]) - :disabled (or (status/disabled-for status) - (not can-submit?)) - :class (status/class-for status)} - "Pay " - [:span " "] - [:span.icon [:i.fa.fa-angle-down {:aria-hidden "true"}]]] - :class "is-fullwidth" - :id ::add-and-print-invoice} - [:div - (list - (for [{:keys [id name type]} (->> (:bank-accounts (:client data)) (filter :visible) (sort-by :sort-order))] - (if (= :cash type) - ^{:key id} [:a.dropdown-item {:on-click (dispatch-event [::save-requested [::add-and-print id :cash]])} "With cash"] - (list - ^{:key (str id "-check")} [:a.dropdown-item {:on-click (dispatch-event [::save-requested [::add-and-print id :check]])} "Print checks from " name] - ^{:key (str id "-debit")} [:a.dropdown-item {:on-click (dispatch-event [::save-requested [::add-and-print id :debit]])} "Debit from " name]))))]]]) - [:div.column - - (submit-button "Save")]]]) - {:key id}))]) + [switch-field {:id "schedule-when-due" + :field [:schedule-when-due] + :label "Same as due date" + :type "checkbox"}]]]]] + [form-builder/field {:required? true} + "Invoice #" + [:input.input {:type "text" + :field [:invoice-number] + :spec ::invoice/invoice-number}]] + [form-builder/field {:required? true} + "Total" + [money-field {:type "money" + :field [:total] + :disabled (if can-change-amount? "" "disabled") + :style {:max-width "8em"} + :min min-total + :spec ::invoice/total + :step "0.01"}]]] + [form-builder/raw-field + [expense-accounts-field {:type "expense-accounts" + :descriptor "expense account" + :locations (:locations (:client data)) + :max (:total data) + :client (or (:client data) active-client) + :field [:expense-accounts]}]] + [form-builder/error-notification] + [:div {:style {:margin-bottom "1em"}}] + [:div.columns + (when-not exists? + [:div.column + [drop-down {:header [:button.button.is-primary-two.is-medium.is-fullwidth {:aria-haspopup true + :type "button" + :on-click (dispatch-event [::events/toggle-menu ::add-and-print-invoice ]) + :disabled (or (status/disabled-for status) + (not can-submit?)) + :class (status/class-for status)} + "Pay " + [:span " "] + [:span.icon [:i.fa.fa-angle-down {:aria-hidden "true"}]]] + :class "is-fullwidth" + :id ::add-and-print-invoice} + [:div + (list + (for [{:keys [id name type]} (->> (:bank-accounts (:client data)) (filter :visible) (sort-by :sort-order))] + (if (= :cash type) + ^{:key id} [:a.dropdown-item {:on-click (dispatch-event [::save-requested [::add-and-print id :cash]])} "With cash"] + (list + ^{:key (str id "-check")} [:a.dropdown-item {:on-click (dispatch-event [::save-requested [::add-and-print id :check]])} "Print checks from " name] + ^{:key (str id "-debit")} [:a.dropdown-item {:on-click (dispatch-event [::save-requested [::add-and-print id :debit]])} "Debit from " name]))))]]]) + [:div.column + [form-builder/submit-button {:class ["is-fullwidth"]} + "Save"]]]])]) (defn form [_] diff --git a/src/cljs/auto_ap/views/utils.cljs b/src/cljs/auto_ap/views/utils.cljs index f5254d9d..6921a9a7 100644 --- a/src/cljs/auto_ap/views/utils.cljs +++ b/src/cljs/auto_ap/views/utils.cljs @@ -38,6 +38,19 @@ (defn ->% [x] (nf% x)) +(defn ->short$ [x] + (cond + (nil? x) + nil + + (int? x) + (str x) + + (float? x) + (.toFixed x 2) + + )) + (defn active-when= [active-page candidate] (when (= active-page candidate) " is-active"))