Kgshop commited on
Commit
6a7469c
·
verified ·
1 Parent(s): 1a44449

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +186 -1831
app.py CHANGED
@@ -1,1764 +1,100 @@
1
 
 
 
 
 
 
 
 
 
 
 
2
 
3
- from flask import Flask, render_template_string, request, redirect, url_for
4
- import json
5
- import os
6
- import logging
7
- import threading
8
- import time
9
- from datetime import datetime
10
- from huggingface_hub import HfApi, hf_hub_download
11
- from huggingface_hub.utils import RepositoryNotFoundError
12
- from werkzeug.utils import secure_filename
13
-
14
- app = Flask(__name__)
15
- DATA_FILE = 'data_firecollection.json'
16
-
17
- # Настройки Hugging Face
18
- REPO_ID = "Kgshop/Clients2" # Замените, если нужно
19
- HF_TOKEN_WRITE = os.getenv("HF_TOKEN")
20
- HF_TOKEN_READ = os.getenv("HF_TOKEN_READ")
21
-
22
- # Настройка логирования
23
- logging.basicConfig(level=logging.DEBUG)
24
-
25
- def load_data():
26
- try:
27
- download_db_from_hf()
28
- with open(DATA_FILE, 'r', encoding='utf-8') as file:
29
- data = json.load(file)
30
- logging.info("Данные успешно загружены из JSON")
31
- if not isinstance(data, dict) or 'products' not in data or 'categories' not in data:
32
- return {'products': [], 'categories': []} # Corrected: return empty dict with lists
33
- return data
34
- except FileNotFoundError:
35
- logging.warning("Локальный файл базы данных не найден после скачивания.")
36
- return {'products': [], 'categories': []}
37
- except json.JSONDecodeError:
38
- logging.error("Ошибка: Невозможно декодировать JSON файл.")
39
- return {'products': [], 'categories': []}
40
- except RepositoryNotFoundError:
41
- logging.error("Репозиторий не найден. Создание локальной базы данных.")
42
- return {'products': [], 'categories': []}
43
- except Exception as e:
44
- logging.error(f"Произошла ошибка при загрузке данных: {e}")
45
- return {'products': [], 'categories': []}
46
-
47
- def save_data(data):
48
- try:
49
- with open(DATA_FILE, 'w', encoding='utf-8') as file:
50
- json.dump(data, file, ensure_ascii=False, indent=4)
51
- logging.info("Данные успешно сохранены в JSON")
52
- upload_db_to_hf()
53
- except Exception as e:
54
- logging.error(f"Ошибка при сохранении данных: {e}")
55
- raise
56
-
57
- def upload_db_to_hf():
58
- try:
59
- api = HfApi()
60
- api.upload_file(
61
- path_or_fileobj=DATA_FILE,
62
- path_in_repo=DATA_FILE,
63
- repo_id=REPO_ID,
64
- repo_type="dataset",
65
- token=HF_TOKEN_WRITE,
66
- commit_message=f"Автоматическое резервное копирование базы данных {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
67
- )
68
- logging.info("Резервная копия JSON базы успешно загружена на Hugging Face.")
69
- except Exception as e:
70
- logging.error(f"Ошибка при загрузке резервной копии: {e}")
71
-
72
- def download_db_from_hf():
73
- try:
74
- hf_hub_download(
75
- repo_id=REPO_ID,
76
- filename=DATA_FILE,
77
- repo_type="dataset",
78
- token=HF_TOKEN_READ,
79
- local_dir=".",
80
- local_dir_use_symlinks=False
81
- )
82
- logging.info("JSON база успешно скачана из Hugging Face.")
83
- except RepositoryNotFoundError as e:
84
- logging.error(f"Репозиторий не найден: {e}")
85
- raise
86
- except Exception as e:
87
- logging.error(f"Ошибка при скачивании JSON базы: {e}")
88
- raise
89
-
90
- def periodic_backup():
91
- while True:
92
- upload_db_to_hf()
93
- time.sleep(800)
94
-
95
- @app.route('/')
96
- def catalog():
97
- data = load_data()
98
- products = data['products']
99
- # categories = data['categories'] Removed categories from main page
100
-
101
- catalog_html = '''
102
- <!DOCTYPE html>
103
- <html lang="ru">
104
- <head>
105
- <meta charset="UTF-8">
106
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
107
- <title>Fire collection</title>
108
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
109
- <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
110
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/Swiper/10.2.0/swiper-bundle.min.css">
111
- <style>
112
- /* ... (your existing styles) ... */
113
- * {
114
- margin: 0;
115
- padding: 0;
116
- box-sizing: border-box;
117
- }
118
- body {
119
- font-family: 'Roboto', sans-serif;
120
- background: linear-gradient(135deg, #f5f7fa, #e4e7eb);
121
- color: #333;
122
- line-height: 1.6;
123
- transition: background 0.3s, color 0.3s;
124
- padding-bottom: 60px;
125
- }
126
- body.dark-mode {
127
- background: linear-gradient(135deg, #1a1a2e, #2a2a3e);
128
- color: #e0e0e0;
129
- }
130
- .container {
131
- max-width: 1400px;
132
- margin: 0 auto;
133
- padding: 20px;
134
- }
135
- .header {
136
- display: flex;
137
- justify-content: space-between;
138
- align-items: center;
139
- padding: 15px 0;
140
- border-bottom: 1px solid rgba(0, 0, 0, 0.1);
141
- }
142
- .header h1 {
143
- font-size: 2rem;
144
- font-weight: 700;
145
- letter-spacing: 1px;
146
- background: linear-gradient(90deg, #526df2, #7a8ff5);
147
- -webkit-background-clip: text;
148
- -webkit-text-fill-color: transparent;
149
- }
150
- .theme-toggle {
151
- background: none;
152
- border: none;
153
- font-size: 1.5rem;
154
- cursor: pointer;
155
- color: #666;
156
- transition: color 0.3s ease;
157
- }
158
- .theme-toggle:hover {
159
- color: #526df2;
160
- }
161
- body.dark-mode .theme-toggle {
162
- color: #bbb;
163
- }
164
- /* Removed .filters-container */
165
- .search-container {
166
- margin: 20px 0;
167
- text-align: center;
168
- }
169
- #search-input {
170
- width: 90%;
171
- max-width: 600px;
172
- padding: 12px 18px;
173
- font-size: 1rem;
174
- border: 1px solid rgba(0, 0, 0, 0.1);
175
- border-radius: 25px;
176
- outline: none;
177
- box-shadow: 0 2px 5px rgba(0,0,0,0.05);
178
- transition: all 0.3s ease;
179
- }
180
- #search-input:focus {
181
- border-color: #526df2;
182
- box-shadow: 0 4px 15px rgba(82, 109, 242, 0.2);
183
- }
184
- .products-grid {
185
- display: grid;
186
- grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
187
- gap: 20px;
188
- padding: 10px;
189
- }
190
- /* ... (rest of your product and modal styles) ... */
191
- .product {
192
- background: rgba(255, 255, 255, 0.8);
193
- border: 1px solid rgba(0, 0, 0, 0.05);
194
- border-radius: 15px;
195
- padding: 15px;
196
- box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05);
197
- transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.3s ease, background 0.3s ease;
198
- cursor: pointer;
199
- position: relative;
200
- overflow: hidden;
201
- }
202
- .product:hover {
203
- transform: translateY(-5px);
204
- box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
205
- background: rgba(255, 255, 255, 0.9);
206
- }
207
- body.dark-mode .product {
208
- background: rgba(42, 42, 62, 0.8);
209
- border-color: rgba(255, 255, 255, 0.05);
210
- color: #e0e0e0;
211
- }
212
- body.dark-mode .product:hover {
213
- background: rgba(42, 42, 62, 0.9);
214
- box-shadow: 0 8px 25px rgba(255, 255, 255, 0.1);
215
- }
216
- .product-image {
217
- width: 100%;
218
- aspect-ratio: 1;
219
- background-color: #fff;
220
- border-radius: 10px;
221
- overflow: hidden;
222
- display: flex;
223
- justify-content: center;
224
- align-items: center;
225
- transition: transform 0.3s ease;
226
- }
227
- body.dark-mode .product-image {
228
- background-color: #333;
229
- }
230
- .product-image img {
231
- max-width: 100%;
232
- max-height: 100%;
233
- object-fit: contain;
234
- transition: transform 0.3s ease;
235
- }
236
- .product-image img:hover {
237
- transform: scale(1.05);
238
- }
239
- .product h2 {
240
- font-size: 1.1rem;
241
- font-weight: 500;
242
- margin: 10px 0;
243
- text-align: center;
244
- white-space: nowrap;
245
- overflow: hidden;
246
- text-overflow: ellipsis;
247
- }
248
- .product-price {
249
- font-size: 1.2rem;
250
- color: #e63946;
251
- font-weight: 700;
252
- text-align: center;
253
- margin: 5px 0;
254
- }
255
- .product-price .wholesale {
256
- font-size: 0.9rem;
257
- color: #526df2;
258
- font-weight: 500;
259
- display: block;
260
- margin-top: 5px;
261
- }
262
- .product-price .discount {
263
- font-size: 0.9rem;
264
- color: #2ecc71;
265
- font-weight: 500;
266
- display: block;
267
- margin-top: 5px;
268
- }
269
- .product-description {
270
- font-size: 0.85rem;
271
- color: #666;
272
- text-align: center;
273
- margin-bottom: 15px;
274
- overflow: hidden;
275
- text-overflow: ellipsis;
276
- white-space: nowrap;
277
- }
278
- body.dark-mode .product-description {
279
- color: #bbb;
280
- }
281
- .product-button {
282
- display: block;
283
- width: 100%;
284
- padding: 10px;
285
- border: none;
286
- border-radius: 25px;
287
- background-color: #526df2;
288
- color: white;
289
- font-size: 0.9rem;
290
- font-weight: 500;
291
- cursor: pointer;
292
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
293
- margin: 5px 0;
294
- text-align: center;
295
- text-decoration: none;
296
- }
297
- .product-button:hover {
298
- background-color: #3e55d1;
299
- box-shadow: 0 4px 15px rgba(62, 85, 209, 0.4);
300
- transform: translateY(-2px);
301
- }
302
- .add-to-cart {
303
- background-color: #2ecc71;
304
- }
305
- .add-to-cart:hover {
306
- background-color: #27ae60;
307
- box-shadow: 0 4px 15px rgba(46, 204, 113, 0.4);
308
- }
309
- .favorite-button {
310
- position: absolute;
311
- top: 10px;
312
- left: 10px;
313
- background: none;
314
- border: none;
315
- font-size: 1.5rem;
316
- cursor: pointer;
317
- color: #666;
318
- transition: color 0.3s ease;
319
- }
320
- .favorite-button.favorited {
321
- color: #e63946;
322
- }
323
- .favorite-button:hover {
324
- color: #e63946;
325
- }
326
- #cart-button {
327
- position: fixed;
328
- bottom: 80px;
329
- right: 20px;
330
- background-color: #e63946;
331
- color: white;
332
- border: none;
333
- border-radius: 50%;
334
- width: 60px;
335
- height: 60px;
336
- font-size: 1.5rem;
337
- cursor: pointer;
338
- display: none;
339
- box-shadow: 0 4px 15px rgba(230, 57, 70, 0.4);
340
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
341
- z-index: 1000;
342
- }
343
- #cart-button:hover {
344
- transform: scale(1.1);
345
- }
346
- .modal {
347
- display: none;
348
- position: fixed;
349
- z-index: 1001;
350
- left: 0;
351
- top: 0;
352
- width: 100%;
353
- height: 100%;
354
- background-color: rgba(0,0,0,0.5);
355
- backdrop-filter: blur(5px);
356
- }
357
- .modal-content {
358
- background: #fff;
359
- margin: 5% auto;
360
- padding: 20px;
361
- border-radius: 15px;
362
- width: 90%;
363
- max-width: 700px;
364
- box-shadow: 0 10px 30px rgba(0,0,0,0.2);
365
- animation: slideIn 0.3s ease-out;
366
- }
367
- body.dark-mode .modal-content {
368
- background: #2a2a3e;
369
- color: #e0e0e0;
370
- }
371
- @keyframes slideIn {
372
- from { transform: translateY(-50px); opacity: 0; }
373
- to { transform: translateY(0); opacity: 1; }
374
- }
375
- .close {
376
- float: right;
377
- font-size: 1.5rem;
378
- color: #666;
379
- cursor: pointer;
380
- transition: color 0.3s;
381
- }
382
- .close:hover {
383
- color: #333;
384
- }
385
- body.dark-mode .close {
386
- color: #bbb;
387
- }
388
- body.dark-mode .close:hover {
389
- color: #fff;
390
- }
391
- .cart-item {
392
- display: flex;
393
- justify-content: space-between;
394
- align-items: center;
395
- padding: 15px 0;
396
- border-bottom: 1px solid rgba(0, 0, 0, 0.1);
397
- }
398
- body.dark-mode .cart-item {
399
- border-bottom: 1px solid rgba(255, 255, 255, 0.1);
400
- }
401
- .cart-item img {
402
- width: 50px;
403
- height: 50px;
404
- object-fit: contain;
405
- border-radius: 8px;
406
- margin-right: 15px;
407
- }
408
- .quantity-input, .color-select {
409
- width: 100%;
410
- max-width: 150px;
411
- padding: 8px;
412
- border: 1px solid rgba(0, 0, 0, 0.1);
413
- border-radius: 8px;
414
- font-size: 1rem;
415
- margin: 5px 0;
416
- }
417
- .clear-cart {
418
- background-color: #e63946;
419
- }
420
- .clear-cart:hover {
421
- background-color: #d62828;
422
- box-shadow: 0 4px 15px rgba(230, 57, 70, 0.4);
423
- }
424
- .order-button {
425
- background-color: #2ecc71;
426
- }
427
- .order-button:hover {
428
- background-color: #27ae60;
429
- box-shadow: 0 4px 15px rgba(46, 204, 113, 0.4);
430
- }
431
- .navbar {
432
- position: fixed;
433
- bottom: 0;
434
- left: 0;
435
- width: 100%;
436
- background-color: #fff;
437
- box-shadow: 0 -2px 10px rgba(0,0,0,0.1);
438
- display: flex;
439
- justify-content: space-around;
440
- padding: 10px 0;
441
- z-index: 1000;
442
- }
443
- body.dark-mode .navbar {
444
- background-color: #2a2a3e;
445
- }
446
- .navbar a {
447
- text-align: center;
448
- color: #666;
449
- text-decoration: none;
450
- font-size: 0.9rem;
451
- transition: color 0.3s ease;
452
- }
453
- .navbar a.active {
454
- color: #526df2;
455
- }
456
- .navbar a i {
457
- display: block;
458
- font-size: 1.5rem;
459
- margin-bottom: 5px;
460
- }
461
- body.dark-mode .navbar a {
462
- color: #bbb;
463
- }
464
- .navbar a:hover {
465
- color: #526df2;
466
- }
467
- .wholesale-badge {
468
- position: absolute;
469
- top: 10px;
470
- right: 10px;
471
- background-color: #526df2;
472
- color: white;
473
- padding: 5px 10px;
474
- border-radius: 15px;
475
- font-size: 0.75rem;
476
- font-weight: 500;
477
- display: none; /* Hidden */
478
- }
479
- .discount-badge {
480
- position: absolute;
481
- top: 40px;
482
- right: 10px;
483
- background-color: #2ecc71;
484
- color: white;
485
- padding: 5px 10px;
486
- border-radius: 15px;
487
- font-size: 0.75rem;
488
- font-weight: 500;
489
- }
490
-
491
- /* Styles for Address and Schedule */
492
- .info-section {
493
- text-align: center;
494
- margin: 20px 0;
495
- padding: 15px;
496
- background: rgba(255, 255, 255, 0.8);
497
- border-radius: 15px;
498
- box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05);
499
- }
500
- body.dark-mode .info-section {
501
- background: rgba(42, 42, 62, 0.8);
502
- }
503
- .info-section h2 {
504
- font-size: 1.5rem;
505
- font-weight: 500;
506
- margin-bottom: 10px;
507
- color: #526df2;
508
- }
509
- .info-section p {
510
- font-size: 1rem;
511
- color: #666;
512
- }
513
- body.dark-mode .info-section p {
514
- color: #bbb;
515
- }
516
- </style>
517
- </head>
518
- <body>
519
- <div class="container">
520
- <div class="header">
521
- <img src="https://huggingface.co/spaces/Kgshop/Fire-collection/resolve/main/Picsart_25-03-25_11-40-25-730.jpg" alt="Fire collection Logo" style="max-height: 50px;">
522
- <button class="theme-toggle" onclick="toggleTheme()">
523
- <i class="fas fa-moon"></i>
524
- </button>
525
- </div>
526
-
527
- <!-- Address and Schedule Section -->
528
- <div class="info-section">
529
- <h2>Наш адрес</h2>
530
- <p>Рынок Дордой, 0 проход , 2034 контейнер</p>
531
- <h2>График работы</h2>
532
- <p>По адресу: без выходных с 8:00 до 16:00, онлайн: круглосуточно</p>
533
- </div>
534
-
535
- <div class="search-container">
536
- <input type="text" id="search-input" placeholder="Поиск товаров...">
537
- </div>
538
- <div class="products-grid" id="products-grid">
539
- {% for product in products %}
540
- <div class="product"
541
- onclick="openModal({{ loop.index0 }})"
542
- data-name="{{ product['name']|lower }}"
543
- data-description="{{ product['description']|lower }}"
544
- data-category="{{ product.get('category', 'Без категории') }}">
545
- <button class="favorite-button" onclick="event.stopPropagation(); toggleFavorite({{ loop.index0 }})">
546
- <i class="fas fa-heart"></i>
547
- </button>
548
- {% if product.get('photos') and product['photos']|length > 0 %}
549
- <div class="product-image">
550
- <img src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/photos/{{ product['photos'][0] }}"
551
- alt="{{ product['name'] }}"
552
- loading="lazy">
553
- </div>
554
- {% endif %}
555
- {% if product.get('wholesale_price') and product.get('min_wholesale') %}
556
- <span class="wholesale-badge">Опт от {{ product['min_wholesale'] }}</span>
557
- {% endif %}
558
- {% if product.get('discount') %}
559
- <span class="discount-badge">Скидка {{ product['discount'] }}%</span>
560
- {% endif %}
561
- <h2>{{ product['name'] }}</h2>
562
- <div class="product-price">
563
- {% if product.get('discount') %}
564
- <span style="text-decoration: line-through; color: #666;">{{ product['price'] }} с</span>
565
- <span>{{ (product['price'] * (1 - product['discount'] / 100))|round(2) }} с</span>
566
- <span class="discount">Скидка: {{ product['discount'] }}%</span>
567
- {% else %}
568
- <span>{{ product['price'] }} с</span>
569
- {% endif %}
570
- </div>
571
- <p class="product-description">{{ product['description'][:50] }}{% if product['description']|length > 50 %}...{% endif %}</p>
572
- <button class="product-button add-to-cart" onclick="event.stopPropagation(); openQuantityModal({{ loop.index0 }})">В корзину</button>
573
- </div>
574
- {% endfor %}
575
- </div>
576
- </div>
577
-
578
- <!-- Product Modal -->
579
- <div id="productModal" class="modal">
580
- <div class="modal-content">
581
- <span class="close" onclick="closeModal('productModal')">×</span>
582
- <div id="modalContent"></div>
583
- </div>
584
- </div>
585
-
586
- <!-- Quantity and Color Modal -->
587
- <div id="quantityModal" class="modal">
588
- <div class="modal-content">
589
- <span class="close" onclick="closeModal('quantityModal')">×</span>
590
- <h2>Укажите количество и цвет</h2>
591
- <input type="number" id="quantityInput" class="quantity-input" min="1" value="1">
592
- <select id="colorSelect" class="color-select"></select>
593
- <button class="product-button" onclick="confirmAddToCart()">Добавить</button>
594
- </div>
595
- </div>
596
-
597
- <!-- Cart Modal -->
598
- <div id="cartModal" class="modal">
599
- <div class="modal-content">
600
- <span class="close" onclick="closeModal('cartModal')">×</span>
601
- <h2>Корзина</h2>
602
- <div id="cartContent"></div>
603
- <div style="margin-top: 20px; text-align: right;">
604
- <strong>Итого: <span id="cartTotal">0</span> с</strong>
605
- <button class="product-button clear-cart" onclick="clearCart()">Очистить</button>
606
- <button class="product-button order-button" onclick="orderViaWhatsApp()">Заказать</button>
607
- </div>
608
- </div>
609
- </div>
610
-
611
- <button id="cart-button" onclick="openCartModal()">🛒</button>
612
-
613
- <!-- Navigation Bar -->
614
- <div class="navbar">
615
- <a href="/" class="active">
616
- <i class="fas fa-home"></i>
617
- Главная
618
- </a>
619
- <a href="/categories">
620
- <i class="fas fa-list"></i>
621
- Каталог
622
- </a>
623
- <a href="/favorites">
624
- <i class="fas fa-heart"></i>
625
- Избранное
626
- </a>
627
- <a href="/discounts">
628
- <i class="fas fa-tag"></i>
629
- Скидки
630
- </a>
631
- <a href="https://api.whatsapp.com/send?phone=996700666692">
632
- <i class="fab fa-whatsapp"></i>
633
- WhatsApp
634
- </a>
635
- </div>
636
-
637
- <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
638
- <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.5.3/dist/umd/popper.min.js"></script>
639
- <script src="https://cdnjs.cloudflare.com/ajax/libs/Swiper/10.2.0/swiper-bundle.min.js"></script>
640
- <script>
641
- const products = {{ products|tojson }};
642
- let selectedProductIndex = null;
643
-
644
- function toggleTheme() {
645
- document.body.classList.toggle('dark-mode');
646
- const icon = document.querySelector('.theme-toggle i');
647
- icon.classList.toggle('fa-moon');
648
- icon.classList.toggle('fa-sun');
649
- localStorage.setItem('theme', document.body.classList.contains('dark-mode') ? 'dark' : 'light');
650
- }
651
-
652
- if (localStorage.getItem('theme') === 'dark') {
653
- document.body.classList.add('dark-mode');
654
- document.querySelector('.theme-toggle i').classList.replace('fa-moon', 'fa-sun');
655
- }
656
-
657
- function openModal(index) {
658
- loadProductDetails(index);
659
- document.getElementById('productModal').style.display = "block";
660
- }
661
-
662
- function closeModal(modalId) {
663
- document.getElementById(modalId).style.display = "none";
664
- }
665
-
666
- function loadProductDetails(index) {
667
- fetch('/product/' + index)
668
- .then(response => response.text())
669
- .then(data => {
670
- document.getElementById('modalContent').innerHTML = data;
671
- initializeSwiper();
672
- })
673
- .catch(error => console.error('Ошибка:', error));
674
- }
675
-
676
- function initializeSwiper() {
677
- new Swiper('.swiper-container', {
678
- slidesPerView: 1,
679
- spaceBetween: 20,
680
- loop: true,
681
- grabCursor: true,
682
- pagination: { el: '.swiper-pagination', clickable: true },
683
- navigation: { nextEl: '.swiper-button-next', prevEl: '.swiper-button-prev' },
684
- zoom: { maxRatio: 3 }
685
- });
686
- }
687
-
688
- function openQuantityModal(index) {
689
- selectedProductIndex = index;
690
- const product = products[index];
691
- const colorSelect = document.getElementById('colorSelect');
692
- colorSelect.innerHTML = '';
693
- if (product.colors && product.colors.length > 0) {
694
- product.colors.forEach(color => {
695
- const option = document.createElement('option');
696
- option.value = color;
697
- option.text = color;
698
- colorSelect.appendChild(option);
699
- });
700
- } else {
701
- const option = document.createElement('option');
702
- option.value = 'Нет цвета';
703
- option.text = 'Нет цвета';
704
- colorSelect.appendChild(option);
705
- }
706
- document.getElementById('quantityModal').style.display = 'block';
707
- document.getElementById('quantityInput').value = 1;
708
- }
709
-
710
- function confirmAddToCart() {
711
- if (selectedProductIndex === null) return;
712
- const quantity = parseInt(document.getElementById('quantityInput').value) || 1;
713
- const color = document.getElementById('colorSelect').value;
714
- if (quantity <= 0) {
715
- alert("Укажите количество больше 0");
716
- return;
717
- }
718
- let cart = JSON.parse(localStorage.getItem('cart') || '[]');
719
- const product = products[selectedProductIndex];
720
- const cartItemId = `${product.name}-${color}`;
721
- const existingItem = cart.find(item => item.id === cartItemId);
722
-
723
- let priceToUse = product.price;
724
- if (product.discount) {
725
- priceToUse = product.price * (1 - product.discount / 100);
726
- }
727
-
728
-
729
- if (existingItem) {
730
- existingItem.quantity += quantity;
731
- existingItem.price = (product.discount ? product.price * (1 - product.discount / 100) : product.price);
732
- } else {
733
- cart.push({
734
- id: cartItemId,
735
- name: product.name,
736
- price: priceToUse,
737
- retail_price: product.price,
738
- wholesale_price: product.wholesale_price,
739
- min_wholesale: product.min_wholesale,
740
- discount: product.discount,
741
- photo: product.photos && product.photos.length > 0 ? product.photos[0] : '',
742
- quantity: quantity,
743
- color: color
744
- });
745
- }
746
-
747
- localStorage.setItem('cart', JSON.stringify(cart));
748
- closeModal('quantityModal');
749
- updateCartButton();
750
- }
751
-
752
- function updateCartButton() {
753
- const cart = JSON.parse(localStorage.getItem('cart') || '[]');
754
- document.getElementById('cart-button').style.display = cart.length > 0 ? 'block' : 'none';
755
- }
756
-
757
- function openCartModal() {
758
- const cart = JSON.parse(localStorage.getItem('cart') || '[]');
759
- const cartContent = document.getElementById('cartContent');
760
- let total = 0;
761
-
762
- cartContent.innerHTML = cart.length === 0 ? '<p>Корзина пуста</p>' : cart.map(item => {
763
- const itemTotal = item.price * item.quantity;
764
- total += itemTotal;
765
- return `
766
- <div class="cart-item">
767
- <div style="display: flex; align-items: center;">
768
- ${item.photo ? `<img src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/photos/${item.photo}" alt="${item.name}">` : ''}
769
- <div>
770
- <strong>${item.name}</strong>
771
- <p>${item.price} с × ${item.quantity} (Цвет: ${item.color})</p>
772
- </div>
773
- </div>
774
- <span>${itemTotal} с</span>
775
- </div>
776
- `;
777
- }).join('');
778
-
779
- document.getElementById('cartTotal').textContent = total;
780
- document.getElementById('cartModal').style.display = 'block';
781
- }
782
-
783
- function orderViaWhatsApp() {
784
- const cart = JSON.parse(localStorage.getItem('cart') || '[]');
785
- if (cart.length === 0) {
786
- alert("Корзина пуста!");
787
- return;
788
- }
789
- let total = 0;
790
- let orderText = "Заказ:%0A";
791
- cart.forEach((item, index) => {
792
- const itemTotal = item.price * item.quantity;
793
- total += itemTotal;
794
- orderText += `${index + 1}. ${item.name} - ${item.price} с × ${item.quantity} (Цвет: ${item.color})%0A`;
795
- });
796
- orderText += `Итого: ${total} с`;
797
- window.open('https://api.whatsapp.com/send?phone=996700666692&text=\${orderText}', '_blank');
798
- }
799
-
800
- function clearCart() {
801
- localStorage.removeItem('cart');
802
- closeModal('cartModal');
803
- updateCartButton();
804
- }
805
-
806
- function toggleFavorite(index) {
807
- let favorites = JSON.parse(localStorage.getItem('favorites') || '[]');
808
- const productId = index.toString();
809
- const favoriteButton = document.querySelector(`.favorite-button[onclick="event.stopPropagation(); toggleFavorite(${index})"]`);
810
- if (favorites.includes(productId)) {
811
- favorites = favorites.filter(id => id !== productId);
812
- favoriteButton.classList.remove('favorited');
813
- } else {
814
- favorites.push(productId);
815
- favoriteButton.classList.add('favorited');
816
- }
817
- localStorage.setItem('favorites', JSON.stringify(favorites));
818
- }
819
-
820
- function loadFavorites() {
821
- const favorites = JSON.parse(localStorage.getItem('favorites') || '[]');
822
- document.querySelectorAll('.favorite-button').forEach(button => {
823
- const index = button.getAttribute('onclick').match(/\d+/)[0];
824
- if (favorites.includes(index)) {
825
- button.classList.add('favorited');
826
- }
827
- });
828
- }
829
-
830
- // Removed openAddressModal function
831
-
832
- window.onclick = function(event) {
833
- if (event.target.className === 'modal') event.target.style.display = "none";
834
- }
835
-
836
- document.getElementById('search-input').addEventListener('input', filterProducts);
837
- // Removed category filter event listeners
838
-
839
- function filterProducts() {
840
- const searchTerm = document.getElementById('search-input').value.toLowerCase();
841
- // Removed activeCategory check
842
- document.querySelectorAll('.product').forEach(product => {
843
- const name = product.getAttribute('data-name');
844
- const description = product.getAttribute('data-description');
845
- // const category = product.getAttribute('data-category'); // Kept for potential use
846
- const matchesSearch = name.includes(searchTerm) || description.includes(searchTerm);
847
- // Removed category matching
848
- product.style.display = matchesSearch ? 'block' : 'none'; //Simplified condition
849
- });
850
- }
851
-
852
- updateCartButton();
853
- loadFavorites();
854
- </script>
855
- </body>
856
- </html>
857
- '''
858
- return render_template_string(catalog_html, products=products, repo_id=REPO_ID)
859
-
860
- @app.route('/categories')
861
- def categories_page():
862
- data = load_data()
863
- categories = data['categories']
864
-
865
- categories_html = '''
866
- <!DOCTYPE html>
867
- <html lang="ru">
868
- <head>
869
- <meta charset="UTF-8">
870
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
871
- <title>Fire collection</title>
872
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
873
- <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
874
- <style>
875
- * {
876
- margin: 0;
877
- padding: 0;
878
- box-sizing: border-box;
879
- }
880
- body {
881
- font-family: 'Roboto', sans-serif;
882
- background: linear-gradient(135deg, #f5f7fa, #e4e7eb);
883
- color: #333;
884
- line-height: 1.6;
885
- transition: background 0.3s, color 0.3s;
886
- padding-bottom: 60px;
887
- }
888
- body.dark-mode {
889
- background: linear-gradient(135deg, #1a1a2e, #2a2a3e);
890
- color: #e0e0e0;
891
- }
892
- .container {
893
- max-width: 1400px;
894
- margin: 0 auto;
895
- padding: 20px;
896
- }
897
- .header {
898
- display: flex;
899
- justify-content: space-between;
900
- align-items: center;
901
- padding: 15px 0;
902
- border-bottom: 1px solid rgba(0, 0, 0, 0.1);
903
- }
904
- .header h1 {
905
- font-size: 2rem;
906
- font-weight: 700;
907
- letter-spacing: 1px;
908
- background: linear-gradient(90deg, #526df2, #7a8ff5);
909
- -webkit-background-clip: text;
910
- -webkit-text-fill-color: transparent;
911
- }
912
- .theme-toggle {
913
- background: none;
914
- border: none;
915
- font-size: 1.5rem;
916
- cursor: pointer;
917
- color: #666;
918
- transition: color 0.3s ease;
919
- }
920
- .theme-toggle:hover {
921
- color: #526df2;
922
- }
923
- body.dark-mode .theme-toggle {
924
- color: #bbb;
925
- }
926
- .categories-grid {
927
- display: grid;
928
- grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
929
- gap: 20px;
930
- padding: 10px;
931
- }
932
- .category-item {
933
- background: rgba(255, 255, 255, 0.8);
934
- border: 1px solid rgba(0, 0, 0, 0.05);
935
- border-radius: 15px;
936
- padding: 20px;
937
- text-align: center;
938
- box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05);
939
- transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.3s ease, background 0.3s ease;
940
- text-decoration: none;
941
- color: #333;
942
- }
943
- body.dark-mode .category-item {
944
- background: rgba(42, 42, 62, 0.8);
945
- border-color: rgba(255, 255, 255, 0.05);
946
- color: #e0e0e0;
947
- }
948
- .category-item:hover {
949
- transform: translateY(-5px);
950
- box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
951
- background: rgba(255, 255, 255, 0.9);
952
- background-color: #526df2;
953
- color: white;
954
- }
955
- body.dark-mode .category-item:hover {
956
- background: rgba(42, 42, 62, 0.9);
957
- box-shadow: 0 8px 25px rgba(255, 255, 255, 0.1);
958
- }
959
- .category-item h2 {
960
- font-size: 1.2rem;
961
- font-weight: 500;
962
- }
963
- .navbar {
964
- position: fixed;
965
- bottom: 0;
966
- left: 0;
967
- width: 100%;
968
- background-color: #fff;
969
- box-shadow: 0 -2px 10px rgba(0,0,0,0.1);
970
- display: flex;
971
- justify-content: space-around;
972
- padding: 10px 0;
973
- z-index: 1000;
974
- }
975
- body.dark-mode .navbar {
976
- background-color: #2a2a3e;
977
- }
978
- .navbar a {
979
- text-align: center;
980
- color: #666;
981
- text-decoration: none;
982
- font-size: 0.9rem;
983
- transition: color 0.3s ease;
984
- }
985
- .navbar a.active {
986
- color: #526df2;
987
- }
988
- .navbar a i {
989
- display: block;
990
- font-size: 1.5rem;
991
- margin-bottom: 5px;
992
- }
993
- body.dark-mode .navbar a {
994
- color: #bbb;
995
- }
996
- .navbar a:hover {
997
- color: #526df2;
998
- }
999
- </style>
1000
- </head>
1001
- <body>
1002
- <div class="container">
1003
- <div class="header">
1004
- <img src="https://huggingface.co/spaces/Kgshop/Fire-collection/resolve/main/Picsart_25-03-25_11-40-25-730.jpg" alt="Fire collection Logo" style="max-height: 50px;">
1005
- <button class="theme-toggle" onclick="toggleTheme()">
1006
- <i class="fas fa-moon"></i>
1007
- </button>
1008
- </div>
1009
- <div class="categories-grid">
1010
- {% for category in categories %}
1011
- <a href="{{ url_for('category_products', category=category) }}" class="category-item">
1012
- <h2>{{ category }}</h2>
1013
- </a>
1014
- {% endfor %}
1015
- </div>
1016
- </div>
1017
-
1018
- <div class="navbar">
1019
- <a href="/">
1020
- <i class="fas fa-home"></i>
1021
- Главная
1022
- </a>
1023
- <a href="/categories" class="active">
1024
- <i class="fas fa-list"></i>
1025
- Каталог
1026
- </a>
1027
- <a href="/favorites">
1028
- <i class="fas fa-heart"></i>
1029
- Избранное
1030
- </a>
1031
- <a href="/discounts">
1032
- <i class="fas fa-tag"></i>
1033
- Скидки
1034
- </a>
1035
- <a href="https://api.whatsapp.com/send?phone=996700666692">
1036
- <i class="fab fa-whatsapp"></i>
1037
- WhatsApp
1038
- </a>
1039
- </div>
1040
-
1041
- <script>
1042
- function toggleTheme() {
1043
- document.body.classList.toggle('dark-mode');
1044
- const icon = document.querySelector('.theme-toggle i');
1045
- icon.classList.toggle('fa-moon');
1046
- icon.classList.toggle('fa-sun');
1047
- localStorage.setItem('theme', document.body.classList.contains('dark-mode') ? 'dark' : 'light');
1048
- }
1049
-
1050
- if (localStorage.getItem('theme') === 'dark') {
1051
- document.body.classList.add('dark-mode');
1052
- document.querySelector('.theme-toggle i').classList.replace('fa-moon', 'fa-sun');
1053
- }
1054
-
1055
- window.onclick = function(event) {
1056
- if (event.target.className === 'modal') event.target.style.display = "none";
1057
- }
1058
- </script>
1059
- </body>
1060
- </html>
1061
- '''
1062
- return render_template_string(categories_html, categories=categories)
1063
-
1064
- @app.route('/category/<category>')
1065
- def category_products(category):
1066
- data = load_data()
1067
- products = [p for p in data['products'] if p.get('category') == category]
1068
- # categories = data['categories'] # No longer needed here
1069
-
1070
- category_html = '''
1071
- <!DOCTYPE html>
1072
- <html lang="ru">
1073
- <head>
1074
- <meta charset="UTF-8">
1075
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
1076
- <title>Fire collection</title>
1077
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
1078
- <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
1079
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/Swiper/10.2.0/swiper-bundle.min.css">
1080
- <style>
1081
- /* ... (your existing styles for product display) ... */
1082
- * {
1083
- margin: 0;
1084
- padding: 0;
1085
- box-sizing: border-box;
1086
- }
1087
- body {
1088
- font-family: 'Roboto', sans-serif;
1089
- background: linear-gradient(135deg, #f5f7fa, #e4e7eb);
1090
- color: #333;
1091
- line-height: 1.6;
1092
- transition: background 0.3s, color 0.3s;
1093
- padding-bottom: 60px;
1094
- }
1095
- body.dark-mode {
1096
- background: linear-gradient(135deg, #1a1a2e, #2a2a3e);
1097
- color: #e0e0e0;
1098
- }
1099
- .container {
1100
- max-width: 1400px;
1101
- margin: 0 auto;
1102
- padding: 20px;
1103
- }
1104
- .header {
1105
- display: flex;
1106
- justify-content: space-between;
1107
- align-items: center;
1108
- padding: 15px 0;
1109
- border-bottom: 1px solid rgba(0, 0, 0, 0.1);
1110
- }
1111
- .header h1 {
1112
- font-size: 2rem;
1113
- font-weight: 700;
1114
- letter-spacing: 1px;
1115
- background: linear-gradient(90deg, #526df2, #7a8ff5);
1116
- -webkit-background-clip: text;
1117
- -webkit-text-fill-color: transparent;
1118
- }
1119
- .theme-toggle {
1120
- background: none;
1121
- border: none;
1122
- font-size: 1.5rem;
1123
- cursor: pointer;
1124
- color: #666;
1125
- transition: color 0.3s ease;
1126
- }
1127
- .theme-toggle:hover {
1128
- color: #526df2;
1129
- }
1130
- body.dark-mode .theme-toggle {
1131
- color: #bbb;
1132
- }
1133
- .products-grid {
1134
- display: grid;
1135
- grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
1136
- gap: 20px;
1137
- padding: 10px;
1138
- }
1139
- .product {
1140
- background: rgba(255, 255, 255, 0.8);
1141
- border: 1px solid rgba(0, 0, 0, 0.05);
1142
- border-radius: 15px;
1143
- padding: 15px;
1144
- box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05);
1145
- transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.3s ease, background 0.3s ease;
1146
- cursor: pointer;
1147
- position: relative;
1148
- overflow: hidden;
1149
- }
1150
- .product:hover {
1151
- transform: translateY(-5px);
1152
- box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
1153
- background: rgba(255, 255, 255, 0.9);
1154
- }
1155
- body.dark-mode .product {
1156
- background: rgba(42, 42, 62, 0.8);
1157
- border-color: rgba(255, 255, 255, 0.05);
1158
- color: #e0e0e0;
1159
- }
1160
- body.dark-mode .product:hover {
1161
- background: rgba(42, 42, 62, 0.9);
1162
- box-shadow: 0 8px 25px rgba(255, 255, 255, 0.1);
1163
- }
1164
- .product-image {
1165
- width: 100%;
1166
- aspect-ratio: 1;
1167
- background-color: #fff;
1168
- border-radius: 10px;
1169
- overflow: hidden;
1170
- display: flex;
1171
- justify-content: center;
1172
- align-items: center;
1173
- transition: transform 0.3s ease;
1174
- }
1175
- body.dark-mode .product-image {
1176
- background-color: #333;
1177
- }
1178
- .product-image img {
1179
- max-width: 100%;
1180
- max-height: 100%;
1181
- object-fit: contain;
1182
- transition: transform 0.3s ease;
1183
- }
1184
- .product-image img:hover {
1185
- transform: scale(1.05);
1186
- }
1187
- .product h2 {
1188
- font-size: 1.1rem;
1189
- font-weight: 500;
1190
- margin: 10px 0;
1191
- text-align: center;
1192
- white-space: nowrap;
1193
- overflow: hidden;
1194
- text-overflow: ellipsis;
1195
- }
1196
- .product-price {
1197
- font-size: 1.2rem;
1198
- color: #e63946;
1199
- font-weight: 700;
1200
- text-align: center;
1201
- margin: 5px 0;
1202
- }
1203
- .product-price .wholesale {
1204
- font-size: 0.9rem;
1205
- color: #526df2;
1206
- font-weight: 500;
1207
- display: block;
1208
- margin-top: 5px;
1209
- display: none; /* Hidden */
1210
- }
1211
- .product-price .discount {
1212
- font-size: 0.9rem;
1213
- color: #2ecc71;
1214
- font-weight: 500;
1215
- display: block;
1216
- margin-top: 5px;
1217
- }
1218
- .product-description {
1219
- font-size: 0.85rem;
1220
- color: #666;
1221
- text-align: center;
1222
- margin-bottom: 15px;
1223
- overflow: hidden;
1224
- text-overflow: ellipsis;
1225
- white-space: nowrap;
1226
- }
1227
- body.dark-mode .product-description {
1228
- color: #bbb;
1229
- }
1230
- .product-button {
1231
- display: block;
1232
- width: 100%;
1233
- padding: 10px;
1234
- border: none;
1235
- border-radius: 25px;
1236
- background-color: #526df2;
1237
- color: white;
1238
- font-size: 0.9rem;
1239
- font-weight: 500;
1240
- cursor: pointer;
1241
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
1242
- margin: 5px 0;
1243
- text-align: center;
1244
- text-decoration: none;
1245
- }
1246
- .product-button:hover {
1247
- background-color: #3e55d1;
1248
- box-shadow: 0 4px 15px rgba(62, 85, 209, 0.4);
1249
- transform: translateY(-2px);
1250
- }
1251
- .add-to-cart {
1252
- background-color: #2ecc71;
1253
- }
1254
- .add-to-cart:hover {
1255
- background-color: #27ae60;
1256
- box-shadow: 0 4px 15px rgba(46, 204, 113, 0.4);
1257
- }
1258
- .favorite-button {
1259
- position: absolute;
1260
- top: 10px;
1261
- left: 10px;
1262
- background: none;
1263
- border: none;
1264
- font-size: 1.5rem;
1265
- cursor: pointer;
1266
- color: #666;
1267
- transition: color 0.3s ease;
1268
- }
1269
- .favorite-button.favorited {
1270
- color: #e63946;
1271
- }
1272
- .favorite-button:hover {
1273
- color: #e63946;
1274
- }
1275
- #cart-button {
1276
- position: fixed;
1277
- bottom: 80px;
1278
- right: 20px;
1279
- background-color: #e63946;
1280
- color: white;
1281
- border: none;
1282
- border-radius: 50%;
1283
- width: 60px;
1284
- height: 60px;
1285
- font-size: 1.5rem;
1286
- cursor: pointer;
1287
- display: none;
1288
- box-shadow: 0 4px 15px rgba(230, 57, 70, 0.4);
1289
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
1290
- z-index: 1000;
1291
- }
1292
- #cart-button:hover {
1293
- transform: scale(1.1);
1294
- }
1295
- .modal {
1296
- display: none;
1297
- position: fixed;
1298
- z-index: 1001;
1299
- left: 0;
1300
- top: 0;
1301
- width: 100%;
1302
- height: 100%;
1303
- background-color: rgba(0,0,0,0.5);
1304
- backdrop-filter: blur(5px);
1305
- }
1306
- .modal-content {
1307
- background: #fff;
1308
- margin: 5% auto;
1309
- padding: 20px;
1310
- border-radius: 15px;
1311
- width: 90%;
1312
- max-width: 700px;
1313
- box-shadow: 0 10px 30px rgba(0,0,0,0.2);
1314
- animation: slideIn 0.3s ease-out;
1315
- }
1316
- body.dark-mode .modal-content {
1317
- background: #2a2a3e;
1318
- color: #e0e0e0;
1319
- }
1320
- @keyframes slideIn {
1321
- from { transform: translateY(-50px); opacity: 0; }
1322
- to { transform: translateY(0); opacity: 1; }
1323
- }
1324
- .close {
1325
- float: right;
1326
- font-size: 1.5rem;
1327
- color: #666;
1328
- cursor: pointer;
1329
- transition: color 0.3s;
1330
- }
1331
- .close:hover {
1332
- color: #333;
1333
- }
1334
- body.dark-mode .close {
1335
- color: #bbb;
1336
- }
1337
- body.dark-mode .close:hover {
1338
- color: #fff;
1339
- }
1340
- .cart-item {
1341
- display: flex;
1342
- justify-content: space-between;
1343
- align-items: center;
1344
- padding: 15px 0;
1345
- border-bottom: 1px solid rgba(0, 0, 0, 0.1);
1346
- }
1347
- body.dark-mode .cart-item {
1348
- border-bottom: 1px solid rgba(255, 255, 255, 0.1);
1349
- }
1350
- .cart-item img {
1351
- width: 50px;
1352
- height: 50px;
1353
- object-fit: contain;
1354
- border-radius: 8px;
1355
- margin-right: 15px;
1356
- }
1357
- .quantity-input, .color-select {
1358
- width: 100%;
1359
- max-width: 150px;
1360
- padding: 8px;
1361
- border: 1px solid rgba(0, 0, 0, 0.1);
1362
- border-radius: 8px;
1363
- font-size: 1rem;
1364
- margin: 5px 0;
1365
- }
1366
- .clear-cart {
1367
- background-color: #e63946;
1368
- }
1369
- .clear-cart:hover {
1370
- background-color: #d62828;
1371
- box-shadow: 0 4px 15px rgba(230, 57, 70, 0.4);
1372
- }
1373
- .order-button {
1374
- background-color: #2ecc71;
1375
- }
1376
- .order-button:hover {
1377
- background-color: #27ae60;
1378
- box-shadow: 0 4px 15px rgba(46, 204, 113, 0.4);
1379
- }
1380
- .navbar {
1381
- position: fixed;
1382
- bottom: 0;
1383
- left: 0;
1384
- width: 100%;
1385
- background-color: #fff;
1386
- box-shadow: 0 -2px 10px rgba(0,0,0,0.1);
1387
- display: flex;
1388
- justify-content: space-around;
1389
- padding: 10px 0;
1390
- z-index: 1000;
1391
- }
1392
- body.dark-mode .navbar {
1393
- background-color: #2a2a3e;
1394
- }
1395
- .navbar a {
1396
- text-align: center;
1397
- color: #666;
1398
- text-decoration: none;
1399
- font-size: 0.9rem;
1400
- transition: color 0.3s ease;
1401
- }
1402
- .navbar a.active {
1403
- color: #526df2;
1404
- }
1405
- .navbar a i {
1406
- display: block;
1407
- font-size: 1.5rem;
1408
- margin-bottom: 5px;
1409
- }
1410
- body.dark-mode .navbar a {
1411
- color: #bbb;
1412
- }
1413
- .navbar a:hover {
1414
- color: #526df2;
1415
- }
1416
- .wholesale-badge {
1417
- position: absolute;
1418
- top: 10px;
1419
- right: 10px;
1420
- background-color: #526df2;
1421
- color: white;
1422
- padding: 5px 10px;
1423
- border-radius: 15px;
1424
- font-size: 0.75rem;
1425
- font-weight: 500;
1426
- display: none; /* Hidden */
1427
- }
1428
- .discount-badge {
1429
- position: absolute;
1430
- top: 40px;
1431
- right: 10px;
1432
- background-color: #2ecc71;
1433
- color: white;
1434
- padding: 5px 10px;
1435
- border-radius: 15px;
1436
- font-size: 0.75rem;
1437
- font-weight: 500;
1438
- }
1439
-
1440
- </style>
1441
- </head>
1442
- <body>
1443
- <div class="container">
1444
- <div class="header">
1445
- <img src="https://huggingface.co/spaces/Kgshop/Fire-collection/resolve/main/Picsart_25-03-25_11-40-25-730.jpg" alt="Fire collection Logo" style="max-height: 50px;">
1446
- <button class="theme-toggle" onclick="toggleTheme()">
1447
- <i class="fas fa-moon"></i>
1448
- </button>
1449
- </div>
1450
- <div class="products-grid" id="products-grid">
1451
- {% for product in products %}
1452
- <div class="product"
1453
- onclick="openModal({{ loop.index0 }})"
1454
- data-name="{{ product['name']|lower }}"
1455
- data-description="{{ product['description']|lower }}"
1456
- data-category="{{ product.get('category', 'Без категории') }}">
1457
- <button class="favorite-button" onclick="event.stopPropagation(); toggleFavorite({{ loop.index0 }})">
1458
- <i class="fas fa-heart"></i>
1459
- </button>
1460
- {% if product.get('photos') and product['photos']|length > 0 %}
1461
- <div class="product-image">
1462
- <img src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/photos/{{ product['photos'][0] }}"
1463
- alt="{{ product['name'] }}"
1464
- loading="lazy">
1465
- </div>
1466
- {% endif %}
1467
- {% if product.get('wholesale_price') and product.get('min_wholesale') %}
1468
- <span class="wholesale-badge">Опт от {{ product['min_wholesale'] }}</span>
1469
- {% endif %}
1470
- {% if product.get('discount') %}
1471
- <span class="discount-badge">Скидка {{ product['discount'] }}%</span>
1472
- {% endif %}
1473
- <h2>{{ product['name'] }}</h2>
1474
- <div class="product-price">
1475
- {% if product.get('discount') %}
1476
- <span style="text-decoration: line-through; color: #666;">{{ product['price'] }} с</span>
1477
- <span>{{ (product['price'] * (1 - product['discount'] / 100))|round(2) }} с</span>
1478
- <span class="discount">Скидка: {{ product['discount'] }}%</span>
1479
- {% else %}
1480
- <span>{{ product['price'] }} с</span>
1481
- {% endif %}
1482
- </div>
1483
- <p class="product-description">{{ product['description'][:50] }}{% if product['description']|length > 50 %}...{% endif %}</p>
1484
- <button class="product-button add-to-cart" onclick="event.stopPropagation(); openQuantityModal({{ loop.index0 }})">В корзину</button>
1485
- </div>
1486
- {% endfor %}
1487
- </div>
1488
- </div>
1489
-
1490
- <!-- Product Modal -->
1491
- <div id="productModal" class="modal">
1492
- <div class="modal-content">
1493
- <span class="close" onclick="closeModal('productModal')">×</span>
1494
- <div id="modalContent"></div>
1495
- </div>
1496
- </div>
1497
-
1498
- <!-- Quantity and Color Modal -->
1499
- <div id="quantityModal" class="modal">
1500
- <div class="modal-content">
1501
- <span class="close" onclick="closeModal('quantityModal')">×</span>
1502
- <h2>Укажите количество и цвет</h2>
1503
- <input type="number" id="quantityInput" class="quantity-input" min="1" value="1">
1504
- <select id="colorSelect" class="color-select"></select>
1505
- <button class="product-button" onclick="confirmAddToCart()">Добавить</button>
1506
- </div>
1507
- </div>
1508
-
1509
- <!-- Cart Modal -->
1510
- <div id="cartModal" class="modal">
1511
- <div class="modal-content">
1512
- <span class="close" onclick="closeModal('cartModal')">×</span>
1513
- <h2>Корзина</h2>
1514
- <div id="cartContent"></div>
1515
- <div style="margin-top: 20px; text-align: right;">
1516
- <strong>Итого: <span id="cartTotal">0</span> с</strong>
1517
- <button class="product-button clear-cart" onclick="clearCart()">Очистить</button>
1518
- <button class="product-button order-button" onclick="orderViaWhatsApp()">Заказать</button>
1519
- </div>
1520
- </div>
1521
- </div>
1522
-
1523
- <button id="cart-button" onclick="openCartModal()">🛒</button>
1524
-
1525
- <div class="navbar">
1526
- <a href="/">
1527
- <i class="fas fa-home"></i>
1528
- Главная
1529
- </a>
1530
- <a href="/categories" class="active">
1531
- <i class="fas fa-list"></i>
1532
- Каталог
1533
- </a>
1534
- <a href="/favorites">
1535
- <i class="fas fa-heart"></i>
1536
- Избранное
1537
- </a>
1538
- <a href="/discounts">
1539
- <i class="fas fa-tag"></i>
1540
- Скидки
1541
- </a>
1542
-
1543
- <a href="https://api.whatsapp.com/send?phone=996700666692">
1544
- <i class="fab fa-whatsapp"></i>
1545
- WhatsApp
1546
- </a>
1547
- </div>
1548
-
1549
- <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
1550
- <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.5.3/dist/umd/popper.min.js"></script>
1551
- <script src="https://cdnjs.cloudflare.com/ajax/libs/Swiper/10.2.0/swiper-bundle.min.js"></script>
1552
- <script>
1553
- const products = {{ products|tojson }};
1554
- let selectedProductIndex = null;
1555
-
1556
- function toggleTheme() {
1557
- document.body.classList.toggle('dark-mode');
1558
- const icon = document.querySelector('.theme-toggle i');
1559
- icon.classList.toggle('fa-moon');
1560
- icon.classList.toggle('fa-sun');
1561
- localStorage.setItem('theme', document.body.classList.contains('dark-mode') ? 'dark' : 'light');
1562
- }
1563
-
1564
- if (localStorage.getItem('theme') === 'dark') {
1565
- document.body.classList.add('dark-mode');
1566
- document.querySelector('.theme-toggle i').classList.replace('fa-moon', 'fa-sun');
1567
- }
1568
-
1569
- function openModal(index) {
1570
- loadProductDetails(index);
1571
- document.getElementById('productModal').style.display = "block";
1572
- }
1573
-
1574
- function closeModal(modalId) {
1575
- document.getElementById(modalId).style.display = "none";
1576
- }
1577
-
1578
- function loadProductDetails(index) {
1579
- fetch('/product/' + index)
1580
- .then(response => response.text())
1581
- .then(data => {
1582
- document.getElementById('modalContent').innerHTML = data;
1583
- initializeSwiper();
1584
- })
1585
- .catch(error => console.error('Ошибка:', error));
1586
- }
1587
-
1588
- function initializeSwiper() {
1589
- new Swiper('.swiper-container', {
1590
- slidesPerView: 1,
1591
- spaceBetween: 20,
1592
- loop: true,
1593
- grabCursor: true,
1594
- pagination: { el: '.swiper-pagination', clickable: true },
1595
- navigation: { nextEl: '.swiper-button-next', prevEl: '.swiper-button-prev' },
1596
- zoom: { maxRatio: 3 }
1597
- });
1598
- }
1599
-
1600
- function openQuantityModal(index) {
1601
- selectedProductIndex = index;
1602
- const product = products[index];
1603
- const colorSelect = document.getElementById('colorSelect');
1604
- colorSelect.innerHTML = '';
1605
- if (product.colors && product.colors.length > 0) {
1606
- product.colors.forEach(color => {
1607
- const option = document.createElement('option');
1608
- option.value = color;
1609
- option.text = color;
1610
- colorSelect.appendChild(option);
1611
- });
1612
- } else {
1613
- const option = document.createElement('option');
1614
- option.value = 'Нет цвета';
1615
- option.text = 'Нет цвета';
1616
- colorSelect.appendChild(option);
1617
- }
1618
- document.getElementById('quantityModal').style.display = 'block';
1619
- document.getElementById('quantityInput').value = 1;
1620
- }
1621
-
1622
- function confirmAddToCart() {
1623
- if (selectedProductIndex === null) return;
1624
- const quantity = parseInt(document.getElementById('quantityInput').value) || 1;
1625
- const color = document.getElementById('colorSelect').value;
1626
- if (quantity <= 0) {
1627
- alert("Укажите количество больше 0");
1628
- return;
1629
- }
1630
- let cart = JSON.parse(localStorage.getItem('cart') || '[]');
1631
- const product = products[selectedProductIndex];
1632
- const cartItemId = `${product.name}-${color}`;
1633
- const existingItem = cart.find(item => item.id === cartItemId);
1634
-
1635
- let priceToUse = product.price;
1636
- if (product.discount) {
1637
- priceToUse = product.price * (1 - product.discount / 100);
1638
- }
1639
-
1640
- if (existingItem) {
1641
- existingItem.quantity += quantity;
1642
- existingItem.price = (product.discount ? product.price * (1 - product.discount / 100) : product.price);
1643
- } else {
1644
- cart.push({
1645
- id: cartItemId,
1646
- name: product.name,
1647
- price: priceToUse,
1648
- retail_price: product.price,
1649
- wholesale_price: product.wholesale_price,
1650
- min_wholesale: product.min_wholesale,
1651
- discount: product.discount,
1652
- photo: product.photos && product.photos.length > 0 ? product.photos[0] : '',
1653
- quantity: quantity,
1654
- color: color
1655
- });
1656
- }
1657
-
1658
- localStorage.setItem('cart', JSON.stringify(cart));
1659
- closeModal('quantityModal');
1660
- updateCartButton();
1661
- }
1662
-
1663
- function updateCartButton() {
1664
- const cart = JSON.parse(localStorage.getItem('cart') || '[]');
1665
- document.getElementById('cart-button').style.display = cart.length > 0 ? 'block' : 'none';
1666
- }
1667
-
1668
- function openCartModal() {
1669
- const cart = JSON.parse(localStorage.getItem('cart') || '[]');
1670
- const cartContent = document.getElementById('cartContent');
1671
- let total = 0;
1672
-
1673
- cartContent.innerHTML = cart.length === 0 ? '<p>Корзина пуста</p>' : cart.map(item => {
1674
- const itemTotal = item.price * item.quantity;
1675
- total += itemTotal;
1676
- return `
1677
- <div class="cart-item">
1678
- <div style="display: flex; align-items: center;">
1679
- ${item.photo ? `<img src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/photos/${item.photo}" alt="${item.name}">` : ''}
1680
- <div>
1681
- <strong>${item.name}</strong>
1682
- <p>${item.price} с × ${item.quantity} (Цвет: ${item.color})</p>
1683
- </div>
1684
- </div>
1685
- <span>${itemTotal} с</span>
1686
- </div>
1687
- `;
1688
- }).join('');
1689
-
1690
- document.getElementById('cartTotal').textContent = total;
1691
- document.getElementById('cartModal').style.display = 'block';
1692
- }
1693
-
1694
- function orderViaWhatsApp() {
1695
- const cart = JSON.parse(localStorage.getItem('cart') || '[]');
1696
- if (cart.length === 0) {
1697
- alert("Корзина пуста!");
1698
- return;
1699
- }
1700
- let total = 0;
1701
- let orderText = "Заказ:%0A";
1702
- cart.forEach((item, index) => {
1703
- const itemTotal = item.price * item.quantity;
1704
- total += itemTotal;
1705
- orderText += `${index + 1}. ${item.name} - ${item.price} с × ${item.quantity} (Цвет: ${item.color})%0A`;
1706
- });
1707
- orderText += `Итого: ${total} с`;
1708
- window.open('https://api.whatsapp.com/send?phone=996700666692&text=\${orderText}', '_blank');
1709
- }
1710
 
1711
- function clearCart() {
1712
- localStorage.removeItem('cart');
1713
- closeModal('cartModal');
1714
- updateCartButton();
1715
- }
1716
 
1717
- function toggleFavorite(index) {
1718
- let favorites = JSON.parse(localStorage.getItem('favorites') || '[]');
1719
- const productId = index.toString();
1720
- const favoriteButton = document.querySelector(`.favorite-button[onclick="event.stopPropagation(); toggleFavorite(${index})"]`);
1721
- if (favorites.includes(productId)) {
1722
- favorites = favorites.filter(id => id !== productId);
1723
- favoriteButton.classList.remove('favorited');
1724
- } else {
1725
- favorites.push(productId);
1726
- favoriteButton.classList.add('favorited');
1727
- }
1728
- localStorage.setItem('favorites', JSON.stringify(favorites));
1729
- }
1730
 
1731
- function loadFavorites() {
1732
- const favorites = JSON.parse(localStorage.getItem('favorites') || '[]');
1733
- document.querySelectorAll('.favorite-button').forEach(button => {
1734
- const index = button.getAttribute('onclick').match(/\d+/)[0];
1735
- if (favorites.includes(index)) {
1736
- button.classList.add('favorited');
1737
- }
1738
- });
1739
- }
 
 
 
 
 
 
 
 
 
 
 
 
1740
 
 
 
 
 
 
 
 
 
 
1741
 
1742
- window.onclick = function(event) {
1743
- if (event.target.className === 'modal') event.target.style.display = "none";
1744
- }
 
 
 
 
 
 
 
 
 
 
 
1745
 
1746
- updateCartButton();
1747
- loadFavorites();
1748
- </script>
1749
- </body>
1750
- </html>
1751
- '''
1752
- return render_template_string(category_html, products=products, category=category, repo_id=REPO_ID)
 
 
 
 
 
 
 
 
 
 
1753
 
 
 
 
 
