Shveiauto commited on
Commit
fcfef10
·
verified ·
1 Parent(s): 7d37fee

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +106 -38
app.py CHANGED
@@ -261,7 +261,7 @@ CATALOG_TEMPLATE = '''
261
  .cart-item-total { font-weight: bold; text-align: right; grid-column: 3; font-size: 1rem; color: #ffcc00;}
262
  .cart-item-remove { grid-column: 4; background:none; border:none; color:#f56565; cursor:pointer; font-size: 1.3em; padding: 5px; line-height: 1; }
263
  .cart-item-remove:hover { color: #c53030; }
264
- .quantity-input, .color-select { width: 100%; max-width: 180px; padding: 10px; border: 1px solid #444; border-radius: 8px; font-size: 1rem; margin: 10px 0; box-sizing: border-box; background-color: #333; color: #eee;}
265
  .cart-summary { margin-top: 20px; text-align: right; border-top: 1px solid #444; padding-top: 15px; }
266
  .cart-summary strong { font-size: 1.2rem; color: #ffcc00;}
267
  .cart-actions { margin-top: 25px; display: flex; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
@@ -348,11 +348,13 @@ CATALOG_TEMPLATE = '''
348
  <div id="quantityModal" class="modal">
349
  <div class="modal-content">
350
  <span class="close" onclick="closeModal('quantityModal')" aria-label="Закрыть">×</span>
351
- <h2>Укажите количество и цвет</h2>
352
  <label for="quantityInput">Количество:</label>
353
  <input type="number" id="quantityInput" class="quantity-input" min="1" value="1">
354
  <label for="colorSelect">Цвет/Вариант:</label>
355
  <select id="colorSelect" class="color-select"></select>
 
 
356
  <button class="product-button add-to-cart" onclick="confirmAddToCart()"><i class="fas fa-check"></i> Добавить в корзину</button>
357
  </div>
358
  </div>
@@ -477,9 +479,7 @@ CATALOG_TEMPLATE = '''
477
  const colorSelect = document.getElementById('colorSelect');
478
  const colorLabel = document.querySelector('label[for="colorSelect"]');
479
  colorSelect.innerHTML = '';
480
-
481
  const validColors = product.colors ? product.colors.filter(c => c && c.trim() !== "") : [];
482
-
483
  if (validColors.length > 0) {
484
  validColors.forEach(color => {
485
  const option = document.createElement('option');
@@ -494,6 +494,25 @@ CATALOG_TEMPLATE = '''
494
  if(colorLabel) colorLabel.style.display = 'none';
495
  }
496
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
497
  document.getElementById('quantityInput').value = 1;
498
  const modal = document.getElementById('quantityModal');
499
  if(modal) {
@@ -507,9 +526,13 @@ CATALOG_TEMPLATE = '''
507
 
508
  const quantityInput = document.getElementById('quantityInput');
509
  const quantity = parseInt(quantityInput.value);
 
510
  const colorSelect = document.getElementById('colorSelect');
511
  const color = colorSelect.style.display !== 'none' && colorSelect.value ? colorSelect.value : 'N/A';
512
 
 
 
 
513
  if (isNaN(quantity) || quantity <= 0) {
514
  alert("Пожалуйста, укажите корректное количество (больше 0).");
515
  quantityInput.focus();
@@ -522,7 +545,7 @@ CATALOG_TEMPLATE = '''
522
  return;
523
  }
524
 
525
- const cartItemId = `${product.name}-${color}`;
526
  const existingItemIndex = cart.findIndex(item => item.id === cartItemId);
527
 
