invoice-system/public/js/views/quote-view.js

101 lines
3.6 KiB
JavaScript

/**
* quote-view.js — Quote list rendering and actions
* Analog to invoice-view.js
*/
import { formatDate } from '../utils/helpers.js';
let quotes = [];
export async function loadQuotes() {
try {
const response = await fetch('/api/quotes');
quotes = await response.json();
renderQuotes();
} catch (error) {
console.error('Error loading quotes:', error);
}
}
export function getQuotesData() {
return quotes;
}
export function renderQuotes() {
const tbody = document.getElementById('quotes-list');
if (!tbody) return;
tbody.innerHTML = quotes.map(quote => {
const total = quote.has_tbd
? `$${parseFloat(quote.total).toFixed(2)}*`
: `$${parseFloat(quote.total).toFixed(2)}`;
return `
<tr>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">${quote.quote_number}</td>
<td class="px-6 py-4 text-sm text-gray-500">${quote.customer_name || 'N/A'}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${formatDate(quote.quote_date)}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900 font-semibold">${total}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium space-x-2">
<button onclick="window.quoteView.viewPDF(${quote.id})" class="text-green-600 hover:text-green-900">PDF</button>
<button onclick="window.quoteView.convertToInvoice(${quote.id})" class="text-purple-600 hover:text-purple-900">→ Invoice</button>
<button onclick="window.quoteView.edit(${quote.id})" class="text-blue-600 hover:text-blue-900">Edit</button>
<button onclick="window.quoteView.remove(${quote.id})" class="text-red-600 hover:text-red-900">Delete</button>
</td>
</tr>
`;
}).join('');
if (quotes.length === 0) {
tbody.innerHTML = `<tr><td colspan="5" class="px-6 py-8 text-center text-gray-500">No quotes found.</td></tr>`;
}
}
export function viewPDF(id) {
window.open(`/api/quotes/${id}/pdf`, '_blank');
}
export async function edit(id) {
if (typeof window.openQuoteModal === 'function') {
await window.openQuoteModal(id);
}
}
export async function remove(id) {
if (!confirm('Are you sure you want to delete this quote?')) return;
try {
const response = await fetch(`/api/quotes/${id}`, { method: 'DELETE' });
if (response.ok) loadQuotes();
else alert('Error deleting quote');
} catch (error) {
console.error('Error:', error);
alert('Error deleting quote');
}
}
export async function convertToInvoice(quoteId) {
if (!confirm('Convert this quote to an invoice?')) return;
try {
const response = await fetch(`/api/quotes/${quoteId}/convert-to-invoice`, { method: 'POST' });
if (response.ok) {
const invoice = await response.json();
alert(`Invoice ${invoice.invoice_number || '(Draft)'} created successfully!`);
if (window.invoiceView) window.invoiceView.loadInvoices();
if (typeof window.showTab === 'function') window.showTab('invoices');
} else {
const error = await response.json();
alert(error.error || 'Error converting quote to invoice');
}
} catch (error) {
console.error('Error:', error);
alert('Error converting quote to invoice');
}
}
// Expose for onclick handlers
window.quoteView = {
loadQuotes,
renderQuotes,
viewPDF,
edit,
remove,
convertToInvoice
};