Kgshop commited on
Commit
7003471
·
verified ·
1 Parent(s): 7f0f138

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +65 -33
app.py CHANGED
@@ -12,15 +12,12 @@ from werkzeug.utils import secure_filename
12
  app = Flask(__name__)
13
  DATA_FILE = 'data.json'
14
 
15
- # Настройки Hugging Face
16
  REPO_ID = "Kgshop/ecokozha.data"
17
  HF_TOKEN_WRITE = os.getenv("HF_TOKEN")
18
  HF_TOKEN_READ = os.getenv("HF_TOKEN_READ")
19
 
20
- # Ссылка на логотип
21
  LOGO_URL = "https://huggingface.co/spaces/Kgshop/ecokozha/resolve/main/412767655_723792496081352_8094264164475669882_n.jpg"
22
 
23
- # Настройка логирования
24
  logging.basicConfig(level=logging.DEBUG)
25
 
26
  def load_data():
@@ -450,7 +447,6 @@ def catalog():
450
  </div>
451
  </div>
452
 
453
- <!-- Product Modal -->
454
  <div id="productModal" class="modal">
455
  <div class="modal-content">
456
  <span class="close" onclick="closeModal('productModal')">×</span>
@@ -458,18 +454,16 @@ def catalog():
458
  </div>
459
  </div>
460
 
461
- <!-- Quantity and Color Modal -->
462
  <div id="quantityModal" class="modal">
463
  <div class="modal-content">
464
  <span class="close" onclick="closeModal('quantityModal')">×</span>
465
  <h2>Укажите количество и цвет</h2>
466
- <input type="number" id="quantityInput" class="quantity-input" min="1" value="1">
467
  <select id="colorSelect" class="color-select"></select>
468
  <button class="product-button" onclick="confirmAddToCart()">Добавить</button>
469
  </div>
470
  </div>
471
 
472
- <!-- Cart Modal -->
473
  <div id="cartModal" class="modal">
474
  <div class="modal-content">
475
  <span class="close" onclick="closeModal('cartModal')">×</span>
@@ -560,12 +554,14 @@ def catalog():
560
 
561
  function confirmAddToCart() {
562
  if (selectedProductIndex === null) return;
563
- const quantity = parseInt(document.getElementById('quantityInput').value) || 1;
564
  const color = document.getElementById('colorSelect').value;
565
- if (quantity <= 0) {
566
- alert("Укажите количество больше 0");
 
567
  return;
568
  }
 
569
  let cart = JSON.parse(localStorage.getItem('cart') || '[]');
570
  const product = products[selectedProductIndex];
571
  const cartItemId = `${product.name}-${color}`;
@@ -608,15 +604,15 @@ def catalog():
608
  ${item.photo ? `<img src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/photos/${item.photo}" alt="${item.name}">` : ''}
609
  <div>
610
  <strong>${item.name}</strong>
611
- <p>${item.price} с × ${item.quantity} (Цвет: ${item.color})</p>
612
  </div>
613
  </div>
614
- <span>${itemTotal} с</span>
615
  </div>
616
  `;
617
  }).join('');
618
 
619
- document.getElementById('cartTotal').textContent = total;
620
  document.getElementById('cartModal').style.display = 'block';
621
  }
622
 
@@ -631,9 +627,9 @@ def catalog():
631
  cart.forEach((item, index) => {
632
  const itemTotal = item.price * item.quantity;
633
  total += itemTotal;
634
- orderText += `${index + 1}. ${item.name} - ${item.price} с × ${item.quantity} (Цвет: ${item.color})%0A`;
635
  });
636
- orderText += `Итого: ${total} с`;
637
  window.open(`https://api.whatsapp.com/send?phone=996702588388&text=${orderText}`, '_blank');
638
  }
639
 
@@ -753,7 +749,7 @@ def admin():
753
  photos_list = []
754
 
755
  if photos_files:
756
- for photo in photos_files[:10]: # Ограничение до 10 фото
757
  if photo and photo.filename:
758
  photo_filename = secure_filename(photo.filename)
759
  uploads_dir = 'uploads'
@@ -783,7 +779,7 @@ def admin():
783
  'description': description,
784
  'category': category if category in categories else 'Без категории',
785
  'photos': photos_list,
786
- 'colors': colors if colors else []
787
  }
788
  products.append(new_product)
789
  save_data(data)
@@ -800,7 +796,7 @@ def admin():
800
 
801
  if photos_files and any(photo.filename for photo in photos_files):
802
  new_photos_list = []
803
- for photo in photos_files[:10]: # Ограничение до 10 фото
804
  if photo and photo.filename:
805
  photo_filename = secure_filename(photo.filename)
806
  uploads_dir = 'uploads'