1754
 
1755
- @app.route('/favorites')
1756
- def favorites_page():
1757
  data = load_data()
1758
  products = data['products']
1759
- favorites = request.args.get('favorites', '[]') # Not really used directly, but kept for consistency
1760
 
1761
- favorites_html = '''
1762
  <!DOCTYPE html>
1763
  <html lang="ru">
1764
  <head>
@@ -1769,7 +105,6 @@ def favorites_page():
1769
  <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
1770
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/Swiper/10.2.0/swiper-bundle.min.css">
1771
  <style>
1772
- /* ... (your existing styles for product display) ... */
1773
  * {
1774
  margin: 0;
1775
  padding: 0;
@@ -1821,6 +156,25 @@ def favorites_page():
1821
  body.dark-mode .theme-toggle {
1822
  color: #bbb;
1823
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1824
  .products-grid {
1825
  display: grid;
1826
  grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
@@ -1895,9 +249,7 @@ def favorites_page():
1895
  font-size: 0.9rem;
1896
  color: #526df2;
1897
  font-weight: 500;
1898
- display: block;
1899
- margin-top: 5px;
1900
- display: none; /* Hidden */
1901
  }
1902
  .product-price .discount {
1903
  font-size: 0.9rem;
@@ -2114,7 +466,7 @@ def favorites_page():
2114
  border-radius: 15px;
2115
  font-size: 0.75rem;
2116
  font-weight: 500;
2117
- display: none; /* Hidden */
2118
  }
2119
  .discount-badge {
2120
  position: absolute;
@@ -2127,6 +479,32 @@ def favorites_page():
2127
  font-size: 0.75rem;
2128
  font-weight: 500;
2129
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2130
  </style>
2131
  </head>
2132
  <body>
@@ -2137,11 +515,54 @@ def favorites_page():
2137
  <i class="fas fa-moon"></i>
2138
  </button>
2139
  </div>
 
 
 
 
 
 
 
 
 
 
 
2140
  <div class="products-grid" id="products-grid">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2141
  </div>
2142
  </div>
2143
 
2144
- <!-- Product Modal -->
2145
  <div id="productModal" class="modal">
2146
  <div class="modal-content">
2147
  <span class="close" onclick="closeModal('productModal')">×</span>
@@ -2149,7 +570,6 @@ def favorites_page():
2149
  </div>
2150
  </div>
2151
 
2152
- <!-- Quantity and Color Modal -->
2153
  <div id="quantityModal" class="modal">
2154
  <div class="modal-content">
2155
  <span class="close" onclick="closeModal('quantityModal')">×</span>
@@ -2160,7 +580,6 @@ def favorites_page():
2160
  </div>
2161
  </div>
2162
 
2163
- <!-- Cart Modal -->
2164
  <div id="cartModal" class="modal">
2165
  <div class="modal-content">
2166
  <span class="close" onclick="closeModal('cartModal')">×</span>
@@ -2174,11 +593,10 @@ def favorites_page():
2174
  </div>
2175
  </div>
2176
 
2177
-
2178
  <button id="cart-button" onclick="openCartModal()">🛒</button>
2179
 
2180
  <div class="navbar">
2181
- <a href="/">
2182
  <i class="fas fa-home"></i>
2183
  Главная
2184
  </a>
@@ -2186,7 +604,7 @@ def favorites_page():
2186
  <i class="fas fa-list"></i>
2187
  Каталог
2188
  </a>
2189
- <a href="/favorites" class="active">
2190
  <i class="fas fa-heart"></i>
2191
  Избранное
2192
  </a>
@@ -2220,52 +638,6 @@ def favorites_page():
2220
  document.querySelector('.theme-toggle i').classList.replace('fa-moon', 'fa-sun');
2221
  }
2222
 
2223
- function loadFavoritesPage() {
2224
- const favorites = JSON.parse(localStorage.getItem('favorites') || '[]');
2225
- const productsGrid = document.getElementById('products-grid');
2226
- productsGrid.innerHTML = '';
2227
-
2228
- if (favorites.length === 0) {
2229
- productsGrid.innerHTML = '<p>Избранное пусто</p>';
2230
- return;
2231
- }
2232
-
2233
- favorites.forEach(index => {
2234
- const product = products[index];
2235
- if (!product) return; //Important check in case product was deleted
2236
- const productElement = document.createElement('div');
2237
- productElement.className = 'product';
2238
- productElement.setAttribute('onclick', `openModal(${index})`);
2239
- productElement.setAttribute('data-name', product.name.toLowerCase());
2240
- productElement.setAttribute('data-description', product.description.toLowerCase());
2241
- productElement.setAttribute('data-category', product.category || 'Без категории');
2242
- productElement.innerHTML = `
2243
- <button class="favorite-button favorited" onclick="event.stopPropagation(); toggleFavorite(${index})">
2244
- <i class="fas fa-heart"></i>
2245
- </button>
2246
- ${product.photos && product.photos.length > 0 ? `
2247
- <div class="product-image">
2248
- <img src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/photos/${product.photos[0]}" alt="${product.name}" loading="lazy">
2249
- </div>` : ''}
2250
- ${product.wholesale_price && product.min_wholesale ? `<span class="wholesale-badge">Опт от ${product.min_wholesale}</span>` : ''}
2251
- ${product.discount ? `<span class="discount-badge">Скидка ${product.discount}%</span>` : ''}
2252
- <h2>${product.name}</h2>
2253
- <div class="product-price">
2254
- {% if product.get('discount') %}
2255
- <span style="text-decoration: line-through; color: #666;">{{ product['price'] }} с</span>
2256
- <span>{{ (product['price'] * (1 - product['discount'] / 100)).toFixed(2)} с</span>
2257
- <span class="discount">Скидка: {{ product['discount'] }}%</span>
2258
- {% else %}
2259
- <span>{{ product['price'] }} с</span>
2260
- {% endif %}
2261
- </div>
2262
- <p class="product-description">${product.description.slice(0, 50)}${product.description.length > 50 ? '...' : ''}</p>
2263
- <button class="product-button add-to-cart" onclick="event.stopPropagation(); openQuantityModal(${index})">В корзину</button>
2264
- `;
2265
- productsGrid.appendChild(productElement);
2266
- });
2267
- }
2268
-
2269
  function openModal(index) {
2270
  loadProductDetails(index);
2271
  document.getElementById('productModal').style.display = "block";
@@ -2426,7 +798,7 @@ def favorites_page():
2426
  favoriteButton.classList.add('favorited');
2427
  }
2428
  localStorage.setItem('favorites', JSON.stringify(favorites));
2429
- loadFavoritesPage(); // Reload the favorites page after toggling
2430
  }
2431
 
2432
  window.onclick = function(event) {
@@ -2434,7 +806,7 @@ def favorites_page():
2434
  }
2435
 
2436
  updateCartButton();
2437
- loadFavoritesPage(); // Load favorites on initial page load
2438
  </script>
2439
  </body>
2440
  </html>
@@ -2442,12 +814,10 @@ def favorites_page():
2442
  return render_template_string(favorites_html, products=products, repo_id=REPO_ID)
2443
 
2444
 
2445
-
2446
  @app.route('/discounts')
2447
  def discounts_page():
2448
  data = load_data()
2449
  products = [p for p in data['products'] if p.get('discount')]
2450
- # categories = data['categories'] # No longer used in this view
2451
 
2452
  discounts_html = '''
