(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. "
"Посещение по предварительной договоренности."))}]
(if title
(HTML [:meta {:property "og:title" :content title}])
"")
(if description
(HTML [:meta {:property "og:description" :content description}])
"")
[:meta {:property "og:type" :content "website"}]
[:meta {:property "og:image"
:content "https://whitetoad.ru/static/og:image.png"}]
[:meta {:property "twitter:image"
:content "https://whitetoad.ru/static/og:image.png"}]
[:meta {:property "vk:image"
:content "https://whitetoad.ru/static/og:image.png"}]
[:meta {:property "og:image:width" :content "1200"}]
[:meta {:property "og:image:height" :content "630"}]
[:meta {:property "twitter:card" :content "summary_large_image"}]
[:meta {:property "og:locale" :content "ru_RU"}]
[: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 logo-bg"}
(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 "/about"
:class (if (= current-path "/about") "active" "")}
"о нас"]
[:span {} "/"]
[:a {:href "https://t.me/whitetoadtea"} "телеграм"]]]))
(fn basket-item [item redirect-url]
(local item-link (.. "/shop/" item.name "#content"))
(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 {:class "with-backdrop"}
[: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) "₽")]]
(if (~= redirect-url "/shop/order")
(HTML [:a {:href "/shop/order#content"} "Оформить заказ ⟶"])
"")])))
(fn product-overview [product classes]
(local classes (or classes ""))
(HTML
[:div {:class classes :style "font-style: italic"}
[:span {:data-parser "type"}
(or (dicts.label dicts.product-type product.type) product.type) ", "]
(if (not (lib.empty? product.year))
(HTML [:span {:data-parser "year"}
[:NO-ESCAPE (.. product.year " год, ")]])
"")
(if (not (lib.empty? product.volume))
(HTML [:span {:data-parser "volume"}
[:NO-ESCAPE (.. product.volume " мл., ")]])
"")
(if (not (lib.empty? product.region)) (.. product.region ", ") "")
(if (= product.packaging "piece")
(HTML [:strong {:data-parser "price"}
(lib.format-price product.price-per) "₽"])
(HTML [:span {}
[:strong {:data-parser "price"}
[: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 {}
[:strong {:class "mb-0-5"} "Телеграм:"]
[:a {:href "https://t.me/whitetoadvlad"} "@whitetoadvlad"]
[:br]
[:strong {} "E-mail:"]
[:a {:href "mailto:vlad@whitetoad.ru"} "vlad@whitetoad.ru"]]
[:p {}
"ИП Горенкин Владислав Константинович"
[:br]
[:strong {:class "mb-0-5"} "ИНН:"] "550414799930"
[:br]
[:strong {} "ОГРН:"] "325554300088182"]]))
(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. Дверь
в офисе узнаете по нашему логотипу. "]
[:strong {} [:NO-ESCAPE "Посещение только по предварительной
договоренности!"]]]]))
(fn conditions-block []
(HTML
[:section {}
[:h2 {} "Условия"]
[:p {:class "mb-0-5"} "После подтверждения заказа мы с вами свяжемся для уточнения
подробностей и оплаты. Оплата по QR-коду."]
[:p {}
"Самовывоз из чайной. Доставка по Омску и России обговаривается
после оформления заказа."]]))
(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 "#content")]
(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}