@@ -825,7 +821,7 @@ def admin():
825
  products[index]['price'] = float(price.replace(',', '.'))
826
  products[index]['description'] = description
827
  products[index]['category'] = category if category in categories else 'Без категории'
828
- products[index]['colors'] = colors if colors else []
829
  save_data(data)
830
  return redirect(url_for('admin'))
831
 
@@ -897,6 +893,7 @@ def admin():
897
  border-radius: 8px;
898
  font-size: 1rem;
899
  transition: all 0.3s ease;
 
900
  }
901
  input:focus, textarea:focus, select:focus {
902
  border-color: #3b82f6;
@@ -946,9 +943,20 @@ def admin():
946
  display: flex;
947
  gap: 10px;
948
  margin-top: 5px;
 
 
 
 
 
 
 
 
 
 
949
  }
950
  .add-color-btn {
951
  background-color: #10b981;
 
952
  }
953
  .add-color-btn:hover {
954
  background-color: #059669;
@@ -980,12 +988,13 @@ def admin():
980
  <label>Фотографии (до 10):</label>
981
  <input type="file" name="photos" accept="image/*" multiple>
982
  <label>Цвета:</label>
983
- <div id="color-inputs">
984
  <div class="color-input-group">
985
  <input type="text" name="colors" placeholder="Например: Красный">
 
986
  </div>
987
  </div>
988
- <button type="button" class="add-color-btn" onclick="addColorInput()">Добавить цвет</button>
989
  <button type="submit">Добавить товар</button>
990
  </form>
991
 
@@ -1029,7 +1038,7 @@ def admin():
1029
  <p><strong>Описание:</strong> {{ product['description'] }}</p>
1030
  <p><strong>Цвета:</strong> {{ product.get('colors', ['Нет цветов'])|join(', ') }}</p>
1031
  {% if product.get('photos') and product['photos']|length > 0 %}
1032
- <div style="display: flex; flex-wrap: wrap; gap: 10px;">
1033
  {% for photo in product['photos'] %}
1034
  <img src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/photos/{{ photo }}"
1035
  alt="{{ product['name'] }}"
@@ -1051,25 +1060,32 @@ def admin():
1051
  <label>Категория:</label>
1052
  <select name="category">
1053
  <option value="Без категории" {% if product.get('category', 'Без категории') == 'Без категории' %}selected{% endif %}>Без категории</option>
1054
- {% for category in categories %}
1055
- <option value="{{ category }}" {% if product.get('category') == category %}selected{% endif %}>{{ category }}</option>
1056
  {% endfor %}
1057
  </select>
1058
- <label>Фотографии (до 10):</label>
1059
  <input type="file" name="photos" accept="image/*" multiple>
1060
  <label>Цвета:</label>
1061
  <div id="edit-color-inputs-{{ loop.index0 }}">
1062
  {% for color in product.get('colors', []) %}
1063
  <div class="color-input-group">
1064
  <input type="text" name="colors" value="{{ color }}">
 
1065
  </div>
1066
  {% endfor %}
 
 
 
 
 
 
1067
  </div>
1068
  <button type="button" class="add-color-btn" onclick="addColorInput('edit-color-inputs-{{ loop.index0 }}')">Добавить цвет</button>
1069
  <button type="submit">Сохранить</button>
1070
  </form>
1071
  </details>
1072
- <form method="POST">
1073
  <input type="hidden" name="action" value="delete">
1074
  <input type="hidden" name="index" value="{{ loop.index0 }}">
1075
  <button type="submit" class="delete-button">Удалить</button>
@@ -1079,12 +1095,27 @@ def admin():
1079
  </div>
1080
  </div>
1081
  <script>
1082
- function addColorInput(containerId = 'color-inputs') {
1083
  const container = document.getElementById(containerId);
1084
- const newInput = document.createElement('div');
1085
- newInput.className = 'color-input-group';
1086
- newInput.innerHTML = '<input type="text" name="colors" placeholder="Например: Красный">';
1087
- container.appendChild(newInput);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1088
  }
1089
  </script>
1090
  </body>
@@ -1103,8 +1134,9 @@ def download():
1103
  return "База данных скачана.", 200
1104
 
1105
  if __name__ == '__main__':
1106
- backup_thread = threading.Thread(target=periodic_backup, daemon=True)
1107
- backup_thread.start()
 
1108
  try:
1109
  load_data()
1110
  except Exception as e:
 
12
  app = Flask(__name__)
13
  DATA_FILE = 'data.json'
14
 
 
15
  REPO_ID = "Kgshop/ecokozha.data"
16
  HF_TOKEN_WRITE = os.getenv("HF_TOKEN")
17
  HF_TOKEN_READ = os.getenv("HF_TOKEN_READ")
18
 
 
19
  LOGO_URL = "https://huggingface.co/spaces/Kgshop/ecokozha/resolve/main/412767655_723792496081352_8094264164475669882_n.jpg"
20
 
 
21
  logging.basicConfig(level=logging.DEBUG)
22
 
23
  def load_data():
 
447
  </div>
448
  </div>
449
 
 
450
  <div id="productModal" class="modal">
451
  <div class="modal-content">
452
  <span class="close" onclick="closeModal('productModal')">×</span>
 
454
  </div>
455
  </div>
456
 
 
457
  <div id="quantityModal" class="modal">
458
  <div class="modal-content">
459
  <span class="close" onclick="closeModal('quantityModal')">×</span>
460
  <h2>Укажите количество и цвет</h2>
461
+ <input type="number" id="quantityInput" class="quantity-input" step="0.1" min="0.1" value="1">
462
  <select id="colorSelect" class="color-select"></select>
463
  <button class="product-button" onclick="confirmAddToCart()">Добавить</button>
464
  </div>
465
  </div>
466
 
 
467
  <div id="cartModal" class="modal">
468
  <div class="modal-content">
469
  <span class="close" onclick="closeModal('cartModal')">×</span>
 
554
 
555
  function confirmAddToCart() {
556
  if (selectedProductIndex === null) return;
557
+ const quantity = parseFloat(document.getElementById('quantityInput').value);
558
  const color = document.getElementById('colorSelect').value;
559
+
560
+ if (isNaN(quantity) || quantity <= 0) {
561
+ alert("Укажите корректное количество больше 0");
562
  return;
563
  }
564
+
565
  let cart = JSON.parse(localStorage.getItem('cart') || '[]');
566
  const product = products[selectedProductIndex];
567
  const cartItemId = `${product.name}-${color}`;
 
604
  ${item.photo ? `<img src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/photos/${item.photo}" alt="${item.name}">` : ''}
605
  <div>
606
  <strong>${item.name}</strong>
607
+ <p>${item.price} с × ${item.quantity.toFixed(2)} (Цвет: ${item.color})</p>
608
  </div>
609
  </div>
610
+ <span>${itemTotal.toFixed(2)} с</span>
611
  </div>
612
  `;
613
  }).join('');
614
 
615
+ document.getElementById('cartTotal').textContent = total.toFixed(2);
616
  document.getElementById('cartModal').style.display = 'block';
617
  }
618
 
 
627
  cart.forEach((item, index) => {
628
  const itemTotal = item.price * item.quantity;
629
  total += itemTotal;
630
+ orderText += `${index + 1}. ${item.name} - ${item.price} с × ${item.quantity.toFixed(2)} (Цвет: ${item.color})%0A`;
631
  });
632
+ orderText += `Итого: ${total.toFixed(2)} с`;
633
  window.open(`https://api.whatsapp.com/send?phone=996702588388&text=${orderText}`, '_blank');
634
  }
635
 
 
749
  photos_list = []
750
 
751
  if photos_files:
752
+ for photo in photos_files[:10]:
753
  if photo and photo.filename:
754
  photo_filename = secure_filename(photo.filename)
755
  uploads_dir = 'uploads'
 
779
  'description': description,
780
  'category': category if category in categories else 'Без категории',
781
  'photos': photos_list,
782
+ 'colors': [c for c in colors if c.strip()] if colors else []
783
  }