2453
  <!DOCTYPE html>
@@ -2460,7 +830,6 @@ def discounts_page():
2460
  <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
2461
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/Swiper/10.2.0/swiper-bundle.min.css">
2462
  <style>
2463
- /* ... (your existing styles for product display) ... */
2464
  * {
2465
  margin: 0;
2466
  padding: 0;
@@ -2586,9 +955,7 @@ def discounts_page():
2586
  font-size: 0.9rem;
2587
  color: #526df2;
2588
  font-weight: 500;
2589
- display: block;
2590
- margin-top: 5px;
2591
- display: none; /* Hidden */
2592
  }
2593
  .product-price .discount {
2594
  font-size: 0.9rem;
@@ -2805,7 +1172,7 @@ def discounts_page():
2805
  border-radius: 15px;
2806
  font-size: 0.75rem;
2807
  font-weight: 500;
2808
- display: none; /* Hidden */
2809
  }
2810
  .discount-badge {
2811
  position: absolute;
@@ -2845,9 +1212,6 @@ def discounts_page():
2845
  loading="lazy">
2846
  </div>
2847
  {% endif %}
2848
- {% if product.get('wholesale_price') and product.get('min_wholesale') %}
2849
- <span class="wholesale-badge">Опт от {{ product['min_wholesale'] }}</span>
2850
- {% endif %}
2851
  {% if product.get('discount') %}
2852
  <span class="discount-badge">Скидка {{ product['discount'] }}%</span>
2853
  {% endif %}
@@ -2868,7 +1232,6 @@ def discounts_page():
2868
  </div>
2869
  </div>
2870
 
2871
- <!-- Product Modal -->
2872
  <div id="productModal" class="modal">
2873
  <div class="modal-content">
2874
  <span class="close" onclick="closeModal('productModal')">×</span>
@@ -2876,7 +1239,6 @@ def discounts_page():
2876
  </div>
2877
  </div>
2878
 
2879
- <!-- Quantity and Color Modal -->
2880
  <div id="quantityModal" class="modal">
2881
  <div class="modal-content">
2882
  <span class="close" onclick="closeModal('quantityModal')">×</span>
@@ -2887,7 +1249,6 @@ def discounts_page():
2887
  </div>
2888
  </div>
2889
 
2890
- <!-- Cart Modal -->
2891
  <div id="cartModal" class="modal">
2892
  <div class="modal-content">
2893
  <span class="close" onclick="closeModal('cartModal')">×</span>
@@ -3135,12 +1496,11 @@ def discounts_page():
3135
  def product_details(index):
3136
  data = load_data()
3137
  if index < 0 or index >= len(data['products']):
3138
- return "Product not found", 404 # Return a 404 error if index is out of bounds
3139
  product = data['products'][index]
3140
 
3141
  product_html = '''
3142
  <style>
3143
- /* ... (your existing styles for product details modal) ... */
3144
  .swiper-container {
3145
  width: 100%;
3146
  max-width: 400px;
@@ -3182,9 +1542,8 @@ def product_details(index):
3182
  font-size: 1rem;
3183
  color: #526df2;
3184
  font-weight: 500;
3185
- display: block;
3186
  margin-top: 5px;
3187
- display: none; /* Hidden */
3188
  }
3189
  .product-details .price .discount {
3190
  font-size: 1rem;
@@ -3296,7 +1655,7 @@ def admin():
3296
  elif action == 'edit':
3297
  index = int(request.form['index'])
3298
  photos = request.files.getlist('photos')
3299
- photo_filenames = products[index].get('photos', []) # Keep existing photos
3300
  for photo in photos:
3301
  if photo and photo.filename:
3302
  filename = secure_filename(photo.filename)
@@ -3308,7 +1667,7 @@ def admin():
3308
  repo_type="dataset",
3309
  token=HF_TOKEN_WRITE
3310
  )
3311
- photo_filenames.append(filename) # Add new photos
3312
 
3313
  colors = request.form.getlist('colors')
3314
  colors = [color.strip() for color in colors if color.strip()]
@@ -3319,13 +1678,12 @@ def admin():
3319
  'description': request.form['description'],
3320
  'category': request.form['category'],
3321
  'colors': colors,
3322
- 'photos': photo_filenames, # Updated photo list
3323
  'discount': float(request.form['discount']) if request.form['discount'] else None
3324
  }
3325
 
3326
  elif action == 'delete':
3327
  index = int(request.form['index'])
3328
- # Delete photos from Hugging Face Hub before deleting the product
3329
  product_to_delete = products[index]
3330
  if 'photos' in product_to_delete:
3331
  api = HfApi()
@@ -3341,7 +1699,6 @@ def admin():
3341
  logging.error(f"Error deleting photo {photo}: {e}")
3342
  products.pop(index)
3343
 
3344
-
3345
  elif action == 'add_category':
3346
  category = request.form['category_name'].strip()
3347
  if category and category not in categories:
@@ -3351,7 +1708,6 @@ def admin():
3351
  index = int(request.form['category_index'])
3352
  category_to_delete = categories[index]
3353
  categories.pop(index)
3354
- #Set category to "Без категории" for products in the deleted category
3355
  for product in products:
3356
  if product.get('category') == category_to_delete:
3357
  product['category'] = 'Без категории'
@@ -3368,7 +1724,6 @@ def admin():
3368
  <title>Fire collection - Админ-панель</title>
3369
  <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
3370
  <style>
3371
- /* ... (your existing admin panel styles) ... */
3372
  * {
3373
  margin: 0;
3374
  padding: 0;
 
1
 
2
+ from flask import Flask, render_template_string, request, redirect, url_for
3
+ import json
4
+ import os
5
+ import logging
6
+ import threading
7
+ import time
8
+ from datetime import datetime
9
+ from huggingface_hub import HfApi, hf_hub_download
10
+ from huggingface_hub.utils import RepositoryNotFoundError
11
+ from werkzeug.utils import secure_filename
12
 
13
+ app = Flask(__name__)
14
+ DATA_FILE = 'data_kanc.json'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
 
16
+ REPO_ID = "Kgshop/clients"
17
+ HF_TOKEN_WRITE = os.getenv("HF_TOKEN")
18
+ HF_TOKEN_READ = os.getenv("HF_TOKEN_READ")
 
 
19
 
20
+ logging.basicConfig(level=logging.DEBUG)
 
 
 
 
 
 
 
 
 
 
 
 
21
 
22
+ def load_data():
23
+ try:
24
+ download_db_from_hf()
25
+ with open(DATA_FILE, 'r', encoding='utf-8') as file:
26
+ data = json.load(file)
27
+ logging.info("Данные успешно загружены из JSON")
28
+ if not isinstance(data, dict) or 'products' not in data or 'categories' not in data:
29
+ return {'products': [], 'categories': []}
30
+ return data
31
+ except FileNotFoundError:
32
+ logging.warning("Локальный файл базы данных не найден после скачивания.")
33
+ return {'products': [], 'categories': []}
34
+ except json.JSONDecodeError:
35
+ logging.error("Ошибка: Невозможно декодировать JSON файл.")
36
+ return {'products': [], 'categories': []}
37
+ except RepositoryNotFoundError:
38
+ logging.error("Репозиторий не найден. Создание локальной базы данных.")
39
+ return {'products': [], 'categories': []}
40
+ except Exception as e:
41
+ logging.error(f"Произошла ошибка при загрузке данных: {e}")
42
+ return {'products': [], 'categories': []}
43
 
44
+ def save_data(data):
45
+ try:
46
+ with open(DATA_FILE, 'w', encoding='utf-8') as file:
47
+ json.dump(data, file, ensure_ascii=False, indent=4)
48
+ logging.info("Данные успешно сохранены в JSON")
49
+ upload_db_to_hf()
50
+ except Exception as e:
51
+ logging.error(f"Ошибка при сохранении данных: {e}")
52
+ raise
53
 
54
+ def upload_db_to_hf():
55
+ try:
56
+ api = HfApi()
57
+ api.upload_file(
58
+ path_or_fileobj=DATA_FILE,
59
+ path_in_repo=DATA_FILE,
60
+ repo_id=REPO_ID,
61
+ repo_type="dataset",
62
+ token=HF_TOKEN_WRITE,
63
+ commit_message=f"Автоматическое резервное копирование базы данных {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
64
+ )
65
+ logging.info("Резервная копия JSON базы успешно загружена на Hugging Face.")
66
+ except Exception as e:
67
+ logging.error(f"Ошибка при загрузке резервной копии: {e}")
68
 
69
+ def download_db_from_hf():
70
+ try:
71
+ hf_hub_download(
72
+ repo_id=REPO_ID,
73
+ filename=DATA_FILE,
74
+ repo_type="dataset",
75
+ token=HF_TOKEN_READ,
76
+ local_dir=".",
77
+ local_dir_use_symlinks=False
78
+ )
79
+ logging.info("JSON база успешно скачана из Hugging Face.")
80
+ except RepositoryNotFoundError as e:
81
+ logging.error(f"Репозиторий не найден: {e}")
82
+ raise
83
+ except Exception as e:
84
+ logging.error(f"Ошибка при скачивании JSON базы: {e}")
85
+ raise
86
 
87
+ def periodic_backup():
88
+ while True:
89
+ upload_db_to_hf()
90
+ time.sleep(800)
91
 
92
+ @app.route('/')
93
+ def catalog():
94
  data = load_data()
95
  products = data['products']
 
96
 
97
+ catalog_html = '''
98
  <!DOCTYPE html>
99
  <html lang="ru">
100
  <head>
 
105
  <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
106
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/Swiper/10.2.0/swiper-bundle.min.css">
107
  <style>
 
108
  * {
109
  margin: 0;
110
  padding: 0;
 
156
  body.dark-mode .theme-toggle {
157
  color: #bbb;
158
  }
159
+ .search-container {
160
+ margin: 20px 0;
161
+ text-align: center;
162
+ }
163
+ #search-input {
164
+ width: 90%;
165
+ max-width: 600px;
166
+ padding: 12px 18px;
167
+ font-size: 1rem;
168
+ border: 1px solid rgba(0, 0, 0, 0.1);
169
+ border-radius: 25px;
170
+ outline: none;
171
+ box-shadow: 0 2px 5px rgba(0,0,0,0.05);
172
+ transition: all 0.3s ease;
173
+ }
174
+ #search-input:focus {
175
+ border-color: #526df2;
176
+ box-shadow: 0 4px 15px rgba(82, 109, 242, 0.2);
177
+ }
178
  .products-grid {
179
  display: grid;
180
  grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
 
249
  font-size: 0.9rem;
250
  color: #526df2;
251
  font-weight: 500;
252
+ display: none;
 
 
253
  }
