frontend product groups

This commit is contained in:
Daniel O'Connell 2021-03-11 23:25:28 +01:00
parent a72255cf30
commit 4ddfa94318
11 changed files with 193 additions and 58 deletions

View File

@ -5,7 +5,9 @@
(defn get-all [user-id]
(->> (sql/query db/db-uri ["select * from customers where deleted is null AND user_id = ?" user-id])
(map (fn [{:customers/keys [id name]}] {:id id :name name}))))
(map (fn [{:customers/keys [id name]}] {:id id :name name
:product-groups [{:name "bla" :products {:eggs 2 :carrots 13}}
{:name "ble" :products {:eggs 12 :milk 3}}]}))))
(defn get-by-name [tx user-id name]
(:customers/id (db/get-by-id tx user-id :customers (:name name) :name)))

View File

@ -88,6 +88,10 @@ html body .popup .popup-content ..popup-form-buttons * {
margin: 20px;
}
html body .wide-popup .popup-content {
width: 45%;
}
html body .scroll-button {
display: none;
}
@ -144,6 +148,12 @@ html body .scroll-button {
width: 15%;
}
html body .wide-popup .popup-content {
width: 45%;
}
html body .calendar {
display: grid;
grid-template-columns: 25% 25% 25% 25%;
@ -266,21 +276,26 @@ html body .customers-modal details {
padding: 0.5em;
}
html body .customers-modal details.customer-order {
html body .customers-modal .customer-block {
margin-left: 1em;
padding: 0.5em;
}
html body .customers-modal details.customer-order .order-date-picker {
html body .customers-modal .customer-block .order-date-picker {
display: inline-block;
width: 75%;
cursor: pointer;
}
html body .customers-modal details.customer-order .product-item-edit {
html body .customers-modal .customer-block .product-item-edit {
margin-left: 1em;
}
html body .customer-product-group-edit {
margin-left: 1.2em;
padding: 0.5em;
}
input::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;

View File

@ -8,7 +8,7 @@
[chicken-master.events :as event]
[chicken-master.time :as time]))
(defn format-raw-order [{:strs [who who-id notes] :as raw-values}]
(defn format-raw-order [{:strs [day who who-id notes] :as raw-values}]
{:who {:name who
:id (if (prod/num-or-nil who-id)
(prod/num-or-nil who-id)
@ -17,24 +17,63 @@
(some->> @(re-frame/subscribe [::subs/available-customers])
(filter (comp #{who} :name))
first :id))}
:day day
:notes notes
:products (prod/collect-products (remove (comp #{"who" "notes"} first) raw-values))})
(defn order-form [order]
(let [state (reagent/atom (or order{}))
customers @(re-frame/subscribe [::subs/available-customers])
available-prods @(re-frame/subscribe [::subs/available-products])]
(defn get-group-products [customers who]
(some->> customers
(filter (comp #{who} :name))
first
:product-groups
(reduce #(assoc %1 (:name %2) (:products %2)) {})))
(defn group-products [state]
[:div {:class :input-item}
[:label {:for :order-group-products} "stałe"]
[:select {:class :order-group-products :id :order-group-products
:value "-" :on-change #(some->> % .-target .-value
(get (:group-products @state))
(reset! (:products @state)))}
[:option "-"]
(for [[group _] (:group-products @state)]
[:option {:key (gensym)} group])]])
(defn order-form
([order] (order-form order #{:who :day :notes :products :group-products}))
([order fields]
(let [customers @(re-frame/subscribe [::subs/available-customers])
available-prods @(re-frame/subscribe [::subs/available-products])
state (-> (or order {})
(update :products reagent/atom)
(assoc :group-products
(get-group-products customers (-> order :who :name)))
reagent/atom)]
(fn []
[:div
(when (:who fields)
(let [who (:who @state)]
[:div
(html/input :who "kto" {:required true :default (:name who) :list :customers})
(html/input :who "kto" {:required true
:default (:name who)
:list :customers
:on-blur #(->> % .-target .-value
(get-group-products customers)
(swap! state assoc :group-products))})
(into [:datalist {:id :customers}]
(for [cust customers] [:option {:value (:name cust) :id (:id cust)}]))
[:input {:id :who-id :name :who-id :type :hidden :value (or (:id who) "")}]])
[:input {:id :who-id :name :who-id :type :hidden :value (or (:id who) "")}]]))
(when (:day fields)
(html/input :day "dzień" {:type :date :required true :default (:day order)}))
(when (and (:group-products fields) (-> @state :group-products seq))
[group-products state])
(when (:notes fields)
(html/input :notes "notka"
{:default (:notes @state)})
[prod/products-edit (:products @state) :available-prods available-prods]])))
{:default (:notes @state)}))
(when (:products fields)
(prn "Asd")
[prod/products-edit (:products @state) :available-prods available-prods])]))))
(defn edit-order []
(html/modal

View File

@ -36,7 +36,8 @@
:right "0"
:bottom "0"}]]
[:.popup {:position :fixed
[:.popup
{:position :fixed
:height "100%"
:width "100%"
:overflow :auto
@ -54,6 +55,7 @@
:display :inline-block}]]
[:..popup-form-buttons {:margin "10px"}
[:* {:margin "20px"}]]]]
[:.wide-popup [:.popup-content {:width "45%"}]]
[:.scroll-button {:display :none}]
(at-media
@ -85,6 +87,7 @@
:border "1px solid #888"
:width "15%"
}]]
[:.wide-popup [:.popup-content {:width "45%"}]]
[:.calendar {:display :grid
:grid-template-columns "25% 25% 25% 25%"
:grid-template-rows "50% 50%"}])
@ -147,9 +150,10 @@
[:.customers-modal
[:details {:padding "0.5em"}]
[:details.customer-order {:margin-left "1em" :padding "0.5em"}
[:.customer-block {:margin-left "1em" :padding "0.5em"}
[:.order-date-picker {:display :inline-block :width "75%" :cursor :pointer}]
[:.product-item-edit {:margin-left "1em"}]]]
[:.customer-product-group-edit {:margin-left "1.2em" :padding "0.5em"}]
]]

View File

@ -10,7 +10,7 @@
(defn order-adder [order]
(let [state (reagent/atom order)]
(fn []
[:details {:class (or (:class order) :customer-order) :key (gensym) :open (:open @state)}
[:details {:class (or (:class order) :customer-block) :key (gensym) :open (:open @state)}
[:summary {:on-click #(swap! state update :open not)}
[prod/item-adder
:type :date
@ -18,9 +18,30 @@
:class :order-date-picker
:callback (fn [day] (swap! state #(assoc % :day day :open true)))]]
(if (:day @state)
[prod/products-edit (:products @state)
[prod/products-edit (reagent/atom (or (:products @state) {}))
:getter-fn #(re-frame/dispatch [::event/save-order (assoc @state :products %)])])])))
(defn product-group-adder [who product-group]
(let [state (reagent/atom product-group)]
(fn []
[:div {:class :customer-block}
(if-not (:edit @state)
[:div
[:span {:class :customer-product-group-name} (:name @state)]
[:button {:type :button :on-click #(swap! state assoc :edit true)}
(if (:name @state) "e" "+")]]
[:div {:class :customer-product-group-edit}
(html/input :customer-product-group-name "nazwa"
{:default (:name @state)
:on-blur #(swap! state assoc :name (-> % .-target .-value))})
[prod/products-edit (reagent/atom (or (:products @state) {}))
:getter-fn #(do
(swap! state dissoc :edit)
(when (and (:name @state) (:products @state))
(re-frame/dispatch [::event/save-product-group (:id who) (assoc @state :products %)])))]])])))
(defn show-customers []
(html/modal
:clients
@ -31,13 +52,21 @@
vals
(group-by #(get-in % [:who :id])))]
(for [{:keys [name id] :as who} @(re-frame/subscribe [::subs/available-customers])]
[:details {:class "client" :key (gensym)}
[:details {:open true :class "client" :key (gensym)}
[:summary [:span name [:button {:on-click #(re-frame/dispatch
[::event/confirm-action
"na pewno usunąć?"
::event/remove-customer id])} "-"]]]
[:details {:class :customer}
[:summary "Stałe zamówienia"]
(for [group (:product-groups who)]
[:div {:key (gensym)}
[product-group-adder who group]])
[product-group-adder {}]]
[:details {:class :client-orders}
[:summary "Zamówienia"]
[order-adder {:who who}]
(for [order (reverse (sort-by :day (client-orders id)))]
[order-adder (assoc order :key (gensym))])
]))]
))
[order-adder (assoc order :key (gensym))])]]))]
:class :wide-popup))

View File

@ -170,6 +170,15 @@
:http-xhrio (http-request :delete (str "customers/" id)
:on-success ::process-stock)}))
(re-frame/reg-event-fx
::save-product-group
(fn [_ [_ id group]]
{:dispatch [::start-loading]
:http-xhrio (http-request :post (str "customers/" id "/product-group")
:body group
:on-success ::process-stock)}))
;;; Storage events
(re-frame/reg-event-fx

View File

@ -39,10 +39,11 @@
content
[:div {:class :form-buttons}
[:button {:type :button :on-click #(re-frame/dispatch [::event/hide-modal modal-id])} "ok"]]]])
([modal-id content & {:keys [on-submit submit-text show-cancel]
([modal-id content & {:keys [on-submit submit-text show-cancel class]
:or {submit-text "ok"
show-cancel true}}]
[:div {:class :popup :on-click #(re-frame/dispatch [::event/hide-modal modal-id])}
show-cancel true
class :popup}}]
[:div {:class [:popup class] :on-click #(re-frame/dispatch [::event/hide-modal modal-id])}
[:form {:action "#"
:class :popup-content
:on-click #(.stopPropagation %)

View File

@ -50,10 +50,9 @@
(number-input (str "amount-" id) nil (@state what)
#(swap! state assoc what (-> % .-target .-value num-or-nil)))]))
(defn products-edit [selected-prods & {:keys [available-prods getter-fn]
(defn products-edit [state & {:keys [available-prods getter-fn]
:or {available-prods @(re-frame/subscribe [::subs/available-products])}}]
(let [state (reagent/atom (or selected-prods {}))
all-product-names (-> available-prods keys set)]
(let [all-product-names (-> available-prods keys set)]
(fn []
(let [available (remove (partial get @state) all-product-names)
product-names (if (seq available)
@ -80,7 +79,7 @@
(let [state (reagent/atom value)]
(fn []
[:div {:class class :on-click #(.stopPropagation %)}
[:input {:type type :name :user-name :default value :value @state
[:input {:type type :name :user-name :default-value value
:on-change #(let [val (-> % .-target .-value)]
(reset! state val)
(if-not button (callback val)))}]

View File

@ -5,18 +5,20 @@
(deftest format-raw-order-test
(testing "no products"
(is (= (sut/format-raw-order {}) {:who {:name nil :id nil} :notes nil :products {}}))
(is (= (sut/format-raw-order {}) {:who {:name nil :id nil} :day nil :notes nil :products {}}))
(is (= (sut/format-raw-order {"who" "bla" "notes" "ble"})
{:who {:name "bla" :id nil} :notes "ble" :products {}}))
(is (= (sut/format-raw-order {"who" "bla" "who-id" "123" "notes" "ble"})
{:who {:name "bla" :id 123} :notes "ble" :products {}})))
{:who {:name "bla" :id nil} :day nil :notes "ble" :products {}}))
(is (= (sut/format-raw-order {"who" "bla" "who-id" "123" "notes" "ble" "day" "2020-10-10"})
{:who {:name "bla" :id 123} :day "2020-10-10" :notes "ble" :products {}})))
(testing "decent products"
(is (= (sut/format-raw-order {"who" "bla" "who-id" "123" "notes" "ble"
"day" "2020-10-10"
"product-eggs" "eggs" "amount-eggs" "12"
"product-cows" "cows" "amount-cows" "22"
"product-milk" "milk" "amount-milk" "3.2"})
{:who {:name "bla" :id 123} :notes "ble" :products {:eggs 12 :cows 22 :milk 3.2}})))
{:who {:name "bla" :id 123} :day "2020-10-10" :notes "ble"
:products {:eggs 12 :cows 22 :milk 3.2}})))
(testing "duplicate products"
(is (= (sut/format-raw-order {"who" "bla" "who-id" "123" "notes" "ble"
@ -25,21 +27,46 @@
"product-cows1" "cows" "amount-cows1" "1"
"product-cows2" "cows" "amount-cows2" "2"
"product-milk" "milk" "amount-milk" "3.2"})
{:who {:name "bla" :id 123} :notes "ble" :products {:eggs 24 :cows 3 :milk 3.2}})))
{:who {:name "bla" :id 123} :day nil :notes "ble" :products {:eggs 24 :cows 3 :milk 3.2}})))
(testing "unselected are ignored"
(is (= (sut/format-raw-order {"who" "bla" "who-id" "123" "notes" "ble"
(is (= (sut/format-raw-order {"who" "bla" "who-id" "123" "notes" "ble" "day" "2020-10-10"
"product-eggs" "eggs" "amount-eggs" "12"
"product-bad1" "" "amount-bad1" "12"
"product-bad2" "" "amount-bad2" "1"
"product-milk" "milk" "amount-milk" "3.2"
"product-bad3" "" "amount-bad3" "2"})
{:who {:name "bla" :id 123} :notes "ble" :products {:eggs 12 :milk 3.2}})))
{:who {:name "bla" :id 123} :day "2020-10-10" :notes "ble" :products {:eggs 12 :milk 3.2}})))
(testing "items with 0 are removed"
(is (= (sut/format-raw-order {"who" "bla" "who-id" "123" "notes" "ble"
(is (= (sut/format-raw-order {"who" "bla" "who-id" "123" "notes" "ble" "day" "2020-10-10"
"product-eggs" "eggs" "amount-eggs" "12"
"product-eggs1" "eggs" "amount-eggs1" "0"
"product-cow" "cow" "amount-cow" "0"
"product-milk" "milk" "amount-milk" "3.2"})
{:who {:name "bla" :id 123} :notes "ble" :products {:eggs 12 :milk 3.2}}))))
{:who {:name "bla" :id 123} :day "2020-10-10" :notes "ble" :products {:eggs 12 :milk 3.2}}))))
(def customers
[{:id 1 :name "mr blobby" :product-groups [{:name "group 1" :products {:eggs 1 :carrots 2}}
{:name "group 2" :products {:eggs 11 :carrots 2}}
{:name "group 3" :products {:milk 2 :eggs 12}}]}
{:id 2 :name "johnny D" :product-groups [{:name "group 4" :products {:eggs 2}}
{:name "group 5" :products {:milk 2}}]}
{:id 3 :name "joe" :product-groups []}
{:id 4 :name "mark"}])
(deftest get-group-products-test
(testing "products get returned if the customer has them"
(is (= (sut/get-group-products customers "mr blobby")
{"group 1" {:eggs 1, :carrots 2}
"group 2" {:eggs 11, :carrots 2}
"group 3" {:milk 2, :eggs 12}})))
(testing "no products are returned if the customer has none"
(is (= (sut/get-group-products customers "joe") {})))
(testing "missing products are handled"
(is (nil? (sut/get-group-products customers "mark"))))
(testing "missing customers are handled"
(is (nil? (sut/get-group-products customers "bla bla bla")))))

View File

@ -366,7 +366,17 @@
(is (= method :delete))
(is (= uri "customers/1"))))
(rf/dispatch [::sut/remove-customer 1]))))
(rf/dispatch [::sut/remove-customer 1])))
;; FIXME: the request handler is not being overloaded
(testing "product groups can be saved"
(rf-test/run-test-sync
(param-validator :http-xhrio (fn [[{:keys [method uri body]}]]
(is (= method :post))
(is (= uri "customers/1/product-group"))
(is (= body {:name "bla" :products {:eggs 1 :milk 3}}))))
(rf/dispatch [::sut/save-product-group 1 {:name "bla" :products {:eggs 1 :milk 3}}]))))
(deftest stock-tests

View File

@ -60,14 +60,14 @@
(deftest test-format-date
(testing "don't show date"
(with-redefs [sut/settings (atom {:show-date nil})]
(is (nil? (sut/format-date (new Date 2020 01 01))))))
(with-redefs [sut/settings (atom {:date-format ""})]
(is (= (sut/format-date (new Date 2020 01 01)) ""))))
(testing "without name"
(with-redefs [sut/settings (atom {:show-date true :show-day-name-with-date nil})]
(with-redefs [sut/settings (atom {:date-format "%m/%d"})]
(is (= (sut/format-date (new Date 2020 01 01)) "2/1"))))
(testing "with name"
(with-redefs [sut/settings (atom {:show-date true :show-day-name-with-date true
(with-redefs [sut/settings (atom {:date-format "%D %m/%d"
:day-names ["Niedz" "Pon" "Wt" "Śr" "Czw" "Pt" "Sob"]})]
(is (= (sut/format-date (new Date 2020 01 01)) "Sob 2/1")))))