diff --git a/src/cljs/auto_ap/views/components/typeahead.cljs b/src/cljs/auto_ap/views/components/typeahead.cljs index 9015405a..cb181187 100644 --- a/src/cljs/auto_ap/views/components/typeahead.cljs +++ b/src/cljs/auto_ap/views/components/typeahead.cljs @@ -13,7 +13,7 @@ valid-matches)) (defn typeahead [{:keys [matches on-change field text-field value class not-found-description - not-found-value auto-focus]}] + disabled not-found-value auto-focus]}] (let [text (r/atom (or (second (first (filter #(= (first %) value) matches))) "")) highlighted (r/atom nil) selected (r/atom (first (first (filter #(= (first %) value) matches)))) @@ -25,68 +25,75 @@ (on-change nil text-description text-value) (on-change id text-description (or text-value text-description)))))] (r/create-class - {:reagent-render (fn [{:keys [matches on-change field text-field value class not-found-description]}] + {:reagent-render (fn [{:keys [matches on-change disabled field text-field value class not-found-description]}] + (let [text @text valid-matches (get-valid-matches matches not-found-description not-found-value text)] [:div.typeahead - (if @selected - ^{:key "typeahead"} [:div.input {:class class - :tab-index "0" - :on-key-up (fn [e] - (if (= 8 (.-keyCode e)) - (do - (select [nil "" nil]) - true) - false))} - [:div.control - [:div.tags.has-addons - [:span.tag text] - [:a.tag.is-delete {:on-click (fn [] (select [nil "" nil]))}]]]] - ^{:key "typeahead"} [:input.input {:type "text" - :class class - :value text - :auto-focus auto-focus - :on-blur (fn [e] - (cond @selected - nil + (if disabled - (#{"" nil} text) - nil + ^{:key (str "typeahead" text) } [:input.input {:disabled "disabled" :value text} ] - @highlighted - (do (select @highlighted) - true) + (if @selected + ^{:key "typeahead"} [:div.input {:class class + :tab-index "0" + :on-key-up (fn [e] + (if (= 8 (.-keyCode e)) + (do + (select [nil "" nil]) + true) + false))} + [:div.control + [:div.tags.has-addons + [:span.tag text] + [:a.tag.is-delete {:on-click (fn [] (select [nil "" nil]))}]]]] + ^{:key "typeahead"} [:input.input {:type "text" + :class class - :else - (do (select [nil ""]) - true))) - :on-key-down (fn [e] - (condp = (.-keyCode e) - ; up - 38 (do - (when-let [new-match (->> valid-matches - (take-while #(not= % @highlighted)) - (last))] - (reset! highlighted new-match)) - true) - ;; dwon - 40 (do - (when-let [new-match (->> valid-matches - (drop-while #(not= % @highlighted)) - (drop 1) - (first))] - (reset! highlighted new-match)) - true) - 13 (do (.preventDefault e) - (when @highlighted - - (select @highlighted) - false)) - true)) - :on-change (fn [e] - (let [new-matches (get-valid-matches matches not-found-description not-found-value (.. e -target -value))] - (reset! highlighted (first new-matches))) - (select [nil (.. e -target -value)]))}]) + :value text + :auto-focus auto-focus + :on-blur (fn [e] + (cond @selected + nil + + (#{"" nil} text) + nil + + @highlighted + (do (select @highlighted) + true) + + :else + (do (select [nil ""]) + true))) + :on-key-down (fn [e] + (condp = (.-keyCode e) + ; up + 38 (do + (when-let [new-match (->> valid-matches + (take-while #(not= % @highlighted)) + (last))] + (reset! highlighted new-match)) + true) + ;; dwon + 40 (do + (when-let [new-match (->> valid-matches + (drop-while #(not= % @highlighted)) + (drop 1) + (first))] + (reset! highlighted new-match)) + true) + 13 (do (.preventDefault e) + (when @highlighted + + (select @highlighted) + false)) + true)) + :on-change (fn [e] + (let [new-matches (get-valid-matches matches not-found-description not-found-value (.. e -target -value))] + (reset! highlighted (first new-matches))) + (select [nil (.. e -target -value)]))}])) + (cond (and (seq text) (not @selected) diff --git a/src/cljs/auto_ap/views/pages/unpaid_invoices.cljs b/src/cljs/auto_ap/views/pages/unpaid_invoices.cljs index 9fddfc9c..a47a2a7d 100644 --- a/src/cljs/auto_ap/views/pages/unpaid_invoices.cljs +++ b/src/cljs/auto_ap/views/pages/unpaid_invoices.cljs @@ -64,16 +64,6 @@ (fn [db] (-> db ::check-results))) -(re-frame/reg-sub - ::new-invoice - (fn [db] - (-> db ::new-invoice))) - -(re-frame/reg-sub - ::edit-invoice - (fn [db] - (-> db ::edit-invoice))) - (re-frame/reg-sub ::params (fn [db] @@ -261,18 +251,31 @@ (re-frame/reg-event-fx ::new-invoice-clicked (fn [{:keys [db]} _] - {:db (forms/start-form db ::new-invoice {:client-id (:id @(re-frame/subscribe [::subs/client])) - :status :unpaid - :date (date->str (c/now) standard) - :location (first (:locations @(re-frame/subscribe [::subs/client])))})})) + {:db + (-> db + (forms/start-form ::new-invoice {:client-id (:id @(re-frame/subscribe [::subs/client])) + :status :unpaid + :date (date->str (c/now) standard) + :location (first (:locations @(re-frame/subscribe [::subs/client])))}))})) -(re-frame/reg-event-fx +(re-frame/reg-event-db ::edit-invoice - (fn [{:keys [db]} [_ which]] + (fn [db [_ which]] (let [edit-invoice (update which :date #(date->str % standard)) edit-invoice (assoc edit-invoice :original edit-invoice)] - {:dispatch [::events/modal-status ::edit-invoice {:visible? true}] - :db (assoc-in db [::edit-invoice] edit-invoice)}))) + + (-> db + + (forms/start-form ::new-invoice {:id (:id edit-invoice) + :status (:status edit-invoice) + :date (:date edit-invoice) #_(date->str (:date edit-invoice) standard) + :invoice-number (:invoice-number edit-invoice) + :total (:total edit-invoice) + :original edit-invoice + :vendor-id (:id (:vendor edit-invoice)) + :vendor-name (:name (:vendor edit-invoice)) + :client-id (:id (:client edit-invoice)) + :client-name (:name (:client edit-invoice))}))))) @@ -300,9 +303,9 @@ (re-frame/reg-event-fx - ::edit-invoice-save + ::edit-invoice-saving (fn [{:keys [db]} _] - (let [{:keys [date total invoice-number id]} @(re-frame/subscribe [::edit-invoice])] + (let [{{:keys [date total invoice-number id]} :data} @(re-frame/subscribe [::forms/form ::new-invoice])] {:graphql {:token (-> db :user) :query-obj {:venia/operation {:operation/type :mutation @@ -312,7 +315,7 @@ {:invoice {:id id :invoice-number invoice-number :date date :total total}} invoice-read]}]} :on-success [::invoice-edited] - :on-error [::invoice-edit-failed]}}))) + :on-error [::forms/save-error ::new-invoice]}}))) (re-frame/reg-event-fx ::unvoid-invoice @@ -372,10 +375,7 @@ invoices))) (dissoc ::handwrite-checks))}))) -(re-frame/reg-event-fx - ::invoice-create-failed - (fn [{:keys [db]} [_ data]] - {:dispatch [::events/modal-failed ::new-invoice (:message data)]})) + (re-frame/reg-event-fx ::invoice-created @@ -385,27 +385,22 @@ (update-in [::invoice-page :invoices] (fn [is] (into [(assoc add-invoice :class "live-added")] - is))) - (dissoc ::new-invoice))})) + is))))})) (re-frame/reg-event-fx ::invoice-edited (fn [{:keys [db]} [_ {:keys [edit-invoice]}]] - {:dispatch [::events/modal-completed ::edit-invoice] - :db (-> db + {:db (-> db + (forms/stop-form ::new-invoice) (update-in [::invoice-page :invoices] (fn [is] (mapv (fn [i] (if (= (:id i) (:id edit-invoice)) (assoc edit-invoice :class "live-added") - i)) is))) - (dissoc ::edit-invoice))})) + i)) is))))})) + -(re-frame/reg-event-fx - ::invoice-edit-failed - (fn [{:keys [db]} [_ data]] - {:dispatch [::events/modal-failed ::edit-invoice "That invoice already exists."]})) (re-frame/reg-event-fx ::invoice-unvoided @@ -561,91 +556,12 @@ [:client-id] value [:location] first-location]}))) -(defn new-invoice-modal [] - (let [data @(re-frame/subscribe [::new-invoice]) - change-event [::events/change-form [::new-invoice]] - locations (get-in @(re-frame/subscribe [::subs/clients-by-id]) [(:client-id data) :locations]) - should-select-location? (and locations - (> (count locations) 1))] - [action-modal {:id ::new-invoice - :title "New Invoice" - :action-text "Create" - :save-event [::create-invoice] - :can-submit? (s/valid? ::invoice/invoice data)} - - (when-not @(re-frame/subscribe [::subs/client]) - [horizontal-field - [:label.label "Client"] - [bind-field - [typeahead {:matches (map (fn [x] [(:id x) (:name x)]) @(re-frame/subscribe [::subs/clients])) - :type "typeahead" - :field [:client-id] - :event [::change-new-invoice-client [::new-invoice]] - :spec ::invoice/client-id - :subscription data}]]]) - (when should-select-location? - [horizontal-field - [:label.label "Location"] - [:div.select - [bind-field - [:select {:type "select" - :field [:location] - :spec (set locations) - :event change-event - :subscription data} - (map (fn [l] [:option {:value l} l]) locations)]]]]) - - [horizontal-field - [:label.label "Vendor"] - [bind-field - [typeahead {:matches (map (fn [x] [(:id x) (:name x)]) @(re-frame/subscribe [::subs/vendors])) - :type "typeahead" - :auto-focus true - :field [:vendor-id] - :text-field [:vendor-name] - :event change-event - :spec (s/nilable ::invoice/vendor-id) - :subscription data}]]] - [horizontal-field - [:label.label "Date"] - - - [bind-field - [:input.input {:type "date" - :field [:date] - :event change-event - :spec ::invoice/date - :subscription data}]]] - - - - [horizontal-field - [:label.label "Invoice #"] - [bind-field - [:input.input {:type "text" - :field [:invoice-number] - :event change-event - :spec ::invoice/invoice-number - :subscription data}]]] - - [horizontal-field - [:label.label "Total"] - [:div.field.has-addons.is-extended - [:p.control [:a.button.is-static "$"]] - [:p.control - [bind-field - [:input.input {:type "number" - :field [:total] - :event change-event - :subscription data - :spec ::invoice/total - :step "0.01"}]]]]] - ])) - (defn edit-invoice-form [{:keys [can-change-amount?]}] [forms/side-bar-form {:form ::new-invoice } - (let [{:keys [data status active? error]} @(re-frame/subscribe [::forms/form ::new-invoice]) - can-change-amount? (= (:status data) :unpaid) + (let [{:keys [data active? error]} @(re-frame/subscribe [::forms/form ::new-invoice]) + _ (println data) + exists? (:id data) + can-change-amount? (#{:unpaid ":unpaid"} (:status data)) change-event [::forms/change ::new-invoice] locations (get-in @(re-frame/subscribe [::subs/clients-by-id]) [(:client-id data) :locations]) min-total (if (= (:total (:original data)) (:outstanding-balance (:original data))) @@ -653,6 +569,7 @@ (- (:total (:original data)) (:outstanding-balance (:original data)))) should-select-location? (and locations (> (count locations) 1))] + ^{:key (or (:id data) "new")} [:form [:h1.title.is-2 "New Invoice"] (when-not @(re-frame/subscribe [::subs/client]) @@ -663,27 +580,30 @@ [typeahead {:matches (map (fn [x] [(:id x) (:name x)]) @(re-frame/subscribe [::subs/clients])) :type "typeahead" :field [:client-id] + :disabled exists? :event [::change-new-invoice-client [::new-invoice]] :spec ::invoice/client-id :subscription data}]]]]) - (when should-select-location? - [horizontal-field - [:label.label "Location"] - [:div.select - [bind-field - [:select {:type "select" - :field [:location] - :spec (set locations) - :event change-event - :subscription data} - (map (fn [l] [:option {:value l} l]) locations)]]]]) + (when (and should-select-location? (not exists?)) + [:div.field + [:p.help "Location"] + [:div.control + [:div.select + [bind-field + [:select {:type "select" + :field [:location] + :spec (set locations) + :event change-event + :subscription data} + (map (fn [l] [:option {:value l} l]) locations)]]]]]) [:div.field [:p.help "Vendor"] [:div.control [bind-field [typeahead {:matches (map (fn [x] [(:id x) (:name x)]) @(re-frame/subscribe [::subs/vendors])) :type "typeahead" + :disabled exists? :auto-focus true :field [:vendor-id] :text-field [:vendor-name] @@ -730,85 +650,23 @@ (when error [:div.notification.is-warning.animated.fadeInUp error]) - (println (s/explain-data ::invoice/invoice data)) + [:submit.button.is-large.is-primary {:disabled (if (and (s/valid? ::invoice/invoice data) (or (not min-total) (>= (:total data) min-total))) "" "disabled") - :on-click (dispatch-event [::create-invoice]) - :class (str @(re-frame/subscribe [::forms/loading-class ::new-invoice]) (when error " animated shake"))} "Save"] - ] - #_[action-modal {:id ::edit-invoice - :title "Update Invoice" - :action-text "Save" - :save-event [::edit-invoice-save] - :can-submit? (and #_(s/valid? ::invoice/invoice data) - (or (not min-total) (>= (:total data) min-total)))} - - - - - - - - ])] + :on-click (fn [e] + #_(when (.-stopPropagation e) + (.stopPropagation e) + (.preventDefault e)) + (if exists? + (re-frame/dispatch-sync [::edit-invoice-saving]) + (re-frame/dispatch-sync [::create-invoice]))) + :class (str @(re-frame/subscribe [::forms/loading-class ::new-invoice]) + (when error " animated shake"))} "Save"]])] ) -(defn edit-invoice-modal [{:keys [can-change-amount?]}] - (let [data @(re-frame/subscribe [::edit-invoice]) - change-event [::events/change-form [::edit-invoice]] - locations (get-in @(re-frame/subscribe [::subs/clients-by-id]) [(:client-id data) :locations]) - min-total (if (= (:total (:original data)) (:outstanding-balance (:original data))) - nil - - (- (:total (:original data)) (:outstanding-balance (:original data)))) - - should-select-location? (and locations - (> (count locations) 1))] - [action-modal {:id ::edit-invoice - :title "Update Invoice" - :action-text "Save" - :save-event [::edit-invoice-save] - :can-submit? (and #_(s/valid? ::invoice/invoice data) - (or (not min-total) (>= (:total data) min-total)))} - - [horizontal-field - [:label.label "Date"] - - - [bind-field - [:input.input {:type "date" - :field [:date] - :event change-event - :spec ::invoice/date - :subscription data}]]] - - - - [horizontal-field - [:label.label "Invoice #"] - [bind-field - [:input.input {:type "text" - :field [:invoice-number] - :event change-event - :spec ::invoice/invoice-number - :subscription data}]]] - - [horizontal-field - [:label.label "Total"] - [:div.field.has-addons.is-extended - [:p.control [:a.button.is-static "$"]] - [:p.control - [bind-field - [:input.input {:type "number" - :field [:total] - :disabled (if can-change-amount? "" "disabled") - :event change-event - :min min-total - :subscription data - :spec ::invoice/total - :step "0.01"}]]]]]])) (re-frame/reg-event-db ::change-selected-vendor @@ -852,59 +710,60 @@ [:div.is-pulled-right - [:button.button.is-success {:on-click (dispatch-event [::new-invoice-clicked])} "New Invoice"] - - - (when current-client - [:div.dropdown.is-right {:class (if print-checks-shown? - "is-active" - "")} - [:div.dropdown-trigger - [:button.button.is-success {:aria-haspopup true - :on-click (dispatch-event [::print-checks-clicked ]) - :disabled (if (and (seq checked-invoices) - (->> checked-invoices - vals - (group-by #(get-in % [:vendor :id])) - (reduce-kv (fn [negative? _ invoices] - (or negative? (< (reduce + 0 (map (fn [x] (-> x :outstanding-balance js/parseFloat)) invoices) ) 0))) - false) - not)) - "" - "disabled") + [:div.buttons + [:button.button.is-success {:on-click (dispatch-event [::new-invoice-clicked])} "New Invoice"] + + + (when current-client + [:div.dropdown.is-right {:class (if print-checks-shown? + "is-active" + "")} + [:div.dropdown-trigger + [:button.button.is-success {:aria-haspopup true + :on-click (dispatch-event [::print-checks-clicked ]) + :disabled (if (and (seq checked-invoices) + (->> checked-invoices + vals + (group-by #(get-in % [:vendor :id])) + (reduce-kv (fn [negative? _ invoices] + (or negative? (< (reduce + 0 (map (fn [x] (-> x :outstanding-balance js/parseFloat)) invoices) ) 0))) + false) + not)) + "" + "disabled") - :class (if print-checks-loading? - "is-loading" - "")} - "Pay " - (when (> (count checked-invoices )) - (str - (count checked-invoices) - " invoices " - "(" (->> checked-invoices - vals - (map (comp js/parseFloat :outstanding-balance)) - (reduce + 0) - (gstring/format "$%.2f" )) - ")")) - - - [:span " "] - [:span.icon.is-small [:i.fa.fa-angle-down {:aria-hidden "true"}]]]] - [:div.dropdown-menu {:role "menu"} - [:div.dropdown-content - (list - (for [{:keys [id number name type]} (->> (:bank-accounts current-client) (filter :visible) (sort-by :sort-order))] - (if (= :cash type) - ^{:key id} [:a.dropdown-item {:on-click (dispatch-event [::print-checks id :cash])} "With cash"] - (list - ^{:key (str id "-check")} [:a.dropdown-item {:on-click (dispatch-event [::print-checks id :check])} "Print checks from " name] - ^{:key (str id "-debit")} [:a.dropdown-item {:on-click (dispatch-event [::print-checks id :debit])} "Debit from " name]))) - ^{:key "advanced-divider"} [:hr.dropdown-divider] + :class (if print-checks-loading? + "is-loading" + "")} + "Pay " + (when (> (count checked-invoices )) + (str + (count checked-invoices) + " invoices " + "(" (->> checked-invoices + vals + (map (comp js/parseFloat :outstanding-balance)) + (reduce + 0) + (gstring/format "$%.2f" )) + ")")) + + + [:span " "] + [:span.icon.is-small [:i.fa.fa-angle-down {:aria-hidden "true"}]]]] + [:div.dropdown-menu {:role "menu"} + [:div.dropdown-content + (list + (for [{:keys [id number name type]} (->> (:bank-accounts current-client) (filter :visible) (sort-by :sort-order))] + (if (= :cash type) + ^{:key id} [:a.dropdown-item {:on-click (dispatch-event [::print-checks id :cash])} "With cash"] + (list + ^{:key (str id "-check")} [:a.dropdown-item {:on-click (dispatch-event [::print-checks id :check])} "Print checks from " name] + ^{:key (str id "-debit")} [:a.dropdown-item {:on-click (dispatch-event [::print-checks id :debit])} "Debit from " name]))) + ^{:key "advanced-divider"} [:hr.dropdown-divider] - (when (= 1 (count checked-invoices)) - ^{:key "handwritten"} [:a.dropdown-item {:on-click (dispatch-event [::handwrite-checks])} "Handwritten Check..."]) - ^{:key "advanced"} [:a.dropdown-item {:on-click (dispatch-event [::advanced-print-checks])} "Advanced..."])]]])] + (when (= 1 (count checked-invoices)) + ^{:key "handwritten"} [:a.dropdown-item {:on-click (dispatch-event [::handwrite-checks])} "Handwritten Check..."]) + ^{:key "advanced"} [:a.dropdown-item {:on-click (dispatch-event [::advanced-print-checks])} "Advanced..."])]]])]] [:div.is-pulled-right (into [:div.tags {:style {:margin-right ".5 rem;"}}] (map (fn [[id invoice]] [:span.tag.is-medium (:invoice-number invoice) [:button.delete.is-small {:on-click (dispatch-event [::toggle-check id invoice])}]]) checked-invoices))]] )) @@ -946,8 +805,6 @@ :expense-event [::expense-accounts-dialog/change-expense-accounts]}] [print-checks-modal] - [new-invoice-modal] - [edit-invoice-modal {:can-change-amount? (= status "unpaid")}] [handwrite-checks-modal] [change-expense-accounts-modal {:updated-event [::expense-accounts-updated]}] (when check-results-shown?