@@ -3749,7 +3754,13 @@ document.addEventListener('DOMContentLoaded', function() {
$rid = (int)$_SESSION['show_receipt_id'];
unset($_SESSION['trigger_receipt_modal']);
?>
- showReceipt(= $rid ?>);
+ document.addEventListener('DOMContentLoaded', function() {
+ if (typeof showReceipt === 'function') {
+ showReceipt(= $rid ?>);
+ } else {
+ console.error('showReceipt function not found');
+ }
+ });
@@ -3824,21 +3835,44 @@ document.addEventListener('DOMContentLoaded', function() {
const row = document.createElement('tr');
row.className = 'item-row';
- const currentInvoiceType = window.invoiceType || 'sale';
+
+ // Determine invoice type from the form context
+ let currentInvoiceType = 'sale';
+ const form = tableBody.closest('form');
+ if (form) {
+ const typeInput = form.querySelector('input[name="type"]');
+ if (typeInput) {
+ currentInvoiceType = typeInput.value;
+ } else if (window.invoiceType) {
+ currentInvoiceType = window.invoiceType;
+ }
+ } else if (window.invoiceType) {
+ currentInvoiceType = window.invoiceType;
+ }
+
const price = customData ? customData.unit_price : (currentInvoiceType === 'sale' ? item.sale_price : item.purchase_price);
const qty = customData ? customData.quantity : 1;
const vatRate = item.vat_rate || 0;
+ const escapeHtml = (unsafe) => {
+ return String(unsafe)
+ .replace(/&/g, "&")
+ .replace(//g, ">")
+ .replace(/"/g, """)
+ .replace(/'/g, "'");
+ };
+
row.innerHTML = `
`;
@@ -3881,54 +3915,145 @@ document.addEventListener('DOMContentLoaded', function() {
window.initInvoiceForm = function(searchInputId, suggestionsId, tableBodyId, grandTotalId, subtotalId, totalVatId) {
const searchInput = document.getElementById(searchInputId);
- const suggestions = document.getElementById(suggestionsId);
+ let suggestions = document.getElementById(suggestionsId);
const tableBody = document.getElementById(tableBodyId);
const grandTotalEl = document.getElementById(grandTotalId);
const subtotalEl = document.getElementById(subtotalId);
const totalVatEl = document.getElementById(totalVatId);
- if (!searchInput || !tableBody) return;
+ if (!searchInput) { console.error('Search input not found:', searchInputId); return; }
+ if (!tableBody) { console.error('Table body not found:', tableBodyId); return; }
+
+ // Move suggestions to body to avoid z-index/overflow issues in modals
+ if (suggestions && suggestions.parentNode !== document.body) {
+ if (suggestions.parentNode) suggestions.parentNode.removeChild(suggestions);
+ document.body.appendChild(suggestions);
+ suggestions.style.position = 'absolute';
+ suggestions.style.zIndex = '10000'; // Ensure it is above everything
+ suggestions.style.width = Math.max(searchInput.offsetWidth, 300) + 'px'; // Initial width
+ suggestions.style.maxHeight = '300px';
+ suggestions.style.overflowY = 'auto';
+ suggestions.style.backgroundColor = 'white';
+ suggestions.style.border = '1px solid #ccc';
+ suggestions.style.borderRadius = '0.25rem';
+ suggestions.style.boxShadow = '0 0.5rem 1rem rgba(0, 0, 0, 0.15)';
+ }
+
+ const positionSuggestions = () => {
+ if (!suggestions || suggestions.style.display === 'none') return;
+ const rect = searchInput.getBoundingClientRect();
+ const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
+ const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
+
+ suggestions.style.top = (rect.bottom + scrollTop) + 'px';
+ suggestions.style.left = (rect.left + scrollLeft) + 'px';
+ suggestions.style.width = Math.max(rect.width, 300) + 'px';
+ };
+
+ window.addEventListener('resize', positionSuggestions);
+ window.addEventListener('scroll', positionSuggestions, true);
+
+ const escapeHtml = (unsafe) => {
+ return String(unsafe)
+ .replace(/&/g, "&")
+ .replace(//g, ">")
+ .replace(/"/g, """)
+ .replace(/'/g, "'");
+ };
let timeout = null;
+ // Prevent form submission on Enter key - Aggressive Fix
+ const handleEnterKey = function(e) {
+ if (e.key === 'Enter' || e.keyCode === 13) {
+ e.preventDefault();
+ e.stopPropagation();
+ e.stopImmediatePropagation();
+
+ // If suggestions are visible, click the first one
+ if (suggestions && suggestions.style.display !== 'none') {
+ const firstBtn = suggestions.querySelector('button');
+ if (firstBtn) firstBtn.click();
+ }
+ return false;
+ }
+ };
+ // Use capture phase to intercept before bubbling
+ searchInput.addEventListener('keydown', handleEnterKey, true);
+ searchInput.addEventListener('keypress', handleEnterKey, true);
+
searchInput.addEventListener('input', function() {
clearTimeout(timeout);
const q = this.value.trim();
+
if (q.length < 1) {
if (suggestions) suggestions.style.display = 'none';
return;
}
+ // Show loading state
+ if (suggestions) {
+ suggestions.innerHTML = '
Searching...
';
+ suggestions.style.display = 'block';
+ positionSuggestions();
+ }
+
timeout = setTimeout(() => {
+ console.log(`Searching for: ${q}`);
fetch(`index.php?action=search_items&q=${encodeURIComponent(q)}`)
.then(res => {
- if (!res.ok) throw new Error('Network response was not ok');
- return res.json();
+ if (!res.ok) throw new Error('Network response: ' + res.statusText);
+ return res.text();
+ })
+ .then(text => {
+ try {
+ return JSON.parse(text);
+ } catch (e) {
+ console.error('JSON Parse Error:', text);
+ throw new Error('Invalid JSON response');
+ }
})
.then(data => {
+ console.log('Search results:', data);
if (!suggestions) return;
suggestions.innerHTML = '';
- if (data.length > 0) {
+ if (data && data.length > 0) {
data.forEach(item => {
const btn = document.createElement('button');
btn.type = 'button';
- btn.className = 'list-group-item list-group-item-action';
+ btn.className = 'list-group-item list-group-item-action p-2';
btn.innerHTML = `
-
-
${item.sku} - ${item.name_en} / ${item.name_ar}
-
Stock: ${item.stock_quantity}
+
+
+
${escapeHtml(item.sku)}
+
${escapeHtml(item.name_en)}
+
+
${parseFloat(item.stock_quantity).toFixed(2)}
`;
- btn.onclick = () => window.addItemToTable(item, tableBody, searchInput, suggestions, grandTotalEl, subtotalEl, totalVatEl);
+ btn.addEventListener('click', (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ window.addItemToTable(item, tableBody, searchInput, suggestions, grandTotalEl, subtotalEl, totalVatEl);
+ suggestions.style.display = 'none';
+ });
suggestions.appendChild(btn);
});
suggestions.style.display = 'block';
+ positionSuggestions();
} else {
- suggestions.style.display = 'none';
+ suggestions.innerHTML = '
No items found
';
+ suggestions.style.display = 'block';
+ positionSuggestions();
}
})
.catch(err => {
console.error('Search error:', err);
- if (suggestions) suggestions.style.display = 'none';
+ if (suggestions) {
+ suggestions.innerHTML = '
Error searching items
';
+ suggestions.style.display = 'block';
+ positionSuggestions();
+ }
});
}, 300);
});
@@ -3938,16 +4063,53 @@ document.addEventListener('DOMContentLoaded', function() {
suggestions.style.display = 'none';
}
});
+
+ // Hide on modal close
+ document.querySelectorAll('.modal').forEach(m => {
+ m.addEventListener('hide.bs.modal', () => {
+ if (suggestions) suggestions.style.display = 'none';
+ });
+ // Update position on modal scroll
+ m.addEventListener('scroll', positionSuggestions);
+ });
};
window.invoiceType = '= in_array($page, ["sales", "quotations"]) ? "sale" : ($page === "purchases" ? "purchase" : "") ?>';
- initInvoiceForm('productSearchInput', 'searchSuggestions', 'invoiceItemsTableBody', 'grandTotal', 'subtotal', 'totalVat');
- initInvoiceForm('editProductSearchInput', 'editSearchSuggestions', 'editInvoiceItemsTableBody', 'edit_grandTotal', 'edit_subtotal', 'edit_totalVat');
+ document.addEventListener('DOMContentLoaded', function() {
+ initInvoiceForm('productSearchInput', 'searchSuggestions', 'invoiceItemsTableBody', 'grandTotal', 'subtotal', 'totalVat');
+ initInvoiceForm('editProductSearchInput', 'editSearchSuggestions', 'editInvoiceItemsTableBody', 'edit_grandTotal', 'edit_subtotal', 'edit_totalVat');
- // Quotation Form Logic
- window.initInvoiceForm('quotProductSearchInput', 'quotSearchSuggestions', 'quotItemsTableBody', 'quot_grand_display', 'quot_subtotal_display', 'quot_vat_display');
- window.initInvoiceForm('editQuotProductSearchInput', 'editQuotSearchSuggestions', 'editQuotItemsTableBody', 'edit_quot_grand_display', 'edit_quot_subtotal_display', 'edit_quot_vat_display');
+ // Quotation Form Logic
+ initInvoiceForm('quotProductSearchInput', 'quotSearchSuggestions', 'quotItemsTableBody', 'quot_grand_display', 'quot_subtotal_display', 'quot_vat_display');
+ initInvoiceForm('editQuotProductSearchInput', 'editQuotSearchSuggestions', 'editQuotItemsTableBody', 'edit_quot_grand_display', 'edit_quot_subtotal_display', 'edit_quot_vat_display');
+
+ // Global safeguard: Prevent form submission on Enter for search inputs
+ document.querySelectorAll('.modal form').forEach(form => {
+ // Prevent submission if triggered by search input
+ form.addEventListener('submit', function(e) {
+ const active = document.activeElement;
+ if (active && active.tagName === 'INPUT' && active.id && active.id.toLowerCase().includes('searchinput')) {
+ e.preventDefault();
+ e.stopPropagation();
+ return false;
+ }
+ });
+
+ // Prevent Keydown/Keypress bubbling up to form submission
+ const preventEnter = function(e) {
+ if ((e.key === 'Enter' || e.keyCode === 13) && e.target.tagName === 'INPUT') {
+ if (e.target.id && e.target.id.toLowerCase().includes('searchinput')) {
+ e.preventDefault();
+ e.stopPropagation();
+ return false;
+ }
+ }
+ };
+ form.addEventListener('keydown', preventEnter, true); // Capture phase
+ form.addEventListener('keypress', preventEnter, true); // Capture phase
+ });
+ });
// Global Actions Handler - Delegation for list buttons
document.addEventListener('click', function(e) {
diff --git a/test_search_setup.php b/test_search_setup.php
new file mode 100644
index 0000000..00dae94
--- /dev/null
+++ b/test_search_setup.php
@@ -0,0 +1,14 @@
+
diff --git a/uploads/items/item_6993ed186c662.jfif b/uploads/items/item_6993ed186c662.jfif
new file mode 100644
index 0000000..d918c28
Binary files /dev/null and b/uploads/items/item_6993ed186c662.jfif differ