Kgshop commited on
Commit
78823fc
·
verified ·
1 Parent(s): d472dcd

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +95 -80
app.py CHANGED
@@ -30,22 +30,20 @@ def load_data():
30
  data = json.load(file)
31
  logging.info("Данные успешно загружены из JSON")
32
  if not isinstance(data, dict) or 'products' not in data or 'categories' not in data:
33
- return {'products': [], 'categories': [], 'options': []}
34
- if 'options' not in data:
35
- data['options'] = []
36
  return data
37
  except FileNotFoundError:
38
  logging.warning("Локальный файл базы данных не найден после скачивания.")
39
- return {'products': [], 'categories': [], 'options': []}
40
  except json.JSONDecodeError:
41
  logging.error("Ошибка: Невозможно декодировать JSON файл.")
42
- return {'products': [], 'categories': [], 'options': []}
43
  except RepositoryNotFoundError:
44
  logging.error("Репозиторий не найден. Создание локальной базы данных.")
45
- return {'products': [], 'categories': [], 'options': []}
46
  except Exception as e:
47
  logging.error(f"Произошла ошибка при загрузке данных: {e}")
48
- return {'products': [], 'categories': [], 'options': []}
49
 
50
  def save_data(data):
51
  try:
@@ -100,7 +98,6 @@ def menu():
100
  data = load_data()
101
  products = data['products']
102
  categories = data['categories']
103
- options = data['options']
104
 
105
  menu_html = '''
106
  <!DOCTYPE html>
@@ -496,7 +493,6 @@ def menu():
496
  <script src="https://cdnjs.cloudflare.com/ajax/libs/Swiper/10.2.0/swiper-bundle.min.js"></script>
497
  <script>
498
  const products = {{ products|tojson }};
499
- const options = {{ options|tojson }};
500
  let selectedProductIndex = null;
501
 
502
  function toggleTheme() {
@@ -547,12 +543,13 @@ def menu():
547
  selectedProductIndex = index;
548
  const product = products[index];
549
  const optionsList = document.getElementById('optionsList');
550
- optionsList.innerHTML = options.map(option => `
551
- <label class="options-checkbox">
552
- <input type="checkbox" class="option-checkbox" data-name="${option.name}" data-price="${option.price}">
553
- ${option.name} (+${option.price} с)
554
- </label>
555
- `).join('');
 
556
  document.getElementById('optionsModal').style.display = 'block';
557
  document.getElementById('quantityInput').value = 1;
558
  }
@@ -572,7 +569,7 @@ def menu():
572
  price: parseFloat(cb.dataset.price)
573
  }));
574
 
575
- const cartItemId = `${product.name}-${Date.now()}`; // Уникальный ID для каждого добавления
576
  cart.push({
577
  id: cartItemId,
578
  name: product.name,
@@ -678,7 +675,7 @@ def menu():
678
  </body>
679
  </html>
680
  '''
681
- return render_template_string(menu_html, products=products, categories=categories, options=options, repo_id=REPO_ID)
682
 
683
  @app.route('/product/<int:index>')
684
  def product_detail(index):
@@ -716,6 +713,13 @@ def product_detail(index):
716
  <p><strong>Категория:</strong> {{ product.get('category', 'Без категории') }}</p>
717
  <p><strong>Цена:</strong> {{ product['price'] }} с</p>
718
  <p><strong>Описание:</strong> {{ product['description'] }}</p>
 
 
 
 
 
 
 
719
  </div>
