No expenses entered yet.
';
return;
}
const categoryOptions = categories.map(c => `
`).join('');
const tableRows = expenseData.expenses.map(exp => `
| ${exp.description} |
$${exp.amount.toFixed(2)} |
|
`).join('');
container.innerHTML = `
| Description |
Amount |
Category |
${tableRows}
`;
document.querySelectorAll('.category-select').forEach(select => {
select.addEventListener('change', (e) => {
const expenseId = parseInt(e.target.dataset.id);
const newCategory = e.target.value;
const expense = expenseData.expenses.find(ex => ex.id === expenseId);
if (expense) expense.category = newCategory;
});
});
}
function renderReport() {
const summary = {};
categories.forEach(c => summary[c] = 0);
let total = 0;
expenseData.expenses.forEach(exp => {
summary[exp.category] += exp.amount;
total += exp.amount;
});
// Render Summary Table
const summaryTableContainer = document.getElementById('summary-table-container');
const summaryRows = categories.map(cat => {
if (summary[cat] > 0) {
return `
| ${cat} |
$${summary[cat].toFixed(2)} |
${total > 0 ? (summary[cat] / total * 100).toFixed(1) : 0}% |
`;
}
return '';
}).join('');
summaryTableContainer.innerHTML = `
Summary
| Category | Total | % of Total |
${summaryRows}
| Total | $${total.toFixed(2)} | 100% |
`;
// Render Chart
const ctx = document.getElementById('expense-chart').getContext('2d');
const chartData = {
labels: [],
datasets: [{
data: [],
backgroundColor: ['#22c55e', '#3b82f6', '#8b5cf6', '#ec4899', '#f97316', '#eab308', '#64748b', '#ef4444'],
}]
};
categories.forEach(cat => {
if (summary[cat] > 0) {
chartData.labels.push(cat);
chartData.datasets[0].data.push(summary[cat]);
}
});
if (expenseChartInstance) expenseChartInstance.destroy();
expenseChartInstance = new Chart(ctx, {
type: 'doughnut',
data: chartData,
options: { responsive: true, maintainAspectRatio: true, plugins: { legend: { position: 'bottom' } } }
});
}
// --- PDF GENERATION ---
async function generatePdfReport() {
downloadPdfBtn.disabled = true;
downloadPdfBtn.textContent = 'Generating...';
const summary = {};
categories.forEach(c => summary[c] = 0);
let total = 0;
expenseData.expenses.forEach(exp => {
summary[exp.category] += exp.amount;
total += exp.amount;
});
// Prepare chart data for PDF
const pdfChartData = { labels: [], datasets: [{ data: [], backgroundColor: ['#22c55e', '#3b82f6', '#8b5cf6', '#ec4899', '#f97316', '#eab308', '#64748b', '#ef4444'] }]};
categories.forEach(cat => {
if (summary[cat] > 0) {
pdfChartData.labels.push(cat);
pdfChartData.datasets[0].data.push(summary[cat]);
}
});
// Prepare table data for PDF
const summaryTableRows = categories.map(cat => {
if (summary[cat] > 0) {
return `
| ${cat} | $${summary[cat].toFixed(2)} | ${total > 0 ? (summary[cat] / total * 100).toFixed(1) : 0}% |
`;
}
return '';
}).join('');
const detailsHtml = categories.map(cat => {
const items = expenseData.expenses.filter(e => e.category === cat);
if (items.length === 0) return '';
const itemRows = items.map(item => `
| ${item.description} | $${item.amount.toFixed(2)} |
`).join('');
return `
| Description | Amount |
${itemRows}
`;
}).join('');
const reportHtml = `
| Category | Total | % |
${summaryTableRows}| Total | $${total.toFixed(2)} | 100% |
${detailsHtml}
`;
const pdfTemplate = document.getElementById('pdf-template');
pdfTemplate.innerHTML = reportHtml;
pdfTemplate.classList.remove('invisible');
// Render chart on the hidden canvas
const pdfCtx = document.getElementById('pdf-chart').getContext('2d');
new Chart(pdfCtx, { type: 'doughnut', data: pdfChartData, options: { animation: { duration: 0 }, plugins: { legend: { display: false } } } });
// A small delay to ensure chart is rendered before html2canvas runs
setTimeout(async () => {
try {
const { jsPDF } = window.jspdf;
const canvas = await html2canvas(pdfTemplate.querySelector('.pdf-report-container'), { scale: 2 });
const imgData = canvas.toDataURL('image/png');
const pdf = new jsPDF({ orientation: 'p', unit: 'pt', format: 'a4' });
const pdfWidth = pdf.internal.pageSize.getWidth();
const pdfHeight = (canvas.height * pdfWidth) / canvas.width;
pdf.addImage(imgData, 'PNG', 0, 0, pdfWidth, pdfHeight);
pdf.save('Expense_Report.pdf');
} catch(e) {
console.error('PDF Generation Error:', e);
} finally {
downloadPdfBtn.disabled = false;
downloadPdfBtn.textContent = 'Download Expense Report';
pdfTemplate.classList.add('invisible');
pdfTemplate.innerHTML = '';
}
}, 500);
}
downloadPdfBtn.addEventListener('click', generatePdfReport);
// --- INITIALIZATION ---
switchTab(0);
});