Update app.py
Browse files
app.py
CHANGED
|
@@ -22,7 +22,7 @@ import requests
|
|
| 22 |
load_dotenv()
|
| 23 |
|
| 24 |
app = Flask(__name__)
|
| 25 |
-
app.secret_key = '
|
| 26 |
DATA_FILE = 'data.json'
|
| 27 |
|
| 28 |
SYNC_FILES = [DATA_FILE]
|
|
@@ -129,7 +129,7 @@ def periodic_backup():
|
|
| 129 |
|
| 130 |
def load_data():
|
| 131 |
default_organization_info = {
|
| 132 |
-
"about_us": "Мы —
|
| 133 |
"shipping": "Доставка осуществляется по всему Кыргызстану. Стоимость и сроки доставки зависят от региона и веса товара. По Бишкеку доставка возможна в течение 1-2 рабочих дней, в регионы — от 3 до 7 дней. Для уточнения деталей свяжитесь с нами.",
|
| 134 |
"returns": "Возврат и обмен товара возможен в течение 14 дней с момента покупки, при условии сохранения товарного вида, упаковки и чека. Некоторые категории товаров могут иметь особые условия возврата. Пожалуйста, свяжитесь с нами для оформления возврата или обмена.",
|
| 135 |
"contact": f"Наш магазин находится по адресу: {STORE_ADDRESS}. Связаться с нами можно по телефону: {WHATSAPP_NUMBER} или через WhatsApp по этому же номеру. Мы работаем ежедневно с 9:00 до 18:00."
|
|
@@ -305,7 +305,7 @@ def generate_chat_response(message, chat_history_from_client):
|
|
| 305 |
|
| 306 |
|
| 307 |
system_instruction_content = (
|
| 308 |
-
"Ты - доброжелательный и очень полезный виртуальный консультант для магазина
|
| 309 |
"Твоя задача - помогать пользователям находить товары, отвечать на вопросы о них, предлагать варианты, а также предоставлять информацию о магазине. "
|
| 310 |
"Всегда будь вежлив, информативен и стремись решить проблему пользователя. "
|
| 311 |
"Никогда не выдумывай товары или категории, которых нет в предоставленных списках. "
|
|
@@ -375,17 +375,27 @@ CATALOG_TEMPLATE = '''
|
|
| 375 |
<head>
|
| 376 |
<meta charset="UTF-8">
|
| 377 |
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
| 378 |
-
<title>
|
| 379 |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
| 380 |
-
<link href="https://fonts.googleapis.com/css2?family=
|
| 381 |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/Swiper/10.2.0/swiper-bundle.min.css">
|
| 382 |
<style>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 383 |
* { margin: 0; padding: 0; box-sizing: border-box; }
|
| 384 |
html { -webkit-tap-highlight-color: transparent; }
|
| 385 |
body {
|
| 386 |
-
font-family: '
|
| 387 |
-
background-color:
|
| 388 |
-
color:
|
| 389 |
line-height: 1.6;
|
| 390 |
}
|
| 391 |
.container {
|
|
@@ -400,8 +410,18 @@ CATALOG_TEMPLATE = '''
|
|
| 400 |
gap: 15px;
|
| 401 |
position: sticky;
|
| 402 |
top: 0;
|
| 403 |
-
background-color:
|
| 404 |
z-index: 999;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 405 |
}
|
| 406 |
.search-wrapper {
|
| 407 |
flex-grow: 1;
|
|
@@ -414,33 +434,19 @@ CATALOG_TEMPLATE = '''
|
|
| 414 |
border: none;
|
| 415 |
border-radius: 25px;
|
| 416 |
outline: none;
|
| 417 |
-
background-color:
|
| 418 |
-
color:
|
| 419 |
transition: all 0.3s ease;
|
| 420 |
}
|
| 421 |
-
#search-input::placeholder { color: rgba(
|
| 422 |
.search-wrapper .fa-search {
|
| 423 |
position: absolute;
|
| 424 |
top: 50%;
|
| 425 |
left: 18px;
|
| 426 |
transform: translateY(-50%);
|
| 427 |
-
color: rgba(
|
| 428 |
font-size: 1rem;
|
| 429 |
}
|
| 430 |
-
.top-bar-icon {
|
| 431 |
-
flex-shrink: 0;
|
| 432 |
-
width: 48px;
|
| 433 |
-
height: 48px;
|
| 434 |
-
background-color: #004282;
|
| 435 |
-
border-radius: 50%;
|
| 436 |
-
display: flex;
|
| 437 |
-
align-items: center;
|
| 438 |
-
justify-content: center;
|
| 439 |
-
color: white;
|
| 440 |
-
font-size: 1.2rem;
|
| 441 |
-
cursor: pointer;
|
| 442 |
-
text-decoration: none;
|
| 443 |
-
}
|
| 444 |
.category-section {
|
| 445 |
margin-top: 20px;
|
| 446 |
}
|
|
@@ -449,7 +455,7 @@ CATALOG_TEMPLATE = '''
|
|
| 449 |
justify-content: space-between;
|
| 450 |
align-items: center;
|
| 451 |
padding: 0 20px;
|
| 452 |
-
margin-bottom:
|
| 453 |
}
|
| 454 |
.category-header h2 {
|
| 455 |
font-size: 1.5rem;
|
|
@@ -457,7 +463,7 @@ CATALOG_TEMPLATE = '''
|
|
| 457 |
}
|
| 458 |
.category-header .view-all-arrow {
|
| 459 |
font-size: 1.8rem;
|
| 460 |
-
color:
|
| 461 |
text-decoration: none;
|
| 462 |
font-weight: 300;
|
| 463 |
}
|
|
@@ -478,10 +484,14 @@ CATALOG_TEMPLATE = '''
|
|
| 478 |
flex-shrink: 0;
|
| 479 |
overflow: hidden;
|
| 480 |
cursor: pointer;
|
| 481 |
-
transition: transform 0.2s ease-in-out;
|
| 482 |
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
|
| 483 |
position: relative;
|
| 484 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 485 |
.product-card:active { transform: scale(0.96); }
|
| 486 |
.product-image-container {
|
| 487 |
width: 100%;
|
|
@@ -497,10 +507,8 @@ CATALOG_TEMPLATE = '''
|
|
| 497 |
bottom: 0;
|
| 498 |
left: 0;
|
| 499 |
right: 0;
|
| 500 |
-
background: linear-gradient(to top, rgba(0, 0, 0, 0.
|
| 501 |
padding: 20px 12px 10px;
|
| 502 |
-
border-bottom-left-radius: 16px;
|
| 503 |
-
border-bottom-right-radius: 16px;
|
| 504 |
color: #fff;
|
| 505 |
pointer-events: none;
|
| 506 |
}
|
|
@@ -512,8 +520,8 @@ CATALOG_TEMPLATE = '''
|
|
| 512 |
position: absolute;
|
| 513 |
top: 8px;
|
| 514 |
right: 8px;
|
| 515 |
-
background-color:
|
| 516 |
-
color:
|
| 517 |
padding: 2px 8px;
|
| 518 |
font-size: 0.7rem;
|
| 519 |
border-radius: 10px;
|
|
@@ -531,8 +539,8 @@ CATALOG_TEMPLATE = '''
|
|
| 531 |
z-index: 1000;
|
| 532 |
}
|
| 533 |
.floating-button {
|
| 534 |
-
background-color:
|
| 535 |
-
color:
|
| 536 |
border: none;
|
| 537 |
border-radius: 50%;
|
| 538 |
width: 55px;
|
|
@@ -542,137 +550,90 @@ CATALOG_TEMPLATE = '''
|
|
| 542 |
display: flex;
|
| 543 |
align-items: center;
|
| 544 |
justify-content: center;
|
| 545 |
-
box-shadow: 0 4px 15px rgba(
|
| 546 |
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
| 547 |
}
|
| 548 |
.floating-button:hover {
|
| 549 |
-
background-color:
|
| 550 |
transform: translateY(-3px);
|
| 551 |
-
box-shadow: 0 6px 20px rgba(0, 112, 209, 0.5);
|
| 552 |
}
|
| 553 |
#cart-button { position: relative; }
|
| 554 |
#cart-count {
|
| 555 |
position: absolute;
|
| 556 |
top: -2px;
|
| 557 |
right: -2px;
|
| 558 |
-
background-color:
|
| 559 |
color: white;
|
| 560 |
border-radius: 50%;
|
| 561 |
padding: 2px 6px;
|
| 562 |
font-size: 0.7rem;
|
| 563 |
font-weight: bold;
|
| 564 |
-
border: 2px solid
|
| 565 |
}
|
| 566 |
.modal { display: none; position: fixed; z-index: 1001; left: 0; top: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.6); backdrop-filter: blur(5px); overflow-y: auto; }
|
| 567 |
-
.modal-content { background: #ffffff; color:
|
| 568 |
@keyframes slideIn { from { transform: translateY(-30px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }
|
| 569 |
.close { position: absolute; top: 15px; right: 15px; font-size: 1.8rem; color: #aaa; cursor: pointer; transition: color 0.3s; line-height: 1; }
|
| 570 |
.close:hover { color: #666; }
|
| 571 |
-
.modal-content h2 { margin-top: 0; margin-bottom: 20px; color:
|
| 572 |
-
.cart-item { display: grid; grid-template-columns:
|
| 573 |
.cart-item:last-child { border-bottom: none; }
|
| 574 |
-
.cart-item img { width: 60px; height: 60px; object-fit:
|
| 575 |
.cart-item-details { grid-column: 2; }
|
| 576 |
-
.cart-item-details strong { display: block; margin-bottom: 5px; font-size: 1rem; color:
|
| 577 |
.cart-item-price { font-size: 0.9rem; color: #666; }
|
| 578 |
-
.cart-item-
|
| 579 |
-
.
|
| 580 |
-
.cart-item-
|
|
|
|
|
|
|
| 581 |
.quantity-input, .color-select { width: 100%; max-width: 180px; padding: 10px; border: 1px solid #e0e0e0; border-radius: 8px; font-size: 1rem; margin: 10px 0; box-sizing: border-box; }
|
| 582 |
-
.quantity-input:focus, .color-select:focus { border-color:
|
| 583 |
.cart-summary { margin-top: 20px; text-align: right; border-top: 1px solid #e0e0e0; padding-top: 15px; }
|
| 584 |
-
.cart-summary strong { font-size: 1.2rem; color:
|
| 585 |
.cart-actions { margin-top: 25px; display: flex; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
|
| 586 |
.product-button { display: block; width: auto; flex-grow: 1; padding: 10px; border: none; border-radius: 8px; color: white; font-size: 0.9rem; font-weight: 500; cursor: pointer; transition: all 0.3s ease; text-align: center; text-decoration: none; }
|
| 587 |
.product-button i { margin-right: 5px; }
|
| 588 |
.clear-cart { background-color: #6c757d; }
|
| 589 |
.clear-cart:hover { background-color: #5a6268; }
|
| 590 |
-
.formulate-order-button { background-color:
|
| 591 |
-
.formulate-order-button:hover { background-color:
|
| 592 |
-
.notification { position: fixed; bottom: 80px; left: 50%; transform: translateX(-50%); background-color:
|
| 593 |
.notification.show { opacity: 1;}
|
| 594 |
#chatModal .modal-content { max-width: 450px; }
|
| 595 |
#chat-messages { height: 350px; overflow-y: auto; border: 1px solid #e0e0e0; border-radius: 8px; padding: 15px; margin-bottom: 15px; display: flex; flex-direction: column; gap: 10px; background-color: #fcfcfc; }
|
| 596 |
.chat-message { padding: 10px 15px; border-radius: 15px; max-width: 80%; word-wrap: break-word; line-height: 1.4;}
|
| 597 |
-
.chat-message.user { align-self: flex-end; background-color:
|
| 598 |
-
.chat-message.ai { align-self: flex-start; background-color: #e6e6e6; color:
|
| 599 |
.chat-input-container { display: flex; gap: 10px; }
|
| 600 |
#chat-input { flex-grow: 1; padding: 10px 15px; border: 1px solid #e0e0e0; border-radius: 20px; font-size: 0.95rem; outline: none; }
|
| 601 |
-
#chat-input:focus { border-color:
|
| 602 |
-
#chat-send-button { background-color:
|
| 603 |
-
#chat-send-button:hover { background-color:
|
| 604 |
#chat-send-button:disabled { background-color: #cccccc; cursor: not-allowed; }
|
| 605 |
|
| 606 |
-
.chat-product-card {
|
| 607 |
-
|
| 608 |
-
|
| 609 |
-
|
| 610 |
-
|
| 611 |
-
|
| 612 |
-
|
| 613 |
-
|
| 614 |
-
|
| 615 |
-
}
|
| 616 |
-
.chat-product-card img {
|
| 617 |
-
width: 50px;
|
| 618 |
-
height: 50px;
|
| 619 |
-
object-fit: cover;
|
| 620 |
-
border-radius: 8px;
|
| 621 |
-
flex-shrink: 0;
|
| 622 |
-
}
|
| 623 |
-
.chat-product-card-info {
|
| 624 |
-
flex-grow: 1;
|
| 625 |
-
}
|
| 626 |
-
.chat-product-card-info strong {
|
| 627 |
-
display: block;
|
| 628 |
-
font-size: 0.9rem;
|
| 629 |
-
color: #333;
|
| 630 |
-
margin-bottom: 2px;
|
| 631 |
-
}
|
| 632 |
-
.chat-product-card-info span {
|
| 633 |
-
font-size: 0.85rem;
|
| 634 |
-
color: #0A2A66;
|
| 635 |
-
font-weight: 500;
|
| 636 |
-
}
|
| 637 |
-
.chat-product-card-actions {
|
| 638 |
-
display: flex;
|
| 639 |
-
flex-direction: column;
|
| 640 |
-
gap: 5px;
|
| 641 |
-
}
|
| 642 |
-
.chat-product-link, .chat-add-to-cart {
|
| 643 |
-
display: inline-block;
|
| 644 |
-
background-color: #E3F2FD;
|
| 645 |
-
color: #0070D1;
|
| 646 |
-
padding: 5px 10px;
|
| 647 |
-
border-radius: 15px;
|
| 648 |
-
cursor: pointer;
|
| 649 |
-
font-size: 0.85rem;
|
| 650 |
-
text-decoration: none;
|
| 651 |
-
transition: background-color 0.2s;
|
| 652 |
-
font-weight: 500;
|
| 653 |
-
text-align: center;
|
| 654 |
-
box-sizing: border-box;
|
| 655 |
-
width: 100%;
|
| 656 |
-
}
|
| 657 |
-
.chat-product-link:hover, .chat-add-to-cart:hover {
|
| 658 |
-
background-color: #BBDEFB;
|
| 659 |
-
}
|
| 660 |
-
.chat-product-card-actions .fa-cart-plus {
|
| 661 |
-
font-size: 0.9em;
|
| 662 |
-
}
|
| 663 |
|
| 664 |
</style>
|
| 665 |
</head>
|
| 666 |
<body>
|
| 667 |
<div class="container">
|
| 668 |
<div class="top-bar">
|
|
|
|
|
|
|
|
|
|
| 669 |
<div class="search-wrapper">
|
| 670 |
<i class="fas fa-search"></i>
|
| 671 |
-
<input type="text" id="search-input" placeholder="
|
| 672 |
</div>
|
| 673 |
-
<a href="#" class="top-bar-icon" onclick="openChatModal()">
|
| 674 |
-
<i class="fas fa-comment-dots"></i>
|
| 675 |
-
</a>
|
| 676 |
</div>
|
| 677 |
|
| 678 |
<div id="catalog-content">
|
|
@@ -701,7 +662,7 @@ CATALOG_TEMPLATE = '''
|
|
| 701 |
alt="{{ product.name }}"
|
| 702 |
loading="lazy">
|
| 703 |
{% else %}
|
| 704 |
-
<img src="https://via.placeholder.com/170x170.png?text=
|
| 705 |
{% endif %}
|
| 706 |
</div>
|
| 707 |
<div class="product-info-overlay">
|
|
@@ -768,7 +729,7 @@ CATALOG_TEMPLATE = '''
|
|
| 768 |
<input type="text" id="chat-input" placeholder="Напишите сообщение...">
|
| 769 |
<button id="chat-send-button"><i class="fas fa-paper-plane"></i></button>
|
| 770 |
</div>
|
| 771 |
-
<button id="clear-chat-button" class="product-button" style="background-color:
|
| 772 |
</div>
|
| 773 |
</div>
|
| 774 |
|
|
@@ -841,7 +802,7 @@ CATALOG_TEMPLATE = '''
|
|
| 841 |
})
|
| 842 |
.catch(error => {
|
| 843 |
console.error('Ошибка загрузки деталей продукта:', error);
|
| 844 |
-
modalContent.innerHTML = `<p style="color:
|
| 845 |
});
|
| 846 |
}
|
| 847 |
|
|
@@ -968,10 +929,15 @@ CATALOG_TEMPLATE = '''
|
|
| 968 |
<img src="${photoUrl}" alt="${item.name}">
|
| 969 |
<div class="cart-item-details">
|
| 970 |
<strong>${item.name}${colorText}</strong>
|
| 971 |
-
<p class="cart-item-price">${item.price.toFixed(2)} ${currencyCode}
|
| 972 |
</div>
|
| 973 |
-
<
|
| 974 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 975 |
</div>
|
| 976 |
`;
|
| 977 |
}).join('');
|
|
@@ -984,6 +950,29 @@ CATALOG_TEMPLATE = '''
|
|
| 984 |
}
|
| 985 |
}
|
| 986 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 987 |
function removeFromCart(itemId) {
|
| 988 |
cart = cart.filter(item => item.id !== itemId);
|
| 989 |
localStorage.setItem('mekaCart', JSON.stringify(cart));
|
|
@@ -1129,7 +1118,7 @@ CATALOG_TEMPLATE = '''
|
|
| 1129 |
if (product) {
|
| 1130 |
const photoUrl = product.photos && product.photos.length > 0
|
| 1131 |
? `https://huggingface.co/datasets/${repoId}/resolve/main/photos/${product.photos[0]}`
|
| 1132 |
-
: 'https://via.placeholder.com/50x50.png?text=
|
| 1133 |
|
| 1134 |
const card = document.createElement('div');
|
| 1135 |
card.className = 'chat-product-card';
|
|
@@ -1249,7 +1238,7 @@ CATALOG_TEMPLATE = '''
|
|
| 1249 |
|
| 1250 |
PRODUCT_DETAIL_TEMPLATE = '''
|
| 1251 |
<div style="padding: 10px;">
|
| 1252 |
-
<h2 style="font-size: 1.6rem; font-weight: 600; margin-bottom: 15px; text-align: center; color: #
|
| 1253 |
<div class="swiper-container" style="max-width: 450px; margin: 0 auto 20px; border-radius: 10px; overflow: hidden; background-color: #fff; border: 1px solid #e0e0e0;">
|
| 1254 |
<div class="swiper-wrapper">
|
| 1255 |
{% if product.get('photos') and product['photos']|length > 0 %}
|
|
@@ -1270,13 +1259,13 @@ PRODUCT_DETAIL_TEMPLATE = '''
|
|
| 1270 |
</div>
|
| 1271 |
{% if product.get('photos') and product['photos']|length > 1 %}
|
| 1272 |
<div class="swiper-pagination" style="position: relative; bottom: 5px;"></div>
|
| 1273 |
-
<div class="swiper-button-next" style="color: #
|
| 1274 |
-
<div class="swiper-button-prev" style="color: #
|
| 1275 |
{% endif %}
|
| 1276 |
</div>
|
| 1277 |
|
| 1278 |
<div style="text-align:center; margin-top:20px; padding: 0 10px;">
|
| 1279 |
-
<p style="font-size: 1.5rem; font-weight: bold; color: #
|
| 1280 |
<button class="product-button formulate-order-button" style="padding: 12px 30px; width: 100%; max-width: 300px;" onclick="closeModal('productModal'); openQuantityModalById('{{ product.get('product_id', '') }}')">
|
| 1281 |
<i class="fas fa-cart-plus"></i> В корзину
|
| 1282 |
</button>
|
|
@@ -1299,33 +1288,43 @@ ORDER_TEMPLATE = '''
|
|
| 1299 |
<head>
|
| 1300 |
<meta charset="UTF-8">
|
| 1301 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 1302 |
-
<title>Заказ №{{ order.id }} -
|
| 1303 |
-
<link href="https://fonts.googleapis.com/css2?family=
|
| 1304 |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
| 1305 |
<style>
|
| 1306 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1307 |
.container { max-width: 800px; margin: 20px auto; padding: 30px; background: #fff; border-radius: 15px; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.08); border: 1px solid #e0e0e0; }
|
| 1308 |
-
h1 { text-align: center; color:
|
| 1309 |
-
h2 { color:
|
| 1310 |
.order-meta { font-size: 0.9rem; color: #999; margin-bottom: 20px; text-align: center; }
|
| 1311 |
-
.order-item { display: grid; grid-template-columns: 60px 1fr auto; gap: 15px; align-items: center; padding: 15px 0; border-bottom: 1px solid #f0f0f0; }
|
| 1312 |
.order-item:last-child { border-bottom: none; }
|
| 1313 |
-
.order-item img { width: 60px; height: 60px; object-fit:
|
| 1314 |
-
.item-details strong { display: block; margin-bottom: 5px; font-size: 1.05rem; color:
|
| 1315 |
.item-details span { font-size: 0.9rem; color: #666; display: block;}
|
| 1316 |
-
.item-
|
| 1317 |
-
.
|
|
|
|
|
|
|
|
|
|
| 1318 |
.order-summary p { margin-bottom: 10px; font-size: 1.1rem; }
|
| 1319 |
-
.order-summary strong { font-size: 1.3rem; color:
|
| 1320 |
.customer-info { margin-top: 30px; background-color: #f9f9f9; padding: 20px; border-radius: 8px; border: 1px solid #e0e0e0;}
|
| 1321 |
.customer-info p { margin-bottom: 8px; font-size: 0.95rem; }
|
| 1322 |
-
.customer-info strong { color:
|
| 1323 |
.actions { margin-top: 30px; text-align: center; }
|
| 1324 |
-
.button { padding: 12px 25px; border: none; border-radius: 8px; background-color:
|
| 1325 |
-
.button:hover { background-color: #
|
| 1326 |
.button:active { transform: scale(0.98); }
|
| 1327 |
.button i { font-size: 1.2rem; }
|
| 1328 |
-
.catalog-link { display: block; text-align: center; margin-top: 25px; color:
|
| 1329 |
.catalog-link:hover { text-decoration: underline; }
|
| 1330 |
.not-found { text-align: center; color: #dc3545; font-size: 1.2rem; padding: 40px 0;}
|
| 1331 |
</style>
|
|
@@ -1333,33 +1332,19 @@ ORDER_TEMPLATE = '''
|
|
| 1333 |
<body>
|
| 1334 |
<div class="container">
|
| 1335 |
{% if order %}
|
| 1336 |
-
<h1><i class="fas fa-receipt"></i> Ваш Заказ
|
| 1337 |
<p class="order-meta">Дата создания: {{ order.created_at }}</p>
|
| 1338 |
|
| 1339 |
<h2><i class="fas fa-shopping-bag"></i> Товары в заказе</h2>
|
| 1340 |
-
<div id="orderItems">
|
| 1341 |
-
{% for item in order.cart %}
|
| 1342 |
-
<div class="order-item">
|
| 1343 |
-
<img src="{{ item.photo_url }}" alt="{{ item.name }}">
|
| 1344 |
-
<div class="item-details">
|
| 1345 |
-
<strong>{{ item.name }} {% if item.color != 'N/A' %}({{ item.color }}){% endif %}</strong>
|
| 1346 |
-
<span>{{ "%.2f"|format(item.price) }} {{ currency_code }} × {{ item.quantity }}</span>
|
| 1347 |
-
</div>
|
| 1348 |
-
<div class="item-total">
|
| 1349 |
-
{{ "%.2f"|format(item.price * item.quantity) }} {{ currency_code }}
|
| 1350 |
-
</div>
|
| 1351 |
-
</div>
|
| 1352 |
-
{% endfor %}
|
| 1353 |
-
</div>
|
| 1354 |
|
| 1355 |
<div class="order-summary">
|
| 1356 |
-
<p
|
| 1357 |
-
<p><strong>ИТОГО К ОПЛАТЕ: {{ "%.2f"|format(order.total_price) }} {{ currency_code }}</strong></p>
|
| 1358 |
</div>
|
| 1359 |
|
| 1360 |
<div class="customer-info">
|
| 1361 |
<h2><i class="fas fa-info-circle"></i> Статус заказа</h2>
|
| 1362 |
-
<p>Этот заказ был оформлен без входа в
|
| 1363 |
<p>Пожалуйста, свяжитесь с нами по WhatsApp для подтверждения и уточнения деталей.</p>
|
| 1364 |
</div>
|
| 1365 |
|
|
@@ -1370,21 +1355,92 @@ ORDER_TEMPLATE = '''
|
|
| 1370 |
<a href="{{ url_for('catalog') }}" class="catalog-link">← Вернуться в каталог</a>
|
| 1371 |
|
| 1372 |
<script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1373 |
function sendOrderViaWhatsApp() {
|
| 1374 |
-
|
| 1375 |
-
|
|
|
|
|
|
|
|
|
|
| 1376 |
const whatsappNumber = "{{ whatsapp_number }}";
|
| 1377 |
-
|
| 1378 |
-
|
| 1379 |
-
|
| 1380 |
-
|
| 1381 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1382 |
|
| 1383 |
const whatsappUrl = `https://api.whatsapp.com/send?phone=${whatsappNumber}&text=${message}`;
|
| 1384 |
window.open(whatsappUrl, '_blank');
|
| 1385 |
}
|
|
|
|
|
|
|
| 1386 |
</script>
|
| 1387 |
-
|
| 1388 |
{% else %}
|
| 1389 |
<h1 style="color: #dc3545;"><i class="fas fa-exclamation-triangle"></i> Ошибка</h1>
|
| 1390 |
<p class="not-found">Заказ с таким ID не найден.</p>
|
|
@@ -1401,60 +1457,68 @@ ADMIN_TEMPLATE = '''
|
|
| 1401 |
<head>
|
| 1402 |
<meta charset="UTF-8">
|
| 1403 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 1404 |
-
<title>Админ-панель -
|
| 1405 |
-
<link href="https://fonts.googleapis.com/css2?family=
|
| 1406 |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
| 1407 |
<style>
|
| 1408 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1409 |
.container { max-width: 1200px; margin: 0 auto; background-color: #fff; padding: 25px; border-radius: 10px; box-shadow: 0 3px 10px rgba(0,0,0,0.05); }
|
| 1410 |
.header { padding-bottom: 15px; margin-bottom: 25px; border-bottom: 1px solid #e0e0e0; display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 10px;}
|
| 1411 |
-
|
| 1412 |
-
h1, h2, h3 { font-weight: 600; color:
|
| 1413 |
h1 { font-size: 1.8rem; }
|
| 1414 |
h2 { font-size: 1.5rem; margin-top: 30px; display: flex; align-items: center; gap: 8px; }
|
| 1415 |
-
h3 { font-size: 1.2rem; color: #
|
| 1416 |
.section { margin-bottom: 30px; padding: 20px; background-color: #fdfdff; border: 1px solid #e0e0e0; border-radius: 8px; }
|
| 1417 |
form { margin-bottom: 20px; }
|
| 1418 |
label { font-weight: 500; margin-top: 10px; display: block; color: #666; font-size: 0.9rem;}
|
| 1419 |
input[type="text"], input[type="number"], input[type="password"], input[type="tel"], textarea, select { width: 100%; padding: 10px 12px; margin-top: 5px; border: 1px solid #e0e0e0; border-radius: 6px; font-size: 0.95rem; box-sizing: border-box; transition: border-color 0.3s ease; background-color: #fff; }
|
| 1420 |
-
input:focus, textarea:focus, select:focus { border-color:
|
| 1421 |
textarea { min-height: 80px; resize: vertical; }
|
| 1422 |
input[type="file"] { padding: 8px; background-color: #ffffff; cursor: pointer; border: 1px solid #e0e0e0;}
|
| 1423 |
input[type="file"]::file-selector-button { padding: 5px 10px; border-radius: 4px; background-color: #f0f0f0; border: 1px solid #e0e0e0; cursor: pointer; margin-right: 10px;}
|
| 1424 |
input[type="checkbox"] { margin-right: 5px; vertical-align: middle; }
|
| 1425 |
label.inline-label { display: inline-block; margin-top: 10px; font-weight: normal; }
|
| 1426 |
-
button, .button { padding: 10px 18px; border: none; border-radius: 6px; background-color:
|
| 1427 |
-
button:hover, .button:hover { background-color:
|
| 1428 |
button:active, .button:active { transform: scale(0.98); }
|
| 1429 |
button[type="submit"] { min-width: 120px; justify-content: center; }
|
| 1430 |
-
.delete-button { background-color:
|
| 1431 |
-
.delete-button:hover { background-color:
|
| 1432 |
-
.add-button { background-color:
|
| 1433 |
-
.add-button:hover { background-color: #
|
| 1434 |
.item-list { display: grid; gap: 20px; }
|
| 1435 |
.item { background: #fff; padding: 15px 20px; border-radius: 8px; box-shadow: 0 2px 5px rgba(0,0,0,0.03); border: 1px solid #f0f0f0; }
|
| 1436 |
.item p { margin: 5px 0; font-size: 0.9rem; color: #666; }
|
| 1437 |
-
.item strong { color:
|
| 1438 |
.item .description { font-size: 0.85rem; color: #999; max-height: 60px; overflow: hidden; text-overflow: ellipsis; }
|
| 1439 |
.item-actions { margin-top: 15px; display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
|
| 1440 |
-
.
|
| 1441 |
-
.item-actions button:not(.delete-button):hover { background-color: #3F51B5; }
|
| 1442 |
-
.edit-form-container { margin-top: 15px; padding: 20px; background: #edf2ff; border: 1px dashed #e0e0e0; border-radius: 6px; display: none; }
|
| 1443 |
details { background-color: #fdfdff; border: 1px solid #e0e0e0; border-radius: 8px; margin-bottom: 20px; }
|
| 1444 |
-
details > summary { cursor: pointer; font-weight: 600; color:
|
| 1445 |
-
details > summary::after { content: '\\f078'; font-family: 'Font Awesome 6 Free'; font-weight: 900; position: absolute; right: 20px; top: 50%; transform: translateY(-50%); transition: transform 0.2s ease; color:
|
| 1446 |
details[open] > summary::after { transform: translateY(-50%) rotate(180deg); }
|
| 1447 |
details[open] > summary { border-bottom: 1px solid #e0e0e0; }
|
| 1448 |
details .form-content { padding: 20px; }
|
| 1449 |
.color-input-group { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
|
| 1450 |
.color-input-group input { flex-grow: 1; margin: 0; }
|
| 1451 |
-
.remove-color-btn { background-color:
|
| 1452 |
-
.remove-color-btn:hover { background-color:
|
| 1453 |
-
.add-color-btn { background-color: #
|
| 1454 |
-
.add-color-btn:hover { background-color:
|
| 1455 |
.photo-preview img { max-width: 70px; max-height: 70px; border-radius: 5px; margin: 5px 5px 0 0; border: 1px solid #e0e0e0; object-fit: cover;}
|
| 1456 |
.sync-buttons { display: flex; gap: 10px; margin-bottom: 20px; flex-wrap: wrap; }
|
| 1457 |
-
.download-hf-button { background-color: #6c757d; }
|
| 1458 |
.download-hf-button:hover { background-color: #5a6268; }
|
| 1459 |
.flex-container { display: flex; flex-wrap: wrap; gap: 20px; }
|
| 1460 |
.flex-item { flex: 1; min-width: 350px; }
|
|
@@ -1465,8 +1529,8 @@ ADMIN_TEMPLATE = '''
|
|
| 1465 |
.status-indicator { display: inline-block; padding: 3px 8px; border-radius: 12px; font-size: 0.8rem; font-weight: 500; margin-left: 10px; vertical-align: middle; }
|
| 1466 |
.status-indicator.in-stock { background-color: #d4edda; color: #155724; }
|
| 1467 |
.status-indicator.out-of-stock { background-color: #f8d7da; color: #721c24; }
|
| 1468 |
-
.status-indicator.top-product { background-color: #
|
| 1469 |
-
.ai-generate-button { background-color: #8D6EC8; margin-top: 5px; margin-bottom: 10px; }
|
| 1470 |
.ai-generate-button:hover { background-color: #7B4DB5; }
|
| 1471 |
</style>
|
| 1472 |
</head>
|
|
@@ -1474,10 +1538,10 @@ ADMIN_TEMPLATE = '''
|
|
| 1474 |
<div class="container">
|
| 1475 |
<div class="header">
|
| 1476 |
<div class="logo-title-container" style="display: flex; align-items: center; gap: 15px;">
|
| 1477 |
-
<img src="https://huggingface.co/spaces/
|
| 1478 |
-
<h1><i class="fas fa-tools"></i> Админ-панель
|
| 1479 |
</div>
|
| 1480 |
-
<a href="{{ url_for('catalog') }}" class="button" style="background-color:
|
| 1481 |
</div>
|
| 1482 |
|
| 1483 |
{% with messages = get_flashed_messages(with_categories=true) %}
|
|
@@ -1492,7 +1556,7 @@ ADMIN_TEMPLATE = '''
|
|
| 1492 |
<h2><i class="fas fa-sync-alt"></i> Синхронизация с Датацентром</h2>
|
| 1493 |
<div class="sync-buttons">
|
| 1494 |
<form method="POST" action="{{ url_for('force_upload') }}" style="display: inline;" onsubmit="return confirm('Вы уверены, что хотите принудительно загрузить локальные данные на сервер? Это перезапишет данные на сервере.');">
|
| 1495 |
-
<button type="submit" class="button" title="Загрузить локальные файлы на Hugging Face"><i class="fas fa-upload"></i> Загрузить БД</button>
|
| 1496 |
</form>
|
| 1497 |
<form method="POST" action="{{ url_for('force_download') }}" style="display: inline;" onsubmit="return confirm('Вы уверены, что хотите принудительно скачать данные с сервера? Это перезапишет ваши локальные файлы.');">
|
| 1498 |
<button type="submit" class="button download-hf-button" title="Скачать файлы (перезапишет локальные)"><i class="fas fa-download"></i> Скачать БД</button>
|
|
@@ -1553,7 +1617,7 @@ ADMIN_TEMPLATE = '''
|
|
| 1553 |
<textarea id="returns" name="returns" rows="4">{{ organization_info.get('returns', '') }}</textarea>
|
| 1554 |
<label for="contact">Контактная информация (для ИИ-ассистента):</label>
|
| 1555 |
<textarea id="contact" name="contact" rows="4">{{ organization_info.get('contact', '') }}</textarea>
|
| 1556 |
-
<button type="submit" class="add-button"><i class="fas fa-save"></i>
|
| 1557 |
</form>
|
| 1558 |
<p style="font-size: 0.85rem; color: #999;">Эта информация будет использоваться ИИ-ассистентом для ответов на вопросы о вашем магазине.</p>
|
| 1559 |
</div>
|
|
@@ -1599,7 +1663,7 @@ ADMIN_TEMPLATE = '''
|
|
| 1599 |
<button type="button" class="remove-color-btn" onclick="removeColorInput(this)"><i class="fas fa-times"></i></button>
|
| 1600 |
</div>
|
| 1601 |
</div>
|
| 1602 |
-
<button type="button" class="button add-color-btn" style="margin-top: 5px;" onclick="addColorInput('add-color-inputs')"><i class="fas fa-palette"></i> Добавить
|
| 1603 |
<br>
|
| 1604 |
<div style="margin-top: 15px;">
|
| 1605 |
<input type="checkbox" id="add_in_stock" name="in_stock" checked>
|
|
@@ -1631,7 +1695,7 @@ ADMIN_TEMPLATE = '''
|
|
| 1631 |
{% endif %}
|
| 1632 |
</div>
|
| 1633 |
<div style="flex-grow: 1;">
|
| 1634 |
-
<h3 style="margin-top: 0; margin-bottom: 5px; color:
|
| 1635 |
{{ product['name'] }}
|
| 1636 |
{% if product.get('in_stock', True) %}
|
| 1637 |
<span class="status-indicator in-stock">В наличии</span>
|
|
@@ -1646,7 +1710,7 @@ ADMIN_TEMPLATE = '''
|
|
| 1646 |
<p><strong>Цена:</strong> {{ "%.2f"|format(product.price) }} {{ currency_code }}</p>
|
| 1647 |
<p class="description" title="{{ product.get('description', '') }}"><strong>Описание:</strong> {{ product.get('description', 'N/A')[:150] }}{% if product.get('description', '')|length > 150 %}...{% endif %}</p>
|
| 1648 |
{% set colors = product.get('colors', []) %}
|
| 1649 |
-
<p><strong
|
| 1650 |
{% if product.get('photos') and product['photos']|length > 1 %}
|
| 1651 |
<p style="font-size: 0.8rem; color: #999;">(Всего фото: {{ product['photos']|length }})</p>
|
| 1652 |
{% endif %}
|
|
@@ -1683,7 +1747,7 @@ ADMIN_TEMPLATE = '''
|
|
| 1683 |
{% endif %}
|
| 1684 |
<label>Описание:</label>
|
| 1685 |
<textarea id="edit_description_{{ loop.index0 }}" name="description" rows="4">{{ product.get('description', '') }}</textarea>
|
| 1686 |
-
<button type="button" class="button ai-generate-button" onclick="generateDescription('edit_photos_{{ loop.index0 }}', 'edit_description_{{ loop.index0 }}', 'edit_gen_lang_{{ loop.index0 }}')"><i class="fas fa-magic"></i>
|
| 1687 |
<label for="edit_gen_lang_{{ loop.index0 }}">Язык генерации:</label>
|
| 1688 |
<select id="edit_gen_lang_{{ loop.index0 }}" name="gen_lang" style="width: auto; display: inline-block; margin-left: 10px;">
|
| 1689 |
<option value="Русский">Русский</option>
|
|
@@ -1717,7 +1781,7 @@ ADMIN_TEMPLATE = '''
|
|
| 1717 |
</div>
|
| 1718 |
{% endif %}
|
| 1719 |
</div>
|
| 1720 |
-
<button type="button" class="button add-color-btn" style="margin-top: 5px;" onclick="addColorInput('edit-color-inputs-{{ loop.index0 }}')"><i class="fas fa-palette"></i> Добавить
|
| 1721 |
<br>
|
| 1722 |
<div style="margin-top: 15px;">
|
| 1723 |
<input type="checkbox" id="edit_in_stock_{{ loop.index0 }}" name="in_stock" {% if product.get('in_stock', True) %}checked{% endif %}>
|
|
@@ -1728,7 +1792,7 @@ ADMIN_TEMPLATE = '''
|
|
| 1728 |
<label for="edit_is_top_{{ loop.index0 }}" class="inline-label">Топ товар</label>
|
| 1729 |
</div>
|
| 1730 |
<br>
|
| 1731 |
-
<button type="submit" class="add-button" style="margin-top: 20px;"><i class="fas fa-save"></i>
|
| 1732 |
</form>
|
| 1733 |
</div>
|
| 1734 |
</div>
|
|
|
|
| 22 |
load_dotenv()
|
| 23 |
|
| 24 |
app = Flask(__name__)
|
| 25 |
+
app.secret_key = 'your_unique_secret_key_gippo_312_shop_54321_no_login'
|
| 26 |
DATA_FILE = 'data.json'
|
| 27 |
|
| 28 |
SYNC_FILES = [DATA_FILE]
|
|
|
|
| 129 |
|
| 130 |
def load_data():
|
| 131 |
default_organization_info = {
|
| 132 |
+
"about_us": "Мы — Gippo312, ваш надежный партнер в мире уникальных товаров. Мы предлагаем широкий ассортимент продукции, от электроники до товаров для дома, всегда стремясь к качеству и доступности. Наша миссия — сделать ваш шопинг приятным и удобным, предлагая только лучшие товары, тщательно отобранные для вас.",
|
| 133 |
"shipping": "Доставка осуществляется по всему Кыргызстану. Стоимость и сроки доставки зависят от региона и веса товара. По Бишкеку доставка возможна в течение 1-2 рабочих дней, в регионы — от 3 до 7 дней. Для уточнения деталей свяжитесь с нами.",
|
| 134 |
"returns": "Возврат и обмен товара возможен в течение 14 дней с момента покупки, при условии сохранения товарного вида, упаковки и чека. Некоторые категории товаров могут иметь особые условия возврата. Пожалуйста, свяжитесь с нами для оформления возврата или обмена.",
|
| 135 |
"contact": f"Наш магазин находится по адресу: {STORE_ADDRESS}. Связаться с нами можно по телефону: {WHATSAPP_NUMBER} или через WhatsApp по этому же номеру. Мы работаем ежедневно с 9:00 до 18:00."
|
|
|
|
| 305 |
|
| 306 |
|
| 307 |
system_instruction_content = (
|
| 308 |
+
"Ты - доброжелательный и очень полезный виртуальный консультант для магазина Gippo312. "
|
| 309 |
"Твоя задача - помогать пользователям находить товары, отвечать на вопросы о них, предлагать варианты, а также предоставлять информацию о магазине. "
|
| 310 |
"Всегда будь вежлив, информативен и стремись решить проблему пользователя. "
|
| 311 |
"Никогда не выдумывай товары или категории, которых нет в предоставленных списках. "
|
|
|
|
| 375 |
<head>
|
| 376 |
<meta charset="UTF-8">
|
| 377 |
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
| 378 |
+
<title>Gippo312 - Каталог</title>
|
| 379 |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
| 380 |
+
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;700&display=swap" rel="stylesheet">
|
| 381 |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/Swiper/10.2.0/swiper-bundle.min.css">
|
| 382 |
<style>
|
| 383 |
+
:root {
|
| 384 |
+
--bg-dark: #003C43;
|
| 385 |
+
--bg-medium: #135D66;
|
| 386 |
+
--accent: #48D1CC;
|
| 387 |
+
--accent-hover: #77E4D8;
|
| 388 |
+
--text-light: #E3FEF7;
|
| 389 |
+
--text-dark: #333;
|
| 390 |
+
--danger: #E57373;
|
| 391 |
+
--danger-hover: #EF5350;
|
| 392 |
+
}
|
| 393 |
* { margin: 0; padding: 0; box-sizing: border-box; }
|
| 394 |
html { -webkit-tap-highlight-color: transparent; }
|
| 395 |
body {
|
| 396 |
+
font-family: 'Montserrat', sans-serif;
|
| 397 |
+
background-color: var(--bg-dark);
|
| 398 |
+
color: var(--text-light);
|
| 399 |
line-height: 1.6;
|
| 400 |
}
|
| 401 |
.container {
|
|
|
|
| 410 |
gap: 15px;
|
| 411 |
position: sticky;
|
| 412 |
top: 0;
|
| 413 |
+
background-color: var(--bg-dark);
|
| 414 |
z-index: 999;
|
| 415 |
+
border-bottom: 1px solid var(--bg-medium);
|
| 416 |
+
}
|
| 417 |
+
.logo {
|
| 418 |
+
flex-shrink: 0;
|
| 419 |
+
}
|
| 420 |
+
.logo img {
|
| 421 |
+
width: 45px;
|
| 422 |
+
height: 45px;
|
| 423 |
+
border-radius: 50%;
|
| 424 |
+
border: 2px solid var(--accent);
|
| 425 |
}
|
| 426 |
.search-wrapper {
|
| 427 |
flex-grow: 1;
|
|
|
|
| 434 |
border: none;
|
| 435 |
border-radius: 25px;
|
| 436 |
outline: none;
|
| 437 |
+
background-color: var(--bg-medium);
|
| 438 |
+
color: var(--text-light);
|
| 439 |
transition: all 0.3s ease;
|
| 440 |
}
|
| 441 |
+
#search-input::placeholder { color: rgba(227, 254, 247, 0.6); }
|
| 442 |
.search-wrapper .fa-search {
|
| 443 |
position: absolute;
|
| 444 |
top: 50%;
|
| 445 |
left: 18px;
|
| 446 |
transform: translateY(-50%);
|
| 447 |
+
color: rgba(227, 254, 247, 0.6);
|
| 448 |
font-size: 1rem;
|
| 449 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 450 |
.category-section {
|
| 451 |
margin-top: 20px;
|
| 452 |
}
|
|
|
|
| 455 |
justify-content: space-between;
|
| 456 |
align-items: center;
|
| 457 |
padding: 0 20px;
|
| 458 |
+
margin-bottom: 15px;
|
| 459 |
}
|
| 460 |
.category-header h2 {
|
| 461 |
font-size: 1.5rem;
|
|
|
|
| 463 |
}
|
| 464 |
.category-header .view-all-arrow {
|
| 465 |
font-size: 1.8rem;
|
| 466 |
+
color: var(--accent);
|
| 467 |
text-decoration: none;
|
| 468 |
font-weight: 300;
|
| 469 |
}
|
|
|
|
| 484 |
flex-shrink: 0;
|
| 485 |
overflow: hidden;
|
| 486 |
cursor: pointer;
|
| 487 |
+
transition: transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
|
| 488 |
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
|
| 489 |
position: relative;
|
| 490 |
}
|
| 491 |
+
.product-card:hover {
|
| 492 |
+
transform: translateY(-5px);
|
| 493 |
+
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.3);
|
| 494 |
+
}
|
| 495 |
.product-card:active { transform: scale(0.96); }
|
| 496 |
.product-image-container {
|
| 497 |
width: 100%;
|
|
|
|
| 507 |
bottom: 0;
|
| 508 |
left: 0;
|
| 509 |
right: 0;
|
| 510 |
+
background: linear-gradient(to top, rgba(0, 0, 0, 0.85) 0%, transparent 100%);
|
| 511 |
padding: 20px 12px 10px;
|
|
|
|
|
|
|
| 512 |
color: #fff;
|
| 513 |
pointer-events: none;
|
| 514 |
}
|
|
|
|
| 520 |
position: absolute;
|
| 521 |
top: 8px;
|
| 522 |
right: 8px;
|
| 523 |
+
background-color: var(--accent);
|
| 524 |
+
color: var(--bg-dark);
|
| 525 |
padding: 2px 8px;
|
| 526 |
font-size: 0.7rem;
|
| 527 |
border-radius: 10px;
|
|
|
|
| 539 |
z-index: 1000;
|
| 540 |
}
|
| 541 |
.floating-button {
|
| 542 |
+
background-color: var(--accent);
|
| 543 |
+
color: var(--bg-dark);
|
| 544 |
border: none;
|
| 545 |
border-radius: 50%;
|
| 546 |
width: 55px;
|
|
|
|
| 550 |
display: flex;
|
| 551 |
align-items: center;
|
| 552 |
justify-content: center;
|
| 553 |
+
box-shadow: 0 4px 15px rgba(72, 209, 204, 0.4);
|
| 554 |
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
| 555 |
}
|
| 556 |
.floating-button:hover {
|
| 557 |
+
background-color: var(--accent-hover);
|
| 558 |
transform: translateY(-3px);
|
|
|
|
| 559 |
}
|
| 560 |
#cart-button { position: relative; }
|
| 561 |
#cart-count {
|
| 562 |
position: absolute;
|
| 563 |
top: -2px;
|
| 564 |
right: -2px;
|
| 565 |
+
background-color: var(--danger);
|
| 566 |
color: white;
|
| 567 |
border-radius: 50%;
|
| 568 |
padding: 2px 6px;
|
| 569 |
font-size: 0.7rem;
|
| 570 |
font-weight: bold;
|
| 571 |
+
border: 2px solid var(--accent);
|
| 572 |
}
|
| 573 |
.modal { display: none; position: fixed; z-index: 1001; left: 0; top: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.6); backdrop-filter: blur(5px); overflow-y: auto; }
|
| 574 |
+
.modal-content { background: #ffffff; color: var(--text-dark); margin: 5% auto; padding: 25px; border-radius: 15px; width: 90%; max-width: 700px; box-shadow: 0 10px 30px rgba(0,0,0,0.1); animation: slideIn 0.3s ease-out; position: relative; }
|
| 575 |
@keyframes slideIn { from { transform: translateY(-30px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }
|
| 576 |
.close { position: absolute; top: 15px; right: 15px; font-size: 1.8rem; color: #aaa; cursor: pointer; transition: color 0.3s; line-height: 1; }
|
| 577 |
.close:hover { color: #666; }
|
| 578 |
+
.modal-content h2 { margin-top: 0; margin-bottom: 20px; color: var(--bg-medium); display: flex; align-items: center; gap: 10px;}
|
| 579 |
+
.cart-item { display: grid; grid-template-columns: 60px 1fr auto auto auto; gap: 15px; align-items: center; padding: 15px 0; border-bottom: 1px solid #e0e0e0; }
|
| 580 |
.cart-item:last-child { border-bottom: none; }
|
| 581 |
+
.cart-item img { width: 60px; height: 60px; object-fit: cover; border-radius: 8px; }
|
| 582 |
.cart-item-details { grid-column: 2; }
|
| 583 |
+
.cart-item-details strong { display: block; margin-bottom: 5px; font-size: 1rem; color: var(--text-dark);}
|
| 584 |
.cart-item-price { font-size: 0.9rem; color: #666; }
|
| 585 |
+
.cart-item-quantity { display: flex; align-items: center; gap: 8px; grid-column: 3;}
|
| 586 |
+
.quantity-btn { background-color: #eee; border: 1px solid #ddd; border-radius: 50%; width: 28px; height: 28px; cursor: pointer; font-size: 1.1rem; line-height: 1; display: flex; align-items: center; justify-content: center; }
|
| 587 |
+
.cart-item-total { font-weight: bold; text-align: right; grid-column: 4; font-size: 1rem; color: var(--bg-medium);}
|
| 588 |
+
.cart-item-remove { grid-column: 5; background:none; border:none; color: var(--danger); cursor:pointer; font-size: 1.3em; padding: 5px; line-height: 1; }
|
| 589 |
+
.cart-item-remove:hover { color: var(--danger-hover); }
|
| 590 |
.quantity-input, .color-select { width: 100%; max-width: 180px; padding: 10px; border: 1px solid #e0e0e0; border-radius: 8px; font-size: 1rem; margin: 10px 0; box-sizing: border-box; }
|
| 591 |
+
.quantity-input:focus, .color-select:focus { border-color: var(--accent); outline: none; box-shadow: 0 0 0 2px rgba(72, 209, 204, 0.2); }
|
| 592 |
.cart-summary { margin-top: 20px; text-align: right; border-top: 1px solid #e0e0e0; padding-top: 15px; }
|
| 593 |
+
.cart-summary strong { font-size: 1.2rem; color: var(--bg-medium);}
|
| 594 |
.cart-actions { margin-top: 25px; display: flex; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
|
| 595 |
.product-button { display: block; width: auto; flex-grow: 1; padding: 10px; border: none; border-radius: 8px; color: white; font-size: 0.9rem; font-weight: 500; cursor: pointer; transition: all 0.3s ease; text-align: center; text-decoration: none; }
|
| 596 |
.product-button i { margin-right: 5px; }
|
| 597 |
.clear-cart { background-color: #6c757d; }
|
| 598 |
.clear-cart:hover { background-color: #5a6268; }
|
| 599 |
+
.formulate-order-button { background-color: var(--accent); color: var(--bg-dark); }
|
| 600 |
+
.formulate-order-button:hover { background-color: var(--accent-hover); }
|
| 601 |
+
.notification { position: fixed; bottom: 80px; left: 50%; transform: translateX(-50%); background-color: var(--accent); color: var(--bg-dark); padding: 10px 20px; border-radius: 20px; box-shadow: 0 4px 10px rgba(0,0,0,0.2); z-index: 1002; opacity: 0; transition: opacity 0.5s ease; font-size: 0.9rem;}
|
| 602 |
.notification.show { opacity: 1;}
|
| 603 |
#chatModal .modal-content { max-width: 450px; }
|
| 604 |
#chat-messages { height: 350px; overflow-y: auto; border: 1px solid #e0e0e0; border-radius: 8px; padding: 15px; margin-bottom: 15px; display: flex; flex-direction: column; gap: 10px; background-color: #fcfcfc; }
|
| 605 |
.chat-message { padding: 10px 15px; border-radius: 15px; max-width: 80%; word-wrap: break-word; line-height: 1.4;}
|
| 606 |
+
.chat-message.user { align-self: flex-end; background-color: var(--bg-medium); color: white; border-bottom-right-radius: 2px; }
|
| 607 |
+
.chat-message.ai { align-self: flex-start; background-color: #e6e6e6; color: var(--text-dark); border-bottom-left-radius: 2px; }
|
| 608 |
.chat-input-container { display: flex; gap: 10px; }
|
| 609 |
#chat-input { flex-grow: 1; padding: 10px 15px; border: 1px solid #e0e0e0; border-radius: 20px; font-size: 0.95rem; outline: none; }
|
| 610 |
+
#chat-input:focus { border-color: var(--bg-medium); box-shadow: 0 0 0 2px rgba(19, 93, 102, 0.15); }
|
| 611 |
+
#chat-send-button { background-color: var(--bg-medium); color: white; border: none; border-radius: 50%; width: 40px; height: 40px; display: flex; justify-content: center; align-items: center; cursor: pointer; transition: background-color 0.3s; flex-shrink: 0; }
|
| 612 |
+
#chat-send-button:hover { background-color: var(--bg-dark); }
|
| 613 |
#chat-send-button:disabled { background-color: #cccccc; cursor: not-allowed; }
|
| 614 |
|
| 615 |
+
.chat-product-card { background-color: #f0f2f5; border-radius: 12px; padding: 10px; margin-top: 8px; display: flex; align-items: center; gap: 12px; border: 1px solid #e0e0e0; }
|
| 616 |
+
.chat-product-card img { width: 50px; height: 50px; object-fit: cover; border-radius: 8px; flex-shrink: 0; }
|
| 617 |
+
.chat-product-card-info { flex-grow: 1; }
|
| 618 |
+
.chat-product-card-info strong { display: block; font-size: 0.9rem; color: var(--text-dark); margin-bottom: 2px; }
|
| 619 |
+
.chat-product-card-info span { font-size: 0.85rem; color: var(--bg-medium); font-weight: 500; }
|
| 620 |
+
.chat-product-card-actions { display: flex; flex-direction: column; gap: 5px; }
|
| 621 |
+
.chat-product-link, .chat-add-to-cart { display: inline-block; background-color: #E0F2F1; color: var(--bg-medium); padding: 5px 10px; border-radius: 15px; cursor: pointer; font-size: 0.85rem; text-decoration: none; transition: background-color 0.2s; font-weight: 500; text-align: center; width: 100%; }
|
| 622 |
+
.chat-product-link:hover, .chat-add-to-cart:hover { background-color: #B2DFDB; }
|
| 623 |
+
.chat-product-card-actions .fa-cart-plus { font-size: 0.9em; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 624 |
|
| 625 |
</style>
|
| 626 |
</head>
|
| 627 |
<body>
|
| 628 |
<div class="container">
|
| 629 |
<div class="top-bar">
|
| 630 |
+
<a href="/" class="logo">
|
| 631 |
+
<img src="https://huggingface.co/spaces/gippo312/admin/resolve/main/Picsart_25-11-04_12-02-21-390.png" alt="Gippo312 Logo">
|
| 632 |
+
</a>
|
| 633 |
<div class="search-wrapper">
|
| 634 |
<i class="fas fa-search"></i>
|
| 635 |
+
<input type="text" id="search-input" placeholder="Искать товары">
|
| 636 |
</div>
|
|
|
|
|
|
|
|
|
|
| 637 |
</div>
|
| 638 |
|
| 639 |
<div id="catalog-content">
|
|
|
|
| 662 |
alt="{{ product.name }}"
|
| 663 |
loading="lazy">
|
| 664 |
{% else %}
|
| 665 |
+
<img src="https://via.placeholder.com/170x170.png?text=Gippo312" alt="No Image" loading="lazy">
|
| 666 |
{% endif %}
|
| 667 |
</div>
|
| 668 |
<div class="product-info-overlay">
|
|
|
|
| 729 |
<input type="text" id="chat-input" placeholder="Напишите сообщение...">
|
| 730 |
<button id="chat-send-button"><i class="fas fa-paper-plane"></i></button>
|
| 731 |
</div>
|
| 732 |
+
<button id="clear-chat-button" class="product-button" style="background-color: var(--danger); margin-top: 15px;"><i class="fas fa-trash"></i> Очистить чат</button>
|
| 733 |
</div>
|
| 734 |
</div>
|
| 735 |
|
|
|
|
| 802 |
})
|
| 803 |
.catch(error => {
|
| 804 |
console.error('Ошибка загрузки деталей продукта:', error);
|
| 805 |
+
modalContent.innerHTML = `<p style="color: var(--danger); text-align:center; padding: 40px;">Не удалось загрузить информацию о товаре. ${error.message}</p>`;
|
| 806 |
});
|
| 807 |
}
|
| 808 |
|
|
|
|
| 929 |
<img src="${photoUrl}" alt="${item.name}">
|
| 930 |
<div class="cart-item-details">
|
| 931 |
<strong>${item.name}${colorText}</strong>
|
| 932 |
+
<p class="cart-item-price">${item.price.toFixed(2)} ${currencyCode}</p>
|
| 933 |
</div>
|
| 934 |
+
<div class="cart-item-quantity">
|
| 935 |
+
<button class="quantity-btn" onclick="decrementCartItem('${item.id}')">-</button>
|
| 936 |
+
<span>${item.quantity}</span>
|
| 937 |
+
<button class="quantity-btn" onclick="incrementCartItem('${item.id}')">+</button>
|
| 938 |
+
</div>
|
| 939 |
+
<span class="cart-item-total">${itemTotal.toFixed(2)}</span>
|
| 940 |
+
<button class="cart-item-remove" onclick="removeFromCart('${item.id}')" title="Удалить товар"><i class="fas fa-trash-alt"></i></button>
|
| 941 |
</div>
|
| 942 |
`;
|
| 943 |
}).join('');
|
|
|
|
| 950 |
}
|
| 951 |
}
|
| 952 |
|
| 953 |
+
function incrementCartItem(itemId) {
|
| 954 |
+
const itemIndex = cart.findIndex(item => item.id === itemId);
|
| 955 |
+
if (itemIndex > -1) {
|
| 956 |
+
cart[itemIndex].quantity++;
|
| 957 |
+
localStorage.setItem('mekaCart', JSON.stringify(cart));
|
| 958 |
+
openCartModal();
|
| 959 |
+
updateCartButton();
|
| 960 |
+
}
|
| 961 |
+
}
|
| 962 |
+
|
| 963 |
+
function decrementCartItem(itemId) {
|
| 964 |
+
const itemIndex = cart.findIndex(item => item.id === itemId);
|
| 965 |
+
if (itemIndex > -1) {
|
| 966 |
+
cart[itemIndex].quantity--;
|
| 967 |
+
if (cart[itemIndex].quantity <= 0) {
|
| 968 |
+
cart.splice(itemIndex, 1);
|
| 969 |
+
}
|
| 970 |
+
localStorage.setItem('mekaCart', JSON.stringify(cart));
|
| 971 |
+
openCartModal();
|
| 972 |
+
updateCartButton();
|
| 973 |
+
}
|
| 974 |
+
}
|
| 975 |
+
|
| 976 |
function removeFromCart(itemId) {
|
| 977 |
cart = cart.filter(item => item.id !== itemId);
|
| 978 |
localStorage.setItem('mekaCart', JSON.stringify(cart));
|
|
|
|
| 1118 |
if (product) {
|
| 1119 |
const photoUrl = product.photos && product.photos.length > 0
|
| 1120 |
? `https://huggingface.co/datasets/${repoId}/resolve/main/photos/${product.photos[0]}`
|
| 1121 |
+
: 'https://via.placeholder.com/50x50.png?text=Gippo312';
|
| 1122 |
|
| 1123 |
const card = document.createElement('div');
|
| 1124 |
card.className = 'chat-product-card';
|
|
|
|
| 1238 |
|
| 1239 |
PRODUCT_DETAIL_TEMPLATE = '''
|
| 1240 |
<div style="padding: 10px;">
|
| 1241 |
+
<h2 style="font-size: 1.6rem; font-weight: 600; margin-bottom: 15px; text-align: center; color: #135D66;">{{ product['name'] }}</h2>
|
| 1242 |
<div class="swiper-container" style="max-width: 450px; margin: 0 auto 20px; border-radius: 10px; overflow: hidden; background-color: #fff; border: 1px solid #e0e0e0;">
|
| 1243 |
<div class="swiper-wrapper">
|
| 1244 |
{% if product.get('photos') and product['photos']|length > 0 %}
|
|
|
|
| 1259 |
</div>
|
| 1260 |
{% if product.get('photos') and product['photos']|length > 1 %}
|
| 1261 |
<div class="swiper-pagination" style="position: relative; bottom: 5px;"></div>
|
| 1262 |
+
<div class="swiper-button-next" style="color: #135D66;"></div>
|
| 1263 |
+
<div class="swiper-button-prev" style="color: #135D66;"></div>
|
| 1264 |
{% endif %}
|
| 1265 |
</div>
|
| 1266 |
|
| 1267 |
<div style="text-align:center; margin-top:20px; padding: 0 10px;">
|
| 1268 |
+
<p style="font-size: 1.5rem; font-weight: bold; color: #135D66; margin-bottom: 15px;"><strong>Цена:</strong> {{ "%.2f"|format(product.price) }} {{ currency_code }}</p>
|
| 1269 |
<button class="product-button formulate-order-button" style="padding: 12px 30px; width: 100%; max-width: 300px;" onclick="closeModal('productModal'); openQuantityModalById('{{ product.get('product_id', '') }}')">
|
| 1270 |
<i class="fas fa-cart-plus"></i> В корзину
|
| 1271 |
</button>
|
|
|
|
| 1288 |
<head>
|
| 1289 |
<meta charset="UTF-8">
|
| 1290 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 1291 |
+
<title>Заказ №{{ order.id }} - Gippo312</title>
|
| 1292 |
+
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;600&display=swap" rel="stylesheet">
|
| 1293 |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
| 1294 |
<style>
|
| 1295 |
+
:root {
|
| 1296 |
+
--bg-light: #f4f6f9;
|
| 1297 |
+
--bg-medium: #135D66;
|
| 1298 |
+
--accent: #48D1CC;
|
| 1299 |
+
--text-dark: #333;
|
| 1300 |
+
--text-light: #E3FEF7;
|
| 1301 |
+
}
|
| 1302 |
+
body { font-family: 'Montserrat', sans-serif; background: var(--bg-light); color: var(--text-dark); line-height: 1.6; padding: 20px; }
|
| 1303 |
.container { max-width: 800px; margin: 20px auto; padding: 30px; background: #fff; border-radius: 15px; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.08); border: 1px solid #e0e0e0; }
|
| 1304 |
+
h1 { text-align: center; color: var(--bg-medium); margin-bottom: 25px; font-size: 1.8rem; font-weight: 600; }
|
| 1305 |
+
h2 { color: var(--bg-medium); margin-top: 30px; margin-bottom: 15px; font-size: 1.4rem; border-bottom: 1px solid #e0e0e0; padding-bottom: 8px;}
|
| 1306 |
.order-meta { font-size: 0.9rem; color: #999; margin-bottom: 20px; text-align: center; }
|
| 1307 |
+
.order-item { display: grid; grid-template-columns: 60px 1fr auto auto; gap: 15px; align-items: center; padding: 15px 0; border-bottom: 1px solid #f0f0f0; }
|
| 1308 |
.order-item:last-child { border-bottom: none; }
|
| 1309 |
+
.order-item img { width: 60px; height: 60px; object-fit: cover; border-radius: 8px; }
|
| 1310 |
+
.item-details strong { display: block; margin-bottom: 5px; font-size: 1.05rem; color: var(--text-dark);}
|
| 1311 |
.item-details span { font-size: 0.9rem; color: #666; display: block;}
|
| 1312 |
+
.item-quantity { display: flex; align-items: center; gap: 8px; }
|
| 1313 |
+
.quantity-btn { background-color: #eee; border: 1px solid #ddd; border-radius: 50%; width: 28px; height: 28px; cursor: pointer; font-size: 1.1rem; line-height: 1; display: flex; align-items: center; justify-content: center; }
|
| 1314 |
+
.quantity-btn:hover { background-color: #e0e0e0; }
|
| 1315 |
+
.item-total { font-weight: bold; text-align: right; font-size: 1rem; color: var(--bg-medium);}
|
| 1316 |
+
.order-summary { margin-top: 30px; padding-top: 20px; border-top: 2px solid var(--accent); text-align: right; }
|
| 1317 |
.order-summary p { margin-bottom: 10px; font-size: 1.1rem; }
|
| 1318 |
+
.order-summary strong { font-size: 1.3rem; color: var(--bg-medium); }
|
| 1319 |
.customer-info { margin-top: 30px; background-color: #f9f9f9; padding: 20px; border-radius: 8px; border: 1px solid #e0e0e0;}
|
| 1320 |
.customer-info p { margin-bottom: 8px; font-size: 0.95rem; }
|
| 1321 |
+
.customer-info strong { color: var(--bg-medium); }
|
| 1322 |
.actions { margin-top: 30px; text-align: center; }
|
| 1323 |
+
.button { padding: 12px 25px; border: none; border-radius: 8px; background-color: var(--accent); color: var(--text-dark); font-weight: 600; cursor: pointer; transition: background-color 0.3s ease, transform 0.1s ease; font-size: 1rem; display: inline-flex; align-items: center; gap: 8px; text-decoration: none; }
|
| 1324 |
+
.button:hover { background-color: #77E4D8; }
|
| 1325 |
.button:active { transform: scale(0.98); }
|
| 1326 |
.button i { font-size: 1.2rem; }
|
| 1327 |
+
.catalog-link { display: block; text-align: center; margin-top: 25px; color: var(--bg-medium); text-decoration: none; font-size: 0.9rem; }
|
| 1328 |
.catalog-link:hover { text-decoration: underline; }
|
| 1329 |
.not-found { text-align: center; color: #dc3545; font-size: 1.2rem; padding: 40px 0;}
|
| 1330 |
</style>
|
|
|
|
| 1332 |
<body>
|
| 1333 |
<div class="container">
|
| 1334 |
{% if order %}
|
| 1335 |
+
<h1><i class="fas fa-receipt"></i> Ваш Заказ №<span id="orderId">{{ order.id }}</span></h1>
|
| 1336 |
<p class="order-meta">Дата создания: {{ order.created_at }}</p>
|
| 1337 |
|
| 1338 |
<h2><i class="fas fa-shopping-bag"></i> Товары в заказе</h2>
|
| 1339 |
+
<div id="orderItems"></div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1340 |
|
| 1341 |
<div class="order-summary">
|
| 1342 |
+
<p><strong>ИТОГО К ОПЛАТЕ: <span id="orderTotal">{{ "%.2f"|format(order.total_price) }}</span> {{ currency_code }}</strong></p>
|
|
|
|
| 1343 |
</div>
|
| 1344 |
|
| 1345 |
<div class="customer-info">
|
| 1346 |
<h2><i class="fas fa-info-circle"></i> Статус заказа</h2>
|
| 1347 |
+
<p>Этот заказ был оформлен без входа в систему. Вы можете скорректировать его и отправить в WhatsApp.</p>
|
| 1348 |
<p>Пожалуйста, свяжитесь с нами по WhatsApp для подтверждения и уточнения деталей.</p>
|
| 1349 |
</div>
|
| 1350 |
|
|
|
|
| 1355 |
<a href="{{ url_for('catalog') }}" class="catalog-link">← Вернуться в каталог</a>
|
| 1356 |
|
| 1357 |
<script>
|
| 1358 |
+
let order = {{ order|tojson|safe }};
|
| 1359 |
+
|
| 1360 |
+
function renderOrderItems() {
|
| 1361 |
+
const container = document.getElementById('orderItems');
|
| 1362 |
+
if (!container) return;
|
| 1363 |
+
|
| 1364 |
+
if (order.cart.length === 0) {
|
| 1365 |
+
container.innerHTML = '<p style="text-align:center; padding: 20px;">Заказ пуст.</p>';
|
| 1366 |
+
document.querySelector('.actions .button').disabled = true;
|
| 1367 |
+
} else {
|
| 1368 |
+
container.innerHTML = order.cart.map((item, index) => `
|
| 1369 |
+
<div class="order-item">
|
| 1370 |
+
<img src="${item.photo_url}" alt="${item.name}">
|
| 1371 |
+
<div class="item-details">
|
| 1372 |
+
<strong>${item.name} ${item.color !== 'N/A' ? `(${item.color})` : ''}</strong>
|
| 1373 |
+
<span>${item.price.toFixed(2)} {{ currency_code }}</span>
|
| 1374 |
+
</div>
|
| 1375 |
+
<div class="item-quantity">
|
| 1376 |
+
<button class="quantity-btn" onclick="decrementItem(${index})">-</button>
|
| 1377 |
+
<span>${item.quantity}</span>
|
| 1378 |
+
<button class="quantity-btn" onclick="incrementItem(${index})">+</button>
|
| 1379 |
+
</div>
|
| 1380 |
+
<div class="item-total">
|
| 1381 |
+
${(item.price * item.quantity).toFixed(2)} {{ currency_code }}
|
| 1382 |
+
</div>
|
| 1383 |
+
</div>
|
| 1384 |
+
`).join('');
|
| 1385 |
+
document.querySelector('.actions .button').disabled = false;
|
| 1386 |
+
}
|
| 1387 |
+
updateOrderTotal();
|
| 1388 |
+
}
|
| 1389 |
+
|
| 1390 |
+
function updateOrderTotal() {
|
| 1391 |
+
const totalElement = document.getElementById('orderTotal');
|
| 1392 |
+
if (!totalElement) return;
|
| 1393 |
+
const total = order.cart.reduce((sum, item) => sum + (item.price * item.quantity), 0);
|
| 1394 |
+
order.total_price = total;
|
| 1395 |
+
totalElement.textContent = total.toFixed(2);
|
| 1396 |
+
}
|
| 1397 |
+
|
| 1398 |
+
function incrementItem(index) {
|
| 1399 |
+
if (order.cart[index]) {
|
| 1400 |
+
order.cart[index].quantity++;
|
| 1401 |
+
renderOrderItems();
|
| 1402 |
+
}
|
| 1403 |
+
}
|
| 1404 |
+
|
| 1405 |
+
function decrementItem(index) {
|
| 1406 |
+
if (order.cart[index]) {
|
| 1407 |
+
order.cart[index].quantity--;
|
| 1408 |
+
if (order.cart[index].quantity <= 0) {
|
| 1409 |
+
if (confirm(`Удалить "${order.cart[index].name}" из заказа?`)) {
|
| 1410 |
+
order.cart.splice(index, 1);
|
| 1411 |
+
} else {
|
| 1412 |
+
order.cart[index].quantity = 1;
|
| 1413 |
+
}
|
| 1414 |
+
}
|
| 1415 |
+
renderOrderItems();
|
| 1416 |
+
}
|
| 1417 |
+
}
|
| 1418 |
+
|
| 1419 |
function sendOrderViaWhatsApp() {
|
| 1420 |
+
if (order.cart.length === 0) {
|
| 1421 |
+
alert("Нельзя отправить пустой заказ.");
|
| 1422 |
+
return;
|
| 1423 |
+
}
|
| 1424 |
+
const orderId = document.getElementById('orderId').textContent;
|
| 1425 |
const whatsappNumber = "{{ whatsapp_number }}";
|
| 1426 |
+
let message = `Здравствуйте! Хочу подтвердить или изменить свой заказ на Gippo312:%0A%0A`;
|
| 1427 |
+
message += `*Номер заказа:* ${orderId}%0A%0A`;
|
| 1428 |
+
|
| 1429 |
+
order.cart.forEach(item => {
|
| 1430 |
+
message += `*${item.name}* ${item.color !== 'N/A' ? `(${item.color})` : ''}%0A`;
|
| 1431 |
+
message += ` - Количество: ${item.quantity}%0A`;
|
| 1432 |
+
message += ` - Цена: ${item.price.toFixed(2)} {{ currency_code }}%0A`;
|
| 1433 |
+
});
|
| 1434 |
+
|
| 1435 |
+
message += `%0A*Итоговая сумма:* ${order.total_price.toFixed(2)} {{ currency_code }}%0A%0A`;
|
| 1436 |
+
message += `Пожалуйста, свяжитесь со мной для уточнения деталей.`;
|
| 1437 |
|
| 1438 |
const whatsappUrl = `https://api.whatsapp.com/send?phone=${whatsappNumber}&text=${message}`;
|
| 1439 |
window.open(whatsappUrl, '_blank');
|
| 1440 |
}
|
| 1441 |
+
|
| 1442 |
+
document.addEventListener('DOMContentLoaded', renderOrderItems);
|
| 1443 |
</script>
|
|
|
|
| 1444 |
{% else %}
|
| 1445 |
<h1 style="color: #dc3545;"><i class="fas fa-exclamation-triangle"></i> Ошибка</h1>
|
| 1446 |
<p class="not-found">Заказ с таким ID не найден.</p>
|
|
|
|
| 1457 |
<head>
|
| 1458 |
<meta charset="UTF-8">
|
| 1459 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 1460 |
+
<title>Админ-панель - Gippo312</title>
|
| 1461 |
+
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;500;600&display=swap" rel="stylesheet">
|
| 1462 |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
| 1463 |
<style>
|
| 1464 |
+
:root {
|
| 1465 |
+
--bg-light: #f4f6f9;
|
| 1466 |
+
--bg-medium: #135D66;
|
| 1467 |
+
--accent: #48D1CC;
|
| 1468 |
+
--accent-hover: #77E4D8;
|
| 1469 |
+
--text-dark: #333;
|
| 1470 |
+
--text-on-accent: #003C43;
|
| 1471 |
+
--danger: #E57373;
|
| 1472 |
+
--danger-hover: #EF5350;
|
| 1473 |
+
}
|
| 1474 |
+
body { font-family: 'Montserrat', sans-serif; background-color: var(--bg-light); color: var(--text-dark); padding: 20px; line-height: 1.6; }
|
| 1475 |
.container { max-width: 1200px; margin: 0 auto; background-color: #fff; padding: 25px; border-radius: 10px; box-shadow: 0 3px 10px rgba(0,0,0,0.05); }
|
| 1476 |
.header { padding-bottom: 15px; margin-bottom: 25px; border-bottom: 1px solid #e0e0e0; display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 10px;}
|
| 1477 |
+
.header .logo-title-container img { height: 50px; width: 50px; border-radius: 50%; object-fit: cover; border: 2px solid var(--bg-medium);}
|
| 1478 |
+
h1, h2, h3 { font-weight: 600; color: var(--bg-medium); margin-bottom: 15px; }
|
| 1479 |
h1 { font-size: 1.8rem; }
|
| 1480 |
h2 { font-size: 1.5rem; margin-top: 30px; display: flex; align-items: center; gap: 8px; }
|
| 1481 |
+
h3 { font-size: 1.2rem; color: #004D40; margin-top: 20px; }
|
| 1482 |
.section { margin-bottom: 30px; padding: 20px; background-color: #fdfdff; border: 1px solid #e0e0e0; border-radius: 8px; }
|
| 1483 |
form { margin-bottom: 20px; }
|
| 1484 |
label { font-weight: 500; margin-top: 10px; display: block; color: #666; font-size: 0.9rem;}
|
| 1485 |
input[type="text"], input[type="number"], input[type="password"], input[type="tel"], textarea, select { width: 100%; padding: 10px 12px; margin-top: 5px; border: 1px solid #e0e0e0; border-radius: 6px; font-size: 0.95rem; box-sizing: border-box; transition: border-color 0.3s ease; background-color: #fff; }
|
| 1486 |
+
input:focus, textarea:focus, select:focus { border-color: var(--bg-medium); outline: none; box-shadow: 0 0 0 2px rgba(19, 93, 102, 0.1); }
|
| 1487 |
textarea { min-height: 80px; resize: vertical; }
|
| 1488 |
input[type="file"] { padding: 8px; background-color: #ffffff; cursor: pointer; border: 1px solid #e0e0e0;}
|
| 1489 |
input[type="file"]::file-selector-button { padding: 5px 10px; border-radius: 4px; background-color: #f0f0f0; border: 1px solid #e0e0e0; cursor: pointer; margin-right: 10px;}
|
| 1490 |
input[type="checkbox"] { margin-right: 5px; vertical-align: middle; }
|
| 1491 |
label.inline-label { display: inline-block; margin-top: 10px; font-weight: normal; }
|
| 1492 |
+
button, .button { padding: 10px 18px; border: none; border-radius: 6px; background-color: var(--accent); color: var(--text-on-accent); font-weight: 600; cursor: pointer; transition: background-color 0.3s ease, transform 0.1s ease; margin-top: 15px; font-size: 0.95rem; display: inline-flex; align-items: center; gap: 5px; text-decoration: none; line-height: 1.2;}
|
| 1493 |
+
button:hover, .button:hover { background-color: var(--accent-hover); }
|
| 1494 |
button:active, .button:active { transform: scale(0.98); }
|
| 1495 |
button[type="submit"] { min-width: 120px; justify-content: center; }
|
| 1496 |
+
.delete-button { background-color: var(--danger); color: white; }
|
| 1497 |
+
.delete-button:hover { background-color: var(--danger-hover); }
|
| 1498 |
+
.add-button { background-color: var(--bg-medium); color: white; }
|
| 1499 |
+
.add-button:hover { background-color: #003C43; }
|
| 1500 |
.item-list { display: grid; gap: 20px; }
|
| 1501 |
.item { background: #fff; padding: 15px 20px; border-radius: 8px; box-shadow: 0 2px 5px rgba(0,0,0,0.03); border: 1px solid #f0f0f0; }
|
| 1502 |
.item p { margin: 5px 0; font-size: 0.9rem; color: #666; }
|
| 1503 |
+
.item strong { color: var(--text-dark); }
|
| 1504 |
.item .description { font-size: 0.85rem; color: #999; max-height: 60px; overflow: hidden; text-overflow: ellipsis; }
|
| 1505 |
.item-actions { margin-top: 15px; display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
|
| 1506 |
+
.edit-form-container { margin-top: 15px; padding: 20px; background: #E0F2F1; border: 1px dashed #B2DFDB; border-radius: 6px; display: none; }
|
|
|
|
|
|
|
| 1507 |
details { background-color: #fdfdff; border: 1px solid #e0e0e0; border-radius: 8px; margin-bottom: 20px; }
|
| 1508 |
+
details > summary { cursor: pointer; font-weight: 600; color: var(--bg-medium); display: block; padding: 15px; border-bottom: 1px solid #e0e0e0; list-style: none; position: relative; }
|
| 1509 |
+
details > summary::after { content: '\\f078'; font-family: 'Font Awesome 6 Free'; font-weight: 900; position: absolute; right: 20px; top: 50%; transform: translateY(-50%); transition: transform 0.2s ease; color: var(--bg-medium); }
|
| 1510 |
details[open] > summary::after { transform: translateY(-50%) rotate(180deg); }
|
| 1511 |
details[open] > summary { border-bottom: 1px solid #e0e0e0; }
|
| 1512 |
details .form-content { padding: 20px; }
|
| 1513 |
.color-input-group { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
|
| 1514 |
.color-input-group input { flex-grow: 1; margin: 0; }
|
| 1515 |
+
.remove-color-btn { background-color: var(--danger); color: white; padding: 6px 10px; font-size: 0.8rem; margin-top: 0; line-height: 1; }
|
| 1516 |
+
.remove-color-btn:hover { background-color: var(--danger-hover); }
|
| 1517 |
+
.add-color-btn { background-color: #B2DFDB; color: var(--bg-medium); border: 1px solid #e0e0e0; }
|
| 1518 |
+
.add-color-btn:hover { background-color: var(--bg-medium); color: white; border-color: var(--bg-medium); }
|
| 1519 |
.photo-preview img { max-width: 70px; max-height: 70px; border-radius: 5px; margin: 5px 5px 0 0; border: 1px solid #e0e0e0; object-fit: cover;}
|
| 1520 |
.sync-buttons { display: flex; gap: 10px; margin-bottom: 20px; flex-wrap: wrap; }
|
| 1521 |
+
.download-hf-button { background-color: #6c757d; color: white; }
|
| 1522 |
.download-hf-button:hover { background-color: #5a6268; }
|
| 1523 |
.flex-container { display: flex; flex-wrap: wrap; gap: 20px; }
|
| 1524 |
.flex-item { flex: 1; min-width: 350px; }
|
|
|
|
| 1529 |
.status-indicator { display: inline-block; padding: 3px 8px; border-radius: 12px; font-size: 0.8rem; font-weight: 500; margin-left: 10px; vertical-align: middle; }
|
| 1530 |
.status-indicator.in-stock { background-color: #d4edda; color: #155724; }
|
| 1531 |
.status-indicator.out-of-stock { background-color: #f8d7da; color: #721c24; }
|
| 1532 |
+
.status-indicator.top-product { background-color: #FFF9C4; color: #F57F17; margin-left: 5px;}
|
| 1533 |
+
.ai-generate-button { background-color: #8D6EC8; color: white; margin-top: 5px; margin-bottom: 10px; }
|
| 1534 |
.ai-generate-button:hover { background-color: #7B4DB5; }
|
| 1535 |
</style>
|
| 1536 |
</head>
|
|
|
|
| 1538 |
<div class="container">
|
| 1539 |
<div class="header">
|
| 1540 |
<div class="logo-title-container" style="display: flex; align-items: center; gap: 15px;">
|
| 1541 |
+
<img src="https://huggingface.co/spaces/gippo312/admin/resolve/main/Picsart_25-11-04_12-02-21-390.png" alt="Gippo312 Logo">
|
| 1542 |
+
<h1><i class="fas fa-tools"></i> Админ-панель Gippo312</h1>
|
| 1543 |
</div>
|
| 1544 |
+
<a href="{{ url_for('catalog') }}" class="button" style="background-color: var(--bg-medium); color: white;"><i class="fas fa-store"></i> Перейти в каталог</a>
|
| 1545 |
</div>
|
| 1546 |
|
| 1547 |
{% with messages = get_flashed_messages(with_categories=true) %}
|
|
|
|
| 1556 |
<h2><i class="fas fa-sync-alt"></i> Синхронизация с Датацентром</h2>
|
| 1557 |
<div class="sync-buttons">
|
| 1558 |
<form method="POST" action="{{ url_for('force_upload') }}" style="display: inline;" onsubmit="return confirm('Вы уверены, что хотите принудительно загрузить локальные данные на сервер? Это перезапишет данные на сервере.');">
|
| 1559 |
+
<button type="submit" class="button add-button" title="Загрузить локальные файлы на Hugging Face"><i class="fas fa-upload"></i> Загрузить БД</button>
|
| 1560 |
</form>
|
| 1561 |
<form method="POST" action="{{ url_for('force_download') }}" style="display: inline;" onsubmit="return confirm('Вы уверены, что хотите принудительно скачать данные с сервера? Это перезапишет ваши локальные файлы.');">
|
| 1562 |
<button type="submit" class="button download-hf-button" title="Скачать файлы (перезапишет локальные)"><i class="fas fa-download"></i> Скачать БД</button>
|
|
|
|
| 1617 |
<textarea id="returns" name="returns" rows="4">{{ organization_info.get('returns', '') }}</textarea>
|
| 1618 |
<label for="contact">Контактная информация (для ИИ-ассистента):</label>
|
| 1619 |
<textarea id="contact" name="contact" rows="4">{{ organization_info.get('contact', '') }}</textarea>
|
| 1620 |
+
<button type="submit" class="add-button"><i class="fas fa-save"></i> Сохранить</button>
|
| 1621 |
</form>
|
| 1622 |
<p style="font-size: 0.85rem; color: #999;">Эта информация будет использоваться ИИ-ассистентом для ответов на вопросы о вашем магазине.</p>
|
| 1623 |
</div>
|
|
|
|
| 1663 |
<button type="button" class="remove-color-btn" onclick="removeColorInput(this)"><i class="fas fa-times"></i></button>
|
| 1664 |
</div>
|
| 1665 |
</div>
|
| 1666 |
+
<button type="button" class="button add-color-btn" style="margin-top: 5px;" onclick="addColorInput('add-color-inputs')"><i class="fas fa-palette"></i> Добавить поле</button>
|
| 1667 |
<br>
|
| 1668 |
<div style="margin-top: 15px;">
|
| 1669 |
<input type="checkbox" id="add_in_stock" name="in_stock" checked>
|
|
|
|
| 1695 |
{% endif %}
|
| 1696 |
</div>
|
| 1697 |
<div style="flex-grow: 1;">
|
| 1698 |
+
<h3 style="margin-top: 0; margin-bottom: 5px; color: var(--text-dark);">
|
| 1699 |
{{ product['name'] }}
|
| 1700 |
{% if product.get('in_stock', True) %}
|
| 1701 |
<span class="status-indicator in-stock">В наличии</span>
|
|
|
|
| 1710 |
<p><strong>Цена:</strong> {{ "%.2f"|format(product.price) }} {{ currency_code }}</p>
|
| 1711 |
<p class="description" title="{{ product.get('description', '') }}"><strong>Описание:</strong> {{ product.get('description', 'N/A')[:150] }}{% if product.get('description', '')|length > 150 %}...{% endif %}</p>
|
| 1712 |
{% set colors = product.get('colors', []) %}
|
| 1713 |
+
<p><strong>Цвета/Вар-ты:</strong> {{ colors|select('ne', '')|join(', ') if colors|select('ne', '')|list|length > 0 else 'Нет' }}</p>
|
| 1714 |
{% if product.get('photos') and product['photos']|length > 1 %}
|
| 1715 |
<p style="font-size: 0.8rem; color: #999;">(Всего фото: {{ product['photos']|length }})</p>
|
| 1716 |
{% endif %}
|
|
|
|
| 1747 |
{% endif %}
|
| 1748 |
<label>Описание:</label>
|
| 1749 |
<textarea id="edit_description_{{ loop.index0 }}" name="description" rows="4">{{ product.get('description', '') }}</textarea>
|
| 1750 |
+
<button type="button" class="button ai-generate-button" onclick="generateDescription('edit_photos_{{ loop.index0 }}', 'edit_description_{{ loop.index0 }}', 'edit_gen_lang_{{ loop.index0 }}')"><i class="fas fa-magic"></i> Сгенерировать</button>
|
| 1751 |
<label for="edit_gen_lang_{{ loop.index0 }}">Язык генерации:</label>
|
| 1752 |
<select id="edit_gen_lang_{{ loop.index0 }}" name="gen_lang" style="width: auto; display: inline-block; margin-left: 10px;">
|
| 1753 |
<option value="Русский">Русский</option>
|
|
|
|
| 1781 |
</div>
|
| 1782 |
{% endif %}
|
| 1783 |
</div>
|
| 1784 |
+
<button type="button" class="button add-color-btn" style="margin-top: 5px;" onclick="addColorInput('edit-color-inputs-{{ loop.index0 }}')"><i class="fas fa-palette"></i> Добавить поле</button>
|
| 1785 |
<br>
|
| 1786 |
<div style="margin-top: 15px;">
|
| 1787 |
<input type="checkbox" id="edit_in_stock_{{ loop.index0 }}" name="in_stock" {% if product.get('in_stock', True) %}checked{% endif %}>
|
|
|
|
| 1792 |
<label for="edit_is_top_{{ loop.index0 }}" class="inline-label">Топ товар</label>
|
| 1793 |
</div>
|
| 1794 |
<br>
|
| 1795 |
+
<button type="submit" class="add-button" style="margin-top: 20px;"><i class="fas fa-save"></i> Сохранить</button>
|
| 1796 |
</form>
|
| 1797 |
</div>
|
| 1798 |
</div>
|