summaryrefslogtreecommitdiff
path: root/pages/shop/index.fnl
blob: 29d93b6a6ef22e591f492d878766aa2deea4f2ae (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
(import-macros {:compile-html HTML} :macros)
(local lib (require :lib))
(local shop (require :shop))
(local dicts (require :dicts))
(local templates (require :templates))

(fn all-products [db filters authenticated?]
  (local where-stmts [])
  (local where-args [])
  (var has-search-query? false)

  (when (not authenticated?)
    (table.insert where-stmts "products.published = true"))

  (when filters.type
    (table.insert where-stmts "products.type = ?")
    (table.insert where-args filters.type))

  (when filters.search
    (table.insert where-stmts "products_search MATCH ?")
    (table.insert where-args filters.search)
    (set has-search-query? true))

  (local where
    (if (< 0 (# where-stmts))
      (.. "WHERE " (table.concat where-stmts " AND\n"))
      ""))

  (local from-sql
    (if has-search-query?
      "products_search INNER JOIN products ON products_search.name = products.name"
      "products"))

  (_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.volume,
                products.stock,
                products.packaging,
                products.type,
                products.region,
                products.image1,
                products.image2,
                products.image3,
                products.image4,
                products.image5
        FROM " from-sql " "
        where
        " ORDER BY products.position ASC,
                   products.creation_time DESC"
                   (if has-search-query? ", rank" ""))
      where-args)))

(fn item-template [product basket]
  (local item-url (.. "/shop/" product.name))
  (local images [])
  (for [i 2 5]
    (table.insert images (. product (.. "image" i))))

  (HTML
   [: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
        (let [without-videos
               (icollect [_ v (ipairs images)]
                 (if (lib.ends-with? (_G.must (luna.utf8.lower v)) ".webm")
                   nil
                   v))]
          (icollect [idx image (ipairs without-videos)]
            (HTML
              [:img {:class "shop-item-img" :src (.. "/static/files/" image "-thumbnail.jpg")
                     :loading "lazy"
                     :style (.. "z-index: " (+ idx 2) ";"
                                "width: calc(100% / " (# without-videos) ");"
                                "left: calc(100% / " (# without-videos) " * " (- idx 1) ")")}]))))]]
    [:a {:href item-url}
      [:h3 {:class "shop-item-title"} product.title]]
    [:div {:class "shop-item-price"}
     (templates.add-to-basket-form product basket "" "/shop")]
    (templates.product-overview product "mb-0-25 font-size-0-875")
    [:div {} product.short-description]]))

(fn content [db products filters basket authenticated?]
  [(HTML
    [:aside {}
     (templates.header "/shop" authenticated?)
     (if (< 0 (# basket)) (templates.basket basket "/shop") "")
     [:section {}
       [:h2 {} "Условия"]
       [:p {} ""]]])
   (HTML
    [:div {:class "content"}
     [:div {:class "back"} [:a {:href "/"} "⟵ Обратно на главную"]]
     [:h2 {:class "product-page-title"} "Магазин"]
     (if authenticated?
       (HTML
        [:div {:class "mb-1" :style "margin-top: -0.5rem"}
          [:a {:style "white-space: nowrap"
               :href (.. "/shop/add")} "+ Добавить"]
          [:a {:style "white-space: nowrap; margin-left: 1rem;"
               :href (.. "/shop/order/list")} "☰ Список заказов"]])
       "")
      [:form {:class "d-flex gap-0-5 mb-1"}
       [:input {:name "search" :type "search"
                :placeholder "Поиск" :value (or filters.search "")}]
       [:select {:name "type"}
         [:option {:value ""} "Все товары"]
         (table.concat
           (icollect [_ v (ipairs dicts.product-type)]
             (HTML [:option (fn [] {:value v.value
                                    :selected (= filters.type v.value)})
                     v.label])))]
       [:button {:type "submit"} "Применить"]]
      [:div {:class "shop-items"}
       (if (< 0 (# products))
         (table.concat
           (icollect [_ v (ipairs products)]
             (item-template v basket)))
         (HTML [:em {} "Пока что здесь ничего нет!"]))]])])

(fn render [request db authenticated?]
  (let [order-id (shop.order-id request)
        basket (if order-id (shop.basket db order-id) [])
        type (if (and request.query.type
                      (not (lib.empty? (. request.query.type 1))))
               (. request.query.type 1)
               nil)
        search (if (and request.query.search
                        (not (lib.empty? (. request.query.search 1))))
                 (. request.query.search 1)
                 nil)
        filters { : type : search }
        products (all-products db filters authenticated?)]
    (values 200 {} (templates.base
                     (content db products filters basket authenticated?)))))

{: render}