mirror of
https://github.com/mruwnik/chicken-master.git
synced 2025-06-08 21:34:43 +02:00
frontend product groups
This commit is contained in:
parent
a72255cf30
commit
4ddfa94318
@ -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)))
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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"}]
|
||||
|
||||
]]
|
||||
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
|
@ -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 %)
|
||||
|
@ -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)))}]
|
||||
|
@ -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")))))
|
||||
|
@ -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
|
||||
|
@ -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")))))
|
||||
|
Loading…
x
Reference in New Issue
Block a user