Update app.py
Browse files
app.py
CHANGED
|
@@ -26,6 +26,7 @@ HF_TOKEN_READ = os.getenv("HF_TOKEN_READ")
|
|
| 26 |
|
| 27 |
WHATSAPP_NUMBER = "+77011333885"
|
| 28 |
CURRENCY_CODE = 'T'
|
|
|
|
| 29 |
|
| 30 |
def download_db_from_hf(specific_file=None, retries=3, delay=5):
|
| 31 |
token_to_use = HF_TOKEN_READ if HF_TOKEN_READ else HF_TOKEN_WRITE
|
|
@@ -167,7 +168,10 @@ CATALOG_TEMPLATE = '''
|
|
| 167 |
* { margin: 0; padding: 0; box-sizing: border-box; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; -webkit-tap-highlight-color: transparent; }
|
| 168 |
body { background-color: var(--bg); color: var(--text); padding-bottom: calc(90px + env(safe-area-inset-bottom)); }
|
| 169 |
|
| 170 |
-
.
|
|
|
|
|
|
|
|
|
|
| 171 |
.header h1 { font-size: 1.4rem; font-weight: 700; letter-spacing: -0.5px; }
|
| 172 |
.back-btn { display: none; font-size: 1.2rem; cursor: pointer; color: var(--text); margin-right: 15px; padding: 5px; }
|
| 173 |
|
|
@@ -211,6 +215,11 @@ CATALOG_TEMPLATE = '''
|
|
| 211 |
.modal-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 25px; }
|
| 212 |
.modal-header h2 { font-size: 1.3rem; font-weight: 700; }
|
| 213 |
.modal-close { font-size: 1.5rem; cursor: pointer; border: none; background: #f1f2f6; width: 36px; height: 36px; border-radius: 50%; display: flex; align-items: center; justify-content: center; color: var(--text); }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 214 |
.cart-item-list { display: flex; flex-direction: column; gap: 15px; margin-bottom: 25px; }
|
| 215 |
.cart-item { display: flex; justify-content: space-between; align-items: center; background: var(--bg); padding: 15px; border-radius: 12px; }
|
| 216 |
.cart-item-name { flex-grow: 1; font-size: 0.95rem; font-weight: 500; line-height: 1.3; margin-right: 15px; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
|
|
@@ -228,6 +237,13 @@ CATALOG_TEMPLATE = '''
|
|
| 228 |
.gallery-dot { width: 8px; height: 8px; border-radius: 50%; background: rgba(255,255,255,0.3); transition: background 0.3s; }
|
| 229 |
.gallery-dot.active { background: #fff; }
|
| 230 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 231 |
@media (min-width: 768px) {
|
| 232 |
.categories-container { grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); }
|
| 233 |
.products-container { display: grid; grid-template-columns: repeat(auto-fill, minmax(350px, 1fr)); }
|
|
@@ -238,6 +254,10 @@ CATALOG_TEMPLATE = '''
|
|
| 238 |
</style>
|
| 239 |
</head>
|
| 240 |
<body>
|
|
|
|
|
|
|
|
|
|
|
|
|
| 241 |
<div class="header">
|
| 242 |
<div style="display: flex; align-items: center;">
|
| 243 |
<i class="fas fa-arrow-left back-btn" id="backBtn" onclick="showCategories()"></i>
|
|
@@ -255,6 +275,12 @@ CATALOG_TEMPLATE = '''
|
|
| 255 |
<div class="categories-container" id="categoriesContainer"></div>
|
| 256 |
<div class="products-container" id="productsContainer"></div>
|
| 257 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 258 |
<div class="cart-bar" id="cartBar">
|
| 259 |
<div class="cart-info">
|
| 260 |
<span style="font-size: 0.85rem; color: var(--text-muted); font-weight: 500;">Сумма заказа:</span>
|
|
@@ -270,6 +296,13 @@ CATALOG_TEMPLATE = '''
|
|
| 270 |
<button class="modal-close" onclick="closeCartModal()"><i class="fas fa-times"></i></button>
|
| 271 |
</div>
|
| 272 |
<div class="cart-item-list" id="cartItemList"></div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 273 |
<button class="confirm-btn" onclick="submitOrder()">Оформить заказ</button>
|
| 274 |
</div>
|
| 275 |
</div>
|
|
@@ -474,6 +507,16 @@ CATALOG_TEMPLATE = '''
|
|
| 474 |
function submitOrder() {
|
| 475 |
const cartArray = Object.values(cart);
|
| 476 |
if(cartArray.length === 0) return;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 477 |
const btn = document.querySelector('.confirm-btn');
|
| 478 |
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Оформление...';
|
| 479 |
btn.disabled = true;
|
|
@@ -481,7 +524,12 @@ CATALOG_TEMPLATE = '''
|
|
| 481 |
fetch('/create_order', {
|
| 482 |
method: 'POST',
|
| 483 |
headers: { 'Content-Type': 'application/json' },
|
| 484 |
-
body: JSON.stringify({
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 485 |
})
|
| 486 |
.then(r => r.json())
|
| 487 |
.then(data => {
|
|
@@ -575,7 +623,9 @@ ORDER_TEMPLATE = '''
|
|
| 575 |
.invoice-box { background: var(--surface); width: 100%; max-width: 900px; padding: 30px; box-shadow: 0 4px 20px rgba(0,0,0,0.05); border-radius: 16px; }
|
| 576 |
.header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 25px; border-bottom: 2px solid var(--border); padding-bottom: 15px; flex-wrap: wrap; gap: 10px; }
|
| 577 |
.header h1 { margin: 0; font-size: 1.8rem; font-weight: 800; }
|
| 578 |
-
.info-row { display: flex; justify-content: space-between; margin-bottom:
|
|
|
|
|
|
|
| 579 |
|
| 580 |
.table-responsive { width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; margin-bottom: 20px; border-radius: 8px; border: 1px solid var(--border); }
|
| 581 |
table { width: 100%; border-collapse: collapse; min-width: 500px; }
|
|
@@ -612,6 +662,10 @@ ORDER_TEMPLATE = '''
|
|
| 612 |
</head>
|
| 613 |
<body>
|
| 614 |
<div class="invoice-box">
|
|
|
|
|
|
|
|
|
|
|
|
|
| 615 |
<div class="header">
|
| 616 |
<h1>Накладная</h1>
|
| 617 |
<div style="text-align: right;">
|
|
@@ -621,8 +675,12 @@ ORDER_TEMPLATE = '''
|
|
| 621 |
</div>
|
| 622 |
|
| 623 |
<div class="info-row">
|
| 624 |
-
<div
|
| 625 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 626 |
</div>
|
| 627 |
|
| 628 |
<div class="table-responsive">
|
|
@@ -956,7 +1014,8 @@ def catalog():
|
|
| 956 |
products_json=json.dumps(all_products),
|
| 957 |
categories_json=json.dumps(categories),
|
| 958 |
repo_id=REPO_ID,
|
| 959 |
-
currency_code=CURRENCY_CODE
|
|
|
|
| 960 |
)
|
| 961 |
|
| 962 |
@app.route('/create_order', methods=['POST'])
|
|
@@ -967,6 +1026,10 @@ def create_order():
|
|
| 967 |
|
| 968 |
cart_items = order_data['cart']
|
| 969 |
total_price = sum(float(item['price']) * int(item['quantity']) for item in cart_items)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 970 |
|
| 971 |
processed_cart = []
|
| 972 |
for item in cart_items:
|
|
@@ -983,7 +1046,10 @@ def create_order():
|
|
| 983 |
"id": order_id,
|
| 984 |
"created_at": datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
|
| 985 |
"cart": processed_cart,
|
| 986 |
-
"total_price": total_price
|
|
|
|
|
|
|
|
|
|
| 987 |
}
|
| 988 |
|
| 989 |
data = load_data()
|
|
@@ -1003,7 +1069,8 @@ def view_order(order_id):
|
|
| 1003 |
ORDER_TEMPLATE,
|
| 1004 |
order=order,
|
| 1005 |
whatsapp_number=WHATSAPP_NUMBER,
|
| 1006 |
-
currency_code=CURRENCY_CODE
|
|
|
|
| 1007 |
)
|
| 1008 |
|
| 1009 |
@app.route('/admin', methods=['GET', 'POST'])
|
|
|
|
| 26 |
|
| 27 |
WHATSAPP_NUMBER = "+77011333885"
|
| 28 |
CURRENCY_CODE = 'T'
|
| 29 |
+
LOGO_URL = "https://huggingface.co/spaces/Metapp/Tech/resolve/main/1776929812446-019db944-b5db-7524-8f44-73942d70a0f8.png"
|
| 30 |
|
| 31 |
def download_db_from_hf(specific_file=None, retries=3, delay=5):
|
| 32 |
token_to_use = HF_TOKEN_READ if HF_TOKEN_READ else HF_TOKEN_WRITE
|
|
|
|
| 168 |
* { margin: 0; padding: 0; box-sizing: border-box; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; -webkit-tap-highlight-color: transparent; }
|
| 169 |
body { background-color: var(--bg); color: var(--text); padding-bottom: calc(90px + env(safe-area-inset-bottom)); }
|
| 170 |
|
| 171 |
+
.top-logo-container { background: var(--surface); padding: max(15px, env(safe-area-inset-top)) 20px 10px; text-align: center; border-bottom: 1px solid var(--border); }
|
| 172 |
+
.top-logo { max-width: 100%; height: auto; max-height: 80px; object-fit: contain; }
|
| 173 |
+
|
| 174 |
+
.header { display: flex; align-items: center; justify-content: space-between; padding: 15px 20px; background: var(--surface); box-shadow: 0 2px 10px rgba(0,0,0,0.03); position: sticky; top: 0; z-index: 100; }
|
| 175 |
.header h1 { font-size: 1.4rem; font-weight: 700; letter-spacing: -0.5px; }
|
| 176 |
.back-btn { display: none; font-size: 1.2rem; cursor: pointer; color: var(--text); margin-right: 15px; padding: 5px; }
|
| 177 |
|
|
|
|
| 215 |
.modal-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 25px; }
|
| 216 |
.modal-header h2 { font-size: 1.3rem; font-weight: 700; }
|
| 217 |
.modal-close { font-size: 1.5rem; cursor: pointer; border: none; background: #f1f2f6; width: 36px; height: 36px; border-radius: 50%; display: flex; align-items: center; justify-content: center; color: var(--text); }
|
| 218 |
+
|
| 219 |
+
.customer-form { display: flex; flex-direction: column; gap: 12px; margin-bottom: 25px; }
|
| 220 |
+
.customer-form input { padding: 14px; border: 1px solid var(--border); border-radius: 12px; font-size: 0.95rem; background: var(--bg); outline: none; transition: border-color 0.2s; }
|
| 221 |
+
.customer-form input:focus { border-color: var(--primary); background: var(--surface); }
|
| 222 |
+
|
| 223 |
.cart-item-list { display: flex; flex-direction: column; gap: 15px; margin-bottom: 25px; }
|
| 224 |
.cart-item { display: flex; justify-content: space-between; align-items: center; background: var(--bg); padding: 15px; border-radius: 12px; }
|
| 225 |
.cart-item-name { flex-grow: 1; font-size: 0.95rem; font-weight: 500; line-height: 1.3; margin-right: 15px; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
|
|
|
|
| 237 |
.gallery-dot { width: 8px; height: 8px; border-radius: 50%; background: rgba(255,255,255,0.3); transition: background 0.3s; }
|
| 238 |
.gallery-dot.active { background: #fff; }
|
| 239 |
|
| 240 |
+
.floating-socials { position: fixed; bottom: max(100px, calc(100px + env(safe-area-inset-bottom))); right: 15px; display: flex; flex-direction: column; gap: 12px; z-index: 90; }
|
| 241 |
+
.social-btn { width: 48px; height: 48px; border-radius: 50%; display: flex; align-items: center; justify-content: center; color: white; font-size: 1.6rem; text-decoration: none; box-shadow: 0 4px 12px rgba(0,0,0,0.25); transition: transform 0.2s; }
|
| 242 |
+
.social-btn:active { transform: scale(0.9); }
|
| 243 |
+
.btn-float-wa { background: #25D366; }
|
| 244 |
+
.btn-float-ig { background: radial-gradient(circle at 30% 107%, #fdf497 0%, #fdf497 5%, #fd5949 45%, #d6249f 60%, #285AEB 90%); }
|
| 245 |
+
.btn-float-tg { background: #0088cc; }
|
| 246 |
+
|
| 247 |
@media (min-width: 768px) {
|
| 248 |
.categories-container { grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); }
|
| 249 |
.products-container { display: grid; grid-template-columns: repeat(auto-fill, minmax(350px, 1fr)); }
|
|
|
|
| 254 |
</style>
|
| 255 |
</head>
|
| 256 |
<body>
|
| 257 |
+
<div class="top-logo-container">
|
| 258 |
+
<img src="{{ logo_url }}" class="top-logo" alt="Логотип">
|
| 259 |
+
</div>
|
| 260 |
+
|
| 261 |
<div class="header">
|
| 262 |
<div style="display: flex; align-items: center;">
|
| 263 |
<i class="fas fa-arrow-left back-btn" id="backBtn" onclick="showCategories()"></i>
|
|
|
|
| 275 |
<div class="categories-container" id="categoriesContainer"></div>
|
| 276 |
<div class="products-container" id="productsContainer"></div>
|
| 277 |
|
| 278 |
+
<div class="floating-socials">
|
| 279 |
+
<a href="https://wa.me/77011333885" class="social-btn btn-float-wa" target="_blank"><i class="fab fa-whatsapp"></i></a>
|
| 280 |
+
<a href="https://instagram.com/14sklad_baisat" class="social-btn btn-float-ig" target="_blank"><i class="fab fa-instagram"></i></a>
|
| 281 |
+
<a href="https://t.me/posuda15konteiner" class="social-btn btn-float-tg" target="_blank"><i class="fab fa-telegram-plane"></i></a>
|
| 282 |
+
</div>
|
| 283 |
+
|
| 284 |
<div class="cart-bar" id="cartBar">
|
| 285 |
<div class="cart-info">
|
| 286 |
<span style="font-size: 0.85rem; color: var(--text-muted); font-weight: 500;">Сумма заказа:</span>
|
|
|
|
| 296 |
<button class="modal-close" onclick="closeCartModal()"><i class="fas fa-times"></i></button>
|
| 297 |
</div>
|
| 298 |
<div class="cart-item-list" id="cartItemList"></div>
|
| 299 |
+
|
| 300 |
+
<div class="customer-form">
|
| 301 |
+
<input type="text" id="custName" placeholder="Ваше Имя" required>
|
| 302 |
+
<input type="text" id="custPhone" placeholder="Номер телефона" required>
|
| 303 |
+
<input type="text" id="custCity" placeholder="Город" required>
|
| 304 |
+
</div>
|
| 305 |
+
|
| 306 |
<button class="confirm-btn" onclick="submitOrder()">Оформить заказ</button>
|
| 307 |
</div>
|
| 308 |
</div>
|
|
|
|
| 507 |
function submitOrder() {
|
| 508 |
const cartArray = Object.values(cart);
|
| 509 |
if(cartArray.length === 0) return;
|
| 510 |
+
|
| 511 |
+
const name = document.getElementById('custName').value.trim();
|
| 512 |
+
const phone = document.getElementById('custPhone').value.trim();
|
| 513 |
+
const city = document.getElementById('custCity').value.trim();
|
| 514 |
+
|
| 515 |
+
if(!name || !phone || !city) {
|
| 516 |
+
alert('Пожалуйста, заполните все поля (Имя, Телефон, Город)');
|
| 517 |
+
return;
|
| 518 |
+
}
|
| 519 |
+
|
| 520 |
const btn = document.querySelector('.confirm-btn');
|
| 521 |
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Оформление...';
|
| 522 |
btn.disabled = true;
|
|
|
|
| 524 |
fetch('/create_order', {
|
| 525 |
method: 'POST',
|
| 526 |
headers: { 'Content-Type': 'application/json' },
|
| 527 |
+
body: JSON.stringify({
|
| 528 |
+
cart: cartArray,
|
| 529 |
+
customer_name: name,
|
| 530 |
+
customer_phone: phone,
|
| 531 |
+
customer_city: city
|
| 532 |
+
})
|
| 533 |
})
|
| 534 |
.then(r => r.json())
|
| 535 |
.then(data => {
|
|
|
|
| 623 |
.invoice-box { background: var(--surface); width: 100%; max-width: 900px; padding: 30px; box-shadow: 0 4px 20px rgba(0,0,0,0.05); border-radius: 16px; }
|
| 624 |
.header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 25px; border-bottom: 2px solid var(--border); padding-bottom: 15px; flex-wrap: wrap; gap: 10px; }
|
| 625 |
.header h1 { margin: 0; font-size: 1.8rem; font-weight: 800; }
|
| 626 |
+
.info-row { display: flex; justify-content: space-between; margin-bottom: 20px; font-size: 1rem; flex-wrap: wrap; gap: 15px; }
|
| 627 |
+
.customer-details { display: flex; flex-direction: column; gap: 6px; }
|
| 628 |
+
.customer-details span { font-weight: 600; color: #1a1a1a; }
|
| 629 |
|
| 630 |
.table-responsive { width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; margin-bottom: 20px; border-radius: 8px; border: 1px solid var(--border); }
|
| 631 |
table { width: 100%; border-collapse: collapse; min-width: 500px; }
|
|
|
|
| 662 |
</head>
|
| 663 |
<body>
|
| 664 |
<div class="invoice-box">
|
| 665 |
+
<div style="text-align: center; margin-bottom: 25px;">
|
| 666 |
+
<img src="{{ logo_url }}" style="max-height: 80px; max-width: 100%; object-fit: contain;">
|
| 667 |
+
</div>
|
| 668 |
+
|
| 669 |
<div class="header">
|
| 670 |
<h1>Накладная</h1>
|
| 671 |
<div style="text-align: right;">
|
|
|
|
| 675 |
</div>
|
| 676 |
|
| 677 |
<div class="info-row">
|
| 678 |
+
<div class="customer-details">
|
| 679 |
+
<div>Покупатель: <span>{{ order.customer_name }}</span></div>
|
| 680 |
+
<div>Телефон: <span>{{ order.customer_phone }}</span></div>
|
| 681 |
+
<div>Город: <span>{{ order.customer_city }}</span></div>
|
| 682 |
+
</div>
|
| 683 |
+
<div style="font-weight: 600;">Статус: <span style="color: #00b894;">Новый</span></div>
|
| 684 |
</div>
|
| 685 |
|
| 686 |
<div class="table-responsive">
|
|
|
|
| 1014 |
products_json=json.dumps(all_products),
|
| 1015 |
categories_json=json.dumps(categories),
|
| 1016 |
repo_id=REPO_ID,
|
| 1017 |
+
currency_code=CURRENCY_CODE,
|
| 1018 |
+
logo_url=LOGO_URL
|
| 1019 |
)
|
| 1020 |
|
| 1021 |
@app.route('/create_order', methods=['POST'])
|
|
|
|
| 1026 |
|
| 1027 |
cart_items = order_data['cart']
|
| 1028 |
total_price = sum(float(item['price']) * int(item['quantity']) for item in cart_items)
|
| 1029 |
+
|
| 1030 |
+
customer_name = order_data.get('customer_name', 'Не указано')
|
| 1031 |
+
customer_phone = order_data.get('customer_phone', 'Не указано')
|
| 1032 |
+
customer_city = order_data.get('customer_city', 'Не указано')
|
| 1033 |
|
| 1034 |
processed_cart = []
|
| 1035 |
for item in cart_items:
|
|
|
|
| 1046 |
"id": order_id,
|
| 1047 |
"created_at": datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
|
| 1048 |
"cart": processed_cart,
|
| 1049 |
+
"total_price": total_price,
|
| 1050 |
+
"customer_name": customer_name,
|
| 1051 |
+
"customer_phone": customer_phone,
|
| 1052 |
+
"customer_city": customer_city
|
| 1053 |
}
|
| 1054 |
|
| 1055 |
data = load_data()
|
|
|
|
| 1069 |
ORDER_TEMPLATE,
|
| 1070 |
order=order,
|
| 1071 |
whatsapp_number=WHATSAPP_NUMBER,
|
| 1072 |
+
currency_code=CURRENCY_CODE,
|
| 1073 |
+
logo_url=LOGO_URL
|
| 1074 |
)
|
| 1075 |
|
| 1076 |
@app.route('/admin', methods=['GET', 'POST'])
|