summaryrefslogtreecommitdiff
path: root/pages/shop/index.fnl
blob: cfddbe7335e0e023986091304254ea0d864e7096 (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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
(import-macros {:compile-html HTML} :macros)
(local lib (require :lib))
(local shop (require :shop))
(local dicts (require :dicts))
(local templates (require :templates))
(local synonyms (require :synonyms))

(local pagination-limit 12)

(local texts {
  :address
    (lib.improve-typography
      "г. Омск, ул. Пушкина, д. 133/9, этаж 2. Вход с крыльца Магнита, дверь
       слева, домофон 4. Дверь в офисе узнаете по нашему логотипу.")})

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

  (when filters.only-published?
    (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 ?")
    (var query (synonyms.replace filters.search))
    (when (not (lib.ends-with? query "*"))
      (set query (.. query "*")))
    (table.insert where-args query)
    (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"))

  {:total
    (. (_G.must
        (luna.db.query db
          (.. "SELECT COUNT(products.ROWID) FROM " from-sql " " where)
          where-args))
       1 1)

   :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" "")
        " LIMIT ? OFFSET ?")
      (lib.concat where-args
                  [pagination-limit
                   (* (- page 1) pagination-limit)])))})

(fn filters-path [page filters]
  (.. "/shop?page=" (tostring page)
      (if filters.type (.. "&type=" filters.type) "")
      (if filters.search (.. "&search=" filters.search) "")))

(fn paginator-template [filters page limit total classes]
  (if (< limit total)
    (HTML
      [:div {:class (.. "paginator " classes)}
        (if (< 1 page)
          (HTML
           [:div {}
            [:a {:href (filters-path (- page 1) filters)}
              "⟵ Предыдущая страница"]])
          "")
        (if (< page (math.ceil (/ total limit)))
          (HTML
           [:div {}
            [:a {:href (filters-path (+ page 1) filters)}
              "Следующая страница ⟶"]])
          "")])
    ""))

(fn item-template [product basket redirect-url]
  (local item-url (.. "/shop/" product.name "#content"))
  (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"
             :data-parser "image"
             :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" :data-parser "title"} product.title]]
    (templates.product-overview product "mb-0-25 font-size-0-875")
    [:div {:class "shop-item-price"}
     (templates.add-to-basket-form product basket "mb-0-5" redirect-url)]
    [:div {:data-parser "description"} product.short-description]]))

(fn content [db products page total filters basket agreed-to-cookies? authenticated?]
  (local redirect-url (filters-path page filters))

  [(HTML
    [:aside {}
     (templates.header "/shop" authenticated?)
     (if (< 0 (# basket)) (templates.basket basket redirect-url) "")
     (templates.address-block)
     (templates.conditions-block)
     (templates.contact-block)])
   (HTML
    [:div {:class "content"}
     (if (not agreed-to-cookies?) (templates.cookies-agreement) "")
     [: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-desktop 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"} "Применить"]]
      (paginator-template filters page pagination-limit total "mb-1")
      [:div {:class "shop-items"}
       (if (< 0 (# products))
         (table.concat
           (icollect [_ v (ipairs products)]
             (item-template v basket redirect-url)))
         (HTML [:em {} "Пока что здесь ничего нет!"]))]
      (paginator-template filters page pagination-limit total "mt-2")])])

(fn render [request db authenticated?]
  (let [agreed-to-cookies? request.cookies.agreed-to-cookies
        order-id request.cookies.order
        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)
        page (if (and request.query.page
                      (not (lib.empty? (. request.query.page 1))))
               (math.max 1 (tonumber (. request.query.page 1)))
               1)
        only-published? (not authenticated?)
        filters {: type : search : page : only-published?}
        {: total : products} (all-products db page filters)]
    (values 200 {}
      (templates.base
        (content db products page total filters basket
                 agreed-to-cookies? authenticated?)
        "Магазин чая"
        "Купить вкусный китайский чай с доставкой по Омску"))))

{: render}