720
  '''
721
  return render_template_string(detail_html, product=product, repo_id=REPO_ID)
@@ -725,7 +729,6 @@ def admin():
725
  data = load_data()
726
  products = data['products']
727
  categories = data['categories']
728
- options = data['options']
729
 
730
  if request.method == 'POST':
731
  action = request.form.get('action')
@@ -747,36 +750,16 @@ def admin():
747
  save_data(data)
748
  return redirect(url_for('admin'))
749
 
750
- elif action == 'add_option':
751
- option_name = request.form.get('option_name')
752
- option_price = float(request.form.get('option_price', '0').replace(',', '.'))
753
- if option_name and option_name not in [opt['name'] for opt in options]:
754
- options.append({'name': option_name, 'price': option_price})
755
- save_data(data)
756
- return redirect(url_for('admin'))
757
- return "Ошибка: Опция уже существует или не указано название", 400
758
-
759
- elif action == 'delete_option':
760
- option_index = int(request.form.get('option_index'))
761
- del options[option_index]
762
- save_data(data)
763
- return redirect(url_for('admin'))
764
-
765
- elif action == 'edit_option':
766
- option_index = int(request.form.get('option_index'))
767
- option_name = request.form.get('option_name')
768
- option_price = float(request.form.get('option_price').replace(',', '.'))
769
- options[option_index] = {'name': option_name, 'price': option_price}
770
- save_data(data)
771
- return redirect(url_for('admin'))
772
-
773
  elif action == 'add':
774
  name = request.form.get('name')
775
  price = request.form.get('price')
776
  description = request.form.get('description')
777
  category = request.form.get('category')
778
  photos_files = request.files.getlist('photos')
 
 
779
  photos_list = []
 
780
 
781
  if photos_files:
782
  for photo in photos_files[:10]:
@@ -799,6 +782,13 @@ def admin():
799
  if os.path.exists(temp_path):
800
  os.remove(temp_path)
801
 
 
 
 
 
 
 
 
802
  if not name or not price or not description:
803
  return "Ошибка: Заполните все обязательные поля", 400
804
 
@@ -808,7 +798,8 @@ def admin():
808
  'price': price,
809
  'description': description,
810
  'category': category if category in categories else 'Без категории',
811
- 'photos': photos_list
 
812
  }
813
  products.append(new_product)
814
  save_data(data)
@@ -821,6 +812,8 @@ def admin():
821
  description = request.form.get('description')
822
  category = request.form.get('category')
823
  photos_files = request.files.getlist('photos')
 
 
824
 
825
  if photos_files and any(photo.filename for photo in photos_files):
826
  new_photos_list = []
@@ -845,10 +838,19 @@ def admin():
845
  os.remove(temp_path)
846
  products[index]['photos'] = new_photos_list
847
 
 
 
 
 
 
 
 
 
848
  products[index]['name'] = name
849
  products[index]['price'] = float(price.replace(',', '.'))
850
  products[index]['description'] = description
851
  products[index]['category'] = category if category in categories else 'Без категории'
 
852
  save_data(data)
853
  return redirect(url_for('admin'))
854
 
@@ -949,11 +951,11 @@ def admin():
949
  background-color: #dc2626;
950
  box-shadow: 0 4px 15px rgba(220, 38, 38, 0.4);
951
  }
952
- .product-list, .category-list, .options-list {
953
  display: grid;
954
  gap: 20px;
955
  }
956
- .product-item, .category-item, .option-item {
957
  background: #fff;
958
  padding: 20px;
959
  border-radius: 15px;
@@ -965,6 +967,17 @@ def admin():
965
  background: #f7fafc;
966
  border-radius: 10px;
967
  }
 
 
 
 
 
 
 
 
 
 
 
968
  </style>
969
  </head>
970
  <body>
@@ -991,6 +1004,14 @@ def admin():
991
  </select>
992
  <label>Фотографии (до 10):</label>
993
  <input type="file" name="photos" accept="image/*" multiple>
 
 
 
 
 
 
 
 
994
  <button type="submit">Добавить блюдо</button>
995
  </form>
996
 
@@ -1016,41 +1037,6 @@ def admin():
1016
  {% endfor %}
1017
  </div>
1018
 
1019
- <h1>Управление опциями</h1>
1020
- <form method="POST">
1021
- <input type="hidden" name="action" value="add_option">
1022
- <label>Название опции:</label>
1023
- <input type="text" name="option_name" required>
1024
- <label>Дополнительная стоимость:</label>
1025
- <input type="number" name="option_price" step="0.01" value="0">
1026
- <button type="submit">Добавить</button>
1027
- </form>
1028
-
1029
- <h2>Список опций</h2>
1030
- <div class="options-list">
1031
- {% for option in options %}
1032
- <div class="option-item">
1033
- <details>
1034
- <summary>{{ option['name'] }} ({{ option['price'] }} с)</summary>
1035
- <form method="POST" class="edit-form">
1036
- <input type="hidden" name="action" value="edit_option">
1037
- <input type="hidden" name="option_index" value="{{ loop.index0 }}">
1038
- <label>Название:</label>
1039
- <input type="text" name="option_name" value="{{ option['name'] }}" required>
1040
- <label>Стоимость:</label>
1041
- <input type="number" name="option_price" step="0.01" value="{{ option['price'] }}" required>
1042
- <button type="submit">Сохранить</button>
1043
- </form>
1044
- </details>
1045
- <form method="POST" style="display: inline;">
1046
- <input type="hidden" name="action" value="delete_option">
1047
- <input type="hidden" name="option_index" value="{{ loop.index0 }}">
1048
- <button type="submit" class="delete-button">Удалить</button>
1049
- </form>
1050
- </div>
1051
- {% endfor %}
1052
- </div>
1053
-
1054
  <h2>Управление базой данных</h2>
1055
  <form method="POST" action="{{ url_for('backup') }}" style="display: inline;">
1056
  <button type="submit">Создать копию</button>
@@ -1067,6 +1053,13 @@ def admin():
1067
  <p><strong>Категория:</strong> {{ product.get('category', 'Без категории') }}</p>
1068
  <p><strong>Цена:</strong> {{ product['price'] }} с</p>
1069
  <p><strong>Описание:</strong> {{ product['description'] }}</p>
 
 
 
 
 
 
 
1070
  {% if product.get('photos') and product['photos']|length > 0 %}
1071
  <div style="display: flex; flex-wrap: wrap; gap: 10px;">
1072
  {% for photo in product['photos'] %}
@@ -1096,6 +1089,16 @@ def admin():
1096
  </select>
1097
  <label>Фотографии (до 10):</label>
1098
  <input type="file" name="photos" accept="image/*" multiple>
 
 
 
 
 
 
 
 
 
 
1099
  <button type="submit">Сохранить</button>
1100
  </form>
1101
  </details>
@@ -1108,10 +1111,22 @@ def admin():
1108
  {% endfor %}
1109
  </div>
1110
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
1111
  </body>
1112
  </html>
1113
  '''
