diff --git "a/app.py" "b/app.py" --- "a/app.py" +++ "b/app.py" @@ -13,10 +13,13 @@ app = Flask(__name__) DATA_FILE = 'data_firecollection.json' # Настройки Hugging Face -REPO_ID = "Kgshop/clients" # Замените, если нужно +REPO_ID = "Kgshop/Clients2" HF_TOKEN_WRITE = os.getenv("HF_TOKEN") HF_TOKEN_READ = os.getenv("HF_TOKEN_READ") +# Ссылка на логотип +LOGO_URL = "https://huggingface.co/spaces/Kgshop/AruuBrand/resolve/main/aruu.brandkg_14031226_144915286.jpg" + # Настройка логирования logging.basicConfig(level=logging.DEBUG) @@ -27,7 +30,7 @@ def load_data(): data = json.load(file) logging.info("Данные успешно загружены из JSON") if not isinstance(data, dict) or 'products' not in data or 'categories' not in data: - return {'products': [], 'categories': []} # Corrected: return empty dict with lists + return {'products': [], 'categories': [] if not isinstance(data, list) else data} return data except FileNotFoundError: logging.warning("Локальный файл базы данных не найден после скачивания.") @@ -94,39 +97,37 @@ def periodic_backup(): def catalog(): data = load_data() products = data['products'] - # categories = data['categories'] Removed categories from main page - + categories = data['categories'] + catalog_html = ''' - Канцелярия Оптом + Aruu Brand - женские брюки оптом - +
-

Fire collection

+ +

Каталог

- - -
-

Наш адрес

-

Рынок Дордой, 0 проход , 2034 контейнер

-

График работы

-

По адресу :С 8:00 до 16:00 без выходных , онлайн : круглосуточно

+
+ + {% for category in categories %} + + {% endfor %}
-
{% for product in products %}
- {% if product.get('photos') and product['photos']|length > 0 %}
{% endif %} - {% if product.get('wholesale_price') and product.get('min_wholesale') %} - Опт от {{ product['min_wholesale'] }} - {% endif %} - {% if product.get('discount') %} - Скидка {{ product['discount'] }}% - {% endif %}

{{ product['name'] }}

-
- {% if product.get('discount') %} - {{ product['price'] }} с - {{ (product['price'] * (1 - product['discount'] / 100))|round(2) }} с - Скидка: {{ product['discount'] }}% - {% else %} - {{ product['price'] }} с - {% endif %} - {% if product.get('wholesale_price') and product.get('min_wholesale') %} - Опт: {{ product['wholesale_price'] }} с - {% endif %} -
+
{{ product['price'] }} с

{{ product['description'][:50] }}{% if product['description']|length > 50 %}...{% endif %}

