flpolprojects commited on
Commit
a283ce0
·
verified ·
1 Parent(s): da11459

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +73 -12
app.py CHANGED
@@ -91,6 +91,9 @@ def periodic_backup():
91
  @app.route('/')
92
  def catalog():
93
  products = load_data()
 
 
 
94
  catalog_html = '''
95
  <!DOCTYPE html>
96
  <html lang="ru">
@@ -117,6 +120,13 @@ def catalog():
117
  max-width: 1200px;
118
  margin: 0 auto;
119
  }
 
 
 
 
 
 
 
120
  .search-container {
121
  text-align: center;
122
  margin-bottom: 20px;
@@ -134,13 +144,26 @@ def catalog():
134
  border-color: #3498db;
135
  box-shadow: 0 0 5px rgba(52, 152, 219, 0.5);
136
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
137
  .products-grid {
138
  display: grid;
139
  grid-template-columns: repeat(2, 1fr);
140
  gap: 10px;
141
  padding: 0 5px;
142
  overflow-y: auto;
143
- max-height: calc(100vh - 120px);
144
  }
145
  .product {
146
  background: #ffffff;
@@ -346,12 +369,21 @@ def catalog():
346
  </head>
347
  <body>
348
  <div class="container">
 
 
 
 
 
 
349
  <div class="search-container">
350
  <input type="text" id="search-input" placeholder="Поиск по названию или описанию...">
351
  </div>
352
  <div class="products-grid" id="products-grid">
353
  {% for product in products %}
354
- <div class="product" data-name="{{ product['name']|lower }}" data-description="{{ product['description']|lower }}">
 
 
 
355
  {% if product.get('photos') and product['photos']|length > 0 %}
356
  <div class="product-image">
357
  <img src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/photos/{{ product['photos'][0] }}"
@@ -585,20 +617,39 @@ def catalog():
585
 
586
  // Поисковая строка
587
  document.getElementById('search-input').addEventListener('input', function(e) {
588
- const searchTerm = e.target.value.toLowerCase();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
589
  const productElements = document.querySelectorAll('.product');
590
-
591
  productElements.forEach(product => {
592
  const name = product.getAttribute('data-name');
593
  const description = product.getAttribute('data-description');
594
-
595
- if (name.includes(searchTerm) || description.includes(searchTerm)) {
 
 
 
 
596
  product.style.display = 'flex';
597
  } else {
598
  product.style.display = 'none';
599
  }
600
  });
601
- });
602
 
603
  console.log("Инициализация страницы, товары:", products);
604
  updateCartButton();
@@ -606,7 +657,7 @@ def catalog():
606
  </body>
607
  </html>
608
  '''
609
- return render_template_string(catalog_html, products=products, repo_id=REPO_ID)
610
 
611
  @app.route('/product/<int:index>')
612
  def product_detail(index):
@@ -640,6 +691,7 @@ def product_detail(index):
640
  <div class="swiper-button-next"></div>
641
  <div class="swiper-button-prev"></div>
642
  </div>
 
643
  <p><strong>Цена:</strong> {{ product['price'] }}</p>
644
  <p><strong>Описание:</strong> {{ product['description'] }}</p>
645
  </div>
@@ -655,10 +707,11 @@ def admin():
655
  name = request.form.get('name')
656
  price = request.form.get('price')
657
  description = request.form.get('description')
 
658
  photos_files = request.files.getlist('photos')
659
  photos_list = []
660
 
661
- logging.info(f"Добавление нового товара: name={name}, price={price}, description={description}")
662
 
663
  if photos_files:
664
  for i, photo in enumerate(photos_files[:2]):
@@ -687,7 +740,7 @@ def admin():
687
  if os.path.exists(temp_path):
688
  os.remove(temp_path)
689
 
690
- if not name or not price or not description:
691
  logging.error("Ошибка: Не все обязательные поля заполнены")
692
  return "Ошибка: Заполните все обязательные поля", 400
693
 
@@ -701,6 +754,7 @@ def admin():
701
  'name': name,
702
  'price': price,
703
  'description': description,
 
704
  'photos': photos_list
705
  }
706
 
@@ -720,6 +774,7 @@ def admin():
720
  name = request.form.get('name')
721
  price = request.form.get('price')
722
  description = request.form.get('description')
 
723
  photos_files = request.files.getlist('photos')
724
 
725
  logging.info(f"Редактирование товара с индексом {index}")
@@ -760,6 +815,7 @@ def admin():
760
  logging.error("Ошибка: Цена должна быть числом")
761
  return "Ошибка: Цена должна быть числом", 400
762
  products[index]['description'] = description
 
763
 
764
  save_data(products)
765
  logging.info(f"Товар с индексом {index} успешно отредактирован")
