ALmost done on UI improvements.

This commit is contained in:
2022-07-23 07:18:52 -07:00
parent 6773af6442
commit 30f3909ee9
14 changed files with 114 additions and 509 deletions

View File

@@ -1,8 +1,8 @@
(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.views.components.level :as level]))
(:require
[auto-ap.entities.address :as address]
[auto-ap.forms.builder :as form-builder]
[auto-ap.views.components.level :as level]))
(defn address2-field [{:keys [value on-change]}]
[form-builder/virtual-builder {:value (or value {})

View File

@@ -1,15 +1,11 @@
(ns auto-ap.views.components.admin.side-bar
(:require [re-frame.core :as re-frame]
[reagent.core :as r]
[clojure.string :as str]
[clojure.spec.alpha :as s]
[cljs-time.core :as c]
[goog.string :as gstring]
[bidi.bidi :as bidi]
[auto-ap.routes :as routes]
[auto-ap.views.utils :refer [active-when dispatch-event bind-field horizontal-field date->str str->date pretty standard]]
[auto-ap.subs :as subs]
[auto-ap.events :as events]))
(:require
[auto-ap.routes :as routes]
[auto-ap.subs :as subs]
[auto-ap.views.utils :refer [active-when]]
[bidi.bidi :as bidi]
[re-frame.core :as re-frame]
[reagent.core :as r]))
(defn admin-side-bar [params ]
(let [ap @(re-frame/subscribe [::subs/active-page])]
@@ -59,14 +55,6 @@
[:ul ]]
[:p.menu-label "History"]
[:ul.menu-list
[:li.menu-item
[:a {:href (bidi/path-for routes/routes :admin-reminders) , :class (str "item" (active-when ap = :admin-reminders))}
[:span {:class "icon"}
[:i {:class "fa fa-star-o"}]]
[:span {:class "name"} "Reminders"]]]]
[:p.menu-label "Import"]
[:ul.menu-list
[:li.menu-item

View File

@@ -10,11 +10,9 @@
[auto-ap.views.components.percentage-field :refer [percentage-field]]
[auto-ap.views.components.typeahead.vendor
:refer [search-backed-typeahead]]
[auto-ap.views.utils :refer [->$ bind-field dispatch-event appearing-group]]
[clojure.string :as str]
[auto-ap.views.utils :refer [->$ appearing-group]]
[goog.string :as gstring]
[malli.core :as m]
[re-frame.core :as re-frame]))
[malli.core :as m]))
(defn can-replace-with-default? [accounts]
(and (or (not (seq accounts))
@@ -56,27 +54,6 @@
;; EVENTS
(re-frame/reg-event-fx
::add-expense-account
(fn [_ [_ event expense-accounts locations]]
{:dispatch (conj event (conj expense-accounts
{:amount 0 :id (str "new-" (random-uuid))
:amount-mode "%"
:amount-percentage 0
:location (if (= 1 (count locations))
(first locations)
nil)}))}))
(re-frame/reg-event-fx
::remove-expense-account
(fn [_ [_ event expense-accounts id]]
{:dispatch (conj event (transduce (filter
(fn [ea]
(not= (:id ea) id)) )
conj
[]
expense-accounts))}))
(defn recalculate-amounts [expense-accounts total]
(mapv
(fn [ea]
@@ -86,146 +63,8 @@
(* (/ (js/parseFloat (:amount-percentage ea)) 100.0) total)))))
expense-accounts))
(re-frame/reg-event-fx
::spread-evenly
(fn [_ [_ event expense-accounts max-value]]
{:dispatch (into event [(recalculate-amounts (mapv
(fn [ea]
(assoc ea :amount-percentage (js/parseFloat
(goog.string/format "%.2f"
(* 100 (/ 1 (count expense-accounts)))))))
expense-accounts)
max-value)])}))
(re-frame/reg-event-fx
::expense-account-changed
(fn [_ [_ event expense-accounts max-value field value]]
(let [updated-accounts (cond-> expense-accounts
true (assoc-in field value)
(= (list :account) (drop 1 field)) (assoc-in [(first field) :location] nil)
(= (list :amount-percentage) (drop 1 field)) (assoc-in [(first field) :amount]
(js/parseFloat
(goog.string/format "%.2f"
(* (/ (cond-> value
(not (float? value)) (js/parseFloat )) 100.0)
(cond-> max-value
(not (float? max-value)) (js/parseFloat)))))))
updated-accounts (if-let [location (get-in updated-accounts [(first field) :account :location])]
(assoc-in updated-accounts [(first field) :location] location)
updated-accounts)]
{:dispatch (into event [updated-accounts])})))
;; VIEWS
(defn expense-accounts-field [{expense-accounts :value client :client max-value :max locations :locations event :event descriptor :descriptor disabled :disabled percentage-only? :percentage-only? :or {percentage-only? false}}]
[:div
[:div.columns
[:div.column
[:h1.subtitle.is-4.is-inline (str/capitalize descriptor) "s"]
(when-not percentage-only?
[:p.help "Remaining" (->$ (- max-value (reduce + 0 (map (comp js/parseFloat :amount) expense-accounts))))])]
[:div.column.is-narrow
(when-not disabled
[:p.buttons
[:a.button {:on-click (dispatch-event [::spread-evenly event expense-accounts max-value])} "Spread evenly"]
[:a.button {:on-click (dispatch-event [::add-expense-account event expense-accounts locations])} "Add"]])]]
(for [[index {:keys [account id location amount amount-percentage amount-mode] :as expense-account}] (map vector (range) expense-accounts)]
^{:key id}
[:div.box
[:div.columns
[:div.column
[:h1.subtitle.is-6 (cond (and account (not percentage-only?))
(str (:name account) " - "
location ": "
(gstring/format "$%.2f" (or amount 0) ))
account
(str (:name account) " - "
location ": %"
amount-percentage)
:else
[:i "New " descriptor])]]
[:div.column.is-narrow
(when-not disabled
[:a.delete {:on-click (dispatch-event [::remove-expense-account event expense-accounts id])}])]]
[:div.field
[:div.columns
[:div.column
[:p.help "Account"]
[:div.control.is-fullwidth
[bind-field
^{:key (:id client)}
[search-backed-typeahead {:search-query (fn [i]
[:search_account
{:query i
:client-id (:id client)}
[:name :id :location]])
:type "typeahead-v3"
:field [index :account]
:disabled disabled
:event [::expense-account-changed event expense-accounts max-value]
:subscription expense-accounts}]]]]
[:div.column.is-narrow
[:p.help "Location"]
[:div.control
(if-let [forced-location (:location account)]
[:div.select
[:select {:disabled "disabled" :style {:width "5em"} :value forced-location} [:option {:value forced-location} forced-location]]]
[:div.select
[bind-field
[:select {:type "select"
:disabled (boolean (or (:location account)
disabled))
:style {:width "5em"}
:field [index :location]
:allow-nil? true
:spec (set locations)
:event [::expense-account-changed event expense-accounts max-value]
:subscription expense-accounts}
(map (fn [l] ^{:key l} [:option {:value l} l]) locations)]]])]]]]
[:div.field
[:p.help "Amount"]
[:div.control
[:div.field.has-addons.is-extended
[:p.control [:span.select
[bind-field
[:select {:type "select"
:disabled (or disabled percentage-only?)
:field [index :amount-mode]
:allow-nil? false
:event [::expense-account-changed event expense-accounts max-value]
:subscription expense-accounts}
[:option "$"]
[:option "%"]]]]]
[:p.control
(if (= "$" amount-mode)
[bind-field
[:input.input {:type "number"
:field [index :amount]
:style {:text-align "right" :width "7em"}
:event [::expense-account-changed event expense-accounts max-value]
:disabled disabled
:subscription expense-accounts
:precision 2
:value (get-in expense-account [:amount])
:max max-value
:step "0.01"}]]
[bind-field
[:input.input {:type "number"
:field [index :amount-percentage]
:style {:text-align "right" :width "7em"}
:disabled disabled
:event [::expense-account-changed event expense-accounts max-value]
:precision 2
:subscription expense-accounts
:value (get-in expense-account [:amount-percentage])
:max "100"
:step "0.01"}]])]]]]])])
(def schema (m/schema [:sequential [:map
[:id :string]

View File

@@ -3,44 +3,53 @@
[auto-ap.events :as events]
[auto-ap.routes :as routes]
[auto-ap.subs :as subs]
[auto-ap.views.components.dropdown :refer [drop-down drop-down-contents]]
[auto-ap.views.components.modal :as modal]
[auto-ap.views.components.vendor-dialog :as vendor-dialog]
[auto-ap.views.utils
:refer [active-when appearing bind-field dispatch-event dispatch-event-with-propagation login-url]]
:refer [active-when
appearing
dispatch-event-with-propagation
login-url]]
[bidi.bidi :as bidi]
[clojure.string :as str]
[re-frame.core :as re-frame]
[reagent.core :as r]))
[reagent.core :as r]
[auto-ap.forms.builder :as form-builder]
[vimsical.re-frame.cofx.inject :as inject]
[auto-ap.forms :as forms]))
(defn navbar-drop-down-contents [{:keys [id]} children ]
(let [toggle-fn (fn [] (re-frame/dispatch [::events/toggle-menu id]))]
(r/create-class {:component-did-mount (fn [] (.addEventListener js/document "click" toggle-fn))
:component-will-unmount (fn [] (.removeEventListener js/document "click" toggle-fn))
:reagent-render
(fn [children]
children)})))
(defn navbar-drop-down [{:keys [ header id class]} child]
(r/create-class
{:reagent-render (fn [{:keys [header id]} child]
(let [menu-active? @(re-frame/subscribe [::subs/menu-active? id])]
[:div { :class (str "navbar-item has-dropdown " (when menu-active? "is-active " ) " " class)}
[:a {:class "navbar-link login" :on-click (fn [e]
(.preventDefault e)
(.stopPropagation e)
(re-frame/dispatch [::events/toggle-menu id])
true)} header]
[appearing {:visible? menu-active? :enter-class "appear" :exit-class "disappear" :timeout 200}
[:div {:class "navbar-dropdown"}
[navbar-drop-down-contents {:id id}
[:div child]]]]]))}))
(defn navbar-drop-down [{:keys [ class]} _]
(let [!child (r/atom nil)]
(r/create-class
{:reagent-render (fn [{:keys [header id]} child]
(let [menu-active? @(re-frame/subscribe [::subs/menu-active? id])]
[:div { :class (str "navbar-item has-dropdown " (when menu-active? "is-active " ) " " class)
:ref (fn [n]
(reset! !child n))
:tab-index 0
:onBlur (fn [e]
(js/setTimeout (fn []
(println @!child)
(println (.-activeElement js/document))
(when-not (.contains @!child (.-activeElement js/document))
(re-frame/dispatch [::events/toggle-menu id])))
2))
}
[:a {:class "navbar-link login" :on-click (fn [e]
(.preventDefault e)
(.stopPropagation e)
(re-frame/dispatch [::events/toggle-menu id])
true)} header]
[appearing {:visible? menu-active? :enter-class "appear" :exit-class "disappear" :timeout 200}
[:div {:class "navbar-dropdown"}
[:div child]]]]))})))
(defn login-dropdown []
(let [user (re-frame/subscribe [::subs/user])]
(if @user
[navbar-drop-down {:header [:span [:span.icon [:i.fa.fa-user] ]
[:span (:user/name @user)]] :id ::account}
[:span (:user/name @user)]]
:id ::account}
[:div
[:a {:class "navbar-item"
:href (bidi/path-for routes/routes :reports)} "My company"]
@@ -60,8 +69,8 @@
(re-frame/reg-sub
::matching-clients
:<- [::subs/clients]
:<- [::client-search]
(fn [[clients {client-search :value}]]
:<- [::forms/form ::client-search]
(fn [[clients {{client-search :value} :data}]]
(if (empty? client-search)
clients
(if-let [exact-match (first (filter
@@ -76,21 +85,46 @@
(str/includes? (str/lower-case (:name client)) (str/lower-case client-search))))
clients)))))
(re-frame/reg-event-db
::client-search-changed
[(re-frame/path [::client-search])]
(fn [client-search [_ path value]]
(assoc-in client-search path value)))
(re-frame/reg-event-fx
::client-searched
[(re-frame/inject-cofx ::inject/sub [::matching-clients])]
(fn [{::keys [matching-clients]}]
{:dispatch-n [[::events/swap-client (first matching-clients)]
[::events/toggle-menu ::select-client]]}))
(defn client-dropdown []
(let [client (re-frame/subscribe [::subs/client])
matching-clients @(re-frame/subscribe [::matching-clients])]
[navbar-drop-down {:header (str "Company: " (if @client (:name @client)
"All"))
:id ::select-client}
[:div
[:a {:class "navbar-item"
:on-click (fn []
(re-frame/dispatch [::events/toggle-menu ::select-client])
(re-frame/dispatch [::forms/form-closing ::client-search])
(re-frame/dispatch [::events/swap-client nil]))} "All" ]
[:hr {:class "navbar-divider"}]
[form-builder/builder {:id ::client-search
:submit-event [::client-searched]}
[form-builder/raw-field-v2 {:field :value}
[:input.input.navbar-item {:placeholder "Company name"
:auto-focus true}]]]
(for [{:keys [name id] :as client} (take 8 matching-clients)]
^{:key id }
[:a {:class "navbar-item"
:on-click (fn []
(re-frame/dispatch [::events/toggle-menu ::select-client])
(re-frame/dispatch [::events/swap-client client]))
} name])]]))
(defn navbar [ap]
(let [navbar-menu-shown? (r/atom false)]
(fn [ap]
(let [user (re-frame/subscribe [::subs/user])
client (re-frame/subscribe [::subs/client])
clients (re-frame/subscribe [::subs/clients])
matching-clients @(re-frame/subscribe [::matching-clients])
menu (re-frame/subscribe [::subs/menu])
client-search @(re-frame/subscribe [::client-search])
(let [user (re-frame/subscribe [::subs/user])
clients (re-frame/subscribe [::subs/clients])
is-initial-loading @(re-frame/subscribe [::subs/is-initial-loading?])]
[:nav {:class "navbar has-shadow is-fixed-top is-grey"}
@@ -133,33 +167,8 @@
[:div.navbar-end
(when (> (count @clients) 1)
[navbar-drop-down {:header (str "Company: " (if @client (:name @client)
"All"))
:id ::select-client}
[:div
[:a {:class "navbar-item"
:on-click (fn []
(re-frame/dispatch [::events/swap-client nil]))} "All" ]
[:hr {:class "navbar-divider"}]
[bind-field
[:input.input.navbar-item {:placeholder "Company name"
:auto-focus true
:field [:value]
:on-key-up (fn [k]
(when (= 13 (.-which k))
(do
(re-frame/dispatch [::events/swap-client (first matching-clients)])
(re-frame/dispatch [::events/toggle-menu ::select-client])
(re-frame/dispatch [::client-search-changed [:value] nil])))
)
:event [::client-search-changed]
:subscription client-search}]]
(for [{:keys [name id] :as client} matching-clients]
^{:key id }
[:a {:class "navbar-item"
:on-click (fn []
(re-frame/dispatch [::events/swap-client client]))
} name])]])])]
[client-dropdown]
)])]
(when-not is-initial-loading
[login-dropdown])]
@@ -167,11 +176,7 @@
]))))
(defn footer []
[:footer {:style {:padding "1em"}}
[:div {:class "content has-text-centered"}
[:p
[:strong "Integreat"] ]]])
(defn appearing-side-bar [{:keys [visible?]} ]
[appearing {:visible? visible? :enter-class "slide-in-right" :exit-class "slide-out-right" :timeout 500}
@@ -179,7 +184,7 @@
(into [:div.sub-main {} ]
(r/children (r/current-component)))]])
(defn side-bar-layout [{:keys [side-bar main ap bottom right-side-bar]}]
(defn side-bar-layout [{:keys [side-bar main bottom right-side-bar]}]
(let [ap @(re-frame/subscribe [::subs/active-page])
client @(re-frame/subscribe [::subs/client])]
[:div
@@ -200,7 +205,6 @@
(when right-side-bar
right-side-bar)
]
#_[footer]
[:div
bottom]
[:div#dz-hidden]]))

View File

@@ -12,7 +12,7 @@
;; TODO just embrace the fact that it will need to be remounted, and use index based keys
(defn multi-field-v2-internal [{:keys [template key-fn 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
(vec prop-value)
[])]
[form-builder/virtual-builder {:value prop-value
:schema schema
@@ -21,21 +21,18 @@
[:div {:style {:margin-bottom "0.25em"}}
(into [(if key-fn
appearing-group
[:<>])]
:<>)]
(for [[i override] (map vector (range) prop-value)
:let [extant? (if key-fn
(boolean (key-fn override))
true)
is-disabled? (boolean (and (= false allow-change?)
extant?))]]
^{:key (or (when key-fn (key-fn override))
(::key override)
i)}
extant?))
key (or (when key-fn (key-fn override))
(::key override)
i)]]
^{:key key}
[form-builder/with-scope {:scope [i]}
^{:key (or (when key-fn (key-fn override))
(::key override)
i)}
[:div.level {:style {:margin-bottom "0.25em"}}
[:div.level-left {:style {:padding "0.5em 1em"}
:class (when-not extant?
@@ -44,9 +41,9 @@
(template override)
template)]
[:fieldset.level-left {:disabled is-disabled?
:style {:padding "0.5em 1em"}
:class (when-not extant?
"has-background-info-light")}
:style {:padding "0.5em 1em"}
:class (when-not extant?
"has-background-info-light")}
(for [[idx template] (map vector (range ) template)]
^{:key idx}
[:div.level-item
@@ -67,6 +64,7 @@
[:button.button.is-outline
{:type "button"
:on-click (fn [e]
(println "ADDING" prop-value)
(on-change (conj prop-value {::key (random-uuid)}))
(.stopPropagation e)
(.preventDefault e))}