This commit is contained in:
Daniel O'Connell 2021-03-01 22:40:46 +01:00
parent 182e613deb
commit 38053bbdb2
13 changed files with 136 additions and 79 deletions

7
config/prod/config.edn Normal file
View File

@ -0,0 +1,7 @@
{;; Networking settings
:port 8000
:allow-origin ["http://91.218.78.71" "https://mruwnik.github.io"]
;; db settings
:db-uri {:jdbcUrl "jdbc:postgresql://localhost/chickens"}
}

13
infra/chickens.service Normal file
View File

@ -0,0 +1,13 @@
[Unit]
Description=Chicken master
After=postgresql.service
[Service]
ExecStart=/usr/bin/java -Dconfig="/home/dan/chicken-master/config.edn" -jar /home/dan/chicken-master/target/chicken-master.jar
Type=simple
Restart=always
RestartSec=1
User=root
[Install]
WantedBy=multi-user.target

35
infra/nginx/chickens.conf Normal file
View File

@ -0,0 +1,35 @@
server {
server_name chickens.ahiru.pl
access_log /var/log/nginx/reverse-access.log;
error_log /var/log/nginx/reverse-error.log;
location / {
proxy_pass http://127.0.0.1:8000;
}
listen [::]:443 ssl ipv6only=on; # managed by Certbot
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/chickens.ahiru.pl/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/chickens.ahiru.pl/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
if ($host = chickens.ahiru.pl) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
listen [::]:80;
server_name chickens.ahiru.pl
access_log /var/log/nginx/reverse-access.log;
return 404; # managed by Certbot
}

View File

