This commit is contained in:
Andreas Knuth 2026-01-22 10:59:05 -06:00
parent 209292a61a
commit f892450914
1 changed files with 108 additions and 42 deletions

View File

@ -306,10 +306,32 @@ function addQuoteItem(item = null) {
const itemsDiv = document.getElementById('quote-items'); const itemsDiv = document.getElementById('quote-items');
const itemDiv = document.createElement('div'); const itemDiv = document.createElement('div');
itemDiv.className = 'grid grid-cols-12 gap-3 items-start'; itemDiv.className = 'border border-gray-300 rounded-lg mb-3';
itemDiv.id = `item-${itemId}`; itemDiv.id = `item-${itemId}`;
// Create summary text
const summaryQty = item ? item.quantity : '';
const summaryDesc = item ? (item.description.replace(/<[^>]*>/g, '').substring(0, 50) + '...') : 'New item';
const summaryAmount = item ? item.amount : '';
itemDiv.innerHTML = ` itemDiv.innerHTML = `
<!-- Summary Header (always visible) -->
<div class="item-header bg-gray-50 px-4 py-3 cursor-pointer flex items-center justify-between" onclick="toggleItem(${itemId})">
<div class="flex items-center space-x-4 flex-1">
<span class="text-sm font-medium text-gray-700">Qty: <span class="item-summary-qty">${summaryQty}</span></span>
<span class="text-sm text-gray-600 flex-1 truncate item-summary-desc">${summaryDesc}</span>
<span class="text-sm font-medium text-gray-900">$<span class="item-summary-amount">${summaryAmount}</span></span>
</div>
<div class="flex items-center space-x-2">
<svg class="item-chevron w-5 h-5 text-gray-500 transition-transform" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
</svg>
</div>
</div>
<!-- Expandable Content (hidden by default) -->
<div class="item-content hidden">
<div class="grid grid-cols-12 gap-3 items-start p-4">
<div class="col-span-1"> <div class="col-span-1">
<label class="block text-xs font-medium text-gray-700 mb-1">Qty</label> <label class="block text-xs font-medium text-gray-700 mb-1">Qty</label>
<input type="text" data-item="${itemId}" data-field="quantity" <input type="text" data-item="${itemId}" data-field="quantity"
@ -348,6 +370,8 @@ function addQuoteItem(item = null) {
× ×
</button> </button>
</div> </div>
</div>
</div>
`; `;
itemsDiv.appendChild(itemDiv); itemsDiv.appendChild(itemDiv);
@ -375,6 +399,7 @@ function addQuoteItem(item = null) {
// Save HTML content on change // Save HTML content on change
quill.on('text-change', () => { quill.on('text-change', () => {
hiddenInput.value = quill.root.innerHTML; hiddenInput.value = quill.root.innerHTML;
updateItemSummary(itemId);
updateTotals(); updateTotals();
}); });
@ -396,12 +421,17 @@ function addQuoteItem(item = null) {
const amount = qty * rateValue; const amount = qty * rateValue;
amountInput.value = amount.toFixed(2); amountInput.value = amount.toFixed(2);
} }
updateItemSummary(itemId);
updateTotals(); updateTotals();
}; };
// Add event listeners for auto-calculation // Add event listeners for auto-calculation and summary update
qtyInput.addEventListener('input', calculateAmount); qtyInput.addEventListener('input', calculateAmount);
rateInput.addEventListener('input', calculateAmount); rateInput.addEventListener('input', calculateAmount);
amountInput.addEventListener('input', () => {
updateItemSummary(itemId);
updateTotals();
});
// Add event listeners for totals update // Add event listeners for totals update
itemDiv.querySelectorAll('.item-input, .item-amount').forEach(input => { itemDiv.querySelectorAll('.item-input, .item-amount').forEach(input => {
@ -438,6 +468,41 @@ function removeQuoteItem(itemId) {
updateTotals(); updateTotals();
} }
function toggleItem(itemId) {
const itemDiv = document.getElementById(`item-${itemId}`);
const content = itemDiv.querySelector('.item-content');
const chevron = itemDiv.querySelector('.item-chevron');
if (content.classList.contains('hidden')) {
content.classList.remove('hidden');
chevron.style.transform = 'rotate(180deg)';
} else {
content.classList.add('hidden');
chevron.style.transform = 'rotate(0deg)';
}
}
function updateItemSummary(itemId) {
const itemDiv = document.getElementById(`item-${itemId}`);
const qtyInput = itemDiv.querySelector('[data-field="quantity"]');
const amountInput = itemDiv.querySelector('[data-field="amount"]');
const descEditor = itemDiv.querySelector('.item-description-editor');
// Update summary displays
const summaryQty = itemDiv.querySelector('.item-summary-qty');
const summaryDesc = itemDiv.querySelector('.item-summary-desc');
const summaryAmount = itemDiv.querySelector('.item-summary-amount');
if (summaryQty) summaryQty.textContent = qtyInput.value || '0';
if (summaryAmount) summaryAmount.textContent = amountInput.value || '0.00';
if (summaryDesc && descEditor.quillInstance) {
const plainText = descEditor.quillInstance.getText().trim();
const preview = plainText.substring(0, 60) + (plainText.length > 60 ? '...' : '');
summaryDesc.textContent = preview || 'New item';
}
}
function updateTotals() { function updateTotals() {
const items = getQuoteItems(); const items = getQuoteItems();
const taxExempt = document.getElementById('quote-tax-exempt').checked; const taxExempt = document.getElementById('quote-tax-exempt').checked;
@ -474,17 +539,18 @@ function getQuoteItems() {
const itemDivs = document.querySelectorAll('#quote-items > div'); const itemDivs = document.querySelectorAll('#quote-items > div');
itemDivs.forEach(div => { itemDivs.forEach(div => {
const descEditor = div.querySelector('.item-description-editor'); const content = div.querySelector('.item-content');
const descEditor = content.querySelector('.item-description-editor');
const descriptionHTML = descEditor && descEditor.quillInstance const descriptionHTML = descEditor && descEditor.quillInstance
? descEditor.quillInstance.root.innerHTML ? descEditor.quillInstance.root.innerHTML
: ''; : '';
const item = { const item = {
quantity: div.querySelector('[data-field="quantity"]').value, quantity: content.querySelector('[data-field="quantity"]').value,
description: descriptionHTML, description: descriptionHTML,
rate: div.querySelector('[data-field="rate"]').value, rate: content.querySelector('[data-field="rate"]').value,
amount: div.querySelector('[data-field="amount"]').value, amount: content.querySelector('[data-field="amount"]').value,
is_tbd: div.querySelector('[data-field="is_tbd"]').checked is_tbd: content.querySelector('[data-field="is_tbd"]').checked
}; };
items.push(item); items.push(item);
}); });