Legal Invoice Generator

Legal Invoice Generator

Thank you for your business. Please make payment within 30 days.

`; }; const renderInvoice = () => { invoicePreview.innerHTML = generateInvoiceHTML(); }; // --- FORM & EVENT HANDLING --- addItemForm.addEventListener('submit', (e) => { e.preventDefault(); const desc = document.getElementById('item-desc').value; const qty = parseFloat(document.getElementById('item-qty').value); const rate = parseFloat(document.getElementById('item-rate').value); if(desc && qty > 0 && rate >= 0) { const newItem = { id: Date.now(), desc, qty, rate }; invoiceItems.push(newItem); renderInvoiceItems(); addItemForm.reset(); } }); window.globalFuncs = { deleteItem: (id) => { invoiceItems = invoiceItems.filter(item => item.id !== id); renderInvoiceItems(); } }; // --- TAB NAVIGATION --- window.switchTab = (tabName) => { currentTab = tabName; Object.values(tabs).forEach(tab => tab.classList.remove('active', 'inactive')); tabs[tabName].classList.add('active'); Object.entries(tabs).forEach(([key, tab]) => { if(key !== tabName) tab.classList.add('inactive'); }); Object.values(contents).forEach(content => content.classList.add('hidden')); contents[tabName].classList.remove('hidden'); updateNavButtons(); }; window.navigateTabs = (direction) => { const tabKeys = Object.keys(tabs); const currentIndex = tabKeys.indexOf(currentTab); let newIndex = (direction === 'next') ? currentIndex + 1 : currentIndex - 1; if (newIndex >= 0 && newIndex < tabKeys.length) { if (tabKeys[newIndex] === 'dashboard') { renderInvoice(); } switchTab(tabKeys[newIndex]); } }; const updateNavButtons = () => { const tabKeys = Object.keys(tabs); const currentIndex = tabKeys.indexOf(currentTab); navButtons.prev.disabled = currentIndex === 0; navButtons.next.disabled = currentIndex === tabKeys.length - 1; }; // --- PDF GENERATION --- downloadPdfBtn.addEventListener('click', () => { const { jsPDF } = window.jspdf; const doc = new jsPDF(); const firmName = document.getElementById('firm-name').value; const firmAddress = document.getElementById('firm-address').value; const clientName = document.getElementById('client-name').value; const clientAddress = document.getElementById('client-address').value; const invoiceNumber = document.getElementById('invoice-number').value; const invoiceDate = new Date(document.getElementById('invoice-date').value + 'T00:00:00').toLocaleDateString('en-US'); doc.setFontSize(22); doc.text(firmName, 14, 22); doc.setFontSize(11); doc.text(firmAddress, 14, 30); doc.setFontSize(16); doc.text('INVOICE', 196, 22, { align: 'right' }); doc.text(`Invoice #: ${invoiceNumber}`, 196, 40, { align: 'right' }); doc.text(`Date: ${invoiceDate}`, 196, 46, { align: 'right' }); doc.text('Bill To:', 14, 60); doc.text(clientName, 14, 66); doc.text(clientAddress, 14, 72); const tableColumn = ["Description", "Hours/Qty", "Rate ($)", "Total ($)"]; let tableRows = []; let subtotal = 0; invoiceItems.forEach(item => { const total = item.qty * item.rate; subtotal += total; tableRows.push([item.desc, item.qty, item.rate.toFixed(2), total.toFixed(2)]); }); doc.autoTable({ head: [tableColumn], body: tableRows, startY: 90, headStyles: { fillColor: [79, 70, 229] }, didDrawPage: (data) => { // Add totals at the end of the table doc.setFontSize(12); doc.text(`Subtotal: $${subtotal.toFixed(2)}`, 196, data.cursor.y + 10, { align: 'right' }); doc.setFontSize(14); doc.setFont('helvetica', 'bold'); doc.text(`Total Due: $${subtotal.toFixed(2)}`, 196, data.cursor.y + 18, { align: 'right' }); } }); doc.save(`Invoice_${invoiceNumber}.pdf`); }); // --- INITIALIZATION --- const initialize = () => { document.getElementById('invoice-date').value = new Date().toISOString().split('T')[0]; renderInvoiceItems(); renderInvoice(); updateNavButtons(); updateInvoiceBtn.addEventListener('click', () => { renderInvoice(); switchTab('dashboard'); }); }; initialize(); });
Scroll to Top