528
  if (existingItemIndex > -1) {
@@ -534,7 +557,8 @@ CATALOG_TEMPLATE = '''
534
  price: product.price,
535
  photo: product.photos && product.photos.length > 0 ? product.photos[0] : null,
536
  quantity: quantity,
537
- color: color
 
538
  });
539
  }
540
 
@@ -579,12 +603,13 @@ CATALOG_TEMPLATE = '''
579
  ? `https://huggingface.co/datasets/${repoId}/resolve/main/photos/${item.photo}`
580
  : 'https://via.placeholder.com/60x60.png?text=N/A';
581
  const colorText = item.color !== 'N/A' ? ` (Цвет: ${item.color})` : '';
 
582
 
583
  return `
584
  <div class="cart-item">
585
  <img src="${photoUrl}" alt="${item.name}">
586
  <div class="cart-item-details">
587
- <strong>${item.name}${colorText}</strong>
588
  <p class="cart-item-price">${item.price.toFixed(2)} ${currencyCode} × ${item.quantity}</p>
589
  </div>
590
  <span class="cart-item-total">${itemTotal.toFixed(2)} ${currencyCode}</span>
@@ -722,7 +747,7 @@ CATALOG_TEMPLATE = '''
722
  const newPlaceholder = document.createElement('div');
723
  newPlaceholder.id = 'notification-placeholder';
724
  newPlaceholder.style.position = 'fixed';
725
- newPlaceholder.style.bottom = '150px'; // Adjusted for bottom nav
726
  newPlaceholder.style.left = '50%';
727
  newPlaceholder.style.transform = 'translateX(-50%)';
728
  newPlaceholder.style.zIndex = '1002';
@@ -805,6 +830,10 @@ PRODUCT_DETAIL_TEMPLATE = '''
805
  {% if colors and colors|select('ne', '')|list|length > 0 %}
806
  <p><strong>Доступные цвета/варианты:</strong> {{ colors|select('ne', '')|join(', ') }}</p>
807
  {% endif %}
 
 
 
 
808
  </div>
809
  </div>
810
  '''
@@ -858,7 +887,7 @@ ORDER_TEMPLATE = '''
858
  <div class="order-item">
859
  <img src="{{ item.photo_url }}" alt="{{ item.name }}">
860
  <div class="item-details">
861
- <strong>{{ item.name }} {% if item.color != 'N/A' %}({{ item.color }}){% endif %}</strong>
862
  <span>{{ "%.2f"|format(item.price) }} {{ currency_code }} × {{ item.quantity }}</span>
863
  </div>
864
  <div class="item-total">
@@ -889,9 +918,8 @@ ORDER_TEMPLATE = '''
889
  function sendOrderViaWhatsApp() {
890
  const orderId = '{{ order.id }}';
891
  const orderUrl = `{{ request.url }}`;
892
- const whatsappNumber = "996700575754"; // Updated Number
893
-
894
- let message = `Здравствуйте! Хочу подтвердить свой заказ на Yasin Luxury:%0A%0A`; // Updated Name
895
  message += `*Номер заказа:* ${orderId}%0A`;
896
  message += `*Ссылка на заказ:* ${encodeURIComponent(orderUrl)}%0A%0A`;
897
  message += `Пожалуйста, свяжитесь со мной для уточнения деталей оплаты и доставки.`;
@@ -965,12 +993,12 @@ ADMIN_TEMPLATE = '''
965
  details[open] > summary::after { transform: translateY(-50%) rotate(180deg); }
966
  details[open] > summary { border-bottom: 1px solid #444; }
967
  details .form-content { padding: 20px; }
968
- .color-input-group { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
969
- .color-input-group input { flex-grow: 1; margin: 0; }
970
- .remove-color-btn { background-color: #c53030; padding: 6px 10px; font-size: 0.8rem; margin-top: 0; line-height: 1; color: white;}
971
- .remove-color-btn:hover { background-color: #9b2c2c; }
972
- .add-color-btn { background-color: #ffd700; color: #1a1a1a;}
973
- .add-color-btn:hover { background-color: #ffcc00; }
974
  .photo-preview img { max-width: 70px; max-height: 70px; border-radius: 5px; margin: 5px 5px 0 0; border: 1px solid #555; object-fit: cover;}
975
  .sync-buttons { display: flex; gap: 10px; margin-bottom: 20px; flex-wrap: wrap; }
976
  .download-hf-button { background-color: #444; color: #eee; }
@@ -1082,15 +1110,27 @@ ADMIN_TEMPLATE = '''
1082
  </select>
1083
  <label for="add_photos">Фотографии (до 10 шт.):</label>
1084
  <input type="file" id="add_photos" name="photos" accept="image/*" multiple>
 
1085
  <label>Цвета/Варианты (оставьте пустым, если нет):</label>
1086
  <div id="add-color-inputs">
1087
- <div class="color-input-group">
1088
  <input type="text" name="colors" placeholder="Например: Розовый">
1089
- <button type="button" class="remove-color-btn" onclick="removeColorInput(this)"><i class="fas fa-times"></i></button>
 
 
 
 
 
 
 
 
 
 
1090
  </div>
1091
  </div>
1092
- <button type="button" class="button add-color-btn" style="margin-top: 5px;" onclick="addColorInput('add-color-inputs')"><i class="fas fa-palette"></i> Добавить поле для цвета/варианта</button>
1093
  <br>
 
1094
  <div style="margin-top: 15px;">
1095
  <input type="checkbox" id="add_in_stock" name="in_stock" checked>
1096
  <label for="add_in_stock" class="inline-label">В наличии</label>
@@ -1137,6 +1177,8 @@ ADMIN_TEMPLATE = '''
1137
  <p class="description" title="{{ product.get('description', '') }}"><strong>Описание:</strong> {{ product.get('description', 'N/A')[:150] }}{% if product.get('description', '')|length > 150 %}...{% endif %}</p>
1138
  {% set colors = product.get('colors', []) %}
1139
  <p><strong>Цвета/Вар-ты:</strong> {{ colors|select('ne', '')|join(', ') if colors|select('ne', '')|list|length > 0 else 'Нет' }}</p>
 
 
1140
  {% if product.get('photos') and product['photos']|length > 1 %}
1141
  <p style="font-size: 0.8rem; color: #bbb;">(Всего фото: {{ product['photos']|length }})</p>
1142
  {% endif %}
@@ -1186,21 +1228,44 @@ ADMIN_TEMPLATE = '''
1186
  {% if current_colors and current_colors|select('ne', '')|list|length > 0 %}
1187
  {% for color in current_colors %}
1188
  {% if color.strip() %}
1189
- <div class="color-input-group">
1190
  <input type="text" name="colors" value="{{ color }}">
1191
- <button type="button" class="remove-color-btn" onclick="removeColorInput(this)"><i class="fas fa-times"></i></button>
1192
  </div>
1193
  {% endif %}
1194
  {% endfor %}
1195
  {% else %}
1196
- <div class="color-input-group">
1197
  <input type="text" name="colors" placeholder="Например: Цвет">
1198
- <button type="button" class="remove-color-btn" onclick="removeColorInput(this)"><i class="fas fa-times"></i></button>
1199
  </div>
1200
  {% endif %}
1201
  </div>
1202
- <button type="button" class="button add-color-btn" style="margin-top: 5px;" onclick="addColorInput('edit-color-inputs-{{ loop.index0 }}')"><i class="fas fa-palette"></i> Добавить поле для цвета</button>
1203
  <br>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1204
  <div style="margin-top: 15px;">
1205
  <input type="checkbox" id="edit_in_stock_{{ loop.index0 }}" name="in_stock" {% if product.get('in_stock', True) %}checked{% endif %}>
1206
  <label for="edit_in_stock_{{ loop.index0 }}" class="inline-label">В наличии</label>
@@ -1231,39 +1296,39 @@ ADMIN_TEMPLATE = '''
1231
  }
1232
  }
1233
 
1234
- function addColorInput(containerId) {
1235
  const container = document.getElementById(containerId);
1236
  if (container) {
1237
  const newInputGroup = document.createElement('div');
1238
- newInputGroup.className = 'color-input-group';
1239
  newInputGroup.innerHTML = `
1240
- <input type="text" name="colors" placeholder="Новый цвет/вариант">
1241
- <button type="button" class="remove-color-btn" onclick="removeColorInput(this)"><i class="fas fa-times"></i></button>
1242
  `;
1243
  container.appendChild(newInputGroup);
1244
- const newInput = newInputGroup.querySelector('input[name="colors"]');
1245
  if (newInput) {
1246
  newInput.focus();
1247
  }
1248
  }
1249
  }
1250
 
1251
- function removeColorInput(button) {
1252
- const group = button.closest('.color-input-group');
1253
  if (group) {
1254
  const container = group.parentNode;
1255
  group.remove();
1256
  if (container && container.children.length === 0) {
1257
  const placeholderGroup = document.createElement('div');
1258
- placeholderGroup.className = 'color-input-group';
1259
  placeholderGroup.innerHTML = `
1260
- <input type="text" name="colors" placeholder="Например: Цвет">
1261
- <button type="button" class="remove-color-btn" onclick="removeColorInput(this)"><i class="fas fa-times"></i></button>
1262
  `;
1263
  container.appendChild(placeholderGroup);
1264
  }
1265
  } else {
1266
- console.warn("Could not find parent .color-input-group for remove button");
1267
  }
1268
  }
1269
  </script>
@@ -1336,6 +1401,7 @@ def create_order():
1336
  "price": price,
1337
  "quantity": quantity,
1338
  "color": item.get('color', 'N/A'),
 
1339
  "photo": item.get('photo'),
1340
  "photo_url": f"https://huggingface.co/datasets/{REPO_ID}/resolve/main/photos/{item['photo']}" if item.get('photo') else "https://via.placeholder.com/60x60.png?text=N/A"
1341
  })
@@ -1437,6 +1503,7 @@ def admin():
1437
  category = request.form.get('category')
1438
  photos_files = request.files.getlist('photos')
1439
  colors = [c.strip() for c in request.form.getlist('colors') if c.strip()]
 
1440
  in_stock = 'in_stock' in request.form
1441
  is_top = 'is_top' in request.form
1442
 
@@ -1508,7 +1575,7 @@ def admin():
1508
  new_product = {
1509
  'name': name, 'price': price, 'description': description,
1510
  'category': category if category in categories else 'Без категории',
1511
- 'photos': photos_list, 'colors': colors,
1512
  'in_stock': in_stock, 'is_top': is_top
1513
  }
1514
  products.append(new_product)
@@ -1541,6 +1608,7 @@ def admin():
1541
  category = request.form.get('category')
1542
  product_to_edit['category'] = category if category in categories else 'Без категории'
1543
  product_to_edit['colors'] = [c.strip() for c in request.form.getlist('colors') if c.strip()]
 
1544
  product_to_edit['in_stock'] = 'in_stock' in request.form
1545
  product_to_edit['is_top'] = 'is_top' in request.form
1546
 
 
261
  .cart-item-total { font-weight: bold; text-align: right; grid-column: 3; font-size: 1rem; color: #ffcc00;}
262
  .cart-item-remove { grid-column: 4; background:none; border:none; color:#f56565; cursor:pointer; font-size: 1.3em; padding: 5px; line-height: 1; }
263
  .cart-item-remove:hover { color: #c53030; }
264
+ .quantity-input, .color-select, .size-select { width: 100%; max-width: 180px; padding: 10px; border: 1px solid #444; border-radius: 8px; font-size: 1rem; margin: 10px 0; box-sizing: border-box; background-color: #333; color: #eee;}
265
  .cart-summary { margin-top: 20px; text-align: right; border-top: 1px solid #444; padding-top: 15px; }
266
  .cart-summary strong { font-size: 1.2rem; color: #ffcc00;}
267
  .cart-actions { margin-top: 25px; display: flex; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
 
348
  <div id="quantityModal" class="modal">
349
  <div class="modal-content">
350
  <span class="close" onclick="closeModal('quantityModal')" aria-label="Закрыть">×</span>
351
+ <h2>Укажите детали</h2>
352
  <label for="quantityInput">Количество:</label>
353
  <input type="number" id="quantityInput" class="quantity-input" min="1" value="1">
354
  <label for="colorSelect">Цвет/Вариант:</label>
355
  <select id="colorSelect" class="color-select"></select>
356
+ <label for="sizeSelect">Размер:</label>
357
+ <select id="sizeSelect" class="size-select"></select>
358
  <button class="product-button add-to-cart" onclick="confirmAddToCart()"><i class="fas fa-check"></i> Добавить в корзину</button>
359
  </div>
360
  </div>
 
479
  const colorSelect = document.getElementById('colorSelect');
480
  const colorLabel = document.querySelector('label[for="colorSelect"]');
481
  colorSelect.innerHTML = '';
 
482
  const validColors = product.colors ? product.colors.filter(c => c && c.trim() !== "") : [];
 
483
  if (validColors.length > 0) {
484
  validColors.forEach(color => {
485
  const option = document.createElement('option');
 
494
  if(colorLabel) colorLabel.style.display = 'none';
495
  }
496
 
497
+ const sizeSelect = document.getElementById('sizeSelect');
498
+ const sizeLabel = document.querySelector('label[for="sizeSelect"]');
499
+ sizeSelect.innerHTML = '';
500
+ const validSizes = product.sizes ? product.sizes.filter(s => s && s.trim() !== "") : [];
501
+ if (validSizes.length > 0) {
502
+ validSizes.forEach(size => {
503
+ const option = document.createElement('option');
504
+ option.value = size.trim();
505
+ option.text = size.trim();
506
+ sizeSelect.appendChild(option);
507
+ });
508
+ sizeSelect.style.display = 'block';
509
+ if(sizeLabel) sizeLabel.style.display = 'block';
510
+ } else {
511
+ sizeSelect.style.display = 'none';
512
+ if(sizeLabel) sizeLabel.style.display = 'none';
513
+ }
514
+
515
+
516
  document.getElementById('quantityInput').value = 1;
517
  const modal = document.getElementById('quantityModal');
518
  if(modal) {
 
526
 
527
  const quantityInput = document.getElementById('quantityInput');
528
  const quantity = parseInt(quantityInput.value);
529
+
530
  const colorSelect = document.getElementById('colorSelect');
531
  const color = colorSelect.style.display !== 'none' && colorSelect.value ? colorSelect.value : 'N/A';
532
 
533
+ const sizeSelect = document.getElementById('sizeSelect');
534
+ const size = sizeSelect.style.display !== 'none' && sizeSelect.value ? sizeSelect.value : 'N/A';
535
+
536
  if (isNaN(quantity) || quantity <= 0) {
537
  alert("Пожалуйста, укажите корректное количество (больше 0).");
538
  quantityInput.focus();
 
545
  return;
546
  }
547
 
548
+ const cartItemId = `${product.name}-${color}-${size}`;
549
  const existingItemIndex = cart.findIndex(item => item.id === cartItemId);
550
 
551
  if (existingItemIndex > -1) {
 
557
  price: product.price,
558
  photo: product.photos && product.photos.length > 0 ? product.photos[0] : null,
559
  quantity: quantity,
560
+ color: color,
561
+ size: size
562
  });
563
  }
564
 
 
603
  ? `https://huggingface.co/datasets/${repoId}/resolve/main/photos/${item.photo}`
604
  : 'https://via.placeholder.com/60x60.png?text=N/A';
605
  const colorText = item.color !== 'N/A' ? ` (Цвет: ${item.color})` : '';
606
+ const sizeText = item.size && item.size !== 'N/A' ? ` (Размер: ${item.size})` : '';
607
 
608
  return `
609
  <div class="cart-item">
610
  <img src="${photoUrl}" alt="${item.name}">
611
  <div class="cart-item-details">
612
+ <strong>${item.name}${colorText}${sizeText}</strong>
613
  <p class="cart-item-price">${item.price.toFixed(2)} ${currencyCode} × ${item.quantity}</p>
614
  </div>
615
  <span class="cart-item-total">${itemTotal.toFixed(2)} ${currencyCode}</span>
 
747
  const newPlaceholder = document.createElement('div');
748
  newPlaceholder.id = 'notification-placeholder';
749
  newPlaceholder.style.position = 'fixed';
750
+ newPlaceholder.style.bottom = '150px';
751
  newPlaceholder.style.left = '50%';
752
  newPlaceholder.style.transform = 'translateX(-50%)';
753
  newPlaceholder.style.zIndex = '1002';
 
830
  {% if colors and colors|select('ne', '')|list|length > 0 %}
831
  <p><strong>Доступные цвета/варианты:</strong> {{ colors|select('ne', '')|join(', ') }}</p>
832
  {% endif %}
833
+ {% set sizes = product.get('sizes', []) %}
834
+ {% if sizes and sizes|select('ne', '')|list|length > 0 %}
835
+ <p><strong>Доступные размеры:</strong> {{ sizes|select('ne', '')|join(', ') }}</p>
836
+ {% endif %}
837
  </div>
838
  </div>
839
  '''
 
887
  <div class="order-item">
888
  <img src="{{ item.photo_url }}" alt="{{ item.name }}">
889
  <div class="item-details">
890
+ <strong>{{ item.name }} {% if item.color != 'N/A' %}({{ item.color }}){% endif %} {% if item.size and item.size != 'N/A' %}(Р: {{ item.size }}){% endif %}</strong>
891
  <span>{{ "%.2f"|format(item.price) }} {{ currency_code }} × {{ item.quantity }}</span>
892
  </div>
893
  <div class="item-total">
 
918
  function sendOrderViaWhatsApp() {
919
  const orderId = '{{ order.id }}';
920
  const orderUrl = `{{ request.url }}`;
921
+ const whatsappNumber = "996700575754";
922
+ let message = `Здравствуйте! Хочу подтвердить свой заказ на Yasin Luxury:%0A%0A`;
 
923
  message += `*Номер заказа:* ${orderId}%0A`;
924
  message += `*Ссылка на заказ:* ${encodeURIComponent(orderUrl)}%0A%0A`;
925
  message += `Пожалуйста, свяжитесь со мной для уточнения деталей оплаты и доставки.`;
 
993
  details[open] > summary::after { transform: translateY(-50%) rotate(180deg); }
994
  details[open] > summary { border-bottom: 1px solid #444; }
995
  details .form-content { padding: 20px; }
996
+ .attr-input-group { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
997
+ .attr-input-group input { flex-grow: 1; margin: 0; }
998
+ .remove-attr-btn { background-color: #c53030; padding: 6px 10px; font-size: 0.8rem; margin-top: 0; line-height: 1; color: white;}
999
+ .remove-attr-btn:hover { background-color: #9b2c2c; }
1000
+ .add-attr-btn { background-color: #ffd700; color: #1a1a1a;}
1001
+ .add-attr-btn:hover { background-color: #ffcc00; }
1002
  .photo-preview img { max-width: 70px; max-height: 70px; border-radius: 5px; margin: 5px 5px 0 0; border: 1px solid #555; object-fit: cover;}
1003
  .sync-buttons { display: flex; gap: 10px; margin-bottom: 20px; flex-wrap: wrap; }
1004
  .download-hf-button { background-color: #444; color: #eee; }
 
1110
  </select>
1111
  <label for="add_photos">Фотографии (до 10 шт.):</label>
1112
  <input type="file" id="add_photos" name="photos" accept="image/*" multiple>
1113
+
1114
  <label>Цвета/Варианты (оставьте пустым, если нет):</label>
1115
  <div id="add-color-inputs">
1116
+ <div class="attr-input-group">
1117
  <input type="text" name="colors" placeholder="Например: Розовый">
1118
+ <button type="button" class="remove-attr-btn" onclick="removeAttributeInput(this, 'colors', 'Например: Розовый')"><i class="fas fa-times"></i></button>
1119
+ </div>
1120
+ </div>
1121
+ <button type="button" class="button add-attr-btn" style="margin-top: 5px;" onclick="addAttributeInput('add-color-inputs', 'colors', 'Например: Розовый')"><i class="fas fa-palette"></i> Добавить поле для цвета</button>
1122
+ <br>
1123
+
1124
+ <label>Размеры (оставьте пустым, если нет):</label>
1125
+ <div id="add-size-inputs">
1126
+ <div class="attr-input-group">
1127
+ <input type="text" name="sizes" placeholder="Например: S, M, L">
1128
+ <button type="button" class="remove-attr-btn" onclick="removeAttributeInput(this, 'sizes', 'Например: S, M, L')"><i class="fas fa-times"></i></button>
1129
  </div>
1130
  </div>
1131
+ <button type="button" class="button add-attr-btn" style="margin-top: 5px;" onclick="addAttributeInput('add-size-inputs', 'sizes', 'Например: S, M, L')"><i class="fas fa-ruler-combined"></i> Добавить поле для размера</button>
1132
  <br>
1133
+
1134
  <div style="margin-top: 15px;">
1135
  <input type="checkbox" id="add_in_stock" name="in_stock" checked>
1136
  <label for="add_in_stock" class="inline-label">В наличии</label>
 
1177
  <p class="description" title="{{ product.get('description', '') }}"><strong>Описание:</strong> {{ product.get('description', 'N/A')[:150] }}{% if product.get('description', '')|length > 150 %}...{% endif %}</p>
1178
  {% set colors = product.get('colors', []) %}
1179
  <p><strong>Цвета/Вар-ты:</strong> {{ colors|select('ne', '')|join(', ') if colors|select('ne', '')|list|length > 0 else 'Нет' }}</p>
1180
+ {% set sizes = product.get('sizes', []) %}
1181
+ <p><strong>Размеры:</strong> {{ sizes|select('ne', '')|join(', ') if sizes|select('ne', '')|list|length > 0 else 'Нет' }}</p>
1182
  {% if product.get('photos') and product['photos']|length > 1 %}
1183
  <p style="font-size: 0.8rem; color: #bbb;">(Всего фото: {{ product['photos']|length }})</p>
1184
  {% endif %}
 
1228
  {% if current_colors and current_colors|select('ne', '')|list|length > 0 %}
1229
  {% for color in current_colors %}
1230
  {% if color.strip() %}
1231
+ <div class="attr-input-group">
1232
  <input type="text" name="colors" value="{{ color }}">
1233
+ <button type="button" class="remove-attr-btn" onclick="removeAttributeInput(this, 'colors', 'Например: Цвет')"><i class="fas fa-times"></i></button>
1234
  </div>
1235
  {% endif %}
1236
  {% endfor %}
1237
  {% else %}
1238
+ <div class="attr-input-group">
1239
  <input type="text" name="colors" placeholder="Например: Цвет">
1240
+ <button type="button" class="remove-attr-btn" onclick="removeAttributeInput(this, 'colors', 'Например: Цвет')"><i class="fas fa-times"></i></button>
1241
  </div>
1242
  {% endif %}
1243
  </div>
1244
+ <button type="button" class="button add-attr-btn" style="margin-top: 5px;" onclick="addAttributeInput('edit-color-inputs-{{ loop.index0 }}', 'colors', 'Например: Цвет')"><i class="fas fa-palette"></i> Добавить поле для цвета</button>
1245
  <br>
1246
+
1247
+ <label>Размеры:</label>
1248
+ <div id="edit-size-inputs-{{ loop.index0 }}">
1249
+ {% set current_sizes = product.get('sizes', []) %}
1250
+ {% if current_sizes and current_sizes|select('ne', '')|list|length > 0 %}
1251
+ {% for size_val in current_sizes %}
1252
+ {% if size_val.strip() %}
1253
+ <div class="attr-input-group">
1254
+ <input type="text" name="sizes" value="{{ size_val }}">
1255
+ <button type="button" class="remove-attr-btn" onclick="removeAttributeInput(this, 'sizes', 'Например: S, M, L')"><i class="fas fa-times"></i></button>
1256
+ </div>
1257
+ {% endif %}
1258
+ {% endfor %}
1259
+ {% else %}
1260
+ <div class="attr-input-group">
1261
+ <input type="text" name="sizes" placeholder="Например: S, M, L">
1262
+ <button type="button" class="remove-attr-btn" onclick="removeAttributeInput(this, 'sizes', 'Например: S, M, L')"><i class="fas fa-times"></i></button>
1263
+ </div>
1264
+ {% endif %}
1265
+ </div>
1266
+ <button type="button" class="button add-attr-btn" style="margin-top: 5px;" onclick="addAttributeInput('edit-size-inputs-{{ loop.index0 }}', 'sizes', 'Например: S, M, L')"><i class="fas fa-ruler-combined"></i> Добавить поле для размера</button>
1267
+ <br>
1268
+
1269
  <div style="margin-top: 15px;">
1270
  <input type="checkbox" id="edit_in_stock_{{ loop.index0 }}" name="in_stock" {% if product.get('in_stock', True) %}checked{% endif %}>
1271
  <label for="edit_in_stock_{{ loop.index0 }}" class="inline-label">В наличии</label>
 
1296
  }
1297
  }
1298
 
1299
+ function addAttributeInput(containerId, attributeName, placeholderText) {
1300
  const container = document.getElementById(containerId);
1301
  if (container) {
1302
  const newInputGroup = document.createElement('div');
1303
+ newInputGroup.className = 'attr-input-group';
1304
  newInputGroup.innerHTML = `
1305
+ <input type="text" name="${attributeName}" placeholder="${placeholderText || 'Новое значение'}">
1306
+ <button type="button" class="remove-attr-btn" onclick="removeAttributeInput(this, '${attributeName}', '${placeholderText}')"><i class="fas fa-times"></i></button>
1307
  `;
1308
  container.appendChild(newInputGroup);
1309
+ const newInput = newInputGroup.querySelector('input[name="' + attributeName + '"]');
1310
  if (newInput) {
1311
  newInput.focus();
1312
  }
1313
  }
1314
  }
1315
 
1316
+ function removeAttributeInput(button, attributeName, placeholderText) {
1317
+ const group = button.closest('.attr-input-group');
1318
  if (group) {
1319
  const container = group.parentNode;
1320
  group.remove();
1321
  if (container && container.children.length === 0) {
1322
  const placeholderGroup = document.createElement('div');
1323
+ placeholderGroup.className = 'attr-input-group';
1324
  placeholderGroup.innerHTML = `
1325
+ <input type="text" name="${attributeName}" placeholder="${placeholderText || 'Значение'}">
1326
+ <button type="button" class="remove-attr-btn" onclick="removeAttributeInput(this, '${attributeName}', '${placeholderText}')"><i class="fas fa-times"></i></button>
1327
  `;
1328
  container.appendChild(placeholderGroup);
1329
  }
1330
  } else {
1331
+ console.warn("Could not find parent .attr-input-group for remove button");
1332
  }
1333
  }
1334
  </script>
 
1401
  "price": price,
1402
  "quantity": quantity,
1403
  "color": item.get('color', 'N/A'),
1404
+ "size": item.get('size', 'N/A'),
1405
  "photo": item.get('photo'),
1406
  "photo_url": f"https://huggingface.co/datasets/{REPO_ID}/resolve/main/photos/{item['photo']}" if item.get('photo') else "https://via.placeholder.com/60x60.png?text=N/A"
1407
  })
 
1503
  category = request.form.get('category')
1504
  photos_files = request.files.getlist('photos')
1505
  colors = [c.strip() for c in request.form.getlist('colors') if c.strip()]
1506
+ sizes = [s.strip() for s in request.form.getlist('sizes') if s.strip()]
1507
  in_stock = 'in_stock' in request.form
1508
  is_top = 'is_top' in request.form
1509
 
 
1575
  new_product = {
1576
  'name': name, 'price': price, 'description': description,
1577
  'category': category if category in categories else 'Без категории',
1578
+ 'photos': photos_list, 'colors': colors, 'sizes': sizes,
1579
  'in_stock': in_stock, 'is_top': is_top
1580
  }
1581
  products.append(new_product)
 
1608
  category = request.form.get('category')
1609
  product_to_edit['category'] = category if category in categories else 'Без категории'
1610
  product_to_edit['colors'] = [c.strip() for c in request.form.getlist('colors') if c.strip()]
1611
+ product_to_edit['sizes'] = [s.strip() for s in request.form.getlist('sizes') if s.strip()]
1612
  product_to_edit['in_stock'] = 'in_stock' in request.form
1613
  product_to_edit['is_top'] = 'is_top' in request.form
1614