1114
- return render_template_string(admin_html, products=products, categories=categories, options=options, repo_id=REPO_ID)
1115
 
1116
  @app.route('/backup', methods=['POST'])
1117
  def backup():
 
30
  data = json.load(file)
31
  logging.info("Данные успешно загружены из JSON")
32
  if not isinstance(data, dict) or 'products' not in data or 'categories' not in data:
33
+ return {'products': [], 'categories': []}
 
 
34
  return data
35
  except FileNotFoundError:
36
  logging.warning("Локальный файл базы данных не найден после скачивания.")
37
+ return {'products': [], 'categories': []}
38
  except json.JSONDecodeError:
39
  logging.error("Ошибка: Невозможно декодировать JSON файл.")
40
+ return {'products': [], 'categories': []}
41
  except RepositoryNotFoundError:
42
  logging.error("Репозиторий не найден. Создание локальной базы данных.")
43
+ return {'products': [], 'categories': []}
44
  except Exception as e:
45
  logging.error(f"Произошла ошибка при загрузке данных: {e}")
46
+ return {'products': [], 'categories': []}
47
 
48
  def save_data(data):
49
  try:
 
98
  data = load_data()
99
  products = data['products']
100
  categories = data['categories']
 
101
 
102
  menu_html = '''
103
  <!DOCTYPE html>
 
493
  <script src="https://cdnjs.cloudflare.com/ajax/libs/Swiper/10.2.0/swiper-bundle.min.js"></script>
494
  <script>
495
  const products = {{ products|tojson }};
 
496
  let selectedProductIndex = null;
497
 
498
  function toggleTheme() {
 
543
  selectedProductIndex = index;
544
  const product = products[index];
545
  const optionsList = document.getElementById('optionsList');
546
+ optionsList.innerHTML = (product.options && product.options.length > 0) ?
547
+ product.options.map(option => `
548
+ <label class="options-checkbox">
549
+ <input type="checkbox" class="option-checkbox" data-name="${option.name}" data-price="${option.price}">
550
+ ${option.name} (+${option.price} с)
551
+ </label>
552
+ `).join('') : '<p>Нет дополнительных опций</p>';
553
  document.getElementById('optionsModal').style.display = 'block';
554
  document.getElementById('quantityInput').value = 1;
555
  }
 
569
  price: parseFloat(cb.dataset.price)
570
  }));
571
 
572
+ const cartItemId = `${product.name}-${Date.now()}`;
573
  cart.push({
574
  id: cartItemId,
575
  name: product.name,
 
675
  </body>
676
  </html>
677
  '''
