diff --git a/cookies_login.txt b/cookies_login.txt new file mode 100644 index 0000000..9e60cfa --- /dev/null +++ b/cookies_login.txt @@ -0,0 +1,5 @@ +# Netscape HTTP Cookie File +# https://curl.se/docs/http-cookies.html +# This file was generated by libcurl! Edit at your own risk. + +127.0.0.1 FALSE / FALSE 0 PHPSESSID 3lfjkalu291jmcfc01kbpt4umj diff --git a/cookies_login_owner.txt b/cookies_login_owner.txt new file mode 100644 index 0000000..10145f2 --- /dev/null +++ b/cookies_login_owner.txt @@ -0,0 +1,5 @@ +# Netscape HTTP Cookie File +# https://curl.se/docs/http-cookies.html +# This file was generated by libcurl! Edit at your own risk. + +127.0.0.1 FALSE / FALSE 0 PHPSESSID 43km9i3a7704fr6prmt70vau78 diff --git a/includes/app.php b/includes/app.php index 965ef32..9ed6092 100644 --- a/includes/app.php +++ b/includes/app.php @@ -9,7 +9,7 @@ require_once __DIR__ . '/../db/config.php'; // Auto-migrate newly added columns try { - $flagFile = sys_get_temp_dir() . '/.migrated_avatar_col_' . md5(__DIR__); + $flagFile = sys_get_temp_dir() . '/.schema_migrated_v2_' . md5(__DIR__); if (!file_exists($flagFile)) { $pdo = db(); $stmt = $pdo->query("SHOW COLUMNS FROM users LIKE 'avatar'"); @@ -28,6 +28,10 @@ try { if ($stmt4->rowCount() === 0) { $pdo->exec("ALTER TABLE sales_orders ADD COLUMN payment_status varchar(20) NOT NULL DEFAULT 'paid'"); } + $stmt5 = $pdo->query("SHOW COLUMNS FROM sales_orders LIKE 'vat_amount'"); + if ($stmt5->rowCount() === 0) { + $pdo->exec("ALTER TABLE sales_orders ADD COLUMN vat_amount decimal(10,3) NOT NULL DEFAULT 0.000 AFTER subtotal"); + } @file_put_contents($flagFile, '1'); } } catch (\Throwable $e) {} @@ -340,6 +344,7 @@ function ensure_sales_table(): void items_json LONGTEXT NOT NULL, item_count INT UNSIGNED NOT NULL DEFAULT 0, subtotal DECIMAL(10,2) NOT NULL DEFAULT 0, + vat_amount DECIMAL(10,3) NOT NULL DEFAULT 0.000, total_amount DECIMAL(10,2) NOT NULL DEFAULT 0, status VARCHAR(20) NOT NULL DEFAULT 'completed', notes TEXT DEFAULT NULL, diff --git a/shop.php b/shop.php index 7c18f54..95c8afe 100644 --- a/shop.php +++ b/shop.php @@ -70,6 +70,32 @@ body { background-color: #f8f9fa; } -ms-overflow-style: none; scrollbar-width: none; } +.shop-pagination-wrap { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + gap: 12px; +} +.shop-pagination-status { + color: #6c757d; + font-weight: 600; +} +.shop-pagination .page-link { + border-radius: 999px !important; + margin: 0 4px; + border: 0; + box-shadow: 0 3px 10px rgba(0,0,0,0.06); + color: #0d6efd; +} +.shop-pagination .page-item.active .page-link { + background: #0d6efd; + color: #fff; +} +.shop-pagination .page-item.disabled .page-link { + color: #adb5bd; + box-shadow: none; +}
@@ -86,7 +112,7 @@ body { background-color: #f8f9fa; } -
+
@@ -102,6 +128,14 @@ body { background-color: #f8f9fa; }
+
+
+ +
+ +
@@ -146,6 +180,13 @@ body { background-color: #f8f9fa; }

+ +
+
+ +
@@ -362,69 +403,149 @@ async function submitOrder() { } } -// Filtering Logic +// Filtering + Pagination Logic let currentCategory = 'all'; +let currentPage = 1; +const itemsPerPage = 24; function setCategoryFilter(cat, btn) { currentCategory = cat; - - // Update active button state + currentPage = 1; + document.querySelectorAll('#categoryFilters button').forEach(b => { b.classList.remove('btn-primary', 'active'); b.classList.add('btn-light', 'text-dark'); }); btn.classList.remove('btn-light', 'text-dark'); btn.classList.add('btn-primary', 'active'); - + filterProducts(); } -function filterProducts() { +function getFilteredItems() { const searchInput = document.getElementById('searchInput'); - if (!searchInput) return; - - const searchVal = searchInput.value.toLowerCase(); - let totalVisible = 0; - + const searchVal = searchInput ? searchInput.value.toLowerCase().trim() : ''; + + return Array.from(document.querySelectorAll('.product-item')).filter(item => { + const itemName = (item.getAttribute('data-name') || '').toLowerCase(); + const itemCategory = item.closest('.category-row')?.getAttribute('data-category') || ''; + const matchesSearch = itemName.includes(searchVal); + const matchesCategory = currentCategory === 'all' || currentCategory === itemCategory; + return matchesSearch && matchesCategory; + }); +} + +function renderPagination(totalItems) { + const top = document.getElementById('paginationTop'); + const bottom = document.getElementById('paginationBottom'); + const statusTop = document.getElementById('paginationStatus'); + const statusBottom = document.getElementById('paginationStatusBottom'); + const footerWrap = document.getElementById('paginationFooter'); + + if (!top || !bottom || !statusTop || !statusBottom) return; + + const totalPages = Math.max(1, Math.ceil(totalItems / itemsPerPage)); + if (currentPage > totalPages) currentPage = totalPages; + + if (totalItems === 0) { + statusTop.textContent = ; + statusBottom.textContent = statusTop.textContent; + top.innerHTML = ''; + bottom.innerHTML = ''; + if (footerWrap) footerWrap.style.display = 'none'; + return; + } + + const start = ((currentPage - 1) * itemsPerPage) + 1; + const end = Math.min(totalItems, currentPage * itemsPerPage); + const statusText = ` ${start}-${end} ${totalItems}`; + statusTop.textContent = statusText; + statusBottom.textContent = statusText; + if (footerWrap) footerWrap.style.display = ''; + + if (totalPages <= 1) { + top.innerHTML = ''; + bottom.innerHTML = ''; + return; + } + + const pages = []; + pages.push(1); + for (let p = Math.max(2, currentPage - 1); p <= Math.min(totalPages - 1, currentPage + 1); p++) { + pages.push(p); + } + if (totalPages > 1) pages.push(totalPages); + + const uniquePages = [...new Set(pages)].sort((a, b) => a - b); + const parts = []; + + const addControl = (label, page, disabled = false, active = false) => { + parts.push(`
  • `); + }; + + addControl('‹', Math.max(1, currentPage - 1), currentPage === 1); + + let lastPage = 0; + uniquePages.forEach(page => { + if (lastPage && page > lastPage + 1) { + parts.push('
  • '); + } + addControl(page, page, false, page === currentPage); + lastPage = page; + }); + + addControl('›', Math.min(totalPages, currentPage + 1), currentPage === totalPages); + + const html = parts.join(''); + top.innerHTML = html; + bottom.innerHTML = html; +} + +function goToPage(page) { + currentPage = page; + filterProducts(false); + window.scrollTo({ top: 0, behavior: 'smooth' }); +} + +function filterProducts(resetPage = true) { + if (resetPage) currentPage = 1; + + const filteredItems = getFilteredItems(); + const totalVisible = filteredItems.length; + const startIndex = (currentPage - 1) * itemsPerPage; + const endIndex = startIndex + itemsPerPage; + const visibleOnPage = new Set(filteredItems.slice(startIndex, endIndex)); + document.querySelectorAll('.category-row').forEach(row => { - const catName = row.getAttribute('data-category'); let hasVisibleItems = false; - + row.querySelectorAll('.product-item').forEach(item => { - const itemName = item.getAttribute('data-name'); - const matchesSearch = itemName.includes(searchVal); - const matchesCategory = currentCategory === 'all' || currentCategory === catName; - - if (matchesSearch && matchesCategory) { + if (visibleOnPage.has(item)) { item.style.display = ''; hasVisibleItems = true; - totalVisible++; } else { item.style.display = 'none'; } }); - - // Hide category title if no items visible + const title = row.previousElementSibling; if (title && title.classList.contains('category-title')) { - if (hasVisibleItems) { - title.style.display = ''; - row.style.display = ''; - } else { - title.style.display = 'none'; - row.style.display = 'none'; - } + title.style.display = hasVisibleItems ? '' : 'none'; + row.style.display = hasVisibleItems ? '' : 'none'; } }); - + const noResultsMsg = document.getElementById('noResultsMsg'); if (noResultsMsg) { noResultsMsg.style.display = totalVisible === 0 ? 'block' : 'none'; } + + renderPagination(totalVisible); } // init updateCartBadge(); +filterProducts(); \ No newline at end of file