Kgshop commited on
Commit
a522dbe
·
verified ·
1 Parent(s): e29f0aa

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +82 -23
app.py CHANGED
@@ -146,6 +146,15 @@ def load_data():
146
  except Exception:
147
  data = default_data
148
 
 
 
 
 
 
 
 
 
 
149
  for product in data['products']:
150
  if 'product_id' not in product:
151
  product['product_id'] = uuid4().hex
@@ -283,11 +292,10 @@ CATALOG_TEMPLATE = '''
283
  .gallery-dot.active { background: var(--primary); }
284
 
285
  .floating-socials { position: fixed; bottom: max(100px, calc(100px + env(safe-area-inset-bottom))); right: 15px; display: flex; flex-direction: column; gap: 12px; z-index: 90; }
286
- .social-btn { width: 48px; height: 48px; border-radius: 50%; display: flex; align-items: center; justify-content: center; color: #000; font-size: 1.6rem; text-decoration: none; box-shadow: 0 4px 12px rgba(0,0,0,0.5); transition: transform 0.2s; }
287
  .social-btn:active { transform: scale(0.9); }
288
- .btn-float-wa { background: var(--primary); }
289
  .btn-float-ig { background: radial-gradient(circle at 30% 107%, #fdf497 0%, #fdf497 5%, #fd5949 45%, #d6249f 60%, #285AEB 90%); color: #fff; }
290
- .btn-float-tg { background: #0088cc; color: #fff; }
291
 
292
  @media (min-width: 768px) {
293
  .categories-container { grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); }
@@ -329,9 +337,8 @@ CATALOG_TEMPLATE = '''
329
  <div class="products-container" id="productsContainer"></div>
330
 
331
  <div class="floating-socials">
332
- <a href="https://wa.me/{{ settings.wa_shift1|replace('+', '') if settings.active_shift == 1 else settings.wa_shift2|replace('+', '') }}" class="social-btn btn-float-wa" target="_blank"><i class="fab fa-whatsapp"></i></a>
333
- <a href="#" class="social-btn btn-float-ig" target="_blank"><i class="fab fa-instagram"></i></a>
334
- <a href="#" class="social-btn btn-float-tg" target="_blank"><i class="fab fa-telegram-plane"></i></a>
335
  </div>
336
 
337
  <div class="cart-bar" id="cartBar">
@@ -364,6 +371,19 @@ CATALOG_TEMPLATE = '''
364
  </div>
365
  </div>
366
 
 
 
 
 
 
 
 
 
 
 
 
 
 
367
  <div class="gallery-modal" id="galleryModal">
368
  <button class="gallery-close" onclick="closeGallery()"><i class="fas fa-times"></i></button>
369
  <div class="gallery-img-container" id="gallerySwipeArea">
@@ -424,17 +444,17 @@ CATALOG_TEMPLATE = '''
424
  container.innerHTML = '';
425
 