254
  .product-price .discount {
255
  font-size: 0.9rem;
 
466
  border-radius: 15px;
467
  font-size: 0.75rem;
468
  font-weight: 500;
469
+ display: none;
470
  }
471
  .discount-badge {
472
  position: absolute;
 
479
  font-size: 0.75rem;
480
  font-weight: 500;
481
  }
482
+
483
+ /* Styles for Address and Schedule */
484
+ .info-section {
485
+ text-align: center;
486
+ margin: 20px 0;
487
+ padding: 15px;
488
+ background: rgba(255, 255, 255, 0.8);
489
+ border-radius: 15px;
490
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05);
491
+ }
492
+ body.dark-mode .info-section {
493
+ background: rgba(42, 42, 62, 0.8);
494
+ }
495
+ .info-section h2 {
496
+ font-size: 1.5rem;
497
+ font-weight: 500;
498
+ margin-bottom: 10px;
499
+ color: #526df2;
500
+ }
501
+ .info-section p {
502
+ font-size: 1rem;
503
+ color: #666;
504
+ }
505
+ body.dark-mode .info-section p {
506
+ color: #bbb;
507
+ }
508
  </style>
509
  </head>
510
  <body>
 
515
  <i class="fas fa-moon"></i>
516
  </button>
517
  </div>
