Income
${Object.entries(incomeByCategory).map(([cat, amt]) => `
${cat}${formatCurrency(amt)}
`).join('')}
Total Income${formatCurrency(income)}
Expenses
${Object.entries(expensesByCategory).map(([cat, amt]) => `
${cat}${formatCurrency(amt)}
`).join('')}
Total Expenses${formatCurrency(expenses)}
Net Operating Income
${formatCurrency(noi)}
`;
elements.reportOutput.innerHTML = reportHTML;
};
const downloadReportPdf = () => {
const reportContent = document.getElementById('pdf-report-content');
if (!reportContent) {
alert('Please generate a report first.');
return;
}
const { jsPDF } = window.jspdf;
html2canvas(reportContent, { scale: 2 }).then(canvas => {
const imgData = canvas.toDataURL('image/png');
const pdf = new jsPDF({ orientation: 'p', unit: 'mm', format: 'a4' });
const pdfWidth = pdf.internal.pageSize.getWidth();
const pdfHeight = pdf.internal.pageSize.getHeight();
const canvasWidth = canvas.width;
const canvasHeight = canvas.height;
const ratio = canvasWidth / canvasHeight;
let imgWidth = pdfWidth - 20;
let imgHeight = imgWidth / ratio;
if (imgHeight > pdfHeight - 20) {
imgHeight = pdfHeight - 20;
imgWidth = imgHeight * ratio;
}
const x = (pdfWidth - imgWidth) / 2;
pdf.addImage(imgData, 'PNG', x, 10, imgWidth, imgHeight);
pdf.save('Property-Report.pdf');
});
};
// --- EVENT LISTENERS ---
elements.tabs.forEach((tab, index) => tab.addEventListener('click', () => {
currentTab = index;
elements.tabs.forEach(t => t.classList.remove('active'));
tab.classList.add('active');
elements.tabContents.forEach(c => c.classList.remove('active'));
elements.tabContents[index].classList.add('active');
}));
elements.addPropertyBtn.addEventListener('click', () => {
elements.propertyForm.reset();
document.getElementById('property-id').value = '';
document.getElementById('property-modal-title').textContent = 'Add Property';
showModal(elements.propertyModal);
});
elements.propertiesTableBody.addEventListener('click', (e) => {
const id = e.target.dataset.id;
if (e.target.classList.contains('edit-property-btn')) {
const property = db.properties.find(p => p.id == id);
document.getElementById('property-id').value = property.id;
document.getElementById('property-name').value = property.name;
document.getElementById('property-address').value = property.address;
document.getElementById('property-modal-title').textContent = 'Edit Property';
showModal(elements.propertyModal);
}
if (e.target.classList.contains('delete-property-btn')) {
if (confirm('Are you sure? This will also delete all associated transactions.')) {
db.properties = db.properties.filter(p => p.id != id);
db.transactions = db.transactions.filter(t => t.propertyId != id);
saveToLocalStorage();
renderProperties();
renderTransactions();
renderDashboard();
}
}
});
elements.propertyForm.addEventListener('submit', handlePropertyFormSubmit);
document.querySelectorAll('.modal-cancel-btn').forEach(btn => btn.addEventListener('click', () => {
hideModal(elements.propertyModal);
hideModal(elements.transactionModal);
}));
elements.addIncomeBtn.addEventListener('click', () => openTransactionModal('income'));
elements.addExpenseBtn.addEventListener('click', () => openTransactionModal('expense'));
elements.transactionForm.addEventListener('submit', handleTransactionFormSubmit);
elements.transactionsTableBody.addEventListener('click', (e) => {
const id = e.target.dataset.id;
if (e.target.classList.contains('edit-transaction-btn')) {
const transaction = db.transactions.find(t => t.id == id);
openTransactionModal(transaction.type, transaction);
}
if (e.target.classList.contains('delete-transaction-btn')) {
if (confirm('Are you sure you want to delete this transaction?')) {
db.transactions = db.transactions.filter(t => t.id != id);
saveToLocalStorage();
renderTransactions();
renderDashboard();
}
}
});
elements.generateReportBtn.addEventListener('click', generateReport);
elements.downloadPdfBtn.addEventListener('click', downloadReportPdf);
// --- INITIALIZATION ---
if (db.properties.length === 0) { // Add sample data if empty
db.properties = [
{ id: 1, name: 'Maple Street Duplex', address: '123 Maple St, Anytown, USA' },
{ id: 2, name: 'Oak Avenue Condo', address: '456 Oak Ave, Anytown, USA' }
];
db.transactions = [
{ id: 1, type: 'income', date: `${new Date().getFullYear()}-01-05`, propertyId: 1, category: 'Rent', amount: 1800 },
{ id: 2, type: 'expense', date: `${new Date().getFullYear()}-01-10`, propertyId: 1, category: 'Repairs', amount: 250 },
{ id: 3, type: 'income', date: `${new Date().getFullYear()}-02-05`, propertyId: 2, category: 'Rent', amount: 1200 },
];
saveToLocalStorage();
}
renderProperties();
renderTransactions();
renderDashboard();
populateYearSelect();
generateReport();
});