426
  categoriesList.forEach(cat => {
427
- const catProducts = products.filter(p => p.category === cat);
428
  const count = catProducts.length;
429
 
430
  const div = document.createElement('div');
431
  div.className = 'category-item';
432
- div.onclick = () => showProducts(cat);
433
  div.innerHTML = `
434
  <div style="background: #1a1a1a; width: 50px; height: 50px; border-radius: 12px; border: 1px solid var(--border); display: flex; align-items: center; justify-content: center; margin-bottom: 5px;">
435
- <i class="fas fa-utensils" style="font-size: 1.5rem; color: var(--primary);"></i>
436
  </div>
437
- <span class="name">${cat}</span>
438
  <span class="count">${count} шт</span>
439
  `;
440
  container.appendChild(div);
@@ -618,6 +638,18 @@ CATALOG_TEMPLATE = '''
618
  setTimeout(() => modal.style.display = 'none', 300);
619
  }
620
 
 
 
 
 
 
 
 
 
 
 
 
 
621
  function submitOrder() {
622
  const cartArray = Object.values(cart);
623
  if(cartArray.length === 0) return;
@@ -1227,6 +1259,7 @@ ADMIN_TEMPLATE = '''
1227
  <form method="POST" class="add-cat-form">
1228
  <input type="hidden" name="action" value="add_category">
1229
  <input type="text" name="category_name" placeholder="Название новой категории" required autocomplete="off">
 
1230
  <button type="submit" class="btn btn-dark"><i class="fas fa-plus"></i> Добавить</button>
1231
  </form>
1232
  </div>
@@ -1241,16 +1274,26 @@ ADMIN_TEMPLATE = '''
1241
  <div class="category-header" onclick="toggleCategory('cat-{{ loop.index }}')">
1242
  <div style="display: flex; align-items: center; gap: 10px;">
1243
  <i class="fas fa-chevron-down" id="icon-cat-{{ loop.index }}" style="color: var(--text-muted);"></i>
1244
- <span class="cat-title-text"><i class="fas fa-folder-open" style="color:var(--info); margin-right:5px;"></i> {{ category }}</span>
1245
  </div>
1246
  <form method="POST" style="margin:0;" onclick="event.stopPropagation();" onsubmit="return confirm('Удалить категорию и все ее блюда?');">
1247
  <input type="hidden" name="action" value="delete_category">
1248
- <input type="hidden" name="category_name" value="{{ category }}">
1249
  <button type="submit" class="btn btn-danger"><i class="fas fa-trash-alt"></i></button>
1250
  </form>
1251
  </div>
1252
  <div class="category-content" id="cat-{{ loop.index }}">
1253
 
 
 
 
 
 
 
 
 
 
 
1254
  <div class="toggle-add-product" onclick="toggleAddProduct('add-prod-{{ loop.index }}')">
1255
  <i class="fas fa-plus"></i> Добавить блюдо
1256
  </div>
@@ -1258,8 +1301,8 @@ ADMIN_TEMPLATE = '''
1258
  <div class="add-product-wrapper" id="add-prod-{{ loop.index }}">
1259
  <form class="add-product-form" method="POST" enctype="multipart/form-data" onsubmit="showLoading(this)">
1260
  <input type="hidden" name="action" value="add_product">
1261
- <input type="hidden" name="category" value="{{ category }}">
1262
- <div style="font-weight: 600; font-size: 0.9rem; color: var(--primary);">Новое блюдо в категории "{{ category }}"</div>
1263
  <div class="form-row">
1264
  <input type="text" name="name" placeholder="Название блюда" required autocomplete="off" style="flex:2;">
1265
  <input type="number" name="price" placeholder="Цена" required step="0.01" style="flex:1;">
@@ -1274,7 +1317,7 @@ ADMIN_TEMPLATE = '''
1274
  </div>
1275
 
1276
  {% for product in products %}
1277
- {% if product.category == category %}
1278
  <div class="product-item">
1279
  <div class="product-info">
1280
  {% if product.photos and product.photos|length > 0 %}
@@ -1302,7 +1345,7 @@ ADMIN_TEMPLATE = '''
1302
  <form class="add-product-form" method="POST" enctype="multipart/form-data" onsubmit="showLoading(this)" style="padding: 0;">
1303
  <input type="hidden" name="action" value="edit_product">
1304
  <input type="hidden" name="product_id" value="{{ product.product_id }}">
1305
- <input type="hidden" name="category" value="{{ category }}">
1306
  <div style="font-weight: 600; font-size: 0.9rem; color: var(--primary);">Редактирование блюда</div>
1307
  <div class="form-row">
1308
  <input type="text" name="name" value="{{ product.name }}" required autocomplete="off" style="flex:2;">
@@ -1587,19 +1630,35 @@ def admin():
1587
 
1588
  elif action == 'add_category':
1589
  cat_name = request.form.get('category_name', '').strip()
1590
- if cat_name and cat_name not in categories:
1591
- categories.append(cat_name)
 
1592
  data['categories'] = categories
1593
  save_data(data)
1594
 
1595
- elif action == 'delete_category':
1596
- cat_name = request.form.get('category_name')
1597
- if cat_name in categories:
1598
- categories.remove(cat_name)
1599
- data['products'] = [p for p in products if p.get('category') != cat_name]
 
 
 
 
 
 
 
 
1600
  data['categories'] = categories
 
1601
  save_data(data)
1602
 
 
 
 
 
 
 
1603
  elif action == 'add_product':
1604
  name = request.form.get('name', '').strip()
1605
  price = float(request.form.get('price', 0))
 
146
  except Exception:
147
  data = default_data
148
 
149
+ migrated_cats = []
150
+ for c in data.get('categories', []):
151
+ if isinstance(c, str):
152
+ migrated_cats.append({'name': c, 'icon': 'fas fa-utensils'})
153
+ else:
154
+ if 'icon' not in c: c['icon'] = 'fas fa-utensils'
155
+ migrated_cats.append(c)
156
+ data['categories'] = migrated_cats
157
+
158
  for product in data['products']:
159
  if 'product_id' not in product:
160
  product['product_id'] = uuid4().hex
 
292
  .gallery-dot.active { background: var(--primary); }
293
 
294
  .floating-socials { position: fixed; bottom: max(100px, calc(100px + env(safe-area-inset-bottom))); right: 15px; display: flex; flex-direction: column; gap: 12px; z-index: 90; }
295
+ .social-btn { width: 48px; height: 48px; border-radius: 50%; display: flex; align-items: center; justify-content: center; color: #000; font-size: 1.6rem; text-decoration: none; box-shadow: 0 4px 12px rgba(0,0,0,0.5); transition: transform 0.2s; border: none; cursor: pointer; }
296
  .social-btn:active { transform: scale(0.9); }
 
297
  .btn-float-ig { background: radial-gradient(circle at 30% 107%, #fdf497 0%, #fdf497 5%, #fd5949 45%, #d6249f 60%, #285AEB 90%); color: #fff; }
298
+ .btn-float-map { background: #ff4757; color: #fff; }
299
 
300
  @media (min-width: 768px) {
301
  .categories-container { grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); }
 
337
  <div class="products-container" id="productsContainer"></div>
338
 
339
  <div class="floating-socials">
340
+ <a href="https://instagram.com/hongkong_milyanfan" class="social-btn btn-float-ig" target="_blank"><i class="fab fa-instagram"></i></a>
341
+ <button class="social-btn btn-float-map" onclick="openAddressModal()"><i class="fas fa-map-marker-alt"></i></button>
 
342
  </div>
343
 
344
  <div class="cart-bar" id="cartBar">
 
371
  </div>
372
  </div>
373
 
374
+ <div class="modal-overlay" id="addressModal" onclick="if(event.target === this) closeAddressModal()">
375
+ <div class="modal-content" style="max-height: 30vh; align-items: center; justify-content: center; text-align: center;">
376
+ <div class="modal-header" style="width: 100%; margin-bottom: 10px;">
377
+ <h2 style="margin: 0 auto;">Наш адрес</h2>
378
+ <button class="modal-close" style="position: absolute; right: 20px;" onclick="closeAddressModal()"><i class="fas fa-times"></i></button>
379
+ </div>
380
+ <div style="font-size: 1.2rem; font-weight: 600; padding: 20px; color: var(--text);">
381
+ <i class="fas fa-map-pin" style="color: var(--primary); margin-right: 10px; font-size: 1.5rem;"></i>
382
+ село Милянфан<br>ул. Фрунзе 99
383
+ </div>
384
+ </div>
385
+ </div>
386
+
387
  <div class="gallery-modal" id="galleryModal">
388
  <button class="gallery-close" onclick="closeGallery()"><i class="fas fa-times"></i></button>
389
  <div class="gallery-img-container" id="gallerySwipeArea">
 
444
  container.innerHTML = '';
445
 
446
  categoriesList.forEach(cat => {
447
+ const catProducts = products.filter(p => p.category === cat.name);
448
  const count = catProducts.length;
449
 
450
  const div = document.createElement('div');
451
  div.className = 'category-item';
452
+ div.onclick = () => showProducts(cat.name);
453
  div.innerHTML = `
454
  <div style="background: #1a1a1a; width: 50px; height: 50px; border-radius: 12px; border: 1px solid var(--border); display: flex; align-items: center; justify-content: center; margin-bottom: 5px;">
455
+ <i class="${cat.icon}" style="font-size: 1.5rem; color: var(--primary);"></i>
456
  </div>
457
+ <span class="name">${cat.name}</span>
458
  <span class="count">${count} шт</span>
459
  `;
460
  container.appendChild(div);
 
638
  setTimeout(() => modal.style.display = 'none', 300);
639
  }
640
 
641
+ function openAddressModal() {
642
+ const modal = document.getElementById('addressModal');
643
+ modal.style.display = 'flex';
644
+ setTimeout(() => modal.classList.add('active'), 10);
645
+ }
646
+
647
+ function closeAddressModal() {
648
+ const modal = document.getElementById('addressModal');
649
+ modal.classList.remove('active');
650
+ setTimeout(() => modal.style.display = 'none', 300);
651
+ }
652
+
653
  function submitOrder() {
654
  const cartArray = Object.values(cart);
655
  if(cartArray.length === 0) return;
 
1259
  <form method="POST" class="add-cat-form">
1260
  <input type="hidden" name="action" value="add_category">
1261
  <input type="text" name="category_name" placeholder="Название новой категории" required autocomplete="off">
1262
+ <input type="text" name="category_icon" placeholder="Класс иконки (напр. fas fa-pizza-slice)" value="fas fa-utensils" required autocomplete="off">
1263
  <button type="submit" class="btn btn-dark"><i class="fas fa-plus"></i> Добавить</button>
1264
  </form>
1265
  </div>
 
1274
  <div class="category-header" onclick="toggleCategory('cat-{{ loop.index }}')">
1275
  <div style="display: flex; align-items: center; gap: 10px;">
1276
  <i class="fas fa-chevron-down" id="icon-cat-{{ loop.index }}" style="color: var(--text-muted);"></i>
1277
+ <span class="cat-title-text"><i class="{{ category.icon }}" style="color:var(--info); margin-right:5px;"></i> {{ category.name }}</span>
1278
  </div>
1279
  <form method="POST" style="margin:0;" onclick="event.stopPropagation();" onsubmit="return confirm('Удалить категорию и все ее блюда?');">
1280
  <input type="hidden" name="action" value="delete_category">
1281
+ <input type="hidden" name="category_name" value="{{ category.name }}">
1282
  <button type="submit" class="btn btn-danger"><i class="fas fa-trash-alt"></i></button>
1283
  </form>
1284
  </div>
1285
  <div class="category-content" id="cat-{{ loop.index }}">
1286
 
1287
+ <div style="padding: 15px; border-bottom: 1px solid var(--border);">
1288
+ <form method="POST" style="display: flex; gap: 10px; flex-wrap: wrap;">
1289
+ <input type="hidden" name="action" value="edit_category">
1290
+ <input type="hidden" name="old_name" value="{{ category.name }}">
1291
+ <input type="text" name="new_name" value="{{ category.name }}" required style="flex: 1; min-width: 150px;">
1292
+ <input type="text" name="new_icon" value="{{ category.icon }}" required style="flex: 1; min-width: 150px;">
1293
+ <button type="submit" class="btn btn-primary" style="white-space: nowrap;"><i class="fas fa-save"></i> Обновить категорию</button>
1294
+ </form>
1295
+ </div>
1296
+
1297
  <div class="toggle-add-product" onclick="toggleAddProduct('add-prod-{{ loop.index }}')">
1298
  <i class="fas fa-plus"></i> Добавить блюдо
1299
  </div>
 
1301
  <div class="add-product-wrapper" id="add-prod-{{ loop.index }}">
1302
  <form class="add-product-form" method="POST" enctype="multipart/form-data" onsubmit="showLoading(this)">
1303
  <input type="hidden" name="action" value="add_product">
1304
+ <input type="hidden" name="category" value="{{ category.name }}">
1305
+ <div style="font-weight: 600; font-size: 0.9rem; color: var(--primary);">Новое блюдо в категории "{{ category.name }}"</div>
1306
  <div class="form-row">
1307
  <input type="text" name="name" placeholder="Название блюда" required autocomplete="off" style="flex:2;">
1308
  <input type="number" name="price" placeholder="Цена" required step="0.01" style="flex:1;">
 
1317
  </div>
1318
 
1319
  {% for product in products %}
1320
+ {% if product.category == category.name %}
1321
  <div class="product-item">
1322
  <div class="product-info">
1323
  {% if product.photos and product.photos|length > 0 %}
 
1345
  <form class="add-product-form" method="POST" enctype="multipart/form-data" onsubmit="showLoading(this)" style="padding: 0;">
1346
  <input type="hidden" name="action" value="edit_product">
1347
  <input type="hidden" name="product_id" value="{{ product.product_id }}">
1348
+ <input type="hidden" name="category" value="{{ category.name }}">
1349
  <div style="font-weight: 600; font-size: 0.9rem; color: var(--primary);">Редактирование блюда</div>
1350
  <div class="form-row">
1351
  <input type="text" name="name" value="{{ product.name }}" required autocomplete="off" style="flex:2;">
 
1630
 
1631
  elif action == 'add_category':
1632
  cat_name = request.form.get('category_name', '').strip()
1633
+ cat_icon = request.form.get('category_icon', 'fas fa-utensils').strip()
1634
+ if cat_name and not any(c.get('name') == cat_name for c in categories):
1635
+ categories.append({'name': cat_name, 'icon': cat_icon})
1636
  data['categories'] = categories
1637
  save_data(data)
1638
 
1639
+ elif action == 'edit_category':
1640
+ old_name = request.form.get('old_name')
1641
+ new_name = request.form.get('new_name', '').strip()
1642
+ new_icon = request.form.get('new_icon', 'fas fa-utensils').strip()
1643
+ if new_name:
1644
+ for c in categories:
1645
+ if c.get('name') == old_name:
1646
+ c['name'] = new_name
1647
+ c['icon'] = new_icon
1648
+ break
1649
+ for p in products:
1650
+ if p.get('category') == old_name:
1651
+ p['category'] = new_name
1652
  data['categories'] = categories
1653
+ data['products'] = products
1654
  save_data(data)
1655
 
1656
+ elif action == 'delete_category':
1657
+ cat_name = request.form.get('category_name')
1658
+ data['categories'] = [c for c in categories if c.get('name') != cat_name]
1659
+ data['products'] = [p for p in products if p.get('category') != cat_name]
1660
+ save_data(data)
1661
+
1662
  elif action == 'add_product':
1663
  name = request.form.get('name', '').strip()
1664
  price = float(request.form.get('price', 0))