Spaces:
Paused
Paused
Update app.py
Browse files
app.py
CHANGED
|
@@ -363,7 +363,6 @@ CATALOG_TEMPLATE = '''
|
|
| 363 |
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 364 |
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 365 |
<link href="https://fonts.googleapis.com/css2?family=Manrope:wght@300;400;500;700&display=swap" rel="stylesheet">
|
| 366 |
-
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/Swiper/11.0.5/swiper-bundle.min.css">
|
| 367 |
<style>
|
| 368 |
:root {
|
| 369 |
--bg-color: #0d0d0d;
|
|
@@ -407,7 +406,7 @@ CATALOG_TEMPLATE = '''
|
|
| 407 |
#cart-button span { position: absolute; top: -5px; right: -5px; background-color: var(--bg-color); color: var(--text-color); border: 1px solid var(--text-color); border-radius: 50%; padding: 3px 8px; font-size: 0.8rem; font-weight: bold; }
|
| 408 |
.modal { display: none; position: fixed; z-index: 1001; left: 0; top: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.8); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); overflow-y: auto; }
|
| 409 |
.modal-content { background: var(--surface-color); margin: 3% auto; padding: 0; border-radius: 8px; width: 95%; max-width: 900px; box-shadow: 0 10px 40px rgba(0,0,0,0.5); animation: fadeIn 0.4s ease-out; position: relative; border: 1px solid var(--border-color); }
|
| 410 |
-
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
|
| 411 |
.close { position: absolute; top: 15px; right: 20px; font-size: 2rem; color: var(--text-muted); cursor: pointer; transition: color 0.3s; line-height: 1; z-index: 10; }
|
| 412 |
.close:hover { color: var(--text-color); }
|
| 413 |
.modal-header { padding: 30px; border-bottom: 1px solid var(--border-color); }
|
|
@@ -448,6 +447,10 @@ CATALOG_TEMPLATE = '''
|
|
| 448 |
.nav-button:hover, .nav-button.active { color: var(--primary-accent); }
|
| 449 |
.nav-button i { font-size: 1.5rem; }
|
| 450 |
.address-list p { margin-bottom: 10px; }
|
|
|
|
|
|
|
|
|
|
|
|
|
| 451 |
</style>
|
| 452 |
</head>
|
| 453 |
<body>
|
|
@@ -459,20 +462,16 @@ CATALOG_TEMPLATE = '''
|
|
| 459 |
<a href="{{ url_for('set_language', language='kk') }}" class="{{ 'active' if lang == 'kk' }}">KZ</a>
|
| 460 |
</div>
|
| 461 |
</div>
|
| 462 |
-
|
| 463 |
<div class="store-address">{{ _('our_address') }} {{ store_address }}</div>
|
| 464 |
-
|
| 465 |
<div class="filters-container">
|
| 466 |
<button class="category-filter active" data-category="all">{{ _('all_categories') }}</button>
|
| 467 |
{% for category in categories %}
|
| 468 |
<button class="category-filter" data-category="{{ category }}">{{ category }}</button>
|
| 469 |
{% endfor %}
|
| 470 |
</div>
|
| 471 |
-
|
| 472 |
<div class="search-container">
|
| 473 |
<input type="text" id="search-input" placeholder="{{ _('search_placeholder') }}">
|
| 474 |
</div>
|
| 475 |
-
|
| 476 |
<div class="products-grid" id="products-grid">
|
| 477 |
{% for product in products %}
|
| 478 |
<div class="product"
|
|
@@ -516,14 +515,12 @@ CATALOG_TEMPLATE = '''
|
|
| 516 |
{% endif %}
|
| 517 |
</div>
|
| 518 |
</div>
|
| 519 |
-
|
| 520 |
<div id="productModal" class="modal">
|
| 521 |
<div class="modal-content">
|
| 522 |
<span class="close" onclick="closeModal('productModal')" aria-label="{{ _('close') }}">×</span>
|
| 523 |
<div id="modalContent">{{ _('loading') }}...</div>
|
| 524 |
</div>
|
| 525 |
</div>
|
| 526 |
-
|
| 527 |
<div id="cartModal" class="modal">
|
| 528 |
<div class="modal-content">
|
| 529 |
<div class="modal-header">
|
|
@@ -549,7 +546,6 @@ CATALOG_TEMPLATE = '''
|
|
| 549 |
</div>
|
| 550 |
</div>
|
| 551 |
</div>
|
| 552 |
-
|
| 553 |
<div id="favoritesModal" class="modal">
|
| 554 |
<div class="modal-content">
|
| 555 |
<div class="modal-header">
|
|
@@ -558,7 +554,6 @@ CATALOG_TEMPLATE = '''
|
|
| 558 |
<div class="modal-body" id="favoritesContent"><p style="text-align: center; padding: 20px;">{{ _('favorites_is_empty') }}</p></div>
|
| 559 |
</div>
|
| 560 |
</div>
|
| 561 |
-
|
| 562 |
<div id="addressModal" class="modal">
|
| 563 |
<div class="modal-content">
|
| 564 |
<div class="modal-header">
|
|
@@ -571,12 +566,10 @@ CATALOG_TEMPLATE = '''
|
|
| 571 |
</div>
|
| 572 |
</div>
|
| 573 |
</div>
|
| 574 |
-
|
| 575 |
<button id="cart-button" onclick="openCartModal()" aria-label="{{ _('open_cart') }}">
|
| 576 |
<i class="fas fa-shopping-cart"></i>
|
| 577 |
<span id="cart-count">0</span>
|
| 578 |
</button>
|
| 579 |
-
|
| 580 |
<div class="bottom-nav">
|
| 581 |
<a href="https://api.whatsapp.com/send?phone=+77477174564" target="_blank" class="nav-button">
|
| 582 |
<i class="fab fa-whatsapp"></i>
|
|
@@ -591,10 +584,11 @@ CATALOG_TEMPLATE = '''
|
|
| 591 |
<span>{{ _('favorites') }}</span>
|
| 592 |
</button>
|
| 593 |
</div>
|
| 594 |
-
|
| 595 |
<div id="notification-placeholder"></div>
|
| 596 |
-
|
| 597 |
-
|
|
|
|
|
|
|
| 598 |
<script>
|
| 599 |
const products = {{ products|tojson }};
|
| 600 |
const employees = {{ employees|tojson }};
|
|
@@ -603,7 +597,7 @@ CATALOG_TEMPLATE = '''
|
|
| 603 |
const t = {{ translations[lang]|tojson|safe }};
|
| 604 |
let cart = JSON.parse(localStorage.getItem('zhanPostelCart') || '[]');
|
| 605 |
let favorites = JSON.parse(localStorage.getItem('zhanPostelFavorites') || '[]');
|
| 606 |
-
|
| 607 |
function openModal(modalId) {
|
| 608 |
const modal = document.getElementById(modalId);
|
| 609 |
if (modal) {
|
|
@@ -611,7 +605,7 @@ CATALOG_TEMPLATE = '''
|
|
| 611 |
document.body.style.overflow = 'hidden';
|
| 612 |
}
|
| 613 |
}
|
| 614 |
-
|
| 615 |
function openModalByIndex(index) {
|
| 616 |
loadProductDetails(index);
|
| 617 |
openModal('productModal');
|
|
@@ -627,7 +621,7 @@ CATALOG_TEMPLATE = '''
|
|
| 627 |
document.body.style.overflow = 'auto';
|
| 628 |
}
|
| 629 |
}
|
| 630 |
-
|
| 631 |
function loadProductDetails(index) {
|
| 632 |
const modalContent = document.getElementById('modalContent');
|
| 633 |
if (!modalContent) return;
|
|
@@ -639,7 +633,6 @@ CATALOG_TEMPLATE = '''
|
|
| 639 |
})
|
| 640 |
.then(data => {
|
| 641 |
modalContent.innerHTML = data;
|
| 642 |
-
initializeSwiper();
|
| 643 |
})
|
| 644 |
.catch(error => {
|
| 645 |
console.error('Error loading product details:', error);
|
|
@@ -647,60 +640,71 @@ CATALOG_TEMPLATE = '''
|
|
| 647 |
});
|
| 648 |
}
|
| 649 |
|
| 650 |
-
function
|
| 651 |
-
const
|
| 652 |
-
if (
|
| 653 |
-
|
| 654 |
-
|
| 655 |
-
|
| 656 |
-
|
| 657 |
-
});
|
| 658 |
}
|
|
|
|
|
|
|
| 659 |
}
|
| 660 |
|
| 661 |
-
function
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 662 |
const product = products.find(p => p.id === productId);
|
| 663 |
if (!product) {
|
| 664 |
alert(t.product_add_error);
|
| 665 |
return;
|
| 666 |
}
|
| 667 |
-
const controlsContainer =
|
| 668 |
const quantityInput = controlsContainer.querySelector('.quantity-input');
|
| 669 |
const sizeSelect = controlsContainer.querySelector('.size-select');
|
| 670 |
-
|
| 671 |
const quantity = parseInt(quantityInput.value);
|
| 672 |
const size = (sizeSelect && sizeSelect.value) ? sizeSelect.value : 'N/A';
|
| 673 |
-
|
| 674 |
if (isNaN(quantity) || quantity <= 0) {
|
| 675 |
alert(t.enter_correct_quantity);
|
| 676 |
quantityInput.focus();
|
| 677 |
return;
|
| 678 |
}
|
| 679 |
-
|
| 680 |
-
const cartItemId = `${product.id}-${photoFilename}-${size}`;
|
| 681 |
const existingItemIndex = cart.findIndex(item => item.id === cartItemId);
|
| 682 |
-
|
| 683 |
if (existingItemIndex > -1) {
|
| 684 |
cart[existingItemIndex].quantity += quantity;
|
| 685 |
} else {
|
| 686 |
cart.push({
|
| 687 |
id: cartItemId, productId: product.id, name: product.name, price: product.price,
|
| 688 |
-
photo:
|
| 689 |
});
|
| 690 |
}
|
| 691 |
-
|
| 692 |
localStorage.setItem('zhanPostelCart', JSON.stringify(cart));
|
| 693 |
updateCartButton();
|
| 694 |
showNotification(`${product.name} ${t.product_added_notification}`);
|
| 695 |
}
|
| 696 |
|
| 697 |
-
|
| 698 |
function updateCartButton() {
|
| 699 |
const cartCountElement = document.getElementById('cart-count');
|
| 700 |
const cartButton = document.getElementById('cart-button');
|
| 701 |
if (!cartCountElement || !cartButton) return;
|
| 702 |
-
const totalItems = cart.
|
| 703 |
-
|
| 704 |
if (totalItems > 0) {
|
| 705 |
cartCountElement.textContent = totalItems;
|
| 706 |
cartButton.style.display = 'flex';
|
|
@@ -709,13 +713,12 @@ CATALOG_TEMPLATE = '''
|
|
| 709 |
cartButton.style.display = 'none';
|
| 710 |
}
|
| 711 |
}
|
| 712 |
-
|
| 713 |
function openCartModal() {
|
| 714 |
const cartContent = document.getElementById('cartContent');
|
| 715 |
const cartTotalElement = document.getElementById('cartTotal');
|
| 716 |
if (!cartContent || !cartTotalElement) return;
|
| 717 |
let total = 0;
|
| 718 |
-
|
| 719 |
if (cart.length === 0) {
|
| 720 |
cartContent.innerHTML = `<p style="text-align: center; padding: 20px;">${t.cart_is_empty}</p>`;
|
| 721 |
cartTotalElement.textContent = '0.00';
|
|
@@ -727,7 +730,6 @@ CATALOG_TEMPLATE = '''
|
|
| 727 |
? `https://huggingface.co/datasets/${repoId}/resolve/main/photos/${item.photo}`
|
| 728 |
: 'https://via.placeholder.com/80x80.png?text=N/A';
|
| 729 |
const sizeText = item.size !== 'N/A' ? ` (${item.size})` : '';
|
| 730 |
-
|
| 731 |
return `
|
| 732 |
<div class="cart-item">
|
| 733 |
<img src="${photoUrl}" alt="${item.name}">
|
|
@@ -741,7 +743,6 @@ CATALOG_TEMPLATE = '''
|
|
| 741 |
}).join('');
|
| 742 |
cartTotalElement.textContent = total.toFixed(0);
|
| 743 |
}
|
| 744 |
-
|
| 745 |
const employeeSelect = document.getElementById('employeeSelect');
|
| 746 |
if (employeeSelect) {
|
| 747 |
employeeSelect.innerHTML = `<option value="${t.online_order}">${t.online_order}</option>`;
|
|
@@ -752,17 +753,16 @@ CATALOG_TEMPLATE = '''
|
|
| 752 |
employeeSelect.appendChild(option);
|
| 753 |
});
|
| 754 |
}
|
| 755 |
-
|
| 756 |
openModal('cartModal');
|
| 757 |
}
|
| 758 |
-
|
| 759 |
function removeFromCart(itemId) {
|
| 760 |
cart = cart.filter(item => item.id !== itemId);
|
| 761 |
localStorage.setItem('zhanPostelCart', JSON.stringify(cart));
|
| 762 |
openCartModal();
|
| 763 |
updateCartButton();
|
| 764 |
}
|
| 765 |
-
|
| 766 |
function clearCart() {
|
| 767 |
if (confirm(t.confirm_clear_cart)) {
|
| 768 |
cart = [];
|
|
@@ -771,7 +771,7 @@ CATALOG_TEMPLATE = '''
|
|
| 771 |
updateCartButton();
|
| 772 |
}
|
| 773 |
}
|
| 774 |
-
|
| 775 |
function formulateOrder() {
|
| 776 |
if (cart.length === 0) {
|
| 777 |
alert(t.cart_empty_error);
|
|
@@ -810,24 +810,21 @@ CATALOG_TEMPLATE = '''
|
|
| 810 |
if (formulateButton) formulateButton.disabled = false;
|
| 811 |
});
|
| 812 |
}
|
| 813 |
-
|
| 814 |
function filterProducts() {
|
| 815 |
const searchTerm = document.getElementById('search-input').value.toLowerCase().trim();
|
| 816 |
const activeCategoryButton = document.querySelector('.category-filter.active');
|
| 817 |
const activeCategory = activeCategoryButton ? activeCategoryButton.dataset.category : 'all';
|
| 818 |
const grid = document.getElementById('products-grid');
|
| 819 |
let visibleProducts = 0;
|
| 820 |
-
|
| 821 |
const existingNoResults = grid.querySelector('.no-results-message');
|
| 822 |
if (existingNoResults) existingNoResults.remove();
|
| 823 |
-
|
| 824 |
document.querySelectorAll('.products-grid .product').forEach(productElement => {
|
| 825 |
const name = productElement.getAttribute('data-name');
|
| 826 |
const description = productElement.getAttribute('data-description');
|
| 827 |
const category = productElement.getAttribute('data-category');
|
| 828 |
const matchesSearch = !searchTerm || name.includes(searchTerm) || description.includes(searchTerm);
|
| 829 |
const matchesCategory = activeCategory === 'all' || category === activeCategory;
|
| 830 |
-
|
| 831 |
if (matchesSearch && matchesCategory) {
|
| 832 |
productElement.style.display = 'flex';
|
| 833 |
visibleProducts++;
|
|
@@ -835,7 +832,6 @@ CATALOG_TEMPLATE = '''
|
|
| 835 |
productElement.style.display = 'none';
|
| 836 |
}
|
| 837 |
});
|
| 838 |
-
|
| 839 |
if (visibleProducts === 0 && products.length > 0) {
|
| 840 |
const p = document.createElement('p');
|
| 841 |
p.className = 'no-results-message';
|
|
@@ -848,7 +844,7 @@ CATALOG_TEMPLATE = '''
|
|
| 848 |
grid.appendChild(p);
|
| 849 |
}
|
| 850 |
}
|
| 851 |
-
|
| 852 |
function setupFilters() {
|
| 853 |
const searchInput = document.getElementById('search-input');
|
| 854 |
const categoryFilters = document.querySelectorAll('.category-filter');
|
|
@@ -896,11 +892,10 @@ CATALOG_TEMPLATE = '''
|
|
| 896 |
}, 250);
|
| 897 |
}
|
| 898 |
}
|
| 899 |
-
|
| 900 |
function openFavoritesModal() {
|
| 901 |
const favoritesContent = document.getElementById('favoritesContent');
|
| 902 |
favoritesContent.innerHTML = '';
|
| 903 |
-
|
| 904 |
if (favorites.length === 0) {
|
| 905 |
favoritesContent.innerHTML = `<p style="text-align: center; padding: 20px;">${t.favorites_is_empty}</p>`;
|
| 906 |
} else {
|
|
@@ -909,7 +904,6 @@ CATALOG_TEMPLATE = '''
|
|
| 909 |
const photoUrl = item.photos && item.photos.length > 0
|
| 910 |
? `https://huggingface.co/datasets/${repoId}/resolve/main/photos/${item.photos[0]}`
|
| 911 |
: 'https://via.placeholder.com/80x80.png?text=N/A';
|
| 912 |
-
|
| 913 |
const itemHtml = `
|
| 914 |
<div class="cart-item" onclick="openFavoriteProductDetail('${item.id}')" style="cursor: pointer;">
|
| 915 |
<img src="${photoUrl}" alt="${item.name}">
|
|
@@ -923,7 +917,6 @@ CATALOG_TEMPLATE = '''
|
|
| 923 |
favoritesContent.innerHTML += itemHtml;
|
| 924 |
});
|
| 925 |
}
|
| 926 |
-
|
| 927 |
openModal('favoritesModal');
|
| 928 |
}
|
| 929 |
|
|
@@ -937,7 +930,7 @@ CATALOG_TEMPLATE = '''
|
|
| 937 |
updateFavoriteIcons();
|
| 938 |
}
|
| 939 |
}
|
| 940 |
-
|
| 941 |
function showNotification(message, duration = 3000) {
|
| 942 |
const placeholder = document.getElementById('notification-placeholder');
|
| 943 |
if (!placeholder) return;
|
|
@@ -952,7 +945,7 @@ CATALOG_TEMPLATE = '''
|
|
| 952 |
notification.addEventListener('transitionend', () => notification.remove());
|
| 953 |
}, duration);
|
| 954 |
}
|
| 955 |
-
|
| 956 |
document.addEventListener('DOMContentLoaded', () => {
|
| 957 |
updateCartButton();
|
| 958 |
setupFilters();
|
|
@@ -967,6 +960,10 @@ CATALOG_TEMPLATE = '''
|
|
| 967 |
document.querySelectorAll('.modal[style*="display: block"]').forEach(modal => {
|
| 968 |
closeModal(modal.id);
|
| 969 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
| 970 |
}
|
| 971 |
});
|
| 972 |
});
|
|
@@ -977,69 +974,85 @@ CATALOG_TEMPLATE = '''
|
|
| 977 |
|
| 978 |
PRODUCT_DETAIL_TEMPLATE = '''
|
| 979 |
<style>
|
| 980 |
-
.product-detail-layout { display: grid; grid-template-columns: 1fr; }
|
| 981 |
-
@media (min-width:
|
| 982 |
-
.
|
| 983 |
-
.
|
| 984 |
-
.
|
| 985 |
-
.
|
| 986 |
-
.
|
| 987 |
-
.
|
| 988 |
-
.
|
| 989 |
-
.
|
| 990 |
-
.
|
| 991 |
-
.
|
| 992 |
-
.
|
| 993 |
-
.
|
| 994 |
-
.
|
| 995 |
-
.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 996 |
</style>
|
| 997 |
<div class="product-detail-layout">
|
| 998 |
-
|
| 999 |
-
|
| 1000 |
-
|
| 1001 |
-
|
| 1002 |
-
<
|
| 1003 |
-
|
| 1004 |
-
|
| 1005 |
-
|
| 1006 |
-
|
| 1007 |
-
|
| 1008 |
-
|
| 1009 |
-
|
| 1010 |
-
|
| 1011 |
-
|
| 1012 |
-
|
| 1013 |
-
|
| 1014 |
-
|
| 1015 |
-
<input type="number" class="form-control quantity-input" value="1" min="1">
|
| 1016 |
-
<button class="product-button" onclick="addToCartFromDetail(this, '{{ product.id }}', '{{ photo }}')">{{ _('add_to_cart') }}</button>
|
| 1017 |
-
</div>
|
| 1018 |
-
</div>
|
| 1019 |
-
{% endfor %}
|
| 1020 |
-
{% else %}
|
| 1021 |
-
<div class="swiper-slide swiper-slide-detail">
|
| 1022 |
-
<img src="https://via.placeholder.com/600x600.png?text=No+Image" alt="No image available">
|
| 1023 |
-
<div class="slide-controls"><p>{{ _('no_description') }}</p></div>
|
| 1024 |
-
</div>
|
| 1025 |
-
{% endif %}
|
| 1026 |
</div>
|
| 1027 |
-
{% if product.get('photos') and product['photos']|length > 1 %}
|
| 1028 |
-
<div class="swiper-pagination"></div>
|
| 1029 |
-
<div class="swiper-button-next"></div>
|
| 1030 |
-
<div class="swiper-button-prev"></div>
|
| 1031 |
{% endif %}
|
| 1032 |
</div>
|
| 1033 |
-
|
| 1034 |
<div class="product-detail-info">
|
| 1035 |
-
<
|
| 1036 |
-
|
| 1037 |
-
|
| 1038 |
-
|
| 1039 |
-
|
| 1040 |
-
|
| 1041 |
-
|
| 1042 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1043 |
</div>
|
| 1044 |
</div>
|
| 1045 |
'''
|
|
|
|
| 363 |
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 364 |
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 365 |
<link href="https://fonts.googleapis.com/css2?family=Manrope:wght@300;400;500;700&display=swap" rel="stylesheet">
|
|
|
|
| 366 |
<style>
|
| 367 |
:root {
|
| 368 |
--bg-color: #0d0d0d;
|
|
|
|
| 406 |
#cart-button span { position: absolute; top: -5px; right: -5px; background-color: var(--bg-color); color: var(--text-color); border: 1px solid var(--text-color); border-radius: 50%; padding: 3px 8px; font-size: 0.8rem; font-weight: bold; }
|
| 407 |
.modal { display: none; position: fixed; z-index: 1001; left: 0; top: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.8); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); overflow-y: auto; }
|
| 408 |
.modal-content { background: var(--surface-color); margin: 3% auto; padding: 0; border-radius: 8px; width: 95%; max-width: 900px; box-shadow: 0 10px 40px rgba(0,0,0,0.5); animation: fadeIn 0.4s ease-out; position: relative; border: 1px solid var(--border-color); }
|
| 409 |
+
@keyframes fadeIn { from { opacity: 0; transform: translateY(-20px); } to { opacity: 1; transform: translateY(0); } }
|
| 410 |
.close { position: absolute; top: 15px; right: 20px; font-size: 2rem; color: var(--text-muted); cursor: pointer; transition: color 0.3s; line-height: 1; z-index: 10; }
|
| 411 |
.close:hover { color: var(--text-color); }
|
| 412 |
.modal-header { padding: 30px; border-bottom: 1px solid var(--border-color); }
|
|
|
|
| 447 |
.nav-button:hover, .nav-button.active { color: var(--primary-accent); }
|
| 448 |
.nav-button i { font-size: 1.5rem; }
|
| 449 |
.address-list p { margin-bottom: 10px; }
|
| 450 |
+
.lightbox { display: none; position: fixed; z-index: 1003; left: 0; top: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.9); justify-content: center; align-items: center; animation: fadeIn 0.3s; }
|
| 451 |
+
.lightbox-content { max-width: 90%; max-height: 90%; object-fit: contain; }
|
| 452 |
+
.lightbox-close { position: absolute; top: 20px; right: 35px; color: #fff; font-size: 40px; font-weight: bold; cursor: pointer; transition: color 0.3s; }
|
| 453 |
+
.lightbox-close:hover { color: #ccc; }
|
| 454 |
</style>
|
| 455 |
</head>
|
| 456 |
<body>
|
|
|
|
| 462 |
<a href="{{ url_for('set_language', language='kk') }}" class="{{ 'active' if lang == 'kk' }}">KZ</a>
|
| 463 |
</div>
|
| 464 |
</div>
|
|
|
|
| 465 |
<div class="store-address">{{ _('our_address') }} {{ store_address }}</div>
|
|
|
|
| 466 |
<div class="filters-container">
|
| 467 |
<button class="category-filter active" data-category="all">{{ _('all_categories') }}</button>
|
| 468 |
{% for category in categories %}
|
| 469 |
<button class="category-filter" data-category="{{ category }}">{{ category }}</button>
|
| 470 |
{% endfor %}
|
| 471 |
</div>
|
|
|
|
| 472 |
<div class="search-container">
|
| 473 |
<input type="text" id="search-input" placeholder="{{ _('search_placeholder') }}">
|
| 474 |
</div>
|
|
|
|
| 475 |
<div class="products-grid" id="products-grid">
|
| 476 |
{% for product in products %}
|
| 477 |
<div class="product"
|
|
|
|
| 515 |
{% endif %}
|
| 516 |
</div>
|
| 517 |
</div>
|
|
|
|
| 518 |
<div id="productModal" class="modal">
|
| 519 |
<div class="modal-content">
|
| 520 |
<span class="close" onclick="closeModal('productModal')" aria-label="{{ _('close') }}">×</span>
|
| 521 |
<div id="modalContent">{{ _('loading') }}...</div>
|
| 522 |
</div>
|
| 523 |
</div>
|
|
|
|
| 524 |
<div id="cartModal" class="modal">
|
| 525 |
<div class="modal-content">
|
| 526 |
<div class="modal-header">
|
|
|
|
| 546 |
</div>
|
| 547 |
</div>
|
| 548 |
</div>
|
|
|
|
| 549 |
<div id="favoritesModal" class="modal">
|
| 550 |
<div class="modal-content">
|
| 551 |
<div class="modal-header">
|
|
|
|
| 554 |
<div class="modal-body" id="favoritesContent"><p style="text-align: center; padding: 20px;">{{ _('favorites_is_empty') }}</p></div>
|
| 555 |
</div>
|
| 556 |
</div>
|
|
|
|
| 557 |
<div id="addressModal" class="modal">
|
| 558 |
<div class="modal-content">
|
| 559 |
<div class="modal-header">
|
|
|
|
| 566 |
</div>
|
| 567 |
</div>
|
| 568 |
</div>
|
|
|
|
| 569 |
<button id="cart-button" onclick="openCartModal()" aria-label="{{ _('open_cart') }}">
|
| 570 |
<i class="fas fa-shopping-cart"></i>
|
| 571 |
<span id="cart-count">0</span>
|
| 572 |
</button>
|
|
|
|
| 573 |
<div class="bottom-nav">
|
| 574 |
<a href="https://api.whatsapp.com/send?phone=+77477174564" target="_blank" class="nav-button">
|
| 575 |
<i class="fab fa-whatsapp"></i>
|
|
|
|
| 584 |
<span>{{ _('favorites') }}</span>
|
| 585 |
</button>
|
| 586 |
</div>
|
|
|
|
| 587 |
<div id="notification-placeholder"></div>
|
| 588 |
+
<div id="lightbox" class="lightbox" onclick="closeLightbox(event)">
|
| 589 |
+
<span class="lightbox-close" onclick="closeLightbox()">×</span>
|
| 590 |
+
<img class="lightbox-content" id="lightbox-img">
|
| 591 |
+
</div>
|
| 592 |
<script>
|
| 593 |
const products = {{ products|tojson }};
|
| 594 |
const employees = {{ employees|tojson }};
|
|
|
|
| 597 |
const t = {{ translations[lang]|tojson|safe }};
|
| 598 |
let cart = JSON.parse(localStorage.getItem('zhanPostelCart') || '[]');
|
| 599 |
let favorites = JSON.parse(localStorage.getItem('zhanPostelFavorites') || '[]');
|
| 600 |
+
|
| 601 |
function openModal(modalId) {
|
| 602 |
const modal = document.getElementById(modalId);
|
| 603 |
if (modal) {
|
|
|
|
| 605 |
document.body.style.overflow = 'hidden';
|
| 606 |
}
|
| 607 |
}
|
| 608 |
+
|
| 609 |
function openModalByIndex(index) {
|
| 610 |
loadProductDetails(index);
|
| 611 |
openModal('productModal');
|
|
|
|
| 621 |
document.body.style.overflow = 'auto';
|
| 622 |
}
|
| 623 |
}
|
| 624 |
+
|
| 625 |
function loadProductDetails(index) {
|
| 626 |
const modalContent = document.getElementById('modalContent');
|
| 627 |
if (!modalContent) return;
|
|
|
|
| 633 |
})
|
| 634 |
.then(data => {
|
| 635 |
modalContent.innerHTML = data;
|
|
|
|
| 636 |
})
|
| 637 |
.catch(error => {
|
| 638 |
console.error('Error loading product details:', error);
|
|
|
|
| 640 |
});
|
| 641 |
}
|
| 642 |
|
| 643 |
+
function updateMainPhoto(newSrc, thumbElement) {
|
| 644 |
+
const mainPhoto = document.getElementById('main-product-photo');
|
| 645 |
+
if (mainPhoto) {
|
| 646 |
+
mainPhoto.style.opacity = 0;
|
| 647 |
+
setTimeout(() => {
|
| 648 |
+
mainPhoto.src = newSrc;
|
| 649 |
+
mainPhoto.style.opacity = 1;
|
| 650 |
+
}, 200);
|
| 651 |
}
|
| 652 |
+
document.querySelectorAll('#thumbnail-grid .thumbnail').forEach(t => t.classList.remove('active'));
|
| 653 |
+
thumbElement.classList.add('active');
|
| 654 |
}
|
| 655 |
|
| 656 |
+
function openLightbox(src) {
|
| 657 |
+
const lightbox = document.getElementById('lightbox');
|
| 658 |
+
if (lightbox) {
|
| 659 |
+
document.getElementById('lightbox-img').src = src;
|
| 660 |
+
lightbox.style.display = 'flex';
|
| 661 |
+
}
|
| 662 |
+
}
|
| 663 |
+
|
| 664 |
+
function closeLightbox(event) {
|
| 665 |
+
const lightbox = document.getElementById('lightbox');
|
| 666 |
+
if (lightbox && (!event || event.target === lightbox || event.target.classList.contains('lightbox-close'))) {
|
| 667 |
+
lightbox.style.display = 'none';
|
| 668 |
+
}
|
| 669 |
+
}
|
| 670 |
+
|
| 671 |
+
function addToCartFromDetail(productId) {
|
| 672 |
const product = products.find(p => p.id === productId);
|
| 673 |
if (!product) {
|
| 674 |
alert(t.product_add_error);
|
| 675 |
return;
|
| 676 |
}
|
| 677 |
+
const controlsContainer = document.getElementById('modalContent');
|
| 678 |
const quantityInput = controlsContainer.querySelector('.quantity-input');
|
| 679 |
const sizeSelect = controlsContainer.querySelector('.size-select');
|
|
|
|
| 680 |
const quantity = parseInt(quantityInput.value);
|
| 681 |
const size = (sizeSelect && sizeSelect.value) ? sizeSelect.value : 'N/A';
|
|
|
|
| 682 |
if (isNaN(quantity) || quantity <= 0) {
|
| 683 |
alert(t.enter_correct_quantity);
|
| 684 |
quantityInput.focus();
|
| 685 |
return;
|
| 686 |
}
|
| 687 |
+
const cartItemId = `${product.id}-${size}`;
|
|
|
|
| 688 |
const existingItemIndex = cart.findIndex(item => item.id === cartItemId);
|
| 689 |
+
const photoForCart = (product.photos && product.photos.length > 0) ? product.photos[0] : null;
|
| 690 |
if (existingItemIndex > -1) {
|
| 691 |
cart[existingItemIndex].quantity += quantity;
|
| 692 |
} else {
|
| 693 |
cart.push({
|
| 694 |
id: cartItemId, productId: product.id, name: product.name, price: product.price,
|
| 695 |
+
photo: photoForCart, quantity: quantity, size: size
|
| 696 |
});
|
| 697 |
}
|
|
|
|
| 698 |
localStorage.setItem('zhanPostelCart', JSON.stringify(cart));
|
| 699 |
updateCartButton();
|
| 700 |
showNotification(`${product.name} ${t.product_added_notification}`);
|
| 701 |
}
|
| 702 |
|
|
|
|
| 703 |
function updateCartButton() {
|
| 704 |
const cartCountElement = document.getElementById('cart-count');
|
| 705 |
const cartButton = document.getElementById('cart-button');
|
| 706 |
if (!cartCountElement || !cartButton) return;
|
| 707 |
+
const totalItems = cart.reduce((sum, item) => sum + item.quantity, 0);
|
|
|
|
| 708 |
if (totalItems > 0) {
|
| 709 |
cartCountElement.textContent = totalItems;
|
| 710 |
cartButton.style.display = 'flex';
|
|
|
|
| 713 |
cartButton.style.display = 'none';
|
| 714 |
}
|
| 715 |
}
|
| 716 |
+
|
| 717 |
function openCartModal() {
|
| 718 |
const cartContent = document.getElementById('cartContent');
|
| 719 |
const cartTotalElement = document.getElementById('cartTotal');
|
| 720 |
if (!cartContent || !cartTotalElement) return;
|
| 721 |
let total = 0;
|
|
|
|
| 722 |
if (cart.length === 0) {
|
| 723 |
cartContent.innerHTML = `<p style="text-align: center; padding: 20px;">${t.cart_is_empty}</p>`;
|
| 724 |
cartTotalElement.textContent = '0.00';
|
|
|
|
| 730 |
? `https://huggingface.co/datasets/${repoId}/resolve/main/photos/${item.photo}`
|
| 731 |
: 'https://via.placeholder.com/80x80.png?text=N/A';
|
| 732 |
const sizeText = item.size !== 'N/A' ? ` (${item.size})` : '';
|
|
|
|
| 733 |
return `
|
| 734 |
<div class="cart-item">
|
| 735 |
<img src="${photoUrl}" alt="${item.name}">
|
|
|
|
| 743 |
}).join('');
|
| 744 |
cartTotalElement.textContent = total.toFixed(0);
|
| 745 |
}
|
|
|
|
| 746 |
const employeeSelect = document.getElementById('employeeSelect');
|
| 747 |
if (employeeSelect) {
|
| 748 |
employeeSelect.innerHTML = `<option value="${t.online_order}">${t.online_order}</option>`;
|
|
|
|
| 753 |
employeeSelect.appendChild(option);
|
| 754 |
});
|
| 755 |
}
|
|
|
|
| 756 |
openModal('cartModal');
|
| 757 |
}
|
| 758 |
+
|
| 759 |
function removeFromCart(itemId) {
|
| 760 |
cart = cart.filter(item => item.id !== itemId);
|
| 761 |
localStorage.setItem('zhanPostelCart', JSON.stringify(cart));
|
| 762 |
openCartModal();
|
| 763 |
updateCartButton();
|
| 764 |
}
|
| 765 |
+
|
| 766 |
function clearCart() {
|
| 767 |
if (confirm(t.confirm_clear_cart)) {
|
| 768 |
cart = [];
|
|
|
|
| 771 |
updateCartButton();
|
| 772 |
}
|
| 773 |
}
|
| 774 |
+
|
| 775 |
function formulateOrder() {
|
| 776 |
if (cart.length === 0) {
|
| 777 |
alert(t.cart_empty_error);
|
|
|
|
| 810 |
if (formulateButton) formulateButton.disabled = false;
|
| 811 |
});
|
| 812 |
}
|
| 813 |
+
|
| 814 |
function filterProducts() {
|
| 815 |
const searchTerm = document.getElementById('search-input').value.toLowerCase().trim();
|
| 816 |
const activeCategoryButton = document.querySelector('.category-filter.active');
|
| 817 |
const activeCategory = activeCategoryButton ? activeCategoryButton.dataset.category : 'all';
|
| 818 |
const grid = document.getElementById('products-grid');
|
| 819 |
let visibleProducts = 0;
|
|
|
|
| 820 |
const existingNoResults = grid.querySelector('.no-results-message');
|
| 821 |
if (existingNoResults) existingNoResults.remove();
|
|
|
|
| 822 |
document.querySelectorAll('.products-grid .product').forEach(productElement => {
|
| 823 |
const name = productElement.getAttribute('data-name');
|
| 824 |
const description = productElement.getAttribute('data-description');
|
| 825 |
const category = productElement.getAttribute('data-category');
|
| 826 |
const matchesSearch = !searchTerm || name.includes(searchTerm) || description.includes(searchTerm);
|
| 827 |
const matchesCategory = activeCategory === 'all' || category === activeCategory;
|
|
|
|
| 828 |
if (matchesSearch && matchesCategory) {
|
| 829 |
productElement.style.display = 'flex';
|
| 830 |
visibleProducts++;
|
|
|
|
| 832 |
productElement.style.display = 'none';
|
| 833 |
}
|
| 834 |
});
|
|
|
|
| 835 |
if (visibleProducts === 0 && products.length > 0) {
|
| 836 |
const p = document.createElement('p');
|
| 837 |
p.className = 'no-results-message';
|
|
|
|
| 844 |
grid.appendChild(p);
|
| 845 |
}
|
| 846 |
}
|
| 847 |
+
|
| 848 |
function setupFilters() {
|
| 849 |
const searchInput = document.getElementById('search-input');
|
| 850 |
const categoryFilters = document.querySelectorAll('.category-filter');
|
|
|
|
| 892 |
}, 250);
|
| 893 |
}
|
| 894 |
}
|
| 895 |
+
|
| 896 |
function openFavoritesModal() {
|
| 897 |
const favoritesContent = document.getElementById('favoritesContent');
|
| 898 |
favoritesContent.innerHTML = '';
|
|
|
|
| 899 |
if (favorites.length === 0) {
|
| 900 |
favoritesContent.innerHTML = `<p style="text-align: center; padding: 20px;">${t.favorites_is_empty}</p>`;
|
| 901 |
} else {
|
|
|
|
| 904 |
const photoUrl = item.photos && item.photos.length > 0
|
| 905 |
? `https://huggingface.co/datasets/${repoId}/resolve/main/photos/${item.photos[0]}`
|
| 906 |
: 'https://via.placeholder.com/80x80.png?text=N/A';
|
|
|
|
| 907 |
const itemHtml = `
|
| 908 |
<div class="cart-item" onclick="openFavoriteProductDetail('${item.id}')" style="cursor: pointer;">
|
| 909 |
<img src="${photoUrl}" alt="${item.name}">
|
|
|
|
| 917 |
favoritesContent.innerHTML += itemHtml;
|
| 918 |
});
|
| 919 |
}
|
|
|
|
| 920 |
openModal('favoritesModal');
|
| 921 |
}
|
| 922 |
|
|
|
|
| 930 |
updateFavoriteIcons();
|
| 931 |
}
|
| 932 |
}
|
| 933 |
+
|
| 934 |
function showNotification(message, duration = 3000) {
|
| 935 |
const placeholder = document.getElementById('notification-placeholder');
|
| 936 |
if (!placeholder) return;
|
|
|
|
| 945 |
notification.addEventListener('transitionend', () => notification.remove());
|
| 946 |
}, duration);
|
| 947 |
}
|
| 948 |
+
|
| 949 |
document.addEventListener('DOMContentLoaded', () => {
|
| 950 |
updateCartButton();
|
| 951 |
setupFilters();
|
|
|
|
| 960 |
document.querySelectorAll('.modal[style*="display: block"]').forEach(modal => {
|
| 961 |
closeModal(modal.id);
|
| 962 |
});
|
| 963 |
+
const lightbox = document.getElementById('lightbox');
|
| 964 |
+
if (lightbox && lightbox.style.display !== 'none') {
|
| 965 |
+
closeLightbox();
|
| 966 |
+
}
|
| 967 |
}
|
| 968 |
});
|
| 969 |
});
|
|
|
|
| 974 |
|
| 975 |
PRODUCT_DETAIL_TEMPLATE = '''
|
| 976 |
<style>
|
| 977 |
+
.product-detail-layout { display: grid; grid-template-columns: 1fr; gap: 20px; padding: 15px; }
|
| 978 |
+
@media (min-width: 768px) { .product-detail-layout { grid-template-columns: 1fr 1fr; gap: 40px; padding: 30px; } }
|
| 979 |
+
.photo-gallery { display: flex; flex-direction: column; gap: 10px; }
|
| 980 |
+
.main-photo-container { width: 100%; aspect-ratio: 1 / 1; background-color: var(--bg-color); border-radius: 8px; overflow: hidden; cursor: zoom-in; position: relative; }
|
| 981 |
+
.main-photo { width: 100%; height: 100%; object-fit: cover; transition: opacity 0.2s ease-in-out; }
|
| 982 |
+
.thumbnail-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(60px, 1fr)); gap: 10px; }
|
| 983 |
+
.thumbnail { width: 100%; aspect-ratio: 1 / 1; object-fit: cover; border-radius: 4px; cursor: pointer; border: 2px solid transparent; transition: border-color 0.3s ease; }
|
| 984 |
+
.thumbnail.active, .thumbnail:hover { border-color: var(--primary-accent); }
|
| 985 |
+
.product-detail-info { display: flex; flex-direction: column; }
|
| 986 |
+
.product-detail-info h2 { font-size: 2rem; font-weight: 500; margin-bottom: 10px; line-height: 1.2; }
|
| 987 |
+
.product-detail-info .price { font-size: 1.8rem; font-weight: 500; margin-bottom: 20px; }
|
| 988 |
+
.product-detail-info .description-block { margin-top: 15px; margin-bottom: 20px; }
|
| 989 |
+
.product-detail-info p { margin-bottom: 10px; color: var(--text-muted); line-height: 1.7; }
|
| 990 |
+
.product-detail-info strong { color: var(--text-color); font-weight: 500; }
|
| 991 |
+
.controls-section { margin-top: auto; padding-top: 20px; border-top: 1px solid var(--border-color); }
|
| 992 |
+
.control-group { margin-bottom: 15px; }
|
| 993 |
+
.control-group label { display: block; font-size: 1rem; color: var(--text-muted); margin-bottom: 8px; font-weight: 400; }
|
| 994 |
+
.control-group .form-control { width: 100%; padding: 14px; border: 1px solid var(--border-color); border-radius: 4px; font-size: 1rem; background-color: var(--bg-color); color: var(--text-color); -moz-appearance: textfield; }
|
| 995 |
+
.control-group input[type=number]::-webkit-inner-spin-button,
|
| 996 |
+
.control-group input[type=number]::-webkit-outer-spin-button {
|
| 997 |
+
-webkit-appearance: none;
|
| 998 |
+
margin: 0;
|
| 999 |
+
}
|
| 1000 |
+
.detail-add-to-cart-btn { width: 100%; padding: 16px; font-size: 1.1rem; }
|
| 1001 |
</style>
|
| 1002 |
<div class="product-detail-layout">
|
| 1003 |
+
{% set photos = product.get('photos', []) %}
|
| 1004 |
+
<div class="photo-gallery">
|
| 1005 |
+
<div class="main-photo-container" onclick="openLightbox(this.querySelector('img').src)">
|
| 1006 |
+
{% if photos %}
|
| 1007 |
+
<img id="main-product-photo" class="main-photo" src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/photos/{{ photos[0] }}" alt="{{ product.name }}">
|
| 1008 |
+
{% else %}
|
| 1009 |
+
<img id="main-product-photo" class="main-photo" src="https://via.placeholder.com/600x600.png?text=No+Image" alt="No image available">
|
| 1010 |
+
{% endif %}
|
| 1011 |
+
</div>
|
| 1012 |
+
{% if photos|length > 1 %}
|
| 1013 |
+
<div class="thumbnail-grid" id="thumbnail-grid">
|
| 1014 |
+
{% for photo in photos %}
|
| 1015 |
+
<img src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/photos/{{ photo }}"
|
| 1016 |
+
alt="Thumbnail {{ loop.index }}"
|
| 1017 |
+
class="thumbnail {{ 'active' if loop.first }}"
|
| 1018 |
+
onclick="updateMainPhoto('https://huggingface.co/datasets/{{ repo_id }}/resolve/main/photos/{{ photo }}', this)">
|
| 1019 |
+
{% endfor %}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1020 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1021 |
{% endif %}
|
| 1022 |
</div>
|
|
|
|
| 1023 |
<div class="product-detail-info">
|
| 1024 |
+
<div>
|
| 1025 |
+
<h2>{{ product['name'] }}</h2>
|
| 1026 |
+
<p class="price">{{ "%.0f"|format(product['price']) }} {{ currency_code }}</p>
|
| 1027 |
+
<p><strong>{{ _('category') }}:</strong> {{ product.get('category', _('no_category')) }}</p>
|
| 1028 |
+
{% set sizes = product.get('sizes', [])|select('ne', '')|list %}
|
| 1029 |
+
{% if sizes %}
|
| 1030 |
+
<p><strong>{{ _('available_sizes') }}:</strong> {{ sizes|join(', ') }}</p>
|
| 1031 |
+
{% endif %}
|
| 1032 |
+
<div class="description-block">
|
| 1033 |
+
<p><strong>{{ _('description') }}:</strong><br>
|
| 1034 |
+
{{ product.get('description', _('no_description'))|replace('\\n', '<br>')|safe }}</p>
|
| 1035 |
+
</div>
|
| 1036 |
+
</div>
|
| 1037 |
+
<div class="controls-section">
|
| 1038 |
+
{% if sizes %}
|
| 1039 |
+
<div class="control-group">
|
| 1040 |
+
<label for="size-select">{{ _('size_variant') }}</label>
|
| 1041 |
+
<select id="size-select" class="form-control size-select">
|
| 1042 |
+
{% for size in sizes %}
|
| 1043 |
+
<option value="{{ size }}">{{ size }}</option>
|
| 1044 |
+
{% endfor %}
|
| 1045 |
+
</select>
|
| 1046 |
+
</div>
|
| 1047 |
+
{% endif %}
|
| 1048 |
+
<div class="control-group">
|
| 1049 |
+
<label for="quantity-input">{{ _('quantity') }}</label>
|
| 1050 |
+
<input type="number" id="quantity-input" class="form-control quantity-input" value="1" min="1">
|
| 1051 |
+
</div>
|
| 1052 |
+
<button class="product-button detail-add-to-cart-btn" onclick="addToCartFromDetail('{{ product.id }}')">
|
| 1053 |
+
<i class="fas fa-cart-plus"></i> {{ _('add_to_cart') }}
|
| 1054 |
+
</button>
|
| 1055 |
+
</div>
|
| 1056 |
</div>
|
| 1057 |
</div>
|
| 1058 |
'''
|