784
  products.append(new_product)
785
  save_data(data)
 
796
 
797
  if photos_files and any(photo.filename for photo in photos_files):
798
  new_photos_list = []
799
+ for photo in photos_files[:10]:
800
  if photo and photo.filename:
801
  photo_filename = secure_filename(photo.filename)
802
  uploads_dir = 'uploads'
 
821
  products[index]['price'] = float(price.replace(',', '.'))
822
  products[index]['description'] = description
823
  products[index]['category'] = category if category in categories else 'Без категории'
824
+ products[index]['colors'] = [c for c in colors if c.strip()] if colors else []
825
  save_data(data)
826
  return redirect(url_for('admin'))
827
 
 
893
  border-radius: 8px;
894
  font-size: 1rem;
895
  transition: all 0.3s ease;
896
+ box-sizing: border-box;
897
  }
898
  input:focus, textarea:focus, select:focus {
899
  border-color: #3b82f6;
 
943
  display: flex;
944
  gap: 10px;
945
  margin-top: 5px;
946
+ align-items: center;
947
+ }
948
+ .color-input-group input {
949
+ flex-grow: 1;
950
+ }
951
+ .remove-color-btn {
952
+ background-color: #ef4444;
953
+ padding: 8px 12px;
954
+ font-size: 0.8rem;
955
+ margin-top: 5px;
956
  }
