diff --git a/index.php b/index.php index 5d253b8..ab02ec1 100644 --- a/index.php +++ b/index.php @@ -393,17 +393,20 @@ if (isset($_GET['action']) || isset($_POST['action'])) { } if ($action === 'search_items') { - file_put_contents('search_debug.log', date('Y-m-d H:i:s') . " - search_items call: q=" . ($_GET['q'] ?? '') . "\n", FILE_APPEND); header('Content-Type: application/json'); $q = $_GET['q'] ?? ''; - if (strlen($q) < 1) { - echo json_encode([]); - exit; - } + $searchTerm = "%$q%"; - $stmt = db()->prepare("SELECT * FROM stock_items WHERE name_en LIKE ? OR name_ar LIKE ? OR sku LIKE ? LIMIT 15"); + // Optimization: Fetch items with promotional prices calculated + $stmt = db()->prepare("SELECT * FROM stock_items WHERE name_en LIKE ? OR name_ar LIKE ? OR sku LIKE ? LIMIT 50"); $stmt->execute([$searchTerm, $searchTerm, $searchTerm]); - echo json_encode($stmt->fetchAll(PDO::FETCH_ASSOC)); + $items = $stmt->fetchAll(PDO::FETCH_ASSOC); + + foreach ($items as &$item) { + $item['original_price'] = (float)$item['sale_price']; + $item['sale_price'] = getPromotionalPrice($item); + } + echo json_encode($items); exit; } @@ -6233,7 +6236,8 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System'; $registers = db()->query("SELECT * FROM cash_registers WHERE status = 'active'")->fetchAll(); $allow_zero_stock_sell = ($data['settings']['allow_zero_stock_sell'] ?? '1') === '1'; - $sql = "SELECT * FROM stock_items ORDER BY name_en ASC"; + // Optimization: Only load top 50 products initially to prevent heavy load + $sql = "SELECT * FROM stock_items ORDER BY id DESC LIMIT 50"; $products_raw = db()->query($sql)->fetchAll(PDO::FETCH_ASSOC); $products = []; foreach ($products_raw as $p) { @@ -7249,38 +7253,80 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System'; } }; - document.querySelectorAll('.product-card').forEach(card => { - card.addEventListener('click', () => { - const product = { - id: parseInt(card.dataset.id), - nameEn: card.dataset.nameEn, - nameAr: card.dataset.nameAr, - price: parseFloat(card.dataset.price), - stock_quantity: parseFloat(card.dataset.stockQuantity), - vatRate: parseFloat(card.dataset.vatRate) || 0 - }; - cart.add(product); - }); - }); - - document.getElementById('productSearch').addEventListener('input', (e) => { - const q = e.target.value.toLowerCase(); + function attachProductClickListeners() { document.querySelectorAll('.product-grid .product-card').forEach(card => { - const name = card.dataset.nameEn.toLowerCase() + ' ' + card.dataset.nameAr.toLowerCase(); - const sku = card.dataset.sku.toLowerCase(); - if (name.includes(q) || sku.includes(q)) { - card.style.display = 'flex'; - } else { - card.style.display = 'none'; - } + card.onclick = () => { + const product = { + id: parseInt(card.dataset.id), + nameEn: card.dataset.nameEn, + nameAr: card.dataset.nameAr, + price: parseFloat(card.dataset.price), + sku: card.dataset.sku, + stock_quantity: parseFloat(card.dataset.stockQuantity), + vatRate: parseFloat(card.dataset.vatRate) || 0 + }; + cart.add(product); + }; }); + } + attachProductClickListeners(); + + let searchTimeout = null; + document.getElementById('productSearch').addEventListener('input', (e) => { + clearTimeout(searchTimeout); + const q = e.target.value.trim(); + searchTimeout = setTimeout(async () => { + try { + const resp = await fetch('index.php?action=search_items&q=' + encodeURIComponent(q)); + const products = await resp.json(); + renderProductGrid(products); + } catch (err) { console.error(err); } + }, 300); }); - document.getElementById('barcodeInput').addEventListener('keypress', (e) => { + function renderProductGrid(products) { + const grid = document.getElementById('productGrid'); + const lang = document.documentElement.lang || 'en'; + if (products.length === 0) { + grid.innerHTML = '
No products found.
'; + return; + } + + grid.innerHTML = products.map(p => { + const salePrice = parseFloat(p.sale_price); + const originalPrice = parseFloat(p.original_price || p.sale_price); + const img = p.image_path ? `${p.name_en}` : ` +
+ +
`; + const promoHtml = salePrice < originalPrice ? `OMR ${originalPrice.toFixed(3)}` : ''; + + return ` +
+ ${img} +
+
${p.name_en}
+
${p.name_ar}
+
+
${p.sku}
+
+
+ ${promoHtml} + OMR ${salePrice.toFixed(3)} +
+ ${parseFloat(p.stock_quantity)} left +
+
`; + }).join(''); + attachProductClickListeners(); + } + + document.getElementById('barcodeInput').addEventListener('keypress', async (e) => { if (e.key === 'Enter') { const barcode = e.target.value.trim(); if (!barcode) return; + // First check local DOM const card = Array.from(document.querySelectorAll('.product-card')).find(c => c.dataset.sku === barcode); if (card) { const product = { @@ -7294,24 +7340,31 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System'; }; cart.add(product); e.target.value = ''; - Swal.fire({ - toast: true, - position: 'top-end', - icon: 'success', - title: 'Added: ' + product.nameEn, - showConfirmButton: false, - timer: 1000 - }); + Swal.fire({ toast: true, position: 'top-end', icon: 'success', title: 'Added: ' + product.nameEn, showConfirmButton: false, timer: 1000 }); } else { - Swal.fire({ - toast: true, - position: 'top-end', - icon: 'error', - title: 'Product not found', - showConfirmButton: false, - timer: 1500 - }); - e.target.select(); + // Not in DOM, check server + try { + const resp = await fetch('index.php?action=search_items&q=' + encodeURIComponent(barcode)); + const products = await resp.json(); + const found = products.find(p => p.sku === barcode); + if (found) { + const product = { + id: found.id, + nameEn: found.name_en, + nameAr: found.name_ar, + price: parseFloat(found.sale_price), + sku: found.sku, + stock_quantity: parseFloat(found.stock_quantity), + vatRate: parseFloat(found.vat_rate) || 0 + }; + cart.add(product); + e.target.value = ''; + Swal.fire({ toast: true, position: 'top-end', icon: 'success', title: 'Added: ' + product.nameEn, showConfirmButton: false, timer: 1000 }); + } else { + Swal.fire({ toast: true, position: 'top-end', icon: 'error', title: 'Product not found', showConfirmButton: false, timer: 1500 }); + e.target.select(); + } + } catch (err) { console.error(err); } } } });