(import-macros {:compile-html HTML} :macros)
(local lib (require :lib))
(local dicts (require :dicts))
(fn read-file [file]
(with-open [f (io.open file "r")]
(f:read :*all)))
(fn base [content title description]
(HTML
[:html {:lang "ru-RU"}
[:head {}
[:title {} (.. "«Белая жаба» / " (or title "Уютный чайный клуб в Омске"))]
[:meta {:charset "utf-8"}]
[:meta {:name "viewport"
:content (.. "width=device-width,initial-scale=1,"
"minimum-scale=1.0,maximum-scale=5.0")}]
[:meta {:name "description"
:content (or description
(.. "Уютный чайный клуб в Омске: "
"ул. Пушкина, д. 133/9, этаж 2. "
"Посещение по предварительной договоренности."))}]
[:style {} [:NO-ESCAPE (read-file "static/style.css")]]
[:link {:rel "icon" :href "/static/favicon.svg" :type "image/svg+xml"}]]
[:body {}
[:main {:class "container"} (table.concat content)]]]))
(fn header [current-path authenticated?]
(local logo
(HTML
[:img {:class "logo-img" :src "/static/logo.svg"
:alt "Белая жаба в мультяшном стиле с чайником на голове"}]))
(HTML
[:section {}
[:div {:class "logo"}
(if authenticated?
(HTML [:img {:class "logo-glasses" :src "/static/glasses.png"
:alt "Солнцезащитные очки"}])
"")
(if (~= current-path "")
(HTML [:a {:href "/" :class "d-inline-block"} logo])
logo)
[:h1 {} [:NO-ESCAPE "Чайный клуб
«Белая жаба»"]]]
[:nav {}
[:a {:href "/shop"
:class (if (lib.starts-with? current-path "/shop") "active" "")}
"магазин"]
[:span {} "–"]
[:a {:href "/information"
:class (if (= current-path "/information") "active" "")}
"информация"]
[:span {} "–"]
[:a {:href "https://t.me/whitetoadtea"} "телеграм"]]]))
(fn basket-item [item redirect-url]
(local item-link (.. "/shop/" item.name))
(HTML
[:div {:class "basket-item"}
[:div {:class "basket-item-image"}
[:a {:href item-link :style "font-size: 0;"}
[:img {:src (.. "/static/files/" item.image1 "-thumbnail.jpg")
:alt item.title}]]]
[:div {}
[:a {:href item-link :class "basket-item-title"} item.title]
[:div {:class "basket-item-price"}
(if (= item.packaging :piece)
(.. (lib.format-price (* item.price-per item.quantity))
"₽ за " item.quantity " шт.")
(.. (lib.format-price (* item.price-per item.quantity))
"₽ за " item.quantity " гр."))
[:form {:class "basket-item-remove" :method "POST"
:action "/shop/cart/remove"}
[:input {:type "hidden" :name "redirect-url" :value redirect-url}]
[:input {:type "hidden" :name "id" :value (tostring item.id)}]
[:button {:type "submit"} "⨯ убрать"]]]]]))
(fn basket [basket redirect-url]
(let [total (accumulate [sum 0 _ v (ipairs basket)]
(+ sum (* v.quantity v.price-per)))]
(HTML
[:section {}
[:h2 {} "Корзина"]
[:div {:class "mb-0-25"}
(table.concat
(icollect [_ item (ipairs basket)]
(basket-item item redirect-url)))]
[:div {:class "mt-0-25"} "—"]
[:div {} [:strong {} (.. "Итого: " (lib.format-price total) "₽")]]
[:a {:href "/shop/order"} "Оформить заказ ⟶"]])))
(fn product-overview [product classes]
(local classes (or classes ""))
(HTML
[:div {:class classes :style "font-style: italic"}
(or (dicts.label dicts.product-type product.type) product.type) ", "
(if (not (lib.empty? product.year))
(HTML [:span {} [:NO-ESCAPE (.. product.year " год, ")]])
"")
(if (not (lib.empty? product.volume))
(HTML [:span {} [:NO-ESCAPE (.. product.volume " мл., ")]])
"")
(if (not (lib.empty? product.region)) (.. product.region ", ") "")
(if (= product.packaging "piece")
(HTML [:strong {} (lib.format-price product.price-per) "₽"])
(HTML [:span {}
[:strong {}
[:NO-ESCAPE
(lib.format-price (* 50 product.price-per))
"₽ за 50 грамм "]]
[:NO-ESCAPE "(" (lib.format-price product.price-per)
"₽ за 1 грамм)"]]))]))
(fn add-to-basket-form [product basket classes redirect-url]
(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)
(var in-basket-quantity nil)
(each [_ basket-item (pairs basket) &until in-basket-quantity]
(when (= product.name basket-item.name)
(set in-basket-quantity basket-item.quantity)))
(var quantity-options [])
(var out-of-stock? false)
(let [piece? (= product.packaging :piece)]
(if (< 0 product.stock)
(each [_ q (ipairs (quantity-steps product.stock (if piece? 1 50)))]
(table.insert
quantity-options
(HTML
[:option (fn [] {:value q
:selected (= in-basket-quantity q)})
(.. (lib.format-price (* product.price-per q))
"₽ за " q (if piece? " шт." " гр."))])))
(do
(table.insert quantity-options (HTML [:option {:value "0"} "Товар закончился"]))
(set out-of-stock? true))))
(local disabled (or out-of-stock? in-basket-quantity))
(HTML
[:form {:method "POST" :action "/shop/cart/add"
:class (.. "d-flex gap-0-5 " classes)}
[:input {:type "hidden" :name "name" :value product.name}]
[:input {:type "hidden" :name "redirect-url" :value redirect-url}]
[:select (fn [] {:name "quantity" :disabled disabled})
(table.concat quantity-options)]
[:button (fn [] {:type "submit" :disabled disabled})
(if in-basket-quantity "В корзине" "Добавить")]]))
(fn contact-block []
(HTML
[:section {}
[:h2 {} "Как связаться"]
[:p {}
"Телеграм: "
[:a {:href "https://t.me/whitetoadvlad"} "@whitetoadvlad"]
[:br {}]
"Почта: "
[:a {:href "mailto:vlad@whitetoad.ru"} "vlad@whitetoad.ru"]]]))
(fn address-block []
(HTML
[:section {}
[:h2 {} "Адрес"]
[:p {} [:NO-ESCAPE
;; FIXME: lib.improve-typography is too slow for this text for
;; some reason
"г. Омск, ул. Пушкина, д. 133/9, этаж 2. Вход
с крыльца Магнита, дверь слева, домофон 4. Дверь
в офисе узнаете по нашему логотипу."]]]))
(fn conditions-block []
"")
;; (HTML
;; [:section {}
;; [:h2 {} "Условия"]
;; [:p {}
;; (lib.improve-typography
;; (.. "Доставка по Омску Яндекс-курьером.
;; Доставка по России обговаривается отдельно."))]]))
(fn order-lines [order-lines]
(HTML
[:div {:class "order mb-0-5"}
(table.concat
(icollect [_ item (ipairs order-lines)]
(let [item-link (.. "/shop/" item.name)]
(HTML
[:div {:class "order-line"}
[:div {:class "order-line-image"}
[:a {:href item-link :style "font-size: 0;"}
[:img {:src (.. "/static/files/" item.image1 "-thumbnail.jpg")
:alt item.title}]]]
[:div {}
[:a {:href item-link :class "order-line-title"} item.title]
[:div {:class "order-line-price"}
(if (= item.packaging :piece)
(.. (lib.format-price (* item.price-per item.quantity))
"₽ за " item.quantity " шт.")
(.. (lib.format-price (* item.price-per item.quantity))
"₽ за " item.quantity " гр."))]
[:em {:class "font-size-0-875"}
(dicts.label dicts.product-type item.type)]]]))))]))
(fn order-state [state]
(HTML [:strong
{:class (.. "order-state order-state-" state)}
(dicts.label dicts.order-state state)]))
(fn cookies-agreement []
(HTML
[:div {:class "cookies-agreement"}
[:span {:class "mr-0-5"}
"Продолжая использовать сайт, вы даете согласие на использование куки
для сохранения вашей корзины."]
[:button {:type "button"
:onclick
(.. "document.cookie='agreed-to-cookies=true;path=/shop;Max-Age=34560000';"
"this.parentElement.remove();")}
"Хорошо"]]))
{: base
: header
: basket-item
: basket
: product-overview
: add-to-basket-form
: order-lines
: order-state
: contact-block
: address-block
: conditions-block
: cookies-agreement}