Update app.py
Browse files
app.py
CHANGED
|
@@ -13,7 +13,7 @@ app = Flask(__name__)
|
|
| 13 |
DATA_FILE = 'data_kanc.json'
|
| 14 |
|
| 15 |
# Настройки Hugging Face
|
| 16 |
-
REPO_ID = "Kgshop/clients"
|
| 17 |
HF_TOKEN_WRITE = os.getenv("HF_TOKEN")
|
| 18 |
HF_TOKEN_READ = os.getenv("HF_TOKEN_READ")
|
| 19 |
|
|
@@ -27,7 +27,7 @@ def load_data():
|
|
| 27 |
data = json.load(file)
|
| 28 |
logging.info("Данные успешно загружены из JSON")
|
| 29 |
if not isinstance(data, dict) or 'products' not in data or 'categories' not in data:
|
| 30 |
-
return {'products': [], 'categories': []
|
| 31 |
return data
|
| 32 |
except FileNotFoundError:
|
| 33 |
logging.warning("Локальный файл базы данных не найден после скачивания.")
|
|
@@ -94,8 +94,8 @@ def periodic_backup():
|
|
| 94 |
def catalog():
|
| 95 |
data = load_data()
|
| 96 |
products = data['products']
|
| 97 |
-
categories = data['categories']
|
| 98 |
-
|
| 99 |
catalog_html = '''
|
| 100 |
<!DOCTYPE html>
|
| 101 |
<html lang="ru">
|
|
@@ -107,6 +107,7 @@ def catalog():
|
|
| 107 |
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
|
| 108 |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/Swiper/10.2.0/swiper-bundle.min.css">
|
| 109 |
<style>
|
|
|
|
| 110 |
* {
|
| 111 |
margin: 0;
|
| 112 |
padding: 0;
|
|
@@ -158,13 +159,7 @@ def catalog():
|
|
| 158 |
body.dark-mode .theme-toggle {
|
| 159 |
color: #bbb;
|
| 160 |
}
|
| 161 |
-
.filters-container
|
| 162 |
-
margin: 20px 0;
|
| 163 |
-
display: flex;
|
| 164 |
-
flex-wrap: wrap;
|
| 165 |
-
gap: 10px;
|
| 166 |
-
justify-content: center;
|
| 167 |
-
}
|
| 168 |
.search-container {
|
| 169 |
margin: 20px 0;
|
| 170 |
text-align: center;
|
|
@@ -184,33 +179,13 @@ def catalog():
|
|
| 184 |
border-color: #526df2;
|
| 185 |
box-shadow: 0 4px 15px rgba(82, 109, 242, 0.2);
|
| 186 |
}
|
| 187 |
-
.category-filter {
|
| 188 |
-
padding: 10px 20px;
|
| 189 |
-
border: 1px solid rgba(0, 0, 0, 0.1);
|
| 190 |
-
border-radius: 25px;
|
| 191 |
-
background-color: #fff;
|
| 192 |
-
cursor: pointer;
|
| 193 |
-
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
| 194 |
-
font-size: 0.9rem;
|
| 195 |
-
font-weight: 500;
|
| 196 |
-
}
|
| 197 |
-
.category-filter.active, .category-filter:hover {
|
| 198 |
-
background-color: #526df2;
|
| 199 |
-
color: white;
|
| 200 |
-
border-color: #526df2;
|
| 201 |
-
box-shadow: 0 2px 10px rgba(82, 109, 242, 0.3);
|
| 202 |
-
}
|
| 203 |
-
body.dark-mode .category-filter {
|
| 204 |
-
background-color: #2a2a3e;
|
| 205 |
-
border-color: rgba(255, 255, 255, 0.1);
|
| 206 |
-
color: #e0e0e0;
|
| 207 |
-
}
|
| 208 |
.products-grid {
|
| 209 |
display: grid;
|
| 210 |
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
| 211 |
gap: 20px;
|
| 212 |
padding: 10px;
|
| 213 |
}
|
|
|
|
| 214 |
.product {
|
| 215 |
background: rgba(255, 255, 255, 0.8);
|
| 216 |
border: 1px solid rgba(0, 0, 0, 0.05);
|
|
@@ -509,6 +484,32 @@ def catalog():
|
|
| 509 |
font-size: 0.75rem;
|
| 510 |
font-weight: 500;
|
| 511 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 512 |
</style>
|
| 513 |
</head>
|
| 514 |
<body>
|
|
@@ -519,12 +520,15 @@ def catalog():
|
|
| 519 |
<i class="fas fa-moon"></i>
|
| 520 |
</button>
|
| 521 |
</div>
|
| 522 |
-
|
| 523 |
-
|
| 524 |
-
|
| 525 |
-
<
|
| 526 |
-
|
|
|
|
|
|
|
| 527 |
</div>
|
|
|
|
| 528 |
<div class="search-container">
|
| 529 |
<input type="text" id="search-input" placeholder="Поиск товаров...">
|
| 530 |
</div>
|
|
@@ -604,15 +608,6 @@ def catalog():
|
|
| 604 |
</div>
|
| 605 |
</div>
|
| 606 |
|
| 607 |
-
<!-- Address Modal -->
|
| 608 |
-
<div id="addressModal" class="modal">
|
| 609 |
-
<div class="modal-content">
|
| 610 |
-
<span class="close" onclick="closeModal('addressModal')">×</span>
|
| 611 |
-
<h2>Наш адрес</h2>
|
| 612 |
-
<p>Рынок Дордой, Джунхай, 5 проход 505-506 контейнеры</p>
|
| 613 |
-
</div>
|
| 614 |
-
</div>
|
| 615 |
-
|
| 616 |
<button id="cart-button" onclick="openCartModal()">🛒</button>
|
| 617 |
|
| 618 |
<!-- Navigation Bar -->
|
|
@@ -633,10 +628,6 @@ def catalog():
|
|
| 633 |
<i class="fas fa-tag"></i>
|
| 634 |
Скидки
|
| 635 |
</a>
|
| 636 |
-
<a href="#" onclick="openAddressModal()">
|
| 637 |
-
<i class="fas fa-map-marker-alt"></i>
|
| 638 |
-
Адрес
|
| 639 |
-
</a>
|
| 640 |
<a href="https://api.whatsapp.com/send?phone=996500654659">
|
| 641 |
<i class="fab fa-whatsapp"></i>
|
| 642 |
WhatsApp
|
|
@@ -839,33 +830,25 @@ def catalog():
|
|
| 839 |
});
|
| 840 |
}
|
| 841 |
|
| 842 |
-
|
| 843 |
-
document.getElementById('addressModal').style.display = 'block';
|
| 844 |
-
}
|
| 845 |
|
| 846 |
window.onclick = function(event) {
|
| 847 |
if (event.target.className === 'modal') event.target.style.display = "none";
|
| 848 |
}
|
| 849 |
|
| 850 |
document.getElementById('search-input').addEventListener('input', filterProducts);
|
| 851 |
-
|
| 852 |
-
filter.addEventListener('click', function() {
|
| 853 |
-
document.querySelectorAll('.category-filter').forEach(f => f.classList.remove('active'));
|
| 854 |
-
this.classList.add('active');
|
| 855 |
-
filterProducts();
|
| 856 |
-
});
|
| 857 |
-
});
|
| 858 |
|
| 859 |
function filterProducts() {
|
| 860 |
const searchTerm = document.getElementById('search-input').value.toLowerCase();
|
| 861 |
-
|
| 862 |
document.querySelectorAll('.product').forEach(product => {
|
| 863 |
const name = product.getAttribute('data-name');
|
| 864 |
const description = product.getAttribute('data-description');
|
| 865 |
-
const category = product.getAttribute('data-category');
|
| 866 |
const matchesSearch = name.includes(searchTerm) || description.includes(searchTerm);
|
| 867 |
-
|
| 868 |
-
product.style.display = matchesSearch
|
| 869 |
});
|
| 870 |
}
|
| 871 |
|
|
@@ -875,7 +858,7 @@ def catalog():
|
|
| 875 |
</body>
|
| 876 |
</html>
|
| 877 |
'''
|
| 878 |
-
return render_template_string(catalog_html, products=products,
|
| 879 |
|
| 880 |
@app.route('/categories')
|
| 881 |
def categories_page():
|
|
@@ -1028,22 +1011,13 @@ def categories_page():
|
|
| 1028 |
</div>
|
| 1029 |
<div class="categories-grid">
|
| 1030 |
{% for category in categories %}
|
| 1031 |
-
<a href="
|
| 1032 |
<h2>{{ category }}</h2>
|
| 1033 |
</a>
|
| 1034 |
{% endfor %}
|
| 1035 |
</div>
|
| 1036 |
</div>
|
| 1037 |
|
| 1038 |
-
<!-- Address Modal -->
|
| 1039 |
-
<div id="addressModal" class="modal">
|
| 1040 |
-
<div class="modal-content">
|
| 1041 |
-
<span class="close" onclick="closeModal('addressModal')">×</span>
|
| 1042 |
-
<h2>Наш адрес</h2>
|
| 1043 |
-
<p>Рынок Дордой, Джунхай, 5 проход 505-506 контейнеры</p>
|
| 1044 |
-
</div>
|
| 1045 |
-
</div>
|
| 1046 |
-
|
| 1047 |
<div class="navbar">
|
| 1048 |
<a href="/">
|
| 1049 |
<i class="fas fa-home"></i>
|
|
@@ -1061,10 +1035,6 @@ def categories_page():
|
|
| 1061 |
<i class="fas fa-tag"></i>
|
| 1062 |
Скидки
|
| 1063 |
</a>
|
| 1064 |
-
<a href="#" onclick="openAddressModal()">
|
| 1065 |
-
<i class="fas fa-map-marker-alt"></i>
|
| 1066 |
-
Адрес
|
| 1067 |
-
</a>
|
| 1068 |
<a href="https://api.whatsapp.com/send?phone=996500654659">
|
| 1069 |
<i class="fab fa-whatsapp"></i>
|
| 1070 |
WhatsApp
|
|
@@ -1085,14 +1055,6 @@ def categories_page():
|
|
| 1085 |
document.querySelector('.theme-toggle i').classList.replace('fa-moon', 'fa-sun');
|
| 1086 |
}
|
| 1087 |
|
| 1088 |
-
function openAddressModal() {
|
| 1089 |
-
document.getElementById('addressModal').style.display = 'block';
|
| 1090 |
-
}
|
| 1091 |
-
|
| 1092 |
-
function closeModal(modalId) {
|
| 1093 |
-
document.getElementById(modalId).style.display = "none";
|
| 1094 |
-
}
|
| 1095 |
-
|
| 1096 |
window.onclick = function(event) {
|
| 1097 |
if (event.target.className === 'modal') event.target.style.display = "none";
|
| 1098 |
}
|
|
@@ -1106,8 +1068,8 @@ def categories_page():
|
|
| 1106 |
def category_products(category):
|
| 1107 |
data = load_data()
|
| 1108 |
products = [p for p in data['products'] if p.get('category') == category]
|
| 1109 |
-
categories = data['categories']
|
| 1110 |
-
|
| 1111 |
category_html = '''
|
| 1112 |
<!DOCTYPE html>
|
| 1113 |
<html lang="ru">
|
|
@@ -1119,6 +1081,7 @@ def category_products(category):
|
|
| 1119 |
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
|
| 1120 |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/Swiper/10.2.0/swiper-bundle.min.css">
|
| 1121 |
<style>
|
|
|
|
| 1122 |
* {
|
| 1123 |
margin: 0;
|
| 1124 |
padding: 0;
|
|
@@ -1474,6 +1437,7 @@ def category_products(category):
|
|
| 1474 |
font-size: 0.75rem;
|
| 1475 |
font-weight: 500;
|
| 1476 |
}
|
|
|
|
| 1477 |
</style>
|
| 1478 |
</head>
|
| 1479 |
<body>
|
|
@@ -1560,15 +1524,6 @@ def category_products(category):
|
|
| 1560 |
</div>
|
| 1561 |
</div>
|
| 1562 |
|
| 1563 |
-
<!-- Address Modal -->
|
| 1564 |
-
<div id="addressModal" class="modal">
|
| 1565 |
-
<div class="modal-content">
|
| 1566 |
-
<span class="close" onclick="closeModal('addressModal')">×</span>
|
| 1567 |
-
<h2>Наш адрес</h2>
|
| 1568 |
-
<p>Рынок Дордой, Джунхай, 5 проход 505-506 контейнеры</p>
|
| 1569 |
-
</div>
|
| 1570 |
-
</div>
|
| 1571 |
-
|
| 1572 |
<button id="cart-button" onclick="openCartModal()">🛒</button>
|
| 1573 |
|
| 1574 |
<div class="navbar">
|
|
@@ -1588,10 +1543,7 @@ def category_products(category):
|
|
| 1588 |
<i class="fas fa-tag"></i>
|
| 1589 |
Скидки
|
| 1590 |
</a>
|
| 1591 |
-
|
| 1592 |
-
<i class="fas fa-map-marker-alt"></i>
|
| 1593 |
-
Адрес
|
| 1594 |
-
</a>
|
| 1595 |
<a href="https://api.whatsapp.com/send?phone=996500654659">
|
| 1596 |
<i class="fab fa-whatsapp"></i>
|
| 1597 |
WhatsApp
|
|
@@ -1794,9 +1746,6 @@ def category_products(category):
|
|
| 1794 |
});
|
| 1795 |
}
|
| 1796 |
|
| 1797 |
-
function openAddressModal() {
|
| 1798 |
-
document.getElementById('addressModal').style.display = 'block';
|
| 1799 |
-
}
|
| 1800 |
|
| 1801 |
window.onclick = function(event) {
|
| 1802 |
if (event.target.className === 'modal') event.target.style.display = "none";
|
|
@@ -1810,12 +1759,13 @@ def category_products(category):
|
|
| 1810 |
'''
|
| 1811 |
return render_template_string(category_html, products=products, category=category, repo_id=REPO_ID)
|
| 1812 |
|
|
|
|
| 1813 |
@app.route('/favorites')
|
| 1814 |
def favorites_page():
|
| 1815 |
data = load_data()
|
| 1816 |
products = data['products']
|
| 1817 |
-
favorites = request.args.get('favorites', '[]')
|
| 1818 |
-
|
| 1819 |
favorites_html = '''
|
| 1820 |
<!DOCTYPE html>
|
| 1821 |
<html lang="ru">
|
|
@@ -1827,6 +1777,7 @@ def favorites_page():
|
|
| 1827 |
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
|
| 1828 |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/Swiper/10.2.0/swiper-bundle.min.css">
|
| 1829 |
<style>
|
|
|
|
| 1830 |
* {
|
| 1831 |
margin: 0;
|
| 1832 |
padding: 0;
|
|
@@ -2229,14 +2180,6 @@ def favorites_page():
|
|
| 2229 |
</div>
|
| 2230 |
</div>
|
| 2231 |
|
| 2232 |
-
<!-- Address Modal -->
|
| 2233 |
-
<div id="addressModal" class="modal">
|
| 2234 |
-
<div class="modal-content">
|
| 2235 |
-
<span class="close" onclick="closeModal('addressModal')">×</span>
|
| 2236 |
-
<h2>Наш адрес</h2>
|
| 2237 |
-
<p>Рынок Дордой, Джунхай, 5 проход 505-506 контейнеры</p>
|
| 2238 |
-
</div>
|
| 2239 |
-
</div>
|
| 2240 |
|
| 2241 |
<button id="cart-button" onclick="openCartModal()">🛒</button>
|
| 2242 |
|
|
@@ -2257,10 +2200,6 @@ def favorites_page():
|
|
| 2257 |
<i class="fas fa-tag"></i>
|
| 2258 |
Скидки
|
| 2259 |
</a>
|
| 2260 |
-
<a href="#" onclick="openAddressModal()">
|
| 2261 |
-
<i class="fas fa-map-marker-alt"></i>
|
| 2262 |
-
Адрес
|
| 2263 |
-
</a>
|
| 2264 |
<a href="https://api.whatsapp.com/send?phone=996500654659">
|
| 2265 |
<i class="fab fa-whatsapp"></i>
|
| 2266 |
WhatsApp
|
|
@@ -2299,7 +2238,7 @@ def favorites_page():
|
|
| 2299 |
|
| 2300 |
favorites.forEach(index => {
|
| 2301 |
const product = products[index];
|
| 2302 |
-
if (!product) return;
|
| 2303 |
const productElement = document.createElement('div');
|
| 2304 |
productElement.className = 'product';
|
| 2305 |
productElement.setAttribute('onclick', `openModal(${index})`);
|
|
@@ -2495,11 +2434,7 @@ def favorites_page():
|
|
| 2495 |
favoriteButton.classList.add('favorited');
|
| 2496 |
}
|
| 2497 |
localStorage.setItem('favorites', JSON.stringify(favorites));
|
| 2498 |
-
loadFavoritesPage();
|
| 2499 |
-
}
|
| 2500 |
-
|
| 2501 |
-
function openAddressModal() {
|
| 2502 |
-
document.getElementById('addressModal').style.display = 'block';
|
| 2503 |
}
|
| 2504 |
|
| 2505 |
window.onclick = function(event) {
|
|
@@ -2507,19 +2442,21 @@ def favorites_page():
|
|
| 2507 |
}
|
| 2508 |
|
| 2509 |
updateCartButton();
|
| 2510 |
-
loadFavoritesPage();
|
| 2511 |
</script>
|
| 2512 |
</body>
|
| 2513 |
</html>
|
| 2514 |
'''
|
| 2515 |
return render_template_string(favorites_html, products=products, repo_id=REPO_ID)
|
| 2516 |
|
|
|
|
|
|
|
| 2517 |
@app.route('/discounts')
|
| 2518 |
def discounts_page():
|
| 2519 |
data = load_data()
|
| 2520 |
products = [p for p in data['products'] if p.get('discount')]
|
| 2521 |
-
categories = data['categories']
|
| 2522 |
-
|
| 2523 |
discounts_html = '''
|
| 2524 |
<!DOCTYPE html>
|
| 2525 |
<html lang="ru">
|
|
@@ -2531,6 +2468,7 @@ def discounts_page():
|
|
| 2531 |
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
|
| 2532 |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/Swiper/10.2.0/swiper-bundle.min.css">
|
| 2533 |
<style>
|
|
|
|
| 2534 |
* {
|
| 2535 |
margin: 0;
|
| 2536 |
padding: 0;
|
|
@@ -2972,15 +2910,6 @@ def discounts_page():
|
|
| 2972 |
</div>
|
| 2973 |
</div>
|
| 2974 |
|
| 2975 |
-
<!-- Address Modal -->
|
| 2976 |
-
<div id="addressModal" class="modal">
|
| 2977 |
-
<div class="modal-content">
|
| 2978 |
-
<span class="close" onclick="closeModal('addressModal')">×</span>
|
| 2979 |
-
<h2>Наш адрес</h2>
|
| 2980 |
-
<p>Рынок Дордой, Джунхай, 5 проход 505-506 контейнеры</p>
|
| 2981 |
-
</div>
|
| 2982 |
-
</div>
|
| 2983 |
-
|
| 2984 |
<button id="cart-button" onclick="openCartModal()">🛒</button>
|
| 2985 |
|
| 2986 |
<div class="navbar">
|
|
@@ -3000,10 +2929,6 @@ def discounts_page():
|
|
| 3000 |
<i class="fas fa-tag"></i>
|
| 3001 |
Скидки
|
| 3002 |
</a>
|
| 3003 |
-
<a href="#" onclick="openAddressModal()">
|
| 3004 |
-
<i class="fas fa-map-marker-alt"></i>
|
| 3005 |
-
Адрес
|
| 3006 |
-
</a>
|
| 3007 |
<a href="https://api.whatsapp.com/send?phone=996500654659">
|
| 3008 |
<i class="fab fa-whatsapp"></i>
|
| 3009 |
WhatsApp
|
|
@@ -3206,10 +3131,6 @@ def discounts_page():
|
|
| 3206 |
});
|
| 3207 |
}
|
| 3208 |
|
| 3209 |
-
function openAddressModal() {
|
| 3210 |
-
document.getElementById('addressModal').style.display = 'block';
|
| 3211 |
-
}
|
| 3212 |
-
|
| 3213 |
window.onclick = function(event) {
|
| 3214 |
if (event.target.className === 'modal') event.target.style.display = "none";
|
| 3215 |
}
|
|
@@ -3220,15 +3141,19 @@ def discounts_page():
|
|
| 3220 |
</body>
|
| 3221 |
</html>
|
| 3222 |
'''
|
| 3223 |
-
return render_template_string(discounts_html, products=products,
|
|
|
|
| 3224 |
|
| 3225 |
@app.route('/product/<int:index>')
|
| 3226 |
def product_details(index):
|
| 3227 |
data = load_data()
|
|
|
|
|
|
|
| 3228 |
product = data['products'][index]
|
| 3229 |
-
|
| 3230 |
product_html = '''
|
| 3231 |
<style>
|
|
|
|
| 3232 |
.swiper-container {
|
| 3233 |
width: 100%;
|
| 3234 |
max-width: 400px;
|
|
@@ -3300,7 +3225,7 @@ def product_details(index):
|
|
| 3300 |
font-size: 0.9rem;
|
| 3301 |
}
|
| 3302 |
body.dark-mode .product-details .colors span {
|
| 3303 |
-
background: #
|
| 3304 |
color: #e0e0e0;
|
| 3305 |
}
|
| 3306 |
</style>
|
|
@@ -3388,7 +3313,7 @@ def admin():
|
|
| 3388 |
elif action == 'edit':
|
| 3389 |
index = int(request.form['index'])
|
| 3390 |
photos = request.files.getlist('photos')
|
| 3391 |
-
photo_filenames = products[index].get('photos', [])
|
| 3392 |
for photo in photos:
|
| 3393 |
if photo and photo.filename:
|
| 3394 |
filename = secure_filename(photo.filename)
|
|
@@ -3400,7 +3325,7 @@ def admin():
|
|
| 3400 |
repo_type="dataset",
|
| 3401 |
token=HF_TOKEN_WRITE
|
| 3402 |
)
|
| 3403 |
-
photo_filenames.append(filename)
|
| 3404 |
|
| 3405 |
colors = request.form.getlist('colors')
|
| 3406 |
colors = [color.strip() for color in colors if color.strip()]
|
|
@@ -3413,13 +3338,28 @@ def admin():
|
|
| 3413 |
'description': request.form['description'],
|
| 3414 |
'category': request.form['category'],
|
| 3415 |
'colors': colors,
|
| 3416 |
-
'photos': photo_filenames,
|
| 3417 |
'discount': float(request.form['discount']) if request.form['discount'] else None
|
| 3418 |
}
|
| 3419 |
|
| 3420 |
elif action == 'delete':
|
| 3421 |
index = int(request.form['index'])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3422 |
products.pop(index)
|
|
|
|
| 3423 |
|
| 3424 |
elif action == 'add_category':
|
| 3425 |
category = request.form['category_name'].strip()
|
|
@@ -3430,6 +3370,7 @@ def admin():
|
|
| 3430 |
index = int(request.form['category_index'])
|
| 3431 |
category_to_delete = categories[index]
|
| 3432 |
categories.pop(index)
|
|
|
|
| 3433 |
for product in products:
|
| 3434 |
if product.get('category') == category_to_delete:
|
| 3435 |
product['category'] = 'Без категории'
|
|
@@ -3446,6 +3387,7 @@ def admin():
|
|
| 3446 |
<title>Админ-панель</title>
|
| 3447 |
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
|
| 3448 |
<style>
|
|
|
|
| 3449 |
* {
|
| 3450 |
margin: 0;
|
| 3451 |
padding: 0;
|
|
@@ -3766,5 +3708,4 @@ if __name__ == '__main__':
|
|
| 3766 |
load_data()
|
| 3767 |
except Exception as e:
|
| 3768 |
logging.error(f"Не удалось загрузить базу данных: {e}")
|
| 3769 |
-
app.run(debug=True, host='0.0.0.0', port=7860)
|
| 3770 |
-
|
|
|
|
| 13 |
DATA_FILE = 'data_kanc.json'
|
| 14 |
|
| 15 |
# Настройки Hugging Face
|
| 16 |
+
REPO_ID = "Kgshop/clients" # Замените, если нужно
|
| 17 |
HF_TOKEN_WRITE = os.getenv("HF_TOKEN")
|
| 18 |
HF_TOKEN_READ = os.getenv("HF_TOKEN_READ")
|
| 19 |
|
|
|
|
| 27 |
data = json.load(file)
|
| 28 |
logging.info("Данные успешно загружены из JSON")
|
| 29 |
if not isinstance(data, dict) or 'products' not in data or 'categories' not in data:
|
| 30 |
+
return {'products': [], 'categories': []} # Corrected: return empty dict with lists
|
| 31 |
return data
|
| 32 |
except FileNotFoundError:
|
| 33 |
logging.warning("Локальный файл базы данных не найден после скачивания.")
|
|
|
|
| 94 |
def catalog():
|
| 95 |
data = load_data()
|
| 96 |
products = data['products']
|
| 97 |
+
# categories = data['categories'] Removed categories from main page
|
| 98 |
+
|
| 99 |
catalog_html = '''
|
| 100 |
<!DOCTYPE html>
|
| 101 |
<html lang="ru">
|
|
|
|
| 107 |
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
|
| 108 |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/Swiper/10.2.0/swiper-bundle.min.css">
|
| 109 |
<style>
|
| 110 |
+
/* ... (your existing styles) ... */
|
| 111 |
* {
|
| 112 |
margin: 0;
|
| 113 |
padding: 0;
|
|
|
|
| 159 |
body.dark-mode .theme-toggle {
|
| 160 |
color: #bbb;
|
| 161 |
}
|
| 162 |
+
/* Removed .filters-container */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 163 |
.search-container {
|
| 164 |
margin: 20px 0;
|
| 165 |
text-align: center;
|
|
|
|
| 179 |
border-color: #526df2;
|
| 180 |
box-shadow: 0 4px 15px rgba(82, 109, 242, 0.2);
|
| 181 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 182 |
.products-grid {
|
| 183 |
display: grid;
|
| 184 |
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
| 185 |
gap: 20px;
|
| 186 |
padding: 10px;
|
| 187 |
}
|
| 188 |
+
/* ... (rest of your product and modal styles) ... */
|
| 189 |
.product {
|
| 190 |
background: rgba(255, 255, 255, 0.8);
|
| 191 |
border: 1px solid rgba(0, 0, 0, 0.05);
|
|
|
|
| 484 |
font-size: 0.75rem;
|
| 485 |
font-weight: 500;
|
| 486 |
}
|
| 487 |
+
|
| 488 |
+
/* Styles for Address and Schedule */
|
| 489 |
+
.info-section {
|
| 490 |
+
text-align: center;
|
| 491 |
+
margin: 20px 0;
|
| 492 |
+
padding: 15px;
|
| 493 |
+
background: rgba(255, 255, 255, 0.8);
|
| 494 |
+
border-radius: 15px;
|
| 495 |
+
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05);
|
| 496 |
+
}
|
| 497 |
+
body.dark-mode .info-section {
|
| 498 |
+
background: rgba(42, 42, 62, 0.8);
|
| 499 |
+
}
|
| 500 |
+
.info-section h2 {
|
| 501 |
+
font-size: 1.5rem;
|
| 502 |
+
font-weight: 500;
|
| 503 |
+
margin-bottom: 10px;
|
| 504 |
+
color: #526df2;
|
| 505 |
+
}
|
| 506 |
+
.info-section p {
|
| 507 |
+
font-size: 1rem;
|
| 508 |
+
color: #666;
|
| 509 |
+
}
|
| 510 |
+
body.dark-mode .info-section p {
|
| 511 |
+
color: #bbb;
|
| 512 |
+
}
|
| 513 |
</style>
|
| 514 |
</head>
|
| 515 |
<body>
|
|
|
|
| 520 |
<i class="fas fa-moon"></i>
|
| 521 |
</button>
|
| 522 |
</div>
|
| 523 |
+
|
| 524 |
+
<!-- Address and Schedule Section -->
|
| 525 |
+
<div class="info-section">
|
| 526 |
+
<h2>Наш адрес</h2>
|
| 527 |
+
<p>Рынок Дордой, Джунхай, 5 проход 505-506 контейнеры</p>
|
| 528 |
+
<h2>График работы</h2>
|
| 529 |
+
<p>С 9:00 до 16:00 без выходных</p>
|
| 530 |
</div>
|
| 531 |
+
|
| 532 |
<div class="search-container">
|
| 533 |
<input type="text" id="search-input" placeholder="Поиск товаров...">
|
| 534 |
</div>
|
|
|
|
| 608 |
</div>
|
| 609 |
</div>
|
| 610 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 611 |
<button id="cart-button" onclick="openCartModal()">🛒</button>
|
| 612 |
|
| 613 |
<!-- Navigation Bar -->
|
|
|
|
| 628 |
<i class="fas fa-tag"></i>
|
| 629 |
Скидки
|
| 630 |
</a>
|
|
|
|
|
|
|
|
|
|
|
|
|
| 631 |
<a href="https://api.whatsapp.com/send?phone=996500654659">
|
| 632 |
<i class="fab fa-whatsapp"></i>
|
| 633 |
WhatsApp
|
|
|
|
| 830 |
});
|
| 831 |
}
|
| 832 |
|
| 833 |
+
// Removed openAddressModal function
|
|
|
|
|
|
|
| 834 |
|
| 835 |
window.onclick = function(event) {
|
| 836 |
if (event.target.className === 'modal') event.target.style.display = "none";
|
| 837 |
}
|
| 838 |
|
| 839 |
document.getElementById('search-input').addEventListener('input', filterProducts);
|
| 840 |
+
// Removed category filter event listeners
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 841 |
|
| 842 |
function filterProducts() {
|
| 843 |
const searchTerm = document.getElementById('search-input').value.toLowerCase();
|
| 844 |
+
// Removed activeCategory check
|
| 845 |
document.querySelectorAll('.product').forEach(product => {
|
| 846 |
const name = product.getAttribute('data-name');
|
| 847 |
const description = product.getAttribute('data-description');
|
| 848 |
+
// const category = product.getAttribute('data-category'); // Kept for potential use
|
| 849 |
const matchesSearch = name.includes(searchTerm) || description.includes(searchTerm);
|
| 850 |
+
// Removed category matching
|
| 851 |
+
product.style.display = matchesSearch ? 'block' : 'none'; //Simplified condition
|
| 852 |
});
|
| 853 |
}
|
| 854 |
|
|
|
|
| 858 |
</body>
|
| 859 |
</html>
|
| 860 |
'''
|
| 861 |
+
return render_template_string(catalog_html, products=products, repo_id=REPO_ID)
|
| 862 |
|
| 863 |
@app.route('/categories')
|
| 864 |
def categories_page():
|
|
|
|
| 1011 |
</div>
|
| 1012 |
<div class="categories-grid">
|
| 1013 |
{% for category in categories %}
|
| 1014 |
+
<a href="{{ url_for('category_products', category=category) }}" class="category-item">
|
| 1015 |
<h2>{{ category }}</h2>
|
| 1016 |
</a>
|
| 1017 |
{% endfor %}
|
| 1018 |
</div>
|
| 1019 |
</div>
|
| 1020 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1021 |
<div class="navbar">
|
| 1022 |
<a href="/">
|
| 1023 |
<i class="fas fa-home"></i>
|
|
|
|
| 1035 |
<i class="fas fa-tag"></i>
|
| 1036 |
Скидки
|
| 1037 |
</a>
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1038 |
<a href="https://api.whatsapp.com/send?phone=996500654659">
|
| 1039 |
<i class="fab fa-whatsapp"></i>
|
| 1040 |
WhatsApp
|
|
|
|
| 1055 |
document.querySelector('.theme-toggle i').classList.replace('fa-moon', 'fa-sun');
|
| 1056 |
}
|
| 1057 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1058 |
window.onclick = function(event) {
|
| 1059 |
if (event.target.className === 'modal') event.target.style.display = "none";
|
| 1060 |
}
|
|
|
|
| 1068 |
def category_products(category):
|
| 1069 |
data = load_data()
|
| 1070 |
products = [p for p in data['products'] if p.get('category') == category]
|
| 1071 |
+
# categories = data['categories'] # No longer needed here
|
| 1072 |
+
|
| 1073 |
category_html = '''
|
| 1074 |
<!DOCTYPE html>
|
| 1075 |
<html lang="ru">
|
|
|
|
| 1081 |
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
|
| 1082 |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/Swiper/10.2.0/swiper-bundle.min.css">
|
| 1083 |
<style>
|
| 1084 |
+
/* ... (your existing styles for product display) ... */
|
| 1085 |
* {
|
| 1086 |
margin: 0;
|
| 1087 |
padding: 0;
|
|
|
|
| 1437 |
font-size: 0.75rem;
|
| 1438 |
font-weight: 500;
|
| 1439 |
}
|
| 1440 |
+
|
| 1441 |
</style>
|
| 1442 |
</head>
|
| 1443 |
<body>
|
|
|
|
| 1524 |
</div>
|
| 1525 |
</div>
|
| 1526 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1527 |
<button id="cart-button" onclick="openCartModal()">🛒</button>
|
| 1528 |
|
| 1529 |
<div class="navbar">
|
|
|
|
| 1543 |
<i class="fas fa-tag"></i>
|
| 1544 |
Скидки
|
| 1545 |
</a>
|
| 1546 |
+
|
|
|
|
|
|
|
|
|
|
| 1547 |
<a href="https://api.whatsapp.com/send?phone=996500654659">
|
| 1548 |
<i class="fab fa-whatsapp"></i>
|
| 1549 |
WhatsApp
|
|
|
|
| 1746 |
});
|
| 1747 |
}
|
| 1748 |
|
|
|
|
|
|
|
|
|
|
| 1749 |
|
| 1750 |
window.onclick = function(event) {
|
| 1751 |
if (event.target.className === 'modal') event.target.style.display = "none";
|
|
|
|
| 1759 |
'''
|
| 1760 |
return render_template_string(category_html, products=products, category=category, repo_id=REPO_ID)
|
| 1761 |
|
| 1762 |
+
|
| 1763 |
@app.route('/favorites')
|
| 1764 |
def favorites_page():
|
| 1765 |
data = load_data()
|
| 1766 |
products = data['products']
|
| 1767 |
+
favorites = request.args.get('favorites', '[]') # Not really used directly, but kept for consistency
|
| 1768 |
+
|
| 1769 |
favorites_html = '''
|
| 1770 |
<!DOCTYPE html>
|
| 1771 |
<html lang="ru">
|
|
|
|
| 1777 |
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
|
| 1778 |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/Swiper/10.2.0/swiper-bundle.min.css">
|
| 1779 |
<style>
|
| 1780 |
+
/* ... (your existing styles for product display) ... */
|
| 1781 |
* {
|
| 1782 |
margin: 0;
|
| 1783 |
padding: 0;
|
|
|
|
| 2180 |
</div>
|
| 2181 |
</div>
|
| 2182 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2183 |
|
| 2184 |
<button id="cart-button" onclick="openCartModal()">🛒</button>
|
| 2185 |
|
|
|
|
| 2200 |
<i class="fas fa-tag"></i>
|
| 2201 |
Скидки
|
| 2202 |
</a>
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2203 |
<a href="https://api.whatsapp.com/send?phone=996500654659">
|
| 2204 |
<i class="fab fa-whatsapp"></i>
|
| 2205 |
WhatsApp
|
|
|
|
| 2238 |
|
| 2239 |
favorites.forEach(index => {
|
| 2240 |
const product = products[index];
|
| 2241 |
+
if (!product) return; //Important check in case product was deleted
|
| 2242 |
const productElement = document.createElement('div');
|
| 2243 |
productElement.className = 'product';
|
| 2244 |
productElement.setAttribute('onclick', `openModal(${index})`);
|
|
|
|
| 2434 |
favoriteButton.classList.add('favorited');
|
| 2435 |
}
|
| 2436 |
localStorage.setItem('favorites', JSON.stringify(favorites));
|
| 2437 |
+
loadFavoritesPage(); // Reload the favorites page after toggling
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2438 |
}
|
| 2439 |
|
| 2440 |
window.onclick = function(event) {
|
|
|
|
| 2442 |
}
|
| 2443 |
|
| 2444 |
updateCartButton();
|
| 2445 |
+
loadFavoritesPage(); // Load favorites on initial page load
|
| 2446 |
</script>
|
| 2447 |
</body>
|
| 2448 |
</html>
|
| 2449 |
'''
|
| 2450 |
return render_template_string(favorites_html, products=products, repo_id=REPO_ID)
|
| 2451 |
|
| 2452 |
+
|
| 2453 |
+
|
| 2454 |
@app.route('/discounts')
|
| 2455 |
def discounts_page():
|
| 2456 |
data = load_data()
|
| 2457 |
products = [p for p in data['products'] if p.get('discount')]
|
| 2458 |
+
# categories = data['categories'] # No longer used in this view
|
| 2459 |
+
|
| 2460 |
discounts_html = '''
|
| 2461 |
<!DOCTYPE html>
|
| 2462 |
<html lang="ru">
|
|
|
|
| 2468 |
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
|
| 2469 |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/Swiper/10.2.0/swiper-bundle.min.css">
|
| 2470 |
<style>
|
| 2471 |
+
/* ... (your existing styles for product display) ... */
|
| 2472 |
* {
|
| 2473 |
margin: 0;
|
| 2474 |
padding: 0;
|
|
|
|
| 2910 |
</div>
|
| 2911 |
</div>
|
| 2912 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2913 |
<button id="cart-button" onclick="openCartModal()">🛒</button>
|
| 2914 |
|
| 2915 |
<div class="navbar">
|
|
|
|
| 2929 |
<i class="fas fa-tag"></i>
|
| 2930 |
Скидки
|
| 2931 |
</a>
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2932 |
<a href="https://api.whatsapp.com/send?phone=996500654659">
|
| 2933 |
<i class="fab fa-whatsapp"></i>
|
| 2934 |
WhatsApp
|
|
|
|
| 3131 |
});
|
| 3132 |
}
|
| 3133 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3134 |
window.onclick = function(event) {
|
| 3135 |
if (event.target.className === 'modal') event.target.style.display = "none";
|
| 3136 |
}
|
|
|
|
| 3141 |
</body>
|
| 3142 |
</html>
|
| 3143 |
'''
|
| 3144 |
+
return render_template_string(discounts_html, products=products, repo_id=REPO_ID)
|
| 3145 |
+
|
| 3146 |
|
| 3147 |
@app.route('/product/<int:index>')
|
| 3148 |
def product_details(index):
|
| 3149 |
data = load_data()
|
| 3150 |
+
if index < 0 or index >= len(data['products']):
|
| 3151 |
+
return "Product not found", 404 # Return a 404 error if index is out of bounds
|
| 3152 |
product = data['products'][index]
|
| 3153 |
+
|
| 3154 |
product_html = '''
|
| 3155 |
<style>
|
| 3156 |
+
/* ... (your existing styles for product details modal) ... */
|
| 3157 |
.swiper-container {
|
| 3158 |
width: 100%;
|
| 3159 |
max-width: 400px;
|
|
|
|
| 3225 |
font-size: 0.9rem;
|
| 3226 |
}
|
| 3227 |
body.dark-mode .product-details .colors span {
|
| 3228 |
+
background: #
|
| 3229 |
color: #e0e0e0;
|
| 3230 |
}
|
| 3231 |
</style>
|
|
|
|
| 3313 |
elif action == 'edit':
|
| 3314 |
index = int(request.form['index'])
|
| 3315 |
photos = request.files.getlist('photos')
|
| 3316 |
+
photo_filenames = products[index].get('photos', []) # Keep existing photos
|
| 3317 |
for photo in photos:
|
| 3318 |
if photo and photo.filename:
|
| 3319 |
filename = secure_filename(photo.filename)
|
|
|
|
| 3325 |
repo_type="dataset",
|
| 3326 |
token=HF_TOKEN_WRITE
|
| 3327 |
)
|
| 3328 |
+
photo_filenames.append(filename) # Add new photos
|
| 3329 |
|
| 3330 |
colors = request.form.getlist('colors')
|
| 3331 |
colors = [color.strip() for color in colors if color.strip()]
|
|
|
|
| 3338 |
'description': request.form['description'],
|
| 3339 |
'category': request.form['category'],
|
| 3340 |
'colors': colors,
|
| 3341 |
+
'photos': photo_filenames, # Updated photo list
|
| 3342 |
'discount': float(request.form['discount']) if request.form['discount'] else None
|
| 3343 |
}
|
| 3344 |
|
| 3345 |
elif action == 'delete':
|
| 3346 |
index = int(request.form['index'])
|
| 3347 |
+
# Delete photos from Hugging Face Hub before deleting the product
|
| 3348 |
+
product_to_delete = products[index]
|
| 3349 |
+
if 'photos' in product_to_delete:
|
| 3350 |
+
api = HfApi()
|
| 3351 |
+
for photo in product_to_delete['photos']:
|
| 3352 |
+
try:
|
| 3353 |
+
api.delete_file(
|
| 3354 |
+
path_in_repo=f"photos/{photo}",
|
| 3355 |
+
repo_id=REPO_ID,
|
| 3356 |
+
repo_type="dataset",
|
| 3357 |
+
token=HF_TOKEN_WRITE
|
| 3358 |
+
)
|
| 3359 |
+
except Exception as e:
|
| 3360 |
+
logging.error(f"Error deleting photo {photo}: {e}")
|
| 3361 |
products.pop(index)
|
| 3362 |
+
|
| 3363 |
|
| 3364 |
elif action == 'add_category':
|
| 3365 |
category = request.form['category_name'].strip()
|
|
|
|
| 3370 |
index = int(request.form['category_index'])
|
| 3371 |
category_to_delete = categories[index]
|
| 3372 |
categories.pop(index)
|
| 3373 |
+
#Set category to "Без категории" for products in the deleted category
|
| 3374 |
for product in products:
|
| 3375 |
if product.get('category') == category_to_delete:
|
| 3376 |
product['category'] = 'Без категории'
|
|
|
|
| 3387 |
<title>Админ-панель</title>
|
| 3388 |
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
|
| 3389 |
<style>
|
| 3390 |
+
/* ... (your existing admin panel styles) ... */
|
| 3391 |
* {
|
| 3392 |
margin: 0;
|
| 3393 |
padding: 0;
|
|
|
|
| 3708 |
load_data()
|
| 3709 |
except Exception as e:
|
| 3710 |
logging.error(f"Не удалось загрузить базу данных: {e}")
|
| 3711 |
+
app.run(debug=True, host='0.0.0.0', port=7860)
|
|
|