@@ -802,7 +858,7 @@ def admin():
802
  margin-top: 10px;
803
  color: #555;
804
  }
805
- input, textarea {
806
  width: 100%;
807
  padding: 8px;
808
  margin-top: 5px;
@@ -846,7 +902,7 @@ def admin():
846
  form {
847
  padding: 10px;
848
  }
849
- input, textarea {
850
  font-size: 0.9em;
851
  }
852
  button {
@@ -869,6 +925,8 @@ def admin():
869
  <input type="number" id="price" name="price" step="0.01" required>
870
  <label for="description">Описание:</label>
871
  <textarea id="description" name="description" rows="4" required></textarea>
 
 
872
  <label for="photos">Фотографии товара (максимум 2):</label>
873
  <input type="file" id="photos" name="photos" accept="image/*" multiple>
874
  <button type="submit">Добавить товар</button>
@@ -887,6 +945,7 @@ def admin():
887
  {% for product in products %}
888
  <div class="product-item">
889
  <h3>{{ product['name'] }}</h3>
 
890
  <p><strong>Цена:</strong> {{ product['price'] }}</p>
891
  <p><strong>Описание:</strong> {{ product['description'] }}</p>
892
  {% if product.get('photos') and product['photos']|length > 0 %}
@@ -907,6 +966,8 @@ def admin():
907
  <input type="number" id="price" name="price" step="0.01" value="{{ product['price'] }}" required>
908
  <label for="description">Описание:</label>
909
  <textarea id="description" name="description" rows="4" required>{{ product['description'] }}</textarea>
 
 
910
  <label for="photos">Фотографии товара (максимум 2):</label>
911
  <input type="file" id="photos" name="photos" accept="image/*" multiple>
912
  <button type="submit">Сохранить изменения</button>
 
91
  @app.route('/')
92
  def catalog():
93
  products = load_data()
94
+ # Получаем уникальные категории из продуктов
95
+ categories = sorted(set(product.get('category', 'Без категории') for product in products))
96
+
97
  catalog_html = '''
98
  <!DOCTYPE html>
99
  <html lang="ru">
 
120
  max-width: 1200px;
121
  margin: 0 auto;
122
  }
123
+ .filters-container {
124
+ margin-bottom: 20px;
125
+ display: flex;
126
+ flex-wrap: wrap;
127
+ gap: 10px;
128
+ justify-content: center;
129
+ }
130
  .search-container {
131
  text-align: center;
132
  margin-bottom: 20px;
 
144
  border-color: #3498db;
145
  box-shadow: 0 0 5px rgba(52, 152, 219, 0.5);
146
  }
147
+ .category-filter {
148
+ padding: 6px 12px;
149
+ border: 1px solid #ddd;
150
+ border-radius: 5px;
151
+ background-color: white;
152
+ cursor: pointer;
153
+ transition: all 0.3s ease;
154
+ }
155
+ .category-filter.active {
156
+ background-color: #3498db;
157
+ color: white;
158
+ border-color: #3498db;
159
+ }
160
  .products-grid {
161
  display: grid;
162
  grid-template-columns: repeat(2, 1fr);
163
  gap: 10px;
164
  padding: 0 5px;
165
  overflow-y: auto;
166
+ max-height: calc(100vh - 160px);
167
  }
168
  .product {
169
  background: #ffffff;
 
369
  </head>
370
  <body>
371
  <div class="container">
372
+ <div class="filters-container">
373
+ <button class="category-filter active" data-category="all">Все категории</button>
374
+ {% for category in categories %}
375
+ <button class="category-filter" data-category="{{ category }}">{{ category }}</button>
376
+ {% endfor %}
377
+ </div>
378
  <div class="search-container">
379
  <input type="text" id="search-input" placeholder="Поиск по названию или описанию...">
380
  </div>
381
  <div class="products-grid" id="products-grid">
382
  {% for product in products %}
383
+ <div class="product"
384
+ data-name="{{ product['name']|lower }}"
385
+ data-description="{{ product['description']|lower }}"
386
+ data-category="{{ product.get('category', 'Без категории') }}">
387
  {% if product.get('photos') and product['photos']|length > 0 %}
388
  <div class="product-image">
389
  <img src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/photos/{{ product['photos'][0] }}"
 
617
 
618
  // Поисковая строка
619
  document.getElementById('search-input').addEventListener('input', function(e) {
620
+ filterProducts();
621
+ });
622
+
623
+ // Фильтрация по категориям
624
+ const categoryFilters = document.querySelectorAll('.category-filter');
625
+ categoryFilters.forEach(filter => {
626
+ filter.addEventListener('click', function() {
627
+ categoryFilters.forEach(f => f.classList.remove('active'));
628
+ this.classList.add('active');
629
+ filterProducts();
630
+ });
631
+ });
632
+
633
+ function filterProducts() {
634
+ const searchTerm = document.getElementById('search-input').value.toLowerCase();
635
+ const activeCategory = document.querySelector('.category-filter.active').dataset.category;
636
  const productElements = document.querySelectorAll('.product');
637
+
638
  productElements.forEach(product => {
639
  const name = product.getAttribute('data-name');
640
  const description = product.getAttribute('data-description');
641
+ const category = product.getAttribute('data-category');
642
+
643
+ const matchesSearch = name.includes(searchTerm) || description.includes(searchTerm);
644
+ const matchesCategory = activeCategory === 'all' || category === activeCategory;
645
+
646
+ if (matchesSearch && matchesCategory) {
647
  product.style.display = 'flex';
648
  } else {
649
  product.style.display = 'none';
650
  }
651
  });
652
+ }
653
 
654
  console.log("Инициализация страницы, товары:", products);
655
  updateCartButton();
 
657
  </body>
658
  </html>
659
  '''
660
+ return render_template_string(catalog_html, products=products, categories=categories, repo_id=REPO_ID)
661
 
662
  @app.route('/product/<int:index>')
663
  def product_detail(index):
 
691
  <div class="swiper-button-next"></div>
692
  <div class="swiper-button-prev"></div>
693
  </div>
694
+ <p><strong>Категория:</strong> {{ product.get('category', 'Без категории') }}</p>
695
  <p><strong>Цена:</strong> {{ product['price'] }}</p>
696
  <p><strong>Описание:</strong> {{ product['description'] }}</p>
697
  </div>
 
707
  name = request.form.get('name')
708
  price = request.form.get('price')
709
  description = request.form.get('description')
710
+ category = request.form.get('category')
711
  photos_files = request.files.getlist('photos')
712
  photos_list = []
713
 
714
+ logging.info(f"Добавление нового товара: name={name}, price={price}, description={description}, category={category}")
715
 
716
  if photos_files:
717
  for i, photo in enumerate(photos_files[:2]):
 
740
  if os.path.exists(temp_path):
741
  os.remove(temp_path)
742
 
743
+ if not name or not price or not description or not category:
744
  logging.error("Ошибка: Не все обязательные поля заполнены")
745
  return "Ошибка: Заполните все обязательные поля", 400
746
 
 
754
  'name': name,
755
  'price': price,
756
  'description': description,
757
+ 'category': category,
758
  'photos': photos_list
759
  }
760
 
 
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
 
780
  logging.info(f"Редактирование товара с индексом {index}")
 
815
  logging.error("Ошибка: Цена должна быть числом")
816
  return "Ошибка: Цена должна быть числом", 400
817
  products[index]['description'] = description
818
+ products[index]['category'] = category
819
 
820
  save_data(products)
821
  logging.info(f"Товар с индексом {index} успешно отредактирован")
 
858
  margin-top: 10px;
859
  color: #555;
860
  }