957
  .add-color-btn {
958
  background-color: #10b981;
959
+ margin-bottom: 10px;
960
  }
961
  .add-color-btn:hover {
962
  background-color: #059669;
 
988
  <label>Фотографии (до 10):</label>
989
  <input type="file" name="photos" accept="image/*" multiple>
990
  <label>Цвета:</label>
991
+ <div id="color-inputs-add">
992
  <div class="color-input-group">
993
  <input type="text" name="colors" placeholder="Например: Красный">
994
+ <button type="button" class="remove-color-btn" onclick="this.parentElement.remove()">Удалить</button>
995
  </div>
996
  </div>
997
+ <button type="button" class="add-color-btn" onclick="addColorInput('color-inputs-add')">Добавить цвет</button>
998
  <button type="submit">Добавить товар</button>
999
  </form>
1000
 
 
1038
  <p><strong>Описание:</strong> {{ product['description'] }}</p>
1039
  <p><strong>Цвета:</strong> {{ product.get('colors', ['Нет цветов'])|join(', ') }}</p>
1040
  {% if product.get('photos') and product['photos']|length > 0 %}
1041
+ <div style="display: flex; flex-wrap: wrap; gap: 10px; margin-bottom:10px;">
1042
  {% for photo in product['photos'] %}
1043
  <img src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/photos/{{ photo }}"
1044
  alt="{{ product['name'] }}"
 
1060
  <label>Категория:</label>
1061
  <select name="category">
1062
  <option value="Без категории" {% if product.get('category', 'Без категории') == 'Без категории' %}selected{% endif %}>Без категории</option>
1063
+ {% for category_item in categories %}
1064
+ <option value="{{ category_item }}" {% if product.get('category') == category_item %}selected{% endif %}>{{ category_item }}</option>
1065
  {% endfor %}
1066
  </select>
1067
+ <label>Фотографии (до 10, выберите новые, чтобы заменить старые):</label>
1068
  <input type="file" name="photos" accept="image/*" multiple>
1069
  <label>Цвета:</label>
1070
  <div id="edit-color-inputs-{{ loop.index0 }}">
1071
  {% for color in product.get('colors', []) %}
1072
  <div class="color-input-group">
1073
  <input type="text" name="colors" value="{{ color }}">
1074
+ <button type="button" class="remove-color-btn" onclick="this.parentElement.remove()">Удалить</button>
1075
  </div>
1076
  {% endfor %}
1077
+ {% if not product.get('colors') %}
1078
+ <div class="color-input-group">
1079
+ <input type="text" name="colors" placeholder="Например: Синий">
1080
+ <button type="button" class="remove-color-btn" onclick="this.parentElement.remove()">Удалить</button>
1081
+ </div>
1082
+ {% endif %}
1083
  </div>
1084
  <button type="button" class="add-color-btn" onclick="addColorInput('edit-color-inputs-{{ loop.index0 }}')">Добавить цвет</button>
1085
  <button type="submit">Сохранить</button>
1086
  </form>
1087
  </details>
1088
+ <form method="POST" style="display: inline;">
1089
  <input type="hidden" name="action" value="delete">
1090
  <input type="hidden" name="index" value="{{ loop.index0 }}">
1091
  <button type="submit" class="delete-button">Удалить</button>
 
1095
  </div>
1096
  </div>
1097
  <script>
1098
+ function addColorInput(containerId) {
1099
  const container = document.getElementById(containerId);
1100
+ const newInputGroup = document.createElement('div');
1101
+ newInputGroup.className = 'color-input-group';
1102
+
1103
+ const newInput = document.createElement('input');
1104
+ newInput.type = 'text';
1105
+ newInput.name = 'colors';
1106
+ newInput.placeholder = 'Например: Красный';
1107
+
1108
+ const removeBtn = document.createElement('button');
1109
+ removeBtn.type = 'button';
1110
+ removeBtn.className = 'remove-color-btn';
1111
+ removeBtn.textContent = 'Удалить';
1112
+ removeBtn.onclick = function() {
1113
+ newInputGroup.remove();
1114
+ };
1115
+
1116
+ newInputGroup.appendChild(newInput);
1117
+ newInputGroup.appendChild(removeBtn);
1118
+ container.appendChild(newInputGroup);
1119
  }
1120
  </script>
1121
  </body>
 
1134
  return "База данных скачана.", 200
1135
 
1136
  if __name__ == '__main__':
1137
+ if HF_TOKEN_WRITE and REPO_ID:
1138
+ backup_thread = threading.Thread(target=periodic_backup, daemon=True)
1139
+ backup_thread.start()
1140
  try:
1141
  load_data()
1142
  except Exception as e: