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; }
= h(tr('لم يتم العثور على نتائج', 'No results found')) ?>
+
+
+
+
+
@@ -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 = = json_encode(tr('0 منتج', '0 products')) ?>;
+ 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 = `= h(tr('عرض', 'Showing')) ?> ${start}-${end} = h(tr('من', 'of')) ?> ${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