diff --git a/public/app.js b/public/app.js index 35735c2..abed82d 100644 --- a/public/app.js +++ b/public/app.js @@ -8,6 +8,66 @@ let currentCustomerId = null; let itemCounter = 0; let currentLogoFile = null; +// Alpine.js Customer Search Component +function customerSearch(type) { + return { + search: '', + selectedId: '', + selectedName: '', + open: false, + highlighted: 0, + + get filteredCustomers() { + if (!this.search) { + return customers.slice(0, 50); // Show first 50 if no search + } + const searchLower = this.search.toLowerCase(); + return customers.filter(c => + c.name.toLowerCase().includes(searchLower) || + c.city.toLowerCase().includes(searchLower) || + (c.account_number && c.account_number.includes(searchLower)) + ).slice(0, 50); // Limit to 50 results + }, + + selectCustomer(customer) { + this.selectedId = customer.id; + this.selectedName = customer.name; + this.search = customer.name; + this.open = false; + this.highlighted = 0; + }, + + highlightNext() { + if (this.highlighted < this.filteredCustomers.length - 1) { + this.highlighted++; + } + }, + + highlightPrev() { + if (this.highlighted > 0) { + this.highlighted--; + } + }, + + selectHighlighted() { + if (this.filteredCustomers[this.highlighted]) { + this.selectCustomer(this.filteredCustomers[this.highlighted]); + } + }, + + reset() { + this.search = ''; + this.selectedId = ''; + this.selectedName = ''; + this.open = false; + this.highlighted = 0; + } + }; +} + +// Make it globally available for Alpine +window.customerSearch = customerSearch; + // Initialize app document.addEventListener('DOMContentLoaded', () => { loadCustomers(); @@ -129,7 +189,6 @@ async function loadCustomers() { const response = await fetch('/api/customers'); customers = await response.json(); renderCustomers(); - updateCustomerDropdown(); } catch (error) { console.error('Error loading customers:', error); alert('Error loading customers'); @@ -151,17 +210,6 @@ function renderCustomers() { `).join(''); } -function updateCustomerDropdown() { - const quoteSelect = document.getElementById('quote-customer'); - const invoiceSelect = document.getElementById('invoice-customer'); - - const options = '' + - customers.map(c => ``).join(''); - - if (quoteSelect) quoteSelect.innerHTML = options; - if (invoiceSelect) invoiceSelect.innerHTML = options; -} - function openCustomerModal(customerId = null) { currentCustomerId = customerId; const modal = document.getElementById('customer-modal'); @@ -289,7 +337,28 @@ async function openQuoteModal(quoteId = null) { const response = await fetch(`/api/quotes/${quoteId}`); const data = await response.json(); + // Set customer in Alpine component + const customer = customers.find(c => c.id === data.quote.customer_id); + if (customer) { + // Find the Alpine component and update it + const customerInput = document.querySelector('#quote-modal input[placeholder="Search customer..."]'); + if (customerInput) { + // Trigger Alpine to update + customerInput.value = customer.name; + customerInput.dispatchEvent(new Event('input')); + + // Set the values directly on the Alpine component + const alpineData = Alpine.$data(customerInput.closest('[x-data]')); + if (alpineData) { + alpineData.search = customer.name; + alpineData.selectedId = customer.id; + alpineData.selectedName = customer.name; + } + } + } + document.getElementById('quote-customer').value = data.quote.customer_id; + const dateOnly = data.quote.quote_date.split('T')[0]; document.getElementById('quote-date').value = dateOnly; document.getElementById('quote-tax-exempt').checked = data.quote.tax_exempt; @@ -326,40 +395,71 @@ function addQuoteItem(item = null) { const itemsDiv = document.getElementById('quote-items'); const itemDiv = document.createElement('div'); - itemDiv.className = 'grid grid-cols-12 gap-3 items-start mb-3'; + itemDiv.className = 'border border-gray-300 rounded-lg mb-3 bg-white'; itemDiv.id = `quote-item-${itemId}`; + itemDiv.setAttribute('x-data', `{ open: ${item ? 'false' : 'true'} }`); + + // Get preview text + const previewQty = item ? item.quantity : ''; + const previewAmount = item ? item.amount : '$0.00'; + let previewDesc = 'New item'; + if (item && item.description) { + const temp = document.createElement('div'); + temp.innerHTML = item.description; + previewDesc = temp.textContent.substring(0, 60) + (temp.textContent.length > 60 ? '...' : ''); + } itemDiv.innerHTML = ` -