861
+ input, textarea, select {
862
  width: 100%;
863
  padding: 8px;
864
  margin-top: 5px;
 
902
  form {
903
  padding: 10px;
904
  }
905
+ input, textarea, select {
906
  font-size: 0.9em;
907
  }
908
  button {
 
925
  <input type="number" id="price" name="price" step="0.01" required>
926
  <label for="description">Описание:</label>
927
  <textarea id="description" name="description" rows="4" required></textarea>
928
+ <label for="category">Категория:</label>
929
+ <input type="text" id="category" name="category" required placeholder="Введите категорию">
930
  <label for="photos">Фотографии товара (максимум 2):</label>
931
  <input type="file" id="photos" name="photos" accept="image/*" multiple>
932
  <button type="submit">Добавить товар</button>
 
945
  {% for product in products %}
946
  <div class="product-item">
947
  <h3>{{ product['name'] }}</h3>
948
+ <p><strong>Категория:</strong> {{ product.get('category', 'Без категории') }}</p>
949
  <p><strong>Цена:</strong> {{ product['price'] }}</p>
950
  <p><strong>Описание:</strong> {{ product['description'] }}</p>
951
  {% if product.get('photos') and product['photos']|length > 0 %}
 
966
  <input type="number" id="price" name="price" step="0.01" value="{{ product['price'] }}" required>
967
  <label for="description">Описание:</label>
968
  <textarea id="description" name="description" rows="4" required>{{ product['description'] }}</textarea>
969
+ <label for="category">Категория:</label>
970
+ <input type="text" id="category" name="category" value="{{ product.get('category', 'Без категории') }}" required>
971
  <label for="photos">Фотографии товара (максимум 2):</label>
972
  <input type="file" id="photos" name="photos" accept="image/*" multiple>
973
  <button type="submit">Сохранить изменения</button>