Kgshop commited on
Commit
db16dda
·
verified ·
1 Parent(s): 1a6ecce

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +74 -26
app.py CHANGED
@@ -190,6 +190,7 @@ CATALOG_TEMPLATE = '''
190
  .photo-count { position: absolute; bottom: 5px; right: 5px; background: rgba(0,0,0,0.6); color: white; font-size: 0.7rem; padding: 2px 6px; border-radius: 10px; pointer-events: none; }
191
  .product-info { flex-grow: 1; display: flex; flex-direction: column; justify-content: space-between; min-width: 0; padding: 5px 0; }
192
  .product-title { font-size: 0.95rem; font-weight: 600; line-height: 1.4; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
 
193
  .product-bottom { display: flex; align-items: center; justify-content: space-between; margin-top: 10px; }
194
  .product-price { font-weight: 700; font-size: 1rem; color: var(--primary); }
195
  .quantity-control { display: flex; align-items: center; background: var(--bg); border-radius: 8px; overflow: hidden; border: 1px solid var(--border); }
@@ -366,6 +367,7 @@ CATALOG_TEMPLATE = '''
366
 
367
  const photoIndicator = hasPhotos && p.photos.length > 1 ? `<div class="photo-count"><i class="fas fa-images"></i> ${p.photos.length}</div>` : '';
368
  const imgClick = hasPhotos ? `onclick="openGallery('${p.product_id}')"` : '';
 
369
 
370
  const div = document.createElement('div');
371
  div.className = 'product-card';
@@ -375,7 +377,10 @@ CATALOG_TEMPLATE = '''
375
  ${photoIndicator}
376
  </div>
377
  <div class="product-info">
378
- <div class="product-title">${p.name}</div>
 
 
 
379
  <div class="product-bottom">
380
  <div class="product-price">${p.price} ${currency}</div>
381
  <div class="quantity-control">
@@ -700,16 +705,19 @@ ADMIN_TEMPLATE = '''
700
  .card { background: var(--surface); padding: 20px; border-radius: 16px; box-shadow: 0 4px 15px rgba(0,0,0,0.03); margin-bottom: 20px; }
701
  .card h2 { margin-top: 0; margin-bottom: 15px; font-size: 1.2rem; }
702
 