518
+
519
+ <div class="info-section">
520
+ <h2>Наш адрес</h2>
521
+ <p>Рынок Дордой, 0 проход , 2034 контейнер</p>
522
+ <h2>График работы</h2>
523
+ <p>По адресу: без выходных с 8:00 до 16:00, онлайн: круглосуточно</p>
524
+ </div>
525
+
526
+ <div class="search-container">
527
+ <input type="text" id="search-input" placeholder="Поиск товаров...">
528
+ </div>
529
  <div class="products-grid" id="products-grid">
530
+ {% for product in products %}
531
+ <div class="product"
532
+ onclick="openModal({{ loop.index0 }})"
533
+ data-name="{{ product['name']|lower }}"
534
+ data-description="{{ product['description']|lower }}"
535
+ data-category="{{ product.get('category', 'Без категории') }}">
536
+ <button class="favorite-button" onclick="event.stopPropagation(); toggleFavorite({{ loop.index0 }})">
537
+ <i class="fas fa-heart"></i>
538
+ </button>
539
+ {% if product.get('photos') and product['photos']|length > 0 %}
540
+ <div class="product-image">
541
+ <img src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/photos/{{ product['photos'][0] }}"
542
+ alt="{{ product['name'] }}"
543
+ loading="lazy">
544
+ </div>
545
+ {% endif %}
546
+ {% if product.get('discount') %}
547
+ <span class="discount-badge">Скидка {{ product['discount'] }}%</span>
548
+ {% endif %}
549
+ <h2>{{ product['name'] }}</h2>
550
+ <div class="product-price">
551
+ {% if product.get('discount') %}
552
+ <span style="text-decoration: line-through; color: #666;">{{ product['price'] }} с</span>
553
+ <span>{{ (product['price'] * (1 - product['discount'] / 100))|round(2) }} с</span>
554
+ <span class="discount">Скидка: {{ product['discount'] }}%</span>
555
+ {% else %}
556
+ <span>{{ product['price'] }} с</span>
557
+ {% endif %}
558
+ </div>
559
+ <p class="product-description">{{ product['description'][:50] }}{% if product['description']|length > 50 %}...{% endif %}</p>
560
+ <button class="product-button add-to-cart" onclick="event.stopPropagation(); openQuantityModal({{ loop.index0 }})">В корзину</button>
561
+ </div>
562
+ {% endfor %}
563
  </div>
