Web-page-generator / chart.js
sosunakuflom3's picture
Upload 9 files
14d2101 verified
raw
history blame
6.97 kB
//////////////////////////////////////////////////////////
let monthlyChart;
async function renderMonthlyChart(transactionsParam = null) {
const transactions = transactionsParam || await (await fetch('/api/accounting')).json();
const contribution = Array(12).fill(0);
const donation = Array(12).fill(0);
const sale = Array(12).fill(0);
const expenses = Array(12).fill(0);
transactions.forEach(tx => {
if (!tx.month) return;
const parts = tx.month.split('-');
if (parts.length !== 2) return;
const monthIndex = parseInt(parts[1], 10) - 1;
if (monthIndex < 0 || monthIndex > 11) return;
if (tx.category === 'contribution') contribution[monthIndex] += tx.amount;
else if (tx.category === 'donation') donation[monthIndex] += tx.amount;
else if (tx.category === 'sale') sale[monthIndex] += tx.amount;
else if (tx.category === 'expense') expenses[monthIndex] += tx.amount;
});
const ctx = document.getElementById('monthlyChart').getContext('2d');
if (monthlyChart) monthlyChart.destroy();
monthlyChart = new Chart(ctx, {
type: 'bar',
data: {
labels: ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'],
datasets: [
{ label: 'Contribution', data: contribution, backgroundColor: 'rgba(75,192,192,0.7)' },
{ label: 'Donation', data: donation, backgroundColor: 'rgba(54,162,235,0.7)' },
{ label: 'Sale', data: sale, backgroundColor: 'rgba(255,206,86,0.7)' },
{ label: 'Expense', data: expenses, backgroundColor: 'rgba(255,99,132,0.7)' }
]
},
options: { responsive: true, plugins: { legend: { position: 'top' }, title: { display: true, text: 'Monthly Income & Expenses by Category' } }, scales: { y: { beginAtZero: true } } }
});
}
async function loadTransactions() {
const res = await fetch('/api/accounting', { cache: 'no-store' });
let transactions = await res.json();
if (filters.start || filters.end) {
const startDate = filters.start ? new Date(filters.start + "-01") : null;
const endDate = filters.end ? new Date(filters.end + "-01") : null;
transactions = transactions.filter(tx => {
if (!tx.month) return false;
const txDate = new Date(tx.month + "-01");
return (!startDate || txDate >= startDate) && (!endDate || txDate <= endDate);
});
}
const tbody = document.getElementById('accountingBody');
tbody.innerHTML = transactions.map((t, i) => `
<tr class="${t.category === 'expense' ? 'expense' : 'income'}">
<td>${i + 1}</td>
<td>${t.payerType === 'member' ? t.memberId : t.payerName}</td>
<td>${t.payerType}</td>
<td>${t.month}</td>
<td>${t.amount.toFixed(2)}</td>
<td>${t.category}</td>
<td>${t.description || ''}</td>
</tr>
`).join('');
updateAccountingSummary(transactions);
renderMonthlyChart(transactions);
renderIncomeExpenseChart(transactions);
}
// Call this on page load and after adding a transaction
renderMonthlyChart();
//////////////////////////////////////////
let filters = { start: null, end: null };
async function loadTransactions() {
const res = await fetch('/api/accounting');
let transactions = await res.json();
// Apply filter if set
if (filters.start || filters.end) {
transactions = transactions.filter(tx => {
if (!tx.month) return false;
const txDate = new Date(tx.month + "-01");
const startDate = filters.start ? new Date(filters.start + "-01") : null;
const endDate = filters.end ? new Date(filters.end + "-01") : null;
return (!startDate || txDate >= startDate) &&
(!endDate || txDate <= endDate);
});
}
const tbody = document.getElementById('accountingBody');
tbody.innerHTML = transactions
.map((t, i) => `
<tr class="${t.category === 'expense' ? 'expense' : 'income'}">
<td>${i + 1}</td>
<td>${t.payerType === 'member' ? t.memberId : t.payerName}</td>
<td>${t.payerType}</td>
<td>${t.month}</td>
<td>${t.amount}</td>
<td>${t.category}</td>
<td>${t.description || ''}</td>
</tr>
`).join('');
// Refresh charts/summary with filtered data
updateAccountingSummary(transactions);
renderMonthlyChart(transactions);
renderIncomeExpenseChart(transactions);
}
let incomeExpenseChart;
async function renderIncomeExpenseChart(transactionsParam = null) {
const transactions = transactionsParam || await (await fetch('/api/accounting')).json();
let totalIncome = 0, totalExpenses = 0;
transactions.forEach(tx => {
if (tx.category === 'expense') totalExpenses += tx.amount;
else totalIncome += tx.amount;
});
const ctx = document.getElementById('incomeExpenseChart').getContext('2d');
if (incomeExpenseChart) incomeExpenseChart.destroy();
incomeExpenseChart = new Chart(ctx, {
type: 'pie',
data: {
labels: ['Income', 'Expenses'],
datasets: [{
data: [totalIncome, totalExpenses],
backgroundColor: ['rgba(75,192,192,0.7)', 'rgba(255,99,132,0.7)']
}]
},
options: { responsive: true, plugins: { legend: { position: 'top' }, title: { display: true, text: 'Income vs Expenses' } } }
});
}
document.getElementById('applyFilters').addEventListener('click', () => {
filters.start = document.getElementById('filterStart').value || null;
filters.end = document.getElementById('filterEnd').value || null;
loadTransactions();
});
document.getElementById('clearFilters').addEventListener('click', () => {
filters.start = null;
filters.end = null;
document.getElementById('filterStart').value = '';
document.getElementById('filterEnd').value = '';
loadTransactions();
});
document.getElementById("exportPdf").addEventListener("click", async () => {
const { jsPDF } = window.jspdf;
const pdf = new jsPDF("p", "pt", "a4");
// Title
pdf.setFontSize(18);
pdf.text("Church Accounting Report", 40, 40);
// Capture summary table
const summaryElement = document.getElementById("summaryTable");
const summaryCanvas = await html2canvas(summaryElement);
const summaryImg = summaryCanvas.toDataURL("image/png");
pdf.addImage(summaryImg, "PNG", 40, 60, 500, 0);
pdf.addPage();
// Capture monthly chart
const chartCanvas = document.getElementById("monthlyChart");
const chartImg = chartCanvas.toDataURL("image/png");
pdf.text("Monthly Breakdown", 40, 40);
pdf.addImage(chartImg, "PNG", 40, 60, 500, 300);
pdf.addPage();
// Capture income vs expense pie
const pieCanvas = document.getElementById("incomeExpenseChart");
const pieImg = pieCanvas.toDataURL("image/png");
pdf.text("Income vs Expense", 40, 40);
pdf.addImage(pieImg, "PNG", 40, 60, 400, 300);
// Save file
pdf.save("accounting-report.pdf");
});