- + +
{% endfor %}
@@ -610,30 +485,6 @@ def catalog(): - - - @@ -720,26 +571,13 @@ def catalog(): const cartItemId = `${product.name}-${color}`; const existingItem = cart.find(item => item.id === cartItemId); - let priceToUse = product.price; - if (product.discount) { - priceToUse = product.price * (1 - product.discount / 100); - } - if (product.min_wholesale && quantity >= product.min_wholesale) { - priceToUse = product.wholesale_price; - } - if (existingItem) { existingItem.quantity += quantity; - existingItem.price = (existingItem.quantity >= product.min_wholesale) ? product.wholesale_price : (product.discount ? product.price * (1 - product.discount / 100) : product.price); } else { cart.push({ id: cartItemId, name: product.name, - price: priceToUse, - retail_price: product.price, - wholesale_price: product.wholesale_price, - min_wholesale: product.min_wholesale, - discount: product.discount, + price: product.price, photo: product.photos && product.photos.length > 0 ? product.photos[0] : '', quantity: quantity, color: color @@ -771,7 +609,6 @@ def catalog():
${item.name}

${item.price} с × ${item.quantity} (Цвет: ${item.color})

-

${item.quantity >= item.min_wholesale ? 'Оптовая цена' : (item.discount ? 'Скидка ' + item.discount + '%' : 'Розничная цена')}

${itemTotal} с @@ -806,2816 +643,399 @@ def catalog(): updateCartButton(); } - function toggleFavorite(index) { - let favorites = JSON.parse(localStorage.getItem('favorites') || '[]'); - const productId = index.toString(); - const favoriteButton = document.querySelector(`.favorite-button[onclick="event.stopPropagation(); toggleFavorite(${index})"]`); - if (favorites.includes(productId)) { - favorites = favorites.filter(id => id !== productId); - favoriteButton.classList.remove('favorited'); - } else { - favorites.push(productId); - favoriteButton.classList.add('favorited'); - } - localStorage.setItem('favorites', JSON.stringify(favorites)); - } - - function loadFavorites() { - const favorites = JSON.parse(localStorage.getItem('favorites') || '[]'); - document.querySelectorAll('.favorite-button').forEach(button => { - const index = button.getAttribute('onclick').match(/\d+/)[0]; - if (favorites.includes(index)) { - button.classList.add('favorited'); - } - }); - } - - // Removed openAddressModal function - window.onclick = function(event) { if (event.target.className === 'modal') event.target.style.display = "none"; } document.getElementById('search-input').addEventListener('input', filterProducts); - // Removed category filter event listeners + document.querySelectorAll('.category-filter').forEach(filter => { + filter.addEventListener('click', function() { + document.querySelectorAll('.category-filter').forEach(f => f.classList.remove('active')); + this.classList.add('active'); + filterProducts(); + }); + }); function filterProducts() { const searchTerm = document.getElementById('search-input').value.toLowerCase(); - // Removed activeCategory check + const activeCategory = document.querySelector('.category-filter.active').dataset.category; document.querySelectorAll('.product').forEach(product => { const name = product.getAttribute('data-name'); const description = product.getAttribute('data-description'); - // const category = product.getAttribute('data-category'); // Kept for potential use + const category = product.getAttribute('data-category'); const matchesSearch = name.includes(searchTerm) || description.includes(searchTerm); - // Removed category matching - product.style.display = matchesSearch ? 'block' : 'none'; //Simplified condition + const matchesCategory = activeCategory === 'all' || category === activeCategory; + product.style.display = matchesSearch && matchesCategory ? 'block' : 'none'; }); } updateCartButton(); - loadFavorites(); ''' - return render_template_string(catalog_html, products=products, repo_id=REPO_ID) + return render_template_string(catalog_html, products=products, categories=categories, repo_id=REPO_ID) + +@app.route('/product/') +def product_detail(index): + data = load_data() + products = data['products'] + try: + product = products[index] + except IndexError: + return "Продукт не найден", 404 + detail_html = ''' +
+

{{ product['name'] }}

+
+
+ {% if product.get('photos') %} + {% for photo in product['photos'] %} +
+
+ {{ product['name'] }} +
+
+ {% endfor %} + {% else %} +
+ No Image +
+ {% endif %} +
+
+
+
+
+

Категория: {{ product.get('category', 'Без категории') }}

+

Цена: {{ product['price'] }} с

+

Описание: {{ product['description'] }}

+

Доступные цвета: {{ product.get('colors', ['Нет цветов'])|join(', ') }}

+
+ ''' + return render_template_string(detail_html, product=product, repo_id=REPO_ID) -@app.route('/categories') -def categories_page(): +@app.route('/admin', methods=['GET', 'POST']) +def admin(): data = load_data() + products = data['products'] categories = data['categories'] + + if request.method == 'POST': + action = request.form.get('action') + + if action == 'add_category': + category_name = request.form.get('category_name') + if category_name and category_name not in categories: + categories.append(category_name) + save_data(data) + return redirect(url_for('admin')) + return "Ошибка: Категория уже существует или не указано название", 400 + + elif action == 'delete_category': + category_index = int(request.form.get('category_index')) + deleted_category = categories.pop(category_index) + for product in products: + if product.get('category') == deleted_category: + product['category'] = 'Без категории' + save_data(data) + return redirect(url_for('admin')) + + elif action == 'add': + name = request.form.get('name') + price = request.form.get('price') + description = request.form.get('description') + category = request.form.get('category') + photos_files = request.files.getlist('photos') + colors = request.form.getlist('colors') + photos_list = [] + + if photos_files: + for photo in photos_files[:10]: # Ограничение до 10 фото + if photo and photo.filename: + photo_filename = secure_filename(photo.filename) + uploads_dir = 'uploads' + os.makedirs(uploads_dir, exist_ok=True) + temp_path = os.path.join(uploads_dir, photo_filename) + photo.save(temp_path) + api = HfApi() + api.upload_file( + path_or_fileobj=temp_path, + path_in_repo=f"photos/{photo_filename}", + repo_id=REPO_ID, + repo_type="dataset", + token=HF_TOKEN_WRITE, + commit_message=f"Добавлено фото для товара {name}" + ) + photos_list.append(photo_filename) + if os.path.exists(temp_path): + os.remove(temp_path) + + if not name or not price or not description: + return "Ошибка: Заполните все обязательные поля", 400 + + price = float(price.replace(',', '.')) + new_product = { + 'name': name, + 'price': price, + 'description': description, + 'category': category if category in categories else 'Без категории', + 'photos': photos_list, + 'colors': colors if colors else [] + } + products.append(new_product) + save_data(data) + return redirect(url_for('admin')) + + elif action == 'edit': + index = int(request.form.get('index')) + name = request.form.get('name') + price = request.form.get('price') + description = request.form.get('description') + category = request.form.get('category') + photos_files = request.files.getlist('photos') + colors = request.form.getlist('colors') + + if photos_files and any(photo.filename for photo in photos_files): + new_photos_list = [] + for photo in photos_files[:10]: # Ограничение до 10 фото + if photo and photo.filename: + photo_filename = secure_filename(photo.filename) + uploads_dir = 'uploads' + os.makedirs(uploads_dir, exist_ok=True) + temp_path = os.path.join(uploads_dir, photo_filename) + photo.save(temp_path) + api = HfApi() + api.upload_file( + path_or_fileobj=temp_path, + path_in_repo=f"photos/{photo_filename}", + repo_id=REPO_ID, + repo_type="dataset", + token=HF_TOKEN_WRITE, + commit_message=f"Обновлено фото для товара {name}" + ) + new_photos_list.append(photo_filename) + if os.path.exists(temp_path): + os.remove(temp_path) + products[index]['photos'] = new_photos_list + + products[index]['name'] = name + products[index]['price'] = float(price.replace(',', '.')) + products[index]['description'] = description + products[index]['category'] = category if category in categories else 'Без категории' + products[index]['colors'] = colors if colors else [] + save_data(data) + return redirect(url_for('admin')) + + elif action == 'delete': + index = int(request.form.get('index')) + del products[index] + save_data(data) + return redirect(url_for('admin')) - categories_html = ''' + admin_html = ''' - Категории - - + Админ-панель + - - -
-
-

Категории

- -
-
- {% for category in categories %} - -

{{ category }}

-
- {% endfor %} -
-
- - - - - - - ''' - return render_template_string(categories_html, categories=categories) - -@app.route('/category/') -def category_products(category): - data = load_data() - products = [p for p in data['products'] if p.get('category') == category] - # categories = data['categories'] # No longer needed here - - category_html = ''' - - - - - - {{ category }} - - - - - - -
-
-

{{ category }}

- -
-
- {% for product in products %} -
- - {% if product.get('photos') and product['photos']|length > 0 %} -
- {{ product['name'] }} -
- {% endif %} - {% if product.get('wholesale_price') and product.get('min_wholesale') %} - Опт от {{ product['min_wholesale'] }} - {% endif %} - {% if product.get('discount') %} - Скидка {{ product['discount'] }}% - {% endif %} -

{{ product['name'] }}

-
- {% if product.get('discount') %} - {{ product['price'] }} с - {{ (product['price'] * (1 - product['discount'] / 100))|round(2) }} с - Скидка: {{ product['discount'] }}% - {% else %} - {{ product['price'] }} с - {% endif %} - {% if product.get('wholesale_price') and product.get('min_wholesale') %} - Опт: {{ product['wholesale_price'] }} с - {% endif %} -
-

{{ product['description'][:50] }}{% if product['description']|length > 50 %}...{% endif %}

- -
- {% endfor %} -
-
- - - - - - - - - - - - - - - - - - - - - ''' - return render_template_string(category_html, products=products, category=category, repo_id=REPO_ID) - - -@app.route('/favorites') -def favorites_page(): - data = load_data() - products = data['products'] - favorites = request.args.get('favorites', '[]') # Not really used directly, but kept for consistency - - favorites_html = ''' - - - - - - Избранное - - - - - - -
-
-

Избранное

- -
-
-
-
- - - - - - - - - - - - - - - - - - - - - - ''' - return render_template_string(favorites_html, products=products, repo_id=REPO_ID) - - - -@app.route('/discounts') -def discounts_page(): - data = load_data() - products = [p for p in data['products'] if p.get('discount')] - # categories = data['categories'] # No longer used in this view - - discounts_html = ''' - - - - - - Скидки - - - - - - -
-
-

Скидки

- -
-
- {% for product in products %} -
- - {% if product.get('photos') and product['photos']|length > 0 %} -
- {{ product['name'] }} -
- {% endif %} - {% if product.get('wholesale_price') and product.get('min_wholesale') %} - Опт от {{ product['min_wholesale'] }} - {% endif %} - {% if product.get('discount') %} - Скидка {{ product['discount'] }}% - {% endif %} -

{{ product['name'] }}

-
- {% if product.get('discount') %} - {{ product['price'] }} с - {{ (product['price'] * (1 - product['discount'] / 100))|round(2) }} с - Скидка: {{ product['discount'] }}% - {% else %} - {{ product['price'] }} с - {% endif %} - {% if product.get('wholesale_price') and product.get('min_wholesale') %} - Опт: {{ product['wholesale_price'] }} с - {% endif %} -
-

{{ product['description'][:50] }}{% if product['description']|length > 50 %}...{% endif %}

- -
- {% endfor %} -
-
- - - - - - - - - - - - - - - - - - - - - ''' - return render_template_string(discounts_html, products=products, repo_id=REPO_ID) - - -@app.route('/product/') -def product_details(index): - data = load_data() - if index < 0 or index >= len(data['products']): - return "Product not found", 404 # Return a 404 error if index is out of bounds - product = data['products'][index] - - product_html = ''' - -
-

{{ product['name'] }}

-
-
- {% for photo in product.get('photos', []) %} -
- {{ product['name'] }} -
- {% endfor %} -
-
-
-
-
-
- {% if product.get('discount') %} - {{ product['price'] }} с - {{ (product['price'] * (1 - product['discount'] / 100))|round(2) }} с - Скидка: {{ product['discount'] }}% - {% else %} - {{ product['price'] }} с - {% endif %} - {% if product.get('wholesale_price') and product.get('min_wholesale') %} - Опт: {{ product['wholesale_price'] }} с (от {{ product['min_wholesale'] }}) - {% endif %} -
-

Описание: {{ product['description'] }}

-

Категория: {{ product.get('category', 'Без категории') }}

- {% if product.get('colors') %} -
- Цвета: - {% for color in product['colors'] %} - {{ color }} - {% endfor %} -
- {% endif %} -
- ''' - return render_template_string(product_html, product=product, repo_id=REPO_ID) - -@app.route('/admin', methods=['GET', 'POST']) -def admin(): - data = load_data() - products = data['products'] - categories = data['categories'] - - if request.method == 'POST': - action = request.form.get('action') - - if action == 'add': - photos = request.files.getlist('photos') - photo_filenames = [] - for photo in photos: - if photo and photo.filename: - filename = secure_filename(photo.filename) - api = HfApi() - api.upload_file( - path_or_fileobj=photo, - path_in_repo=f"photos/{filename}", - repo_id=REPO_ID, - repo_type="dataset", - token=HF_TOKEN_WRITE - ) - photo_filenames.append(filename) - - colors = request.form.getlist('colors') - colors = [color.strip() for color in colors if color.strip()] - - new_product = { - 'name': request.form['name'], - 'price': float(request.form['price']), - 'wholesale_price': float(request.form['wholesale_price']) if request.form['wholesale_price'] else None, - 'min_wholesale': int(request.form['min_wholesale']) if request.form['min_wholesale'] else None, - 'description': request.form['description'], - 'category': request.form['category'], - 'colors': colors, - 'photos': photo_filenames, - 'discount': float(request.form['discount']) if request.form['discount'] else None - } - products.append(new_product) - - elif action == 'edit': - index = int(request.form['index']) - photos = request.files.getlist('photos') - photo_filenames = products[index].get('photos', []) # Keep existing photos - for photo in photos: - if photo and photo.filename: - filename = secure_filename(photo.filename) - api = HfApi() - api.upload_file( - path_or_fileobj=photo, - path_in_repo=f"photos/{filename}", - repo_id=REPO_ID, - repo_type="dataset", - token=HF_TOKEN_WRITE - ) - photo_filenames.append(filename) # Add new photos - - colors = request.form.getlist('colors') - colors = [color.strip() for color in colors if color.strip()] - - products[index] = { - 'name': request.form['name'], - 'price': float(request.form['price']), - 'wholesale_price': float(request.form['wholesale_price']) if request.form['wholesale_price'] else None, - 'min_wholesale': int(request.form['min_wholesale']) if request.form['min_wholesale'] else None, - 'description': request.form['description'], - 'category': request.form['category'], - 'colors': colors, - 'photos': photo_filenames, # Updated photo list - 'discount': float(request.form['discount']) if request.form['discount'] else None - } - - elif action == 'delete': - index = int(request.form['index']) - # Delete photos from Hugging Face Hub before deleting the product - product_to_delete = products[index] - if 'photos' in product_to_delete: - api = HfApi() - for photo in product_to_delete['photos']: - try: - api.delete_file( - path_in_repo=f"photos/{photo}", - repo_id=REPO_ID, - repo_type="dataset", - token=HF_TOKEN_WRITE - ) - except Exception as e: - logging.error(f"Error deleting photo {photo}: {e}") - products.pop(index) - - - elif action == 'add_category': - category = request.form['category_name'].strip() - if category and category not in categories: - categories.append(category) - - elif action == 'delete_category': - index = int(request.form['category_index']) - category_to_delete = categories[index] - categories.pop(index) - #Set category to "Без категории" for products in the deleted category - for product in products: - if product.get('category') == category_to_delete: - product['category'] = 'Без категории' - - save_data({'products': products, 'categories': categories}) - return redirect(url_for('admin')) - - admin_html = ''' - - - - - - Админ-панель - -
-

Админ-панель

- -
-

Добавить товар

-
- - - - - - - - - - - - - - - - - - -
-
- -
-
- - -
-
- -
-

Добавить категорию

-
- - - - -
+
+ +

Админ-панель

- +

Добавление товара

+
+ + + + + + + + + + + + +
+
+ +
+
+ + +
+ +

Управление категориями

+
+ + + + +
+ +

Список категорий

{% for category in categories %}
-

{{ category }}

-
+

{{ category }}

+ - +
{% endfor %}
-

Список товаров

-
- -
-
+

Управление базой данных

+
+ +
+
+ +
+ +

Список товаров

+
{% for product in products %} -
-

{{ product['name'] }}

-

Цена (розница): {{ product['price'] }} с

- {% if product.get('wholesale_price') and product.get('min_wholesale') %} -

Цена (опт): {{ product['wholesale_price'] }} с (от {{ product['min_wholesale'] }})

- {% endif %} - {% if product.get('discount') %} -

Скидка: {{ product['discount'] }}%

- {% endif %} -

Описание: {{ product['description'] }}

-

Категория: {{ product.get('category', 'Без категории') }}

-

Цвета: {{ product.get('colors', ['Нет цветов'])|join(', ') }}

- {% if product.get('photos') %} -

Фотографии:

- {% for photo in product['photos'] %} - {{ product['name'] }} - {% endfor %} +
+

{{ product['name'] }}

+

Категория: {{ product.get('category', 'Без категории') }}

+

Цена: {{ product['price'] }} с

+

Описание: {{ product['description'] }}

+

Цвета: {{ product.get('colors', ['Нет цветов'])|join(', ') }}

+ {% if product.get('photos') and product['photos']|length > 0 %} +
+ {% for photo in product['photos'] %} + {{ product['name'] }} + {% endfor %} +
{% endif %}
Редактировать @@ -3624,14 +1044,8 @@ def admin(): - + - - - - - - @@ -3672,19 +1086,6 @@ def admin(): newInput.innerHTML = ''; container.appendChild(newInput); } - - document.getElementById('search-input').addEventListener('input', filterProducts); - - function filterProducts() { - const searchTerm = document.getElementById('search-input').value.toLowerCase(); - document.querySelectorAll('.product-item').forEach(product => { - const name = product.getAttribute('data-name'); - const description = product.getAttribute('data-description'); - const category = product.getAttribute('data-category'); - const matchesSearch = name.includes(searchTerm) || description.includes(searchTerm) || category.includes(searchTerm); - product.style.display = matchesSearch ? 'block' : 'none'; - }); - }