678
+ return render_template_string(menu_html, products=products, categories=categories, repo_id=REPO_ID)
679
 
680
  @app.route('/product/<int:index>')
681
  def product_detail(index):
 
713
  <p><strong>Категория:</strong> {{ product.get('category', 'Без категории') }}</p>
714
  <p><strong>Цена:</strong> {{ product['price'] }} с</p>
715
  <p><strong>Описание:</strong> {{ product['description'] }}</p>
716
+ <p><strong>Дополнительные опции:</strong>
717
+ {% if product.get('options') and product['options']|length > 0 %}
718
+ {{ product['options']|map(attribute='name')|join(', ') }}
719
+ {% else %}
720
+ Нет опций
721
+ {% endif %}
722
+ </p>
723
  </div>
724
  '''
725
  return render_template_string(detail_html, product=product, repo_id=REPO_ID)
 
729
  data = load_data()
730
  products = data['products']
731
  categories = data['categories']
 
732
 
733
  if request.method == 'POST':
734
  action = request.form.get('action')
 
750
  save_data(data)
751
  return redirect(url_for('admin'))
752
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
753
  elif action == 'add':
754
  name = request.form.get('name')
755
  price = request.form.get('price')
756
  description = request.form.get('description')
757
  category = request.form.get('category')
758
  photos_files = request.files.getlist('photos')
759
+ option_names = request.form.getlist('option_names')
760
+ option_prices = request.form.getlist('option_prices')
761
  photos_list = []
762
+ options_list = []
763
 
764
  if photos_files:
765
  for photo in photos_files[:10]:
 
782
  if os.path.exists(temp_path):
783
  os.remove(temp_path)
784
 
785
+ for opt_name, opt_price in zip(option_names, option_prices):
786
+ if opt_name and opt_price:
787
+ options_list.append({
788
+ 'name': opt_name,
789
+ 'price': float(opt_price.replace(',', '.'))
790
+ })
791
+
792
  if not name or not price or not description:
793
  return "Ошибка: Заполните все обязательные поля", 400
794
 
 
798
  'price': price,
799
  'description': description,
800
  'category': category if category in categories else 'Без категории',
801
+ 'photos': photos_list,
802
+ 'options': options_list
803
  }
804
  products.append(new_product)
805
  save_data(data)
 
812
  description = request.form.get('description')
813
  category = request.form.get('category')
814
  photos_files = request.files.getlist('photos')
815
+ option_names = request.form.getlist('option_names')
816
+ option_prices = request.form.getlist('option_prices')
817
 
818
  if photos_files and any(photo.filename for photo in photos_files):
819
  new_photos_list = []
 
838
  os.remove(temp_path)
839
  products[index]['photos'] = new_photos_list
840
 
841
+ options_list = []
842
+ for opt_name, opt_price in zip(option_names, option_prices):
843
+ if opt_name and opt_price:
844
+ options_list.append({
845
+ 'name': opt_name,
846
+ 'price': float(opt_price.replace(',', '.'))
847
+ })
848
+
849
  products[index]['name'] = name
850
  products[index]['price'] = float(price.replace(',', '.'))
851
  products[index]['description'] = description
852
  products[index]['category'] = category if category in categories else 'Без категории'
853
+ products[index]['options'] = options_list
854
  save_data(data)
855
  return redirect(url_for('admin'))
856
 
 
951
  background-color: #dc2626;
952
  box-shadow: 0 4px 15px rgba(220, 38, 38, 0.4);
953
  }
954
+ .product-list, .category-list {
955
  display: grid;
956
  gap: 20px;
957
  }
958
+ .product-item, .category-item {
959
  background: #fff;
960
  padding: 20px;
961
  border-radius: 15px;
 
967
  background: #f7fafc;
968
  border-radius: 10px;
969
  }
970
+ .option-input-group {
971
+ display: flex;
972
+ gap: 10px;
973
+ margin-top: 5px;
974
+ }
975
+ .add-option-btn {
976
+ background-color: #10b981;
977
+ }
978
+ .add-option-btn:hover {
979
+ background-color: #059669;
980
+ }
981
  </style>
982
  </head>
983
  <body>
 
1004
  </select>
1005
  <label>Фотографии (до 10):</label>
1006
  <input type="file" name="photos" accept="image/*" multiple>
1007
+ <label>Дополнительные опции:</label>
1008
+ <div id="option-inputs">
1009
+ <div class="option-input-group">
1010
+ <input type="text" name="option_names" placeholder="Название опции">
1011
+ <input type="number" name="option_prices" step="0.01" value="0" placeholder="Цена">
1012
+ </div>
1013
+ </div>
1014
+ <button type="button" class="add-option-btn" onclick="addOptionInput()">Добавить опцию</button>
1015
  <button type="submit">Добавить блюдо</button>
1016
  </form>
1017
 
 
1037
  {% endfor %}
1038
  </div>
1039
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1040
  <h2>Управление базой данных</h2>
1041
  <form method="POST" action="{{ url_for('backup') }}" style="display: inline;">
1042
  <button type="submit">Создать копию</button>
 
1053
  <p><strong>Категория:</strong> {{ product.get('category', 'Без категории') }}</p>
1054
  <p><strong>Цена:</strong> {{ product['price'] }} с</p>
1055
  <p><strong>Описание:</strong> {{ product['description'] }}</p>
1056
+ <p><strong>Опции:</strong>
1057
+ {% if product.get('options') and product['options']|length > 0 %}
1058
+ {{ product['options']|map(attribute='name')|join(', ') }}
1059
+ {% else %}
1060
+ Нет опций
1061
+ {% endif %}
1062
+ </p>
1063
  {% if product.get('photos') and product['photos']|length > 0 %}
1064
  <div style="display: flex; flex-wrap: wrap; gap: 10px;">
1065
  {% for photo in product['photos'] %}
 
1089
  </select>
1090
  <label>Фотографии (до 10):</label>
1091
  <input type="file" name="photos" accept="image/*" multiple>
1092
+ <label>Дополнительные опции:</label>
1093
+ <div id="edit-option-inputs-{{ loop.index0 }}">
1094
+ {% for option in product.get('options', []) %}
1095
+ <div class="option-input-group">
1096
+ <input type="text" name="option_names" value="{{ option['name'] }}">
1097
+ <input type="number" name="option_prices" step="0.01" value="{{ option['price'] }}">
1098
+ </div>
1099
+ {% endfor %}
1100
+ </div>
1101
+ <button type="button" class="add-option-btn" onclick="addOptionInput('edit-option-inputs-{{ loop.index0 }}')">Добавить опцию</button>
1102
  <button type="submit">Сохранить</button>
1103
  </form>
1104
  </details>
 
1111
  {% endfor %}
1112
  </div>
1113
  </div>
1114
+ <script>
1115
+ function addOptionInput(containerId = 'option-inputs') {
1116
+ const container = document.getElementById(containerId);
1117
+ const newInput = document.createElement('div');
1118
+ newInput.className = 'option-input-group';
1119
+ newInput.innerHTML = `
1120
+ <input type="text" name="option_names" placeholder="Название опции">
1121
+ <input type="number" name="option_prices" step="0.01" value="0" placeholder="Цена">
1122
+ `;
1123
+ container.appendChild(newInput);
1124
+ }
1125
+ </script>
1126
  </body>
1127
  </html>
1128
  '''
1129
+ return render_template_string(admin_html, products=products, categories=categories, repo_id=REPO_ID)
1130
 
1131
  @app.route('/backup', methods=['POST'])
1132
  def backup():