703
- input[type="text"], input[type="number"], select { width: 100%; padding: 12px 15px; border: 1px solid var(--border); border-radius: 10px; font-size: 0.95rem; outline: none; transition: border-color 0.2s; background: #fafafa; }
704
- input[type="text"]:focus, input[type="number"]:focus { border-color: var(--info); background: #fff; }
 
705
 
706
  .add-cat-form { display: flex; gap: 10px; flex-wrap: wrap; }
707
  .add-cat-form input { flex: 1; min-width: 200px; }
708
  .add-cat-form button { white-space: nowrap; }
709
 
710
  .category-block { border: 1px solid var(--border); margin-bottom: 15px; border-radius: 12px; overflow: hidden; background: #fff; }
711
- .category-header { background: #fafafa; padding: 15px 20px; font-weight: 700; display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid var(--border); }
712
- .category-content { padding: 0; }
 
 
713
 
714
  .product-item { display: flex; justify-content: space-between; align-items: center; padding: 15px 20px; border-bottom: 1px solid var(--border); flex-wrap: wrap; gap: 10px; }
715
  .product-item:last-child { border-bottom: none; }
@@ -717,9 +725,15 @@ ADMIN_TEMPLATE = '''
717
  .product-img { width: 50px; height: 50px; object-fit: cover; border-radius: 8px; border: 1px solid #eee; background: #fafafa; }
718
  .product-details { display: flex; flex-direction: column; }
719
  .product-name { font-weight: 600; font-size: 0.95rem; }
720
- .product-meta { font-size: 0.8rem; color: #636e72; margin-top: 4px; }
 
 
 
 
 
 
721
 
722
- .add-product-form { background: #fdfdfd; padding: 20px; border-top: 1px dashed var(--border); display: flex; flex-direction: column; gap: 15px; }
723
  .form-row { display: flex; gap: 10px; flex-wrap: wrap; }
724
  .form-row > * { flex: 1; min-width: 150px; }
725
 
@@ -762,16 +776,41 @@ ADMIN_TEMPLATE = '''
762
 
763
  {% for category in categories %}
764
  <div class="category-block">
765
- <div class="category-header">
766
- <span><i class="fas fa-folder-open" style="color:var(--info); margin-right:8px;"></i> {{ category }}</span>
767
- <form method="POST" style="margin:0;" onsubmit="return confirm('Удалить категорию и все ее товары?');">
 
 
 
768
  <input type="hidden" name="action" value="delete_category">
769
  <input type="hidden" name="category_name" value="{{ category }}">
770
  <button type="submit" class="btn btn-danger"><i class="fas fa-trash-alt"></i></button>
771
  </form>
772
  </div>
773
- <div class="category-content">
 
 
 
 
774
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
775
  {% for product in products %}
776
  {% if product.category == category %}
777
  <div class="product-item">
@@ -783,6 +822,9 @@ ADMIN_TEMPLATE = '''
783
  {% endif %}
784
  <div class="product-details">
785
  <span class="product-name">{{ product.name }}</span>
 
 
 
786
  <span class="product-meta">{{ product.price }} {{ currency_code }} • Фото: {{ product.photos|length if product.photos else 0 }}/10</span>
787
  </div>
788
  </div>
@@ -794,21 +836,6 @@ ADMIN_TEMPLATE = '''
794
  </div>
795
  {% endif %}
796
  {% endfor %}
797
-
798
- <form class="add-product-form" method="POST" enctype="multipart/form-data" onsubmit="showLoading(this)">
799
- <input type="hidden" name="action" value="add_product">
800
- <input type="hidden" name="category" value="{{ category }}">
801
- <div style="font-weight: 600; font-size: 0.9rem; color: #636e72;">Добавить товар в "{{ category }}"</div>
802
- <div class="form-row">
803
- <input type="text" name="name" placeholder="Название товара" required autocomplete="off" style="flex:2;">
804
- <input type="number" name="price" placeholder="Цена" required step="0.01" style="flex:1;">
805
- </div>
806
- <div class="file-input-wrapper">
807
- <input type="file" name="photos" accept="image/*" multiple max="10" required>
808
- <div style="font-size: 0.8rem; color: #999; margin-top: 5px;">Можно выбрать до 10 фото</div>
809
- </div>
810
- <button type="submit" class="btn btn-success" style="width: 100%; justify-content: center;"><i class="fas fa-plus-circle"></i> Добавить товар</button>
811
- </form>
812
  </div>
813
  </div>
814
  {% endfor %}
@@ -820,6 +847,25 @@ ADMIN_TEMPLATE = '''
820
  btn.style.pointerEvents = 'none';
821
  btn.style.opacity = '0.7';
822
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
823
  </script>
824
  </body>
825
  </html>
@@ -913,6 +959,7 @@ def admin():
913
  elif action == 'add_product':
914
  name = request.form.get('name', '').strip()
915
  price = float(request.form.get('price', 0))
 
916
  category = request.form.get('category')
917
  uploaded_photos = request.files.getlist('photos')[:10]
918
 
@@ -948,6 +995,7 @@ def admin():
948
  'product_id': uuid4().hex,
949
  'name': name,
950
  'price': price,
 
951
  'category': category,
952
  'photos': photos_list
953
  }
 
190
  .photo-count { position: absolute; bottom: 5px; right: 5px; background: rgba(0,0,0,0.6); color: white; font-size: 0.7rem; padding: 2px 6px; border-radius: 10px; pointer-events: none; }
191
  .product-info { flex-grow: 1; display: flex; flex-direction: column; justify-content: space-between; min-width: 0; padding: 5px 0; }
192
  .product-title { font-size: 0.95rem; font-weight: 600; line-height: 1.4; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
193
+ .product-desc { font-size: 0.8rem; color: var(--text-muted); margin-top: 4px; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
194
  .product-bottom { display: flex; align-items: center; justify-content: space-between; margin-top: 10px; }
195
  .product-price { font-weight: 700; font-size: 1rem; color: var(--primary); }
196
  .quantity-control { display: flex; align-items: center; background: var(--bg); border-radius: 8px; overflow: hidden; border: 1px solid var(--border); }
 
367
 
368
  const photoIndicator = hasPhotos && p.photos.length > 1 ? `<div class="photo-count"><i class="fas fa-images"></i> ${p.photos.length}</div>` : '';
369
  const imgClick = hasPhotos ? `onclick="openGallery('${p.product_id}')"` : '';
370
+ const descHtml = p.description ? `<div class="product-desc">${p.description}</div>` : '';
371
 
372
  const div = document.createElement('div');
373
  div.className = 'product-card';
 
377
  ${photoIndicator}
378
  </div>
379
  <div class="product-info">
380
+ <div>
381
+ <div class="product-title">${p.name}</div>
382
+ ${descHtml}
383
+ </div>
384
  <div class="product-bottom">
385
  <div class="product-price">${p.price} ${currency}</div>
386
  <div class="quantity-control">
 
705
  .card { background: var(--surface); padding: 20px; border-radius: 16px; box-shadow: 0 4px 15px rgba(0,0,0,0.03); margin-bottom: 20px; }
706
  .card h2 { margin-top: 0; margin-bottom: 15px; font-size: 1.2rem; }
707
 
708
+ input[type="text"], input[type="number"], select, textarea { width: 100%; padding: 12px 15px; border: 1px solid var(--border); border-radius: 10px; font-size: 0.95rem; outline: none; transition: border-color 0.2s; background: #fafafa; }
709
+ input[type="text"]:focus, input[type="number"]:focus, textarea:focus { border-color: var(--info); background: #fff; }
710
+ textarea { resize: vertical; min-height: 80px; font-family: inherit; }
711
 
712
  .add-cat-form { display: flex; gap: 10px; flex-wrap: wrap; }
713
  .add-cat-form input { flex: 1; min-width: 200px; }
714
  .add-cat-form button { white-space: nowrap; }
715
 
716
  .category-block { border: 1px solid var(--border); margin-bottom: 15px; border-radius: 12px; overflow: hidden; background: #fff; }
717
+ .category-header { background: #fafafa; padding: 15px 20px; font-weight: 700; display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid var(--border); cursor: pointer; transition: background 0.2s; }
718
+ .category-header:hover { background: #f0f0f0; }
719
+ .category-content { padding: 0; display: none; }
720
+ .category-content.active { display: block; }
721
 
722
  .product-item { display: flex; justify-content: space-between; align-items: center; padding: 15px 20px; border-bottom: 1px solid var(--border); flex-wrap: wrap; gap: 10px; }
723
  .product-item:last-child { border-bottom: none; }
 
725
  .product-img { width: 50px; height: 50px; object-fit: cover; border-radius: 8px; border: 1px solid #eee; background: #fafafa; }
726
  .product-details { display: flex; flex-direction: column; }
727
  .product-name { font-weight: 600; font-size: 0.95rem; }
728
+ .product-desc { font-size: 0.85rem; color: #636e72; margin-top: 2px; }
729
+ .product-meta { font-size: 0.8rem; color: #b2bec3; margin-top: 4px; }
730
+
731
+ .add-product-wrapper { display: none; }
732
+ .add-product-wrapper.active { display: block; }
733
+ .toggle-add-product { width: 100%; text-align: center; background: #fafafa; padding: 15px; cursor: pointer; color: var(--success); font-weight: 600; transition: background 0.2s; border-bottom: 1px solid var(--border); }
734
+ .toggle-add-product:hover { background: #f0f0f0; }
735
 
736
+ .add-product-form { background: #fdfdfd; padding: 20px; display: flex; flex-direction: column; gap: 15px; }
737
  .form-row { display: flex; gap: 10px; flex-wrap: wrap; }
738
  .form-row > * { flex: 1; min-width: 150px; }
739
 
 
776
 
777
  {% for category in categories %}
778
  <div class="category-block">
779
+ <div class="category-header" onclick="toggleCategory('cat-{{ loop.index }}')">
780
+ <div style="display: flex; align-items: center; gap: 10px;">
781
+ <i class="fas fa-chevron-down" id="icon-cat-{{ loop.index }}" style="color: #636e72;"></i>
782
+ <span><i class="fas fa-folder-open" style="color:var(--info); margin-right:5px;"></i> {{ category }}</span>
783
+ </div>
784
+ <form method="POST" style="margin:0;" onclick="event.stopPropagation();" onsubmit="return confirm('Удалить категорию и все ее товары?');">
785
  <input type="hidden" name="action" value="delete_category">
786
  <input type="hidden" name="category_name" value="{{ category }}">
787
  <button type="submit" class="btn btn-danger"><i class="fas fa-trash-alt"></i></button>
788
  </form>
789
  </div>
790
+ <div class="category-content" id="cat-{{ loop.index }}">
791
+
792
+ <div class="toggle-add-product" onclick="toggleAddProduct('add-prod-{{ loop.index }}')">
793
+ <i class="fas fa-plus"></i> Добавить товар
794
+ </div>
795
 
796
+ <div class="add-product-wrapper" id="add-prod-{{ loop.index }}">
797
+ <form class="add-product-form" method="POST" enctype="multipart/form-data" onsubmit="showLoading(this)">
798
+ <input type="hidden" name="action" value="add_product">
799
+ <input type="hidden" name="category" value="{{ category }}">
800
+ <div style="font-weight: 600; font-size: 0.9rem; color: #636e72;">Новый товар в категории "{{ category }}"</div>
801
+ <div class="form-row">
802
+ <input type="text" name="name" placeholder="Название товара" required autocomplete="off" style="flex:2;">
803
+ <input type="number" name="price" placeholder="Цена" required step="0.01" style="flex:1;">
804
+ </div>
805
+ <textarea name="description" placeholder="Описание товара (необязательно)"></textarea>
806
+ <div class="file-input-wrapper">
807
+ <input type="file" name="photos" accept="image/*" multiple max="10" required>
808
+ <div style="font-size: 0.8rem; color: #999; margin-top: 5px;">Можно выбрать до 10 фото</div>
809
+ </div>
810
+ <button type="submit" class="btn btn-success" style="width: 100%; justify-content: center;"><i class="fas fa-check"></i> Сохранить товар</button>
811
+ </form>
812
+ </div>
813
+
814
  {% for product in products %}
815
  {% if product.category == category %}
816
  <div class="product-item">
 
822
  {% endif %}
823
  <div class="product-details">
824
  <span class="product-name">{{ product.name }}</span>
825
+ {% if product.description %}
826
+ <span class="product-desc">{{ product.description[:50] }}{{ '...' if product.description|length > 50 else '' }}</span>
827
+ {% endif %}
828
  <span class="product-meta">{{ product.price }} {{ currency_code }} • Фото: {{ product.photos|length if product.photos else 0 }}/10</span>
829
  </div>
830
  </div>
 
836
  </div>
837
  {% endif %}
838
  {% endfor %}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
839
  </div>
840
  </div>
841
  {% endfor %}
 
847
  btn.style.pointerEvents = 'none';
848
  btn.style.opacity = '0.7';
849
  }
850
+
851
+ function toggleCategory(id) {
852
+ const content = document.getElementById(id);
853
+ const icon = document.getElementById('icon-' + id);
854
+ if(content.classList.contains('active')) {
855
+ content.classList.remove('active');
856
+ icon.classList.remove('fa-chevron-up');
857
+ icon.classList.add('fa-chevron-down');
858
+ } else {
859
+ content.classList.add('active');
860
+ icon.classList.remove('fa-chevron-down');
861
+ icon.classList.add('fa-chevron-up');
862
+ }
863
+ }
864
+
865
+ function toggleAddProduct(id) {
866
+ const form = document.getElementById(id);
867
+ form.classList.toggle('active');
868
+ }
869
  </script>
870
  </body>
871
  </html>
 
959
  elif action == 'add_product':
960
  name = request.form.get('name', '').strip()
961
  price = float(request.form.get('price', 0))
962
+ description = request.form.get('description', '').strip()
963
  category = request.form.get('category')
964
  uploaded_photos = request.files.getlist('photos')[:10]
965
 
 
995
  'product_id': uuid4().hex,
996
  'name': name,
997
  'price': price,
998
+ 'description': description,
999
  'category': category,
1000
  'photos': photos_list
1001
  }