564
  </div>
565
 
 
566
  <div id="productModal" class="modal">
567
  <div class="modal-content">
568
  <span class="close" onclick="closeModal('productModal')">×</span>
 
570
  </div>
571
  </div>
572
 
 
573
  <div id="quantityModal" class="modal">
574
  <div class="modal-content">
575
  <span class="close" onclick="closeModal('quantityModal')">×</span>
 
580
  </div>
581
  </div>
582
 
 
583
  <div id="cartModal" class="modal">
584
  <div class="modal-content">
585
  <span class="close" onclick="closeModal('cartModal')">×</span>
 
593
  </div>
594
  </div>
595
 
 
596
  <button id="cart-button" onclick="openCartModal()">🛒</button>
597
 
598
  <div class="navbar">
599
+ <a href="/" class="active">
600
  <i class="fas fa-home"></i>
601
  Главная
602
  </a>
 
604
  <i class="fas fa-list"></i>
605
  Каталог
606
  </a>
607
+ <a href="/favorites">
608
  <i class="fas fa-heart"></i>
609
  Избранное
610
  </a>
 
638
  document.querySelector('.theme-toggle i').classList.replace('fa-moon', 'fa-sun');
639
  }
640
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
641
  function openModal(index) {
642
  loadProductDetails(index);
643
  document.getElementById('productModal').style.display = "block";
 
798
  favoriteButton.classList.add('favorited');
799
  }
