Total Liabilities
$${totals.totalLiabilities.toLocaleString()}
`;
const allocData = { labels: Object.keys(totals.investmentAllocation), datasets: [{ data: Object.values(totals.investmentAllocation), backgroundColor: ['#3b82f6', '#14b8a6', '#8b5cf6'] }] };
if (allocationChart) allocationChart.destroy();
allocationChart = new Chart(document.getElementById('allocation-chart'), { type: 'doughnut', data: allocData, options: { responsive: true, plugins: { legend: { position: 'top' } } } });
const nwData = { labels: ['2023', '2024', '2025'], datasets: [{ label: 'Net Worth', data: [totals.netWorth - 50000, totals.netWorth - 20000, totals.netWorth], backgroundColor: '#14b8a680', borderColor: '#0d9488', tension: 0.1, fill: true }] };
if (networthChart) networthChart.destroy();
networthChart = new Chart(document.getElementById('networth-chart'), { type: 'line', data: nwData, options: { responsive: true, maintainAspectRatio: false, scales: { y: { ticks: { callback: v => `$${(v/1000)}k` } } } } });
}
function renderTables() {
['assets', 'liabilities'].forEach(type => {
const container = document.getElementById(`${type}-table-container`);
const data = wealthData[type];
const rows = data.map(item => `
| ${item.description} ${item.category} | $${item.value.toLocaleString()} |
|
`).join('');
const total = data.reduce((s, i) => s + i.value, 0);
container.innerHTML = `
| Description | Value | |
${rows}
| Total | $${total.toLocaleString()} | |
`;
});
}
function renderGoals() {
const list = document.getElementById('goals-list');
list.innerHTML = wealthData.goals.map((goal, index) => `
${goal}`).join('');
}
// --- MODAL LOGIC ---
function openModal(type, id = null) {
itemTypeInput.value = type;
const isAsset = type === 'assets';
modalTitle.textContent = id ? `Edit ${isAsset ? 'Asset' : 'Liability'}` : `Add ${isAsset ? 'Asset' : 'Liability'}`;
itemCatInput.innerHTML = (isAsset ? ASSET_CATEGORIES : LIABILITY_CATEGORIES).map(c => `
`).join('');
if (id) {
const item = wealthData[type].find(i => i.id == id);
itemIdInput.value = id;
itemDescInput.value = item.description;
itemCatInput.value = item.category;
itemValueInput.value = item.value;
} else {
itemIdInput.value = '';
itemDescInput.value = '';
itemValueInput.value = '';
}
itemModal.classList.remove('hidden');
}
function saveItem() {
const type = itemTypeInput.value, id = itemIdInput.value;
const newItem = {
id: id ? parseInt(id) : Date.now(),
description: itemDescInput.value,
category: itemCatInput.value,
value: parseFloat(itemValueInput.value) || 0
};
if (id) { // Edit
const index = wealthData[type].findIndex(i => i.id == id);
wealthData[type][index] = newItem;
} else { // Add
wealthData[type].push(newItem);
}
itemModal.classList.add('hidden');
renderTables();
}
// --- EVENT LISTENERS ---
document.getElementById('add-asset-btn').addEventListener('click', () => openModal('assets'));
document.getElementById('add-liability-btn').addEventListener('click', () => openModal('liabilities'));
document.getElementById('details-content').addEventListener('click', e => {
if (e.target.classList.contains('edit-item-btn')) openModal(e.target.dataset.type, e.target.dataset.id);
if (e.target.classList.contains('delete-item-btn')) {
const { type, id } = e.target.dataset;
wealthData[type] = wealthData[type].filter(i => i.id != id);
renderTables();
}
});
document.getElementById('add-goal-btn').addEventListener('click', () => {
const input = document.getElementById('goal-input');
if (input.value.trim()) {
wealthData.goals.push(input.value.trim());
input.value = '';
renderGoals();
}
});
document.getElementById('goals-list').addEventListener('click', e => {
if(e.target.classList.contains('delete-goal-btn')) {
wealthData.goals.splice(e.target.dataset.index, 1);
renderGoals();
}
});
saveItemBtn.addEventListener('click', saveItem);
document.getElementById('cancel-item-btn').addEventListener('click', () => itemModal.classList.add('hidden'));
// --- PDF GENERATION ---
async function generatePdfReport() {
downloadPdfBtn.disabled = true;
downloadPdfBtn.textContent = 'Generating...';
const totals = calculateTotals();
const renderTable = (data) => data.map(i => `
| ${i.description} (${i.category}) | $${i.value.toLocaleString()} |
`).join('') + `
| Total | $${data.reduce((s,i) => s + i.value, 0).toLocaleString()} |
`;
const reportHtml = `
NET WORTH
$${totals.netWorth.toLocaleString()}
TOTAL ASSETS
$${totals.totalAssets.toLocaleString()}
TOTAL LIABILITIES
$${totals.totalLiabilities.toLocaleString()}
Investment Allocation
Detailed Breakdown
Assets
| Description | Value |
${renderTable(wealthData.assets)}
Liabilities
| Description | Value |
${renderTable(wealthData.liabilities)}
Financial Goals
${wealthData.goals.map(g => `- ${g}
`).join('')}
`;
const pdfTemplate = document.getElementById('pdf-template');
pdfTemplate.innerHTML = reportHtml;
pdfTemplate.classList.remove('invisible');
new Chart(document.getElementById('pdf-alloc-chart'), { type: 'doughnut', data: allocationChart.data, options: { animation: { duration: 0 }, plugins: { legend: { position: 'right' } } } });
setTimeout(async () => {
try {
const { jsPDF } = window.jspdf;
const pdf = new jsPDF({ orientation: 'p', unit: 'pt', format: 'a4' });
const pages = pdfTemplate.querySelectorAll('.pdf-page');
for (let i = 0; i < pages.length; i++) {
const canvas = await html2canvas(pages[i], { scale: 2 });
const imgData = canvas.toDataURL('image/png');
const pdfWidth = pdf.internal.pageSize.getWidth(), pdfHeight = (canvas.height * pdfWidth) / canvas.width;
if (i > 0) pdf.addPage();
pdf.addImage(imgData, 'PNG', 0, 0, pdfWidth, pdfHeight);
}
pdf.save('Wealth_Management_Report.pdf');
} catch (e) { console.error('PDF Generation Error:', e); } finally {
downloadPdfBtn.disabled = false;
downloadPdfBtn.textContent = 'Download PDF Report';
pdfTemplate.classList.add('invisible');
pdfTemplate.innerHTML = '';
}
}, 500);
}
downloadPdfBtn.addEventListener('click', generatePdfReport);
// --- INITIALIZATION ---
switchTab(0);
renderTables();
renderGoals();
});