making main form better.

This commit is contained in:
2022-07-16 15:53:27 -07:00
parent 16a1d243e8
commit 2830004092
9 changed files with 284 additions and 227 deletions

View File

@@ -10,7 +10,6 @@
(s/def ::due (s/nilable ::shared/date)) (s/def ::due (s/nilable ::shared/date))
(s/def ::scheduled-payment (s/nilable ::shared/date)) (s/def ::scheduled-payment (s/nilable ::shared/date))
(s/def ::total ::shared/money) (s/def ::total ::shared/money)
(s/def ::vendor-id ::shared/identifier)
(s/def ::invoice (s/keys :req-un [::client (s/def ::invoice (s/keys :req-un [::client
::invoice-number ::invoice-number

View File

@@ -3,7 +3,7 @@
[clojure.string :as str])) [clojure.string :as str]))
(def date-regex #"[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}") (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 numeric-regex #"^[0-9]+$")
(def only-upper-case #"^[A-Z]+$") (def only-upper-case #"^[A-Z]+$")
@@ -14,4 +14,5 @@
(s/def ::money (s/or :string (s/and string? (s/def ::money (s/or :string (s/and string?
#(re-matches money-regex %)) #(re-matches money-regex %))
:float float?)) :float float?
:int int?))

View File

@@ -18,7 +18,7 @@
(defn builder [{:keys [can-submit data-sub change-event submit-event id fullwidth?] :as z}] (defn builder [{:keys [can-submit data-sub change-event submit-event id fullwidth?] :as z}]
(let [data-sub (or data-sub [::forms/form id]) (let [data-sub (or data-sub [::forms/form id])
change-event (or change-event [::forms/change 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])] status @(re-frame/subscribe [::status/single id])]
(r/create-element Provider #js {:value #js {:can-submit @(re-frame/subscribe can-submit) (r/create-element Provider #js {:value #js {:can-submit @(re-frame/subscribe can-submit)
:change-event change-event :change-event change-event
@@ -29,6 +29,7 @@
:data data :data data
:fullwidth? fullwidth?}} :fullwidth? fullwidth?}}
(r/as-element (r/as-element
^{:key form-key}
[:form {:on-submit (fn [e] [:form {:on-submit (fn [e]
(when (.-stopPropagation e) (when (.-stopPropagation e)
(.stopPropagation e) (.stopPropagation e)
@@ -78,13 +79,19 @@
(into [:div.control ] children)]))])) (into [:div.control ] children)]))]))
(defn field [] (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 {} [:> Consumer {}
(fn [consume] (fn [consume]
(r/as-element (r/as-element
[:div.field [:div.field
(when label (if (aget consume "fullwidth?") [:p.help label] (when label
[:label.label 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]]]))])) [:div.control [raw-field {} child]]]))]))
(defn horizontal-control [] (defn horizontal-control []
@@ -111,7 +118,7 @@
(into [:div {:style {:margin-bottom "5em"}}] (into [:div {:style {:margin-bottom "5em"}}]
(r/children (r/current-component)))]) (r/children (r/current-component)))])
(defn submit-button [] (defn submit-button [{:keys [class]}]
(let [[child] (r/children (r/current-component))] (let [[child] (r/children (r/current-component))]
[:> Consumer {} [:> Consumer {}
(fn [consume] (fn [consume]
@@ -121,8 +128,9 @@
(r/as-element (r/as-element
[:button.button.is-medium.is-primary {:disabled (or (status/disabled-for status) [:button.button.is-medium.is-primary {:disabled (or (status/disabled-for status)
(not can-submit)) (not can-submit))
:class (cond-> (status/class-for status) :class (cond-> (or class [])
fullwidth? (conj "is-fullwidth")) } (status/class-for status) (conj (status/class-for status))
fullwidth? (conj "is-fullwidth")) }
child])))])) child])))]))
(defn hidden-submit-button [] (defn hidden-submit-button []

View File

@@ -103,7 +103,6 @@
updated-accounts (if-let [location (get-in updated-accounts [(first field) :account :location])] updated-accounts (if-let [location (get-in updated-accounts [(first field) :account :location])]
(assoc-in updated-accounts [(first field) :location] location) (assoc-in updated-accounts [(first field) :location] location)
updated-accounts)] updated-accounts)]
(println updated-accounts)
{:dispatch (into event [updated-accounts])}))) {:dispatch (into event [updated-accounts])})))
@@ -163,7 +162,6 @@
[:div.column.is-narrow [:div.column.is-narrow
[:p.help "Location"] [:p.help "Location"]
[:div.control [:div.control
(println account)
(if-let [forced-location (:location account)] (if-let [forced-location (:location account)]
[:div.select [:div.select
[:select {:disabled "disabled" :style {:width "5em"} :value forced-location} [:option {:value forced-location} forced-location]]] [:select {:disabled "disabled" :style {:width "5em"} :value forced-location} [:option {:value forced-location} forced-location]]]

View File

@@ -1,23 +1,71 @@
(ns auto-ap.views.components.money-field (ns auto-ap.views.components.money-field
(:require [reagent.core :as r] (: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]}] (defn -money-field [{:keys [min max disabled on-change value class style]}]
(let [parsed-amount (r/atom {:parsed value (let [[ parsed-amount set-parsed-amount] (react/useState {:parsed value
:raw (str value)})] :raw (cond
(fn [{:keys [min max disabled on-change value]}] (str/blank? value)
[:input.input {:type "number" ""
:disabled disabled
:on-change (fn [e] (js/Number.isNaN (js/parseFloat value))
(let [raw (.. e -target -value) ""
new-value (when (and raw (not (str/blank? raw)))
(js/parseFloat raw))] :else
(swap! parsed-amount assoc (->short$ (js/parseFloat value)))})]
:raw raw (react/useEffect (fn []
:parsed new-value) ;; allow the controlling field to change the raw representation
(when (not= value new-value) ;; when the raw amount is a valid representation, so that 33.
(on-change new-value)))) ;; doesn't get unset
:value (:raw @parsed-amount) (when (or
:min min (and (:raw parsed-amount)
:max max (re-find good-$ (:raw parsed-amount)))
:step "0.01"}]))) (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))])

View File

@@ -13,7 +13,7 @@
::search-completed ::search-completed
(fn [_ [_ set-items set-loading-status result]] (fn [_ [_ set-items set-loading-status result]]
(set-loading-status nil) (set-loading-status nil)
(set-items (:search-results result)) (set-items (into-array (:search-results result)))
{})) {}))
(re-frame/reg-event-fx (re-frame/reg-event-fx
::search-failed ::search-failed
@@ -54,31 +54,40 @@
(re-frame/reg-event-fx (re-frame/reg-event-fx
::input-value-changed ::input-value-changed
(fn [_ [_ input-value search-query set-items set-loading-status]] (fn [_ [_ input-value search-query set-items set-loading-status]]
(set-items []) (set-items #js [])
(when (> (count input-value) 2) (when (> (count input-value) 2)
(set-loading-status :loading) (set-loading-status :loading)
{:dispatch-debounce {:event [::input-value-settled input-value search-query set-items set-loading-status] {:dispatch-debounce {:event [::input-value-settled input-value search-query set-items set-loading-status]
:time 250 :time 250
:key ::input-value-settled}}))) :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}] (defn typeahead-v3-internal [{:keys [class entity->text entities on-input-change style ^js on-change disabled value name auto-focus] :or {disabled false}
(let [[items set-items] (react/useState (or (clj->js entities) prop-value :value
:as i}]
(let [[items set-items] (react/useState (or entities
[])) []))
[focused set-focus] (react/useState (boolean auto-focus)) [focused set-focus] (react/useState (boolean auto-focus))
[loading-status set-loading-status] (react/useState false) [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] [getMenuProps getComboboxProps getInputProps getItemProps isOpen highlightedIndex selectItem selectedItem setInputValue]
(as-> (useCombobox (clj->js {:items items (as-> (useCombobox #js {:items (into-array items)
:defaultHighlightedIndex 0 :defaultHighlightedIndex 0
:defaultSelectedItem value :defaultSelectedItem value
:itemToString (fn [] :itemToString (fn []
;; once an item is selected, you just use empty text ;; once an item is selected, you just use empty text
"") "")
:onInputValueChange (fn [input] :onInputValueChange (fn [input]
(on-input-change input set-items set-loading-status)) (on-input-change input set-items set-loading-status))
:stateReducer state-reducer :stateReducer state-reducer
:onSelectedItemChange (fn [z] :selectedItem value
(when on-change :onSelectedItemChange (fn [z]
(on-change (js->clj (aget z "selectedItem") :keywordize-keys true))))})) $ (set-value (aget z "selectedItem"))
(when on-change
(on-change (aget z "selectedItem"))))}) $
(map #(aget $ %) ["getMenuProps" "getComboboxProps" "getInputProps" "getItemProps" "isOpen" "highlightedIndex" "selectItem" "selectedItem" "setInputValue"]))] (map #(aget $ %) ["getMenuProps" "getComboboxProps" "getInputProps" "getItemProps" "isOpen" "highlightedIndex" "selectItem" "selectedItem" "setInputValue"]))]
[:<> [:<>
[:div.typeahead (assoc (js->clj (getComboboxProps)) [:div.typeahead (assoc (js->clj (getComboboxProps))
@@ -110,7 +119,7 @@
[:div.level-item [:div.level-item
[:div.control [:div.control
[:div.tags.has-addons [:div.tags.has-addons
[:span.tag (:name (js->clj selectedItem :keywordize-keys true))] [:span.tag (:name selectedItem)]
(when name (when name
[:input {:type "hidden" :name name :value (:id (js->clj selectedItem :keywordize-keys true))}]) [:input {:type "hidden" :name name :value (:id (js->clj selectedItem :keywordize-keys true))}])
(when-not disabled (when-not disabled
@@ -166,11 +175,12 @@
(fn [input set-items] (fn [input set-items]
(if entities-by-id (if entities-by-id
(do (do
(->> (.search entity-index (or (aget input "inputValue") "") #js {:fuzzy 0.2} ) (set-items
clj->js (into-array
(take 10) (->> (.search entity-index (or (aget input "inputValue") "") #js {:fuzzy 0.2} )
(set-items))) (take 10)))))
(set-items (map clj->js (take 10 (filter (fn [x] (str/includes? (or (some-> (entity->text x) str/lower-case) "") (set-items (into-array
(or (some-> (aget input "inputValue") str/lower-case) ""))) (take 10 (filter (fn [x] (str/includes? (or (some-> (entity->text x) str/lower-case) "")
entities)))))))]]) (or (some-> (aget input "inputValue") str/lower-case) "")))
entities)))))))]])

View File

@@ -45,7 +45,7 @@
:name name :name name
:print-as print-as :print-as print-as
:terms (or (str->int terms) :terms (or (str->int terms)
0) nil)
:default-account-id (:id default-account) :default-account-id (:id default-account)
:address address :address address
:primary-contact primary-contact :primary-contact primary-contact

View File

@@ -1,34 +1,41 @@
(ns auto-ap.views.pages.invoices.form (ns auto-ap.views.pages.invoices.form
(:require [auto-ap.entities.invoice :as invoice] (:require
[auto-ap.events :as events] [auto-ap.entities.invoice :as invoice]
[auto-ap.forms :as forms] [auto-ap.events :as events]
[auto-ap.status :as status] [auto-ap.forms :as forms]
[auto-ap.subs :as subs] [auto-ap.forms.builder :as form-builder]
[auto-ap.time-utils :refer [next-dom]] [auto-ap.status :as status]
[auto-ap.utils :refer [dollars=]] [auto-ap.subs :as subs]
[auto-ap.views.components.dropdown :refer [drop-down]] [auto-ap.time-utils :refer [next-dom]]
[auto-ap.views.components.expense-accounts-field [auto-ap.utils :refer [dollars=]]
:as [auto-ap.views.components.expense-accounts-field
expense-accounts-field :as expense-accounts-field
:refer :refer [recalculate-amounts]]
[expense-accounts-field recalculate-amounts]] [auto-ap.views.components.layouts :as layouts]
[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.modal :as modal]
[auto-ap.views.components.money-field :refer [money-field]] [auto-ap.views.components.expense-accounts-field :refer [expense-accounts-field]]
[auto-ap.views.components.switch-field :refer [switch-field]] [auto-ap.views.components.dropdown :refer [drop-down]]
[auto-ap.views.components.typeahead :refer [typeahead-v3]] [auto-ap.views.components.money-field :refer [money-field]]
[auto-ap.views.components.typeahead.vendor :refer [search-backed-typeahead]] [auto-ap.views.components.switch-field :refer [switch-field]]
[auto-ap.views.pages.invoices.common :refer [invoice-read]] [auto-ap.views.components.typeahead :refer [typeahead-v3]]
[auto-ap.views.utils [auto-ap.views.components.typeahead.vendor
:refer :refer [search-backed-typeahead]]
[date->str date-picker dispatch-event standard str->date with-user]] [auto-ap.views.pages.invoices.common :refer [invoice-read]]
[cljs-time.core :as c] [auto-ap.views.utils
[clojure.spec.alpha :as s] :refer [date->str
[clojure.string :as str] date-picker-friendly
[re-frame.core :as re-frame] dispatch-event
[reagent.core :as r] standard
[vimsical.re-frame.fx.track :as track] str->date
[vimsical.re-frame.cofx.inject :as inject])) 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 ;; SUBS
(re-frame/reg-sub (re-frame/reg-sub
@@ -134,7 +141,8 @@
:vendor-preferences vendor-preferences :vendor-preferences vendor-preferences
:scheduled-payment (:scheduled-payment edit-invoice) :scheduled-payment (:scheduled-payment edit-invoice)
:invoice-number (:invoice-number 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 :original edit-invoice
:vendor (:vendor edit-invoice) :vendor (:vendor edit-invoice)
:client (:client edit-invoice) :client (:client edit-invoice)
@@ -320,153 +328,125 @@
min-total (if (= (:total (:original data)) (:outstanding-balance (:original data))) min-total (if (= (:total (:original data)) (:outstanding-balance (:original data)))
nil nil
(- (:total (:original data)) (:outstanding-balance (:original data))))] (- (:total (:original data)) (:outstanding-balance (:original data))))]
(with-meta [form-builder/builder {:can-submit [::can-submit]
(form-inline (assoc params :title [:div "New Invoice " :change-event [::changed]
(cond :submit-event [::save-requested [::saving ]]
(#{:unpaid ":unpaid"} (:status data)) :id ::form}
nil
(#{:voided ":voided"} (:status data)) [form-builder/section {:title [:div "New Invoice "
[:div.tag.is-info.is-light "Voided"] (cond
(#{:unpaid ":unpaid"} (:status data))
nil
(and (#{:paid ":paid"} (:status data)) (#{:voided ":voided"} (:status data))
(not (seq (:payments data)))) [:div.tag.is-info.is-light "Voided"]
[:div.tag.is-info.is-light "Automatically paid"]
(#{:paid ":paid"} (:status data)) (and (#{:paid ":paid"} (:status data))
(if-let [check-number (:check-number (:payment (first (:payments data))))] (not (seq (:payments data))))
[:div.tag.is-info.is-light "Paid by check #" check-number ] [:div.tag.is-info.is-light "Automatically paid"]
[:div.tag.is-info.is-light "Paid"])
:else (#{:paid ":paid"} (:status data))
nil)]) (if-let [check-number (:check-number (:payment (first (:payments data))))]
[:<> [:div.tag.is-info.is-light "Paid by check #" check-number ]
(when-not active-client [:div.tag.is-info.is-light "Paid"])
(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]}])
(field [:span "Date" :else
[:span.has-text-danger " *"]] nil)]}
[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}])
(field "Due (optional)" (when-not active-client
[date-picker {:class-name "input" [form-builder/field {:required? true}
:class "input" "Client"
:format-week-number (fn [] "") [typeahead-v3 {:entities @(re-frame/subscribe [::subs/clients])
:previous-month-button-label "" :entity->text :name
:placeholder "mm/dd/yyyy" :type "typeahead-v3"
:disable-keyboard-navigation true :auto-focus (if active-client false true)
:next-month-button-label "" :field [:client]
:next-month-label "" :disabled exists?
:type "date" :spec ::invoice/client}]])
:field [:due] [form-builder/field {:required? true}
:spec ::invoice/due}]) "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)"] [form-builder/field
[:div.level "Due (optional)"
[:div.level-left [date-picker-friendly {:type "date"
[:div.level-item :field [:due]
[:div.control :spec ::invoice/due}]]
(raw-field [form-builder/vertical-control
[date-picker {:class-name "input" "Scheduled payment (optional)"
:class "input" [left-stack
:disabled (boolean (:schedule-when-due data)) [:div.control
:format-week-number (fn [] "") [form-builder/raw-field
:previous-month-button-label "" [date-picker-friendly {:type "date"
:placeholder "mm/dd/yyyy" :field [:scheduled-payment]
:next-month-button-label "" :spec ::invoice/scheduled-payment}]]]
:next-month-label "" [:div.control
:type "date" [form-builder/raw-field
: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"}])]]]]
(field [:span "Invoice #" [switch-field {:id "schedule-when-due"
[:span.has-text-danger " *"]] :field [:schedule-when-due]
[:input.input {:type "text" :label "Same as due date"
:field [:invoice-number] :type "checkbox"}]]]]]
:spec ::invoice/invoice-number}]) [form-builder/field {:required? true}
"Invoice #"
[:input.input {:type "text"
(field [:span "Total" :field [:invoice-number]
[:span.has-text-danger " *"]] :spec ::invoice/invoice-number}]]
[money-field {:type "money" [form-builder/field {:required? true}
:field [:total] "Total"
:disabled (if can-change-amount? "" "disabled") [money-field {:type "money"
:min min-total :field [:total]
:spec ::invoice/total :disabled (if can-change-amount? "" "disabled")
:step "0.01"}]) :style {:max-width "8em"}
:min min-total
(with-meta :spec ::invoice/total
(field nil :step "0.01"}]]]
[expense-accounts-field {:type "expense-accounts" [form-builder/raw-field
:descriptor "expense account" [expense-accounts-field {:type "expense-accounts"
:locations (:locations (:client data)) :descriptor "expense account"
:max (:total data) :locations (:locations (:client data))
:client (or (:client data) active-client) :max (:total data)
:field [:expense-accounts]}]) :client (or (:client data) active-client)
{:key (str (:id (:vendor data) "none") "-" (:id (:client data) "none") )}) :field [:expense-accounts]}]]
[form-builder/error-notification]
(error-notification) [:div {:style {:margin-bottom "1em"}}]
[:div.columns
[:div.columns (when-not exists?
(when-not exists? [:div.column
[:div.column [drop-down {:header [:button.button.is-primary-two.is-medium.is-fullwidth {:aria-haspopup true
[drop-down {:header [:button.button.is-primary-two.is-medium.is-fullwidth {:aria-haspopup true :type "button"
:type "button" :on-click (dispatch-event [::events/toggle-menu ::add-and-print-invoice ])
:on-click (dispatch-event [::events/toggle-menu ::add-and-print-invoice ]) :disabled (or (status/disabled-for status)
:disabled (or (status/disabled-for status) (not can-submit?))
(not can-submit?)) :class (status/class-for status)}
:class (status/class-for status)} "Pay "
"Pay " [:span " "]
[:span " "] [:span.icon [:i.fa.fa-angle-down {:aria-hidden "true"}]]]
[:span.icon [:i.fa.fa-angle-down {:aria-hidden "true"}]]] :class "is-fullwidth"
:class "is-fullwidth" :id ::add-and-print-invoice}
:id ::add-and-print-invoice} [:div
[:div (list
(list (for [{:keys [id name type]} (->> (:bank-accounts (:client data)) (filter :visible) (sort-by :sort-order))]
(for [{:keys [id name type]} (->> (:bank-accounts (:client data)) (filter :visible) (sort-by :sort-order))] (if (= :cash type)
(if (= :cash type) ^{:key id} [:a.dropdown-item {:on-click (dispatch-event [::save-requested [::add-and-print id :cash]])} "With cash"]
^{:key id} [:a.dropdown-item {:on-click (dispatch-event [::save-requested [::add-and-print id :cash]])} "With cash"] (list
(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 "-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]))))]]])
^{:key (str id "-debit")} [:a.dropdown-item {:on-click (dispatch-event [::save-requested [::add-and-print id :debit]])} "Debit from " name]))))]]]) [:div.column
[:div.column [form-builder/submit-button {:class ["is-fullwidth"]}
"Save"]]]])])
(submit-button "Save")]]])
{:key id}))])
(defn form [_] (defn form [_]

View File

@@ -38,6 +38,19 @@
(defn ->% [x] (defn ->% [x]
(nf% 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] (defn active-when= [active-page candidate]
(when (= active-page candidate) " is-active")) (when (= active-page candidate) " is-active"))