@ -23,9 +23,9 @@
(defn get-products [user-id] (as-edn (products/get-all user-id))) (defn get-products [user-id] (as-edn (products/get-all user-id)))
(defn save-products [{:keys [body basic-authentication]}] (defn save-products [{:keys [body basic-authentication]}]
(some->> body (products/update! basic-authentication) as-edn)) (some->> body (products/update! basic-authentication) (assoc {} :products) as-edn))
(defn get-orders [user-id] (as-edn {:orders (orders/get-all user-id)})) (defn get-orders [user-id] (as-edn (orders/get-all user-id)))
(defn update-order [request] (defn update-order [request]
(let [user-id (:basic-authentication request) (let [user-id (:basic-authentication request)
id (some-> request :route-params :id (Integer/parseInt)) id (some-> request :route-params :id (Integer/parseInt))

View File

@ -43,7 +43,7 @@
(defn get-order [tx user-id id] (defn get-order [tx user-id id]
(first (get-orders tx "WHERE o.id = ? AND o.user_id = ?" [id user-id]))) (first (get-orders tx "WHERE o.id = ? AND o.user_id = ?" [id user-id])))
(defn get-all [user-id] (get-orders db/db-uri "WHERE o.user_id = ?" [user-id])) (defn get-all [user-id] (group-by :day (get-orders db/db-uri "WHERE o.user_id = ?" [user-id])))
(defn- orders-for-days [tx user-id & days] (defn- orders-for-days [tx user-id & days]
(let [days (remove nil? days)] (let [days (remove nil? days)]
@ -51,18 +51,11 @@
(map t/inst->timestamp) (map t/inst->timestamp)
(map jdbc.types/as-date) (map jdbc.types/as-date)
(into [user-id]) (into [user-id])
(get-orders tx (str "WHERE o.user_id = ? AND o.order_date::date IN " (db/psql-list days)))))) (get-orders tx (str "WHERE o.user_id = ? AND o.order_date::date IN " (db/psql-list days)))
(group-by :day)
(defn- orders-between [tx user-id from to] (merge (reduce #(assoc %1 (t/format-date %2) {}) {} days)))))
(get-orders
tx
"WHERE o.order_date::date >= ? AND o.order_date::date <= ? AND o.user_id = ?"
[(some-> from t/inst->timestamp jdbc.types/as-date)
(some-> to t/inst->timestamp jdbc.types/as-date)
user-id]))
(defn replace! [user-id {:keys [who products] :as order}] (defn replace! [user-id {:keys [who products] :as order}]
(prn order)
(jdbc/with-transaction [tx db/db-uri] (jdbc/with-transaction [tx db/db-uri]
(let [customer-id (or (:id who) (let [customer-id (or (:id who)
(:customers/id (db/get-by-id tx user-id :customers (:name who) :name))) (:customers/id (db/get-by-id tx user-id :customers (:name who) :name)))

View File

@ -9,11 +9,12 @@
(into {}))) (into {})))
(defn products-map [tx products] (defn products-map [tx products]
(->> (map name (keys products)) (when (seq products)
(into [(str "SELECT id, name from products where name IN " (db/psql-list (keys products)))]) (->> (map name (keys products))
(sql/query tx) (into [(str "SELECT id, name from products where name IN " (db/psql-list (keys products)))])
(map #(vector (:products/name %) (:products/id %))) (sql/query tx)
(into {}))) (map #(vector (:products/name %) (:products/id %)))
(into {}))))
(defn update! [user-id new-products] (defn update! [user-id new-products]
(jdbc/with-transaction [tx db/db-uri] (jdbc/with-transaction [tx db/db-uri]

View File

@ -1,11 +1,17 @@
(ns chicken-master.time (ns chicken-master.time
(:import [java.time Instant LocalDate ZoneOffset] (:import [java.time Instant LocalDate ZoneOffset]
[java.time.format DateTimeFormatter]
[java.sql Timestamp])) [java.sql Timestamp]))
(defn parse-date [date] (defn parse-date [date]
(-> date (LocalDate/parse) (.atStartOfDay) (.toInstant ZoneOffset/UTC))) (-> date (LocalDate/parse) (.atStartOfDay) (.toInstant ZoneOffset/UTC)))
(defn format-date [date]
(-> DateTimeFormatter/ISO_LOCAL_DATE
(.withZone ZoneOffset/UTC)
(.format date)))
(defn inst->timestamp [inst] (Timestamp/from inst)) (defn inst->timestamp [inst] (Timestamp/from inst))
(defn now [] (Instant/now)) (defn now [] (Instant/now))

View File

@ -1,6 +1,7 @@
(ns chicken-master.calendar (ns chicken-master.calendar
(:require (:require
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[reagent.core :as reagent]
[chicken-master.subs :as subs] [chicken-master.subs :as subs]
[chicken-master.html :as html] [chicken-master.html :as html]
[chicken-master.products :as prod] [chicken-master.products :as prod]
@ -19,22 +20,28 @@
:notes notes :notes notes
:products (prod/collect-products (remove (comp #{"who" "notes"} first) raw-values))}) :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])]
(fn []
[:div
(let [who (:who @state)]
[:div
(html/input :who "kto" {:required true :default (:name who) :list :customers})
(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) "")}]])
(html/input :notes "notka"
{:default (:notes @state)})
[prod/products-edit (:products @state) :available-prods available-prods]])))
(defn edit-order [] (defn edit-order []
(html/modal (html/modal
:order-edit :order-edit
[:div [order-form @(re-frame/subscribe [::subs/editted-order])]
(let [who @(re-frame/subscribe [::subs/order-edit-who])
customers @(re-frame/subscribe [::subs/available-customers])]
[:div
(html/input :who "kto" {:required true :default (:name who) :list :customers})
(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) "")}]])
(html/input :notes "notka"
{:default @(re-frame/subscribe [::subs/order-edit-notes])})
[prod/products-edit @(re-frame/subscribe [::subs/order-edit-products])]]
;; On success ;; On success
(fn [form] (re-frame/dispatch [::event/save-order (format-raw-order form)])))) :on-submit (fn [form] (re-frame/dispatch [::event/save-order (format-raw-order form)]))))
(defn format-order [settings {:keys [id who day hour notes products state]}] (defn format-order [settings {:keys [id who day hour notes products state]}]
[:div {:class [:order state] :key (gensym) [:div {:class [:order state] :key (gensym)

View File

@ -47,11 +47,6 @@
(when (js/confirm msg) (when (js/confirm msg)
{:fx [[:dispatch (into [on-confirm-event] params)]]}))) {:fx [[:dispatch (into [on-confirm-event] params)]]})))
(re-frame/reg-event-fx
::remove-order
(fn [_ [_ id]]
{:http-xhrio (http-request :delete (str "orders/" id))}))
(re-frame/reg-event-db (re-frame/reg-event-db
::failed-request ::failed-request
(fn [db [_ response]] (fn [db [_ response]]
@ -59,6 +54,11 @@
(js/alert "Wystąpił błąd") (js/alert "Wystąpił błąd")
(assoc db :loading? false))) (assoc db :loading? false)))
(re-frame/reg-event-fx
::remove-order
(fn [_ [_ id]]
{:http-xhrio (http-request :delete (str "orders/" id))}))
(re-frame/reg-event-fx (re-frame/reg-event-fx
::move-order ::move-order
(fn [{{orders :orders start-date :start-date} :db} [_ id day]] (fn [{{orders :orders start-date :start-date} :db} [_ id day]]
@ -91,20 +91,19 @@
(fn [{{order :order-edit} :db} [_ form]] (fn [{{order :order-edit} :db} [_ form]]
{:dispatch [::hide-modal :order-edit] {:dispatch [::hide-modal :order-edit]
:http-xhrio (http-post (str "orders") :http-xhrio (http-post (str "orders")
(merge (merge
(select-keys order [:id :day :hour :state]) (select-keys order [:id :day :hour :state])
(select-keys form [:id :day :hour :state :who :notes :products])))})) (select-keys form [:id :day :hour :state :who :notes :products])))}))
(re-frame/reg-event-db (re-frame/reg-event-db
::process-fetched-days ::process-fetched-days
(fn [db [_ orders]] (fn [db [_ days]]
(-> db (-> db
(assoc :loading? nil) (assoc :loading? nil)
(update :current-days (fn [current-days] (update :current-days (fn [current-days]
(let [days (group-by :day orders)] (for [[day orders] current-days]
(for [[day orders] current-days] [day (if (contains? days day) (days day) orders)])))
[day (if (contains? days day) (days day) orders)])))) (update :orders #(reduce (fn [m order] (assoc m (:id order) order)) % (mapcat second days))))))
(update :orders #(reduce (fn [m cust] (assoc m (:id cust) cust)) % orders)))))
(re-frame/reg-event-fx (re-frame/reg-event-fx
::scroll-weeks ::scroll-weeks
@ -131,7 +130,7 @@
::fetch-orders ::fetch-orders
(fn [_ [_ from to]] (fn [_ [_ from to]]
{:dispatch [::start-loading] {:dispatch [::start-loading]
:http-xhrio (http-get "orders" {} ::process-stock)})) :http-xhrio (http-request :get "orders")}))
;; Customers events ;; Customers events
(re-frame/reg-event-fx (re-frame/reg-event-fx
@ -170,12 +169,11 @@
(defn assoc-if [coll key val] (if val (assoc coll key val) coll)) (defn assoc-if [coll key val] (if val (assoc coll key val) coll))
(re-frame/reg-event-fx (re-frame/reg-event-fx
::process-stock ::process-stock
(fn [{db :db} [_ {:keys [products customers orders]}]] (fn [{db :db} [_ {:keys [products customers]}]]
{:db (-> db {:db (-> db
(assoc-if :products products) (assoc-if :products products)
(assoc-if :customers customers) (assoc-if :customers customers))
(assoc-if :orders (some->> orders (into {} (map #(vector (:id %) %)))))) :dispatch [::stop-loading]
:dispatch [::scroll-weeks 0]
})) }))
(re-frame/reg-event-fx (re-frame/reg-event-fx
@ -183,7 +181,7 @@
(fn [_ [_ products]] (fn [_ [_ products]]
{:fx [[:dispatch [::hide-modal :stock]] {:fx [[:dispatch [::hide-modal :stock]]
[:dispatch [::start-loading]]] [:dispatch [::start-loading]]]
:http-xhrio (http-request :post "products" :body products :on-sucess ::process-stock)})) :http-xhrio (http-request :post "products" :body products :on-success ::process-stock)}))
;; Settings ;; Settings

View File

@ -39,7 +39,8 @@
content content
[:div {:class :form-buttons} [:div {:class :form-buttons}
[:button {:type :button :on-click #(re-frame/dispatch [::event/hide-modal modal-id])} "ok"]]]]) [:button {:type :button :on-click #(re-frame/dispatch [::event/hide-modal modal-id])} "ok"]]]])
([modal-id content on-submit] ([modal-id content & {:keys [on-submit submit-text]
:or {submit-text "ok"}}]
[:div {:class :popup :on-click #(re-frame/dispatch [::event/hide-modal modal-id])} [:div {:class :popup :on-click #(re-frame/dispatch [::event/hide-modal modal-id])}
[:form {:action "#" [:form {:action "#"
:class :popup-content :class :popup-content
@ -50,7 +51,7 @@
(re-frame/dispatch [::event/hide-modal modal-id])))} (re-frame/dispatch [::event/hide-modal modal-id])))}
content content
[:div {:class :form-buttons} [:div {:class :form-buttons}
[:button "ok"] [:button submit-text]
[:button {:type :button :on-click #(re-frame/dispatch [::event/hide-modal modal-id])} "anuluj"]]]])) [:button {:type :button :on-click #(re-frame/dispatch [::event/hide-modal modal-id])} "anuluj"]]]]))

View File

@ -40,36 +40,34 @@
[:div {:class :product-item-edit :key (gensym)} [:div {:class :product-item-edit :key (gensym)}
[:div {:class :input-item} [:div {:class :input-item}
;; [:label {:for :product} "co"] ;; [:label {:for :product} "co"]
[:select {:name (str "product-" id) :id :product :defaultValue (some-> what name) [:select {:name (str "product-" id) :id :product :defaultValue (or (some-> what name) "-")
:on-change #(let [prod (-> % .-target .-value keyword)] :on-change #(let [prod (-> % .-target .-value keyword)]
(if-not (= prod :-) (if-not (= prod :-)
(swap! state assoc prod (+ (@state prod) (@state what)))) (swap! state assoc prod (+ (@state prod) (@state what))))
(swap! state dissoc what) (swap! state dissoc what))}
)} (for [product (->> available (concat [what]) (remove nil?) sort vec)]
(for [product (-> available (conj what) sort vec (conj nil))] [:option {:key (gensym) :value product} (name product)])
[:option {:key (gensym) :value product} (if product (name product) "-")])] [:option {:key (gensym) :value nil} "-"]]]
]
(number-input (str "amount-" id) nil (@state what) (number-input (str "amount-" id) nil (@state what)
#(swap! state assoc what (-> % .-target .-value num-or-nil))) #(swap! state assoc what (-> % .-target .-value num-or-nil)))]))
]))
(defn products-edit [selected-prods & {:keys [available-prods getter-fn] (defn products-edit [selected-prods & {:keys [available-prods getter-fn]
:or {available-prods @(re-frame/subscribe [::subs/available-products])}}] :or {available-prods @(re-frame/subscribe [::subs/available-products])}}]
(let [state (reagent/atom (or selected-prods {}))] (let [state (reagent/atom (or selected-prods {}))
all-product-names (-> available-prods keys set)]
(fn [] (fn []
(let [available (remove (partial get @state) (keys available-prods)) (let [available (remove (partial get @state) all-product-names)
products (->> @state product-names (if (seq available)
keys (conj (->> @state (map first) vec) nil)
sort (map first @state))
products (->> product-names
(map (partial product-item available state)) (map (partial product-item available state))
(into [:div {:class :product-items-edit}])) (into [:div {:class :product-items-edit}]))]
products (conj products (product-item available state nil))]
(if getter-fn (if getter-fn
(conj products (conj products
[:button {:type :button [:button {:type :button
:on-click #(getter-fn (dissoc @state nil))} "ok"]) :on-click #(getter-fn (dissoc @state nil))} "ok"])
products products)))))
)))))
(defn format-product [settings [product amount]] (defn format-product [settings [product amount]]
[:div {:key (gensym) :class :product} [:div {:key (gensym) :class :product}

View File

@ -29,11 +29,11 @@
[:h2 "Magazyn"] [:h2 "Magazyn"]
[stock-form @(re-frame/subscribe [::subs/available-products])]] [stock-form @(re-frame/subscribe [::subs/available-products])]]
;; On success ;; On success
(fn [form] :on-submit (fn [form]
(->> form (->> form
(reduce-kv #(if-let [val (prod/num-or-nil %3)] (reduce-kv #(if-let [val (prod/num-or-nil %3)]
(assoc %1 (keyword %2) val) (assoc %1 (keyword %2) val)
%1) %1)
{}) {})
(conj [::event/save-stock]) (conj [::event/save-stock])
re-frame/dispatch)))) re-frame/dispatch))))

View File

@ -14,8 +14,6 @@
(re-frame/reg-sub ::show-customers-modal (fn [db] (-> db :clients :show))) (re-frame/reg-sub ::show-customers-modal (fn [db] (-> db :clients :show)))
(re-frame/reg-sub ::show-settings-modal (fn [db] (-> db :settings :show))) (re-frame/reg-sub ::show-settings-modal (fn [db] (-> db :settings :show)))
(re-frame/reg-sub ::order-edit-who (fn [db] (-> db :order-edit :who))) (re-frame/reg-sub ::editted-order (fn [db] (:order-edit db)))
(re-frame/reg-sub ::order-edit-notes (fn [db] (-> db :order-edit :notes)))
(re-frame/reg-sub ::order-edit-products (fn [db] (-> db :order-edit :products)))
(re-frame/reg-sub ::current-days (fn [db] (:current-days db))) (re-frame/reg-sub ::current-days (fn [db] (:current-days db)))