`;
};
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();
});
