From c9c0b5f4bd27a5b5610b76cb1b87e968e938206f Mon Sep 17 00:00:00 2001 From: Daniel O'Connell Date: Thu, 11 Mar 2021 19:41:02 +0100 Subject: [PATCH] use counter for loading --- frontend/.dir-locals.el | 2 +- frontend/resources/public/css/screen.css | 296 ++++++++++++++++++ frontend/src/chicken_master/config.cljs | 5 +- frontend/src/chicken_master/events.cljs | 39 ++- frontend/src/chicken_master/subs.cljs | 2 +- frontend/test/chicken_master/events_test.cljs | 23 +- 6 files changed, 340 insertions(+), 27 deletions(-) diff --git a/frontend/.dir-locals.el b/frontend/.dir-locals.el index f37be7a..ba75324 100644 --- a/frontend/.dir-locals.el +++ b/frontend/.dir-locals.el @@ -7,4 +7,4 @@ ;; This tells shadow cljs what to build and should match a key in your shadow-cljs.edn ;; build map. e.g :builds {: {...}} ;; pramas passed to shadow-cljs to start nrepl via cider-jack-in - (cider-shadow-default-options . "frontend -A:dev"))) + (cider-shadow-default-options . "frontend"))) diff --git a/frontend/resources/public/css/screen.css b/frontend/resources/public/css/screen.css index e69de29..f23bbee 100644 --- a/frontend/resources/public/css/screen.css +++ b/frontend/resources/public/css/screen.css @@ -0,0 +1,296 @@ +@keyframes spin { + +0% { + transform: rotate(0deg); + } + +100% { + transform: rotate(360deg); + } + +} + +html { + height: 100%; +} + +html body { + height: 100%; +} + +html body .hidden { + display: none; +} + +html body .loader-container { + position: absolute; + width: 100%; + height: 100%; + z-index: 1000; + background-color: rgba(0,0,0,0.4); +} + +html body .loader-container .loader { + width: 30px; + top: 40%; + height: 30px; + margin: auto; + border: 5px solid #f3f3f3; + position: relative; + animation: spin 1s linear infinite; + border-top: 5px solid #3498db; + border-radius: 50%; +} + +html body .full-height { + height: 100%; +} + +html body .scroll-bar { + position: absolute; + right: 10px; + width: 50px; +} + +html body .scroll-bar #scroll-down { + position: fixed; + right: 0; + bottom: 0; +} + +html body .popup { + position: fixed; + height: 100%; + width: 100%; + overflow: auto; + z-index: 1; + background-color: rgba(0,0,0,0.4); +} + +html body .popup .popup-content { + background-color: #fefefe; + margin: 15% auto; + padding: 20px; + border: 1px solid #888; + width: 15%; +} + +html body .popup .popup-content .input-item label { + min-width: 60px; + display: inline-block; +} + +html body .popup .popup-content ..popup-form-buttons { + margin: 10px; +} + +html body .popup .popup-content ..popup-form-buttons * { + margin: 20px; +} + +html body .scroll-button { + display: none; +} + +@media (max-width: 800px) { + + html body .scroll-bar { + display: none; + } + + html body .menu-button { + width: 100%; + font-size: 3em; + display: inherit; + } + + + + html body .popup .popup-content { + background-color: #fefefe; + margin: 3% auto; + padding: 20px; + border: 1px solid #888; + width: 60%; + } + + html body .popup .popup-content .product-items-edit { + margin-top: 1.5em; + } + + + + html body .popup .popup-content .product-items-edit .product-item-edit label { + display: none; + } + + + + html body .calendar .day { + min-height: 12em; + } + +} + +@media (min-width: 800px) { + + + + html body .popup .popup-content { + background-color: #fefefe; + margin: 15% auto; + padding: 20px; + border: 1px solid #888; + width: 15%; + } + + html body .calendar { + display: grid; + grid-template-columns: 25% 25% 25% 25%; + grid-template-rows: 50% 50%; + } + +} + +@media (min-width: 1200px) { + + html body .calendar { + display: grid; + grid-template-columns: 14% 14% 14% 14% 14% 14% 14%; + grid-template-rows: 50% 50%; + } + +} + +html body .calendar .day-header { + border: 2px solid black; + text-align: center; + font-size: 2em; +} + +html body .calendar .day.today { + border: 0.4em solid red; +} + +html body .calendar .day { + border: 2px solid black; + overflow: auto; +} + +html body .calendar .day .day-header { + border: none; + border-bottom: 2px solid black; +} + +html body .calendar .day .orders { + padding-left: 25px; +} + +html body .calendar .day .orders .actions { + display: none; + float: right; +} + +html body .calendar .day .orders .order:hover .actions { + display: inline; +} + +html body .calendar .day .orders .order.pending { + color: grey; +} + +html body .calendar .day .orders .order.fulfilled { + color: red; +} + +html body .calendar .day .orders .who { + font-size: 18px; + font-weight: bold; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +html body .calendar .day .product { + margin-bottom: 5px; +} + +html body .calendar .day .product .product-name { + width: 5em; + display: inline-block; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + margin-right: 10px; +} + +html body .calendar .day .product .product-amount { + width: 40px; + max-height: 5px; +} + +html body .calendar .day .summary { + margin-top: 10px; +} + +html body .stock-modal .add-product { + float: right; +} + +html body .stock-modal .stock-product { + margin: 1em 0; +} + +html body .stock-modal .stock-product .product-name { + display: inline-block; + width: 6em; +} + +html body .stock-modal .stock-product .stock-product-amount { + display: inline-block; +} + +html body .stock-modal .stock-product .stock-product-amount .input-item { + display: inline; +} + +html body .stock-modal .stock-product .stock-product-amount .input-item input { + width: 40px; +} + +html body .stock-modal .stock-product .stock-product-amount .input-item label { + display: none; +} + +html body .customers-modal details { + padding: 0.5em; +} + +html body .customers-modal details.customer-order { + margin-left: 1em; + padding: 0.5em; +} + +html body .customers-modal details.customer-order .order-date-picker { + display: inline-block; + width: 75%; + cursor: pointer; +} + +html body .customers-modal details.customer-order .product-item-edit { + margin-left: 1em; +} + +input::-webkit-outer-spin-button { + -webkit-appearance: none; + margin: 0; +} + +input::-webkit-inner-spin-button { + -webkit-appearance: none; + margin: 0; +} + +input[type=number] { + -moz-appearance: textfield; +} \ No newline at end of file diff --git a/frontend/src/chicken_master/config.cljs b/frontend/src/chicken_master/config.cljs index 2bd1397..59b9b68 100644 --- a/frontend/src/chicken_master/config.cljs +++ b/frontend/src/chicken_master/config.cljs @@ -33,7 +33,10 @@ :editable-number-inputs (get-setting :editable-number-inputs false) ; only allow number modifications in the edit modal :hide-fulfilled-orders (get-setting :hide-fulfilled-orders false) - :backend-url (get-setting :backend-url (str (.. js/window -location -href) "api/")) ; "http://localhost:3000/" + :backend-url (get-setting :backend-url + (if (= (.. js/window -location -href) "http://localhost:8280/") + "http://localhost:3000/api/" + (str (.. js/window -location -href) "api/"))) }) diff --git a/frontend/src/chicken_master/events.cljs b/frontend/src/chicken_master/events.cljs index 51bf408..072bd9e 100644 --- a/frontend/src/chicken_master/events.cljs +++ b/frontend/src/chicken_master/events.cljs @@ -50,8 +50,8 @@ [:dispatch [::fetch-orders]]]})) (re-frame/reg-event-db ::hide-modal (fn [db [_ modal]] (assoc-in db [modal :show] nil))) -(re-frame/reg-event-db ::start-loading (fn [db _] (assoc db :loading? true))) -(re-frame/reg-event-db ::stop-loading (fn [db _] (assoc db :loading? nil))) +(re-frame/reg-event-db ::start-loading (fn [db _] (update db :loading? inc))) +(re-frame/reg-event-db ::stop-loading (fn [db _] (update db :loading? dec))) (re-frame/reg-event-fx ::confirm-action @@ -68,10 +68,9 @@ (re-frame/reg-event-fx ::failed-request (fn [{db :db} [_ response]] - {:db (-> db - (assoc :loading? false) - (update :current-user #(when-not (= (:status response) 401) %))) - :dispatch [::log-error (str response)]})) + {:db (update db :current-user #(when-not (= (:status response) 401) %)) + :fx [[:dispatch [::log-error (str response)]] + [:dispatch [::stop-loading]]]})) (re-frame/reg-event-fx ::remove-order @@ -114,15 +113,15 @@ (select-keys order [:id :day :hour :state]) (select-keys form [:id :day :hour :state :who :notes :products])))})) -(re-frame/reg-event-db +(re-frame/reg-event-fx ::process-fetched-days - (fn [db [_ days]] - (-> db - (assoc :loading? nil) - (update :current-days (fn [current-days] - (for [[day orders] current-days] - [day (if (contains? days day) (days day) orders)]))) - (update :orders #(reduce (fn [m order] (assoc m (:id order) order)) % (mapcat second days)))))) + (fn [{db :db} [_ days]] + {:db (-> db + (update :current-days (fn [current-days] + (for [[day orders] current-days] + [day (if (contains? days day) (days day) orders)]))) + (update :orders #(reduce (fn [m order] (assoc m (:id order) order)) % (mapcat second days)))) + :dispatch [::stop-loading]})) (re-frame/reg-event-fx ::scroll-weeks @@ -134,16 +133,16 @@ (time/date-offset (* 7 offset)) time/iso-date)]]]})) -(re-frame/reg-event-db +(re-frame/reg-event-fx ::show-from-date - (fn [{:keys [start-date orders] :as db} [_ day]] + (fn [{{:keys [start-date orders] :as db} :db} [_ day]] (let [day (or day start-date) days (into #{} (time/get-weeks day 2)) filtered-orders (->> orders vals (filter (comp days :day)) (group-by :day))] - (assoc db - :loading? nil - :start-date day - :current-days (map #(vector % (get filtered-orders %)) (sort days)))))) + {:db (assoc db + :start-date day + :current-days (map #(vector % (get filtered-orders %)) (sort days))) + :dispatch [::stop-loading]}))) (re-frame/reg-event-fx ::fetch-orders diff --git a/frontend/src/chicken_master/subs.cljs b/frontend/src/chicken_master/subs.cljs index 13af447..811aaaa 100644 --- a/frontend/src/chicken_master/subs.cljs +++ b/frontend/src/chicken_master/subs.cljs @@ -4,7 +4,7 @@ (re-frame/reg-sub ::name (fn [db] (:name db))) (re-frame/reg-sub ::current-user (fn [db] (:current-user db))) (re-frame/reg-sub ::settings (fn [db] (:settings db))) -(re-frame/reg-sub ::loading? (fn [db] (:loading? db))) +(re-frame/reg-sub ::loading? (fn [db] (-> db :loading? pos?))) (re-frame/reg-sub ::available-products (fn [db] (:products db))) (re-frame/reg-sub ::available-customers (fn [db] (:customers db))) diff --git a/frontend/test/chicken_master/events_test.cljs b/frontend/test/chicken_master/events_test.cljs index 60a1a92..f2a945e 100644 --- a/frontend/test/chicken_master/events_test.cljs +++ b/frontend/test/chicken_master/events_test.cljs @@ -55,7 +55,7 @@ (testing "loader gets set" (rf-test/run-test-sync (set-db {:loading? nil}) - (is (nil? @(rf/subscribe [::subs/loading?]))) + (is (not @(rf/subscribe [::subs/loading?]))) (rf/dispatch [::sut/start-loading]) (is @(rf/subscribe [::subs/loading?])))) @@ -64,7 +64,22 @@ (set-db {:loading? true}) (is @(rf/subscribe [::subs/loading?])) (rf/dispatch [::sut/stop-loading]) - (is (nil? @(rf/subscribe [::subs/loading?])))))) + (is (not @(rf/subscribe [::subs/loading?]))))) + + (testing "multiple loads handled" + (rf-test/run-test-sync + (is (not @(rf/subscribe [::subs/loading?]))) + (rf/dispatch [::sut/start-loading]) + (rf/dispatch [::sut/start-loading]) + (rf/dispatch [::sut/start-loading]) + (is @(rf/subscribe [::subs/loading?])) + + (rf/dispatch [::sut/stop-loading]) + (is @(rf/subscribe [::subs/loading?])) + + (rf/dispatch [::sut/stop-loading]) + (rf/dispatch [::sut/stop-loading]) + (is (not @(rf/subscribe [::subs/loading?])))))) (deftest confirm-action-test (testing "when confirmed, the provided event is called" @@ -212,7 +227,7 @@ (set-db {:orders {} :current-days {} :loading? true}) (rf/dispatch [::sut/process-fetched-days {}]) - (is (nil? @(rf/subscribe [::subs/loading?]))))) + (is (not @(rf/subscribe [::subs/loading?]))))) (testing "orders get set correctly" (rf-test/run-test-sync @@ -270,7 +285,7 @@ (set-db {:orders {} :current-days {} :loading? true}) (rf/dispatch [::sut/show-from-date "2020-01-01"]) - (is (nil? @(rf/subscribe [::subs/loading?]))))) + (is (not @(rf/subscribe [::subs/loading?]))))) (testing "showing from date works" (rf-test/run-test-sync