Collections Performance Dashboard

Manage Outstanding Invoices

Account NameInvoice IDAmount ($)Due DateStatusAction

Collections Performance Dashboard

As of

Accounts Receivable Aging

Invoice Status

    Top Overdue Accounts

    Account NameAmount Overdue ($)Days OverdueStatus

    ${metrics.avgDaysDelinquent.toFixed(0)}

    `; } renderAgingChart(agingData) { const maxAmount = Math.max(...Object.values(agingData)); this.dom.agingChart.innerHTML = Object.entries(agingData).map(([bucket, amount]) => `
    $${(amount/1000).toFixed(1)}k
    ${bucket} days
    `).join(''); } renderStatusChart(statusData) { const total = Object.values(statusData).reduce((sum, v) => sum + v, 0); const colors = { 'Open': '#1e88e5', 'Paid': '#43a047', 'Promise to Pay': '#fdd835', 'Disputed': '#fb8c00' }; let gradientString = 'conic-gradient('; let currentPercentage = 0; this.dom.statusLegend.innerHTML = ''; Object.entries(statusData).forEach(([status, count]) => { const percentage = total > 0 ? (count / total) * 100 : 0; const color = colors[status]; if (percentage > 0) { gradientString += `${color} ${currentPercentage}% ${currentPercentage + percentage}%, `; currentPercentage += percentage; } this.dom.statusLegend.innerHTML += `
  • ${status} (${count})
  • `; }); gradientString = gradientString.slice(0, -2) + ')'; this.dom.statusChart.style.background = gradientString; } renderTopOverdueTable(overdueAccounts) { this.dom.topOverdueBody.innerHTML = overdueAccounts .sort((a,b) => b.daysOverdue - a.daysOverdue) .slice(0, 5) // Top 5 .map(acc => ` ${acc.name} $${acc.amount.toLocaleString()} ${acc.daysOverdue} ${acc.status} `).join(''); if(overdueAccounts.length === 0) { this.dom.topOverdueBody.innerHTML = 'No overdue accounts. Well done!'; } } // --- PDF & Navigation --- openTab(event, tabName) { this.state.activeTab = tabName; this.updateUI(); } navigateTabs(dir) { const i = this.TABS_ORDER.indexOf(this.state.activeTab); let n = i; if (dir === 'next' && i < this.TABS_ORDER.length - 1) n++; if (dir === 'prev' && i > 0) n--; this.openTab(null, this.TABS_ORDER[n]); } async generatePdf() { if(this.state.activeTab !== 'dashboard') { this.openTab(null, 'dashboard'); await new Promise(res => setTimeout(res, 50)); } this.dom.downloadPdfBtn.disabled = true; this.dom.downloadPdfBtn.textContent = 'Generating...'; this.dom.container.classList.add('pdf-export-mode'); try { const { jsPDF } = window.jspdf; const canvas = await html2canvas(this.dom.pdfOutput, { scale: 2, useCORS: true, backgroundColor: '#ffffff' }); const imgData = canvas.toDataURL('image/png'); const pdf = new jsPDF({ orientation: 'landscape', unit: 'pt', format: 'a4' }); const margin = 40; const pdfWidth = pdf.internal.pageSize.getWidth(); const imgWidth = pdfWidth - margin * 2; const imgHeight = canvas.height * imgWidth / canvas.width; pdf.addImage(imgData, 'PNG', margin, margin, imgWidth, imgHeight); pdf.save(`Collections_Performance_Dashboard.pdf`); } catch(e) { console.error(e); alert('Error generating PDF.'); } finally { this.dom.container.classList.remove('pdf-export-mode'); this.dom.downloadPdfBtn.disabled = false; this.dom.downloadPdfBtn.textContent = 'Download Dashboard as PDF'; } } } const app = new CollectionsDashboard();
    Scroll to Top