800
  localStorage.setItem('favorites', JSON.stringify(favorites));
801
+ loadFavoritesPage();
802
  }
803
 
804
  window.onclick = function(event) {
 
806
  }
807
 
808
  updateCartButton();
809
+ loadFavoritesPage();
810
  </script>
811
  </body>
812
  </html>
 
814
  return render_template_string(favorites_html, products=products, repo_id=REPO_ID)
815
 
816
 
 
817
  @app.route('/discounts')
818
  def discounts_page():
819
  data = load_data()
820
  products = [p for p in data['products'] if p.get('discount')]
 
821
 
822
  discounts_html = '''
823
  <!DOCTYPE html>
 
830
  <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
831
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/Swiper/10.2.0/swiper-bundle.min.css">
832
  <style>
 
833
  * {
834
  margin: 0;
835
  padding: 0;
 
955
  font-size: 0.9rem;
956
  color: #526df2;
957
  font-weight: 500;
958
+ display: none;
 
 
959
  }
960
  .product-price .discount {
961
  font-size: 0.9rem;
 
1172
  border-radius: 15px;
1173
  font-size: 0.75rem;
1174
  font-weight: 500;
1175
+ display: none;
1176
  }
1177
  .discount-badge {
1178
  position: absolute;
 
1212
  loading="lazy">
1213
  </div>
1214
  {% endif %}
 
 
 
1215
  {% if product.get('discount') %}
1216
  <span class="discount-badge">Скидка {{ product['discount'] }}%</span>
1217
  {% endif %}
 
1232
  </div>
1233
  </div>
1234
 
 
1235
  <div id="productModal" class="modal">
1236
  <div class="modal-content">
1237
  <span class="close" onclick="closeModal('productModal')">×</span>
 
1239
  </div>
1240
  </div>
1241
 
 
1242
  <div id="quantityModal" class="modal">
1243
  <div class="modal-content">
1244
  <span class="close" onclick="closeModal('quantityModal')">×</span>
 
1249
  </div>
1250
  </div>
1251
 
 
1252
  <div id="cartModal" class="modal">
1253
  <div class="modal-content">
1254
  <span class="close" onclick="closeModal('cartModal')">×</span>
 
1496
  def product_details(index):
1497
  data = load_data()
1498
  if index < 0 or index >= len(data['products']):
1499
+ return "Product not found", 404
1500
  product = data['products'][index]
1501
 
1502
  product_html = '''
1503
  <style>
 
1504
  .swiper-container {
1505
  width: 100%;
1506
  max-width: 400px;
 
1542
  font-size: 1rem;
1543
  color: #526df2;
1544
  font-weight: 500;
1545
+ display: none;
1546
  margin-top: 5px;
 
1547
  }
1548
  .product-details .price .discount {
1549
  font-size: 1rem;
 
1655
  elif action == 'edit':
1656
  index = int(request.form['index'])
1657
  photos = request.files.getlist('photos')
1658
+ photo_filenames = products[index].get('photos', [])
1659
  for photo in photos:
1660
  if photo and photo.filename:
1661
  filename = secure_filename(photo.filename)
 
1667
  repo_type="dataset",
1668
  token=HF_TOKEN_WRITE
1669
  )
1670
+ photo_filenames.append(filename)
1671
 
1672
  colors = request.form.getlist('colors')
1673
  colors = [color.strip() for color in colors if color.strip()]
 
1678
  'description': request.form['description'],
1679
  'category': request.form['category'],
1680
  'colors': colors,
1681
+ 'photos': photo_filenames,
1682
  'discount': float(request.form['discount']) if request.form['discount'] else None
1683
  }
1684
 
1685
  elif action == 'delete':
1686
  index = int(request.form['index'])
 
1687
  product_to_delete = products[index]
1688
  if 'photos' in product_to_delete:
1689
  api = HfApi()
 
1699
  logging.error(f"Error deleting photo {photo}: {e}")
1700
  products.pop(index)
1701
 
 
1702
  elif action == 'add_category':
1703
  category = request.form['category_name'].strip()
1704
  if category and category not in categories:
 
1708
  index = int(request.form['category_index'])
1709
  category_to_delete = categories[index]
1710
  categories.pop(index)
 
1711
  for product in products:
1712
  if product.get('category') == category_to_delete:
1713
  product['category'] = 'Без категории'
 
1724
  <title>Fire collection - Админ-панель</title>
1725
  <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
1726
  <style>
 
1727
  * {
1728
  margin: 0;
1729
  padding: 0;