summaryrefslogtreecommitdiff
path: root/pages/shop/index.fnl
diff options
context:
space:
mode:
Diffstat (limited to 'pages/shop/index.fnl')
-rw-r--r--pages/shop/index.fnl155
1 files changed, 155 insertions, 0 deletions
diff --git a/pages/shop/index.fnl b/pages/shop/index.fnl
new file mode 100644
index 0000000..5a96ab2
--- /dev/null
+++ b/pages/shop/index.fnl
@@ -0,0 +1,155 @@
+(import-macros {:compile-html <>} :macros)
+(local lib (require :lib))
+(local dicts (require :dicts))
+(local templates (require :templates))
+
+(fn all-products [db authenticated?]
+ (local where
+ (if (not authenticated?)
+ "WHERE products.published = true"
+ ""))
+ (_G.must
+ (luna.db.query-assoc db
+ (..
+ "SELECT products.name,
+ products.title,
+ products.published,
+ products.short_description as \"short-description\",
+ products.price_per AS \"price-per\",
+ products.type,
+ products.image1,
+ products.image2,
+ products.image3,
+ products.image4,
+ products.image5
+ FROM products "
+ where
+ " ORDER BY products.position") [])))
+
+(fn quantity-steps [stock step]
+ (assert (< 0 step) "step must be greater than 0")
+
+ (var result [])
+ (var first (math.min stock step))
+ (while (<= first stock)
+ (table.insert result first)
+ (set first (+ first step)))
+ result)
+
+(fn item-template [product]
+ (local item-url (.. "/shop/" product.name))
+ ;; (var quantity-options [])
+ ;; (if (< 0 product.stock)
+ ;; (each [_ q (ipairs (quantity-steps product.stock 50))]
+ ;; (table.insert quantity-options
+ ;; (<>
+ ;; [:option {:value (tostring q)}
+ ;; (.. q " грамм за " (* product.price-per q) "₽")])))
+ ;; (table.insert quantity-options (<> [:option {:value "0"} "Товар закончился"])))
+
+ (local images [])
+ (for [i 2 5]
+ (table.insert images (. product (.. "image" i))))
+
+ (<>
+ [:section {:class (.. "shop-item"
+ (if (not product.published)
+ " shop-item-not-published" ""))}
+ [:a {:href item-url}
+ [:div {:class "shop-item-imgs"}
+ [:img {:class "shop-item-img"
+ :src (.. "/static/files/" (. product.image1) "-thumbnail.jpg")}]
+ (table.concat
+ (icollect [idx image (ipairs images)]
+ (<>
+ [:img {:class "shop-item-img" :src (.. "/static/files/" image "-thumbnail.jpg")
+ :loading "lazy"
+ :style (.. "z-index: " (+ idx 2) ";"
+ "width: calc(100% / " (# images) ");"
+ "left: calc(100% / " (# images) " * " (- idx 1) ")")}])))]]
+ [:a {:href item-url} [:h3 {:class "shop-item-title"} product.title]]
+ [:div {:style "font-style: italic; margin-bottom: 0.25rem;"}
+ (or (dicts.label dicts.tea-type product.type) product.type) ", "
+ [:strong {} (* 50 product.price-per) "₽ за 50 гр. "]]
+ ;; [:div {:class "shop-item-price"}
+ ;; [:form {:method "POST"}
+ ;; [:input {:type "hidden" :name "name" :value product.name}]
+ ;; [:select {:name "quantity"} (table.concat quantity-options)]
+ ;; [:button {:type "submit"} "Добавить"]]]
+ [:div {} product.short-description]]))
+
+(fn content [db basket basket-total authenticated?]
+ [(<>
+ [:div {:class "side"}
+ (templates.header "/shop" authenticated?)
+ (if (< 0 (# basket))
+ (<>
+ [:article {:class "article"}
+ [:h2 {} "Корзина"]
+ [:div {}
+ (table.concat
+ (icollect [_ item (ipairs basket)]
+ (templates.basket-item item "/shop")))]
+ [:div {} "~~~"]
+ [:div {:class "basket-total"} (.. "Итого: " basket-total "₽")]
+ [:a {:href "/shop/order"} "Оформить заказ"]])
+ "")])
+ (<>
+ [:div {:class "content"}
+ [:div {:class "mb-1"} [:a {:href "/"} "⟵ Обратно на главную"]]
+ [:h2 {:class "mb-1 product-page-title"}
+ "Магазин"
+ (if authenticated?
+ (<> [:a {:style "font-size: 1rem; margin-left: 0.75rem;"
+ :href (.. "/shop/add")} "+ Добавить"])
+ "")]
+ [:div {:class "shop-items"}
+ (let [products (all-products db authenticated?)]
+ (if (< 0 (# products))
+ (table.concat
+ (icollect [_ v (ipairs products)]
+ (item-template v)))
+ (<> [:em {} "Пока что здесь ничего нет!"])))]])])
+
+(fn create-order [db]
+ (let [id (_G.must (luna.crypto.random-string 64))]
+ (_G.must
+ (luna.db.exec
+ db "INSERT INTO orders (id, creation_time) VALUES (?, ?)"
+ [id (lib.now)]))
+ id))
+
+(fn create-order-line [db order-id name quantity]
+ (_G.must
+ (luna.db.exec
+ db
+ "INSERT INTO order_lines (order_id, product_name, quantity) VALUES (?, ?, ?)"
+ [order-id name quantity])))
+
+(fn render [request db authenticated?]
+ (let [order-id (lib.order-id request)
+ basket (if order-id (lib.basket db order-id) [])
+ basket-total (accumulate [sum 0 _ v (ipairs basket)]
+ (+ sum (* v.quantity v.price-per)))]
+ (if (= request.method "POST")
+ (do
+ (var order-id (lib.order-id request))
+ (var headers
+ (if (not order-id)
+ (do
+ (set order-id (create-order db))
+ {:Set-Cookie (.. "order= " order-id "; HttpOnly; SameSite=strict"
+ (if luna.debug? "" "; Secure"))})
+ {}))
+
+ (if (and order-id request.body)
+ (let [body-values (lib.parse-values request.body)]
+ (create-order-line db order-id body-values.name body-values.quantity)
+ (tset headers :Location "/shop")
+ (values 302 headers ""))
+ (values 400 {} "bad body")))
+ (values
+ 200 {}
+ (templates.base (content db basket basket-total authenticated?))))))
+
+{: render}