Account Health Dashboard

Health Score Configuration

Set the weight for each metric to calculate the overall health score. Total must equal 100%.

Total: 100%

Account Roster

Account NameMRR ($)Renewal DateProduct UsageSatisfaction (NPS)Critical TicketsRelationshipAction

Account Health Portfolio Overview

Health Distribution

All Accounts Overview

${atRisk}

Accounts at Risk

${healthy}

Healthy Accounts

`; // Render distribution chart this.renderDistributionChart(processedAccounts); // Render dashboard table this.dom.dashboardTableHead.innerHTML = `AccountHealth ScoreStatusMRRRenewal DateUpcoming`; this.dom.dashboardTableBody.innerHTML = processedAccounts .sort((a,b) => a.healthScore - b.healthScore) .map(acc => { const { status, color } = this.getHealthStatus(acc.healthScore); const renewalDate = new Date(acc.renewal); const daysToRenewal = Math.ceil((renewalDate - new Date()) / (1000 * 60 * 60 * 24)); let upcoming = '-'; if (daysToRenewal <= 30 && daysToRenewal >= 0) upcoming = `In ${daysToRenewal} days`; else if (daysToRenewal <= 90 && daysToRenewal > 0) upcoming = `In ${daysToRenewal} days`; return ` ${acc.name}
${acc.healthScore.toFixed(0)}%
${status} $${acc.mrr.toLocaleString()} ${acc.renewal} ${upcoming} `; }).join(''); } renderDistributionChart(accounts) { const buckets = { poor: 0, ok: 0, good: 0 }; accounts.forEach(acc => { if (acc.healthScore < 50) buckets.poor++; else if (acc.healthScore < 80) buckets.ok++; else buckets.good++; }); const max = Math.max(...Object.values(buckets)) || 1; this.dom.healthDistributionChart.innerHTML = ` ${Object.keys(buckets).map(key => `

${key.charAt(0).toUpperCase() + key.slice(1)} (${buckets[key]})

`).join('')} `; } // --- Calculation Logic --- calculateHealthScore(account) { if (Object.values(this.state.weights).reduce((s,w) => s+w, 0) !== 100) return 0; const scores = { usage: { 'High': 100, 'Medium': 60, 'Low': 10 }[account.usage], satisfaction: Math.max(0, (account.satisfaction + 100) / 2), tickets: Math.max(0, 100 - (account.tickets * 20)), relationship: { 'Strong': 100, 'Neutral': 60, 'Weak': 20 }[account.relationship] }; let totalScore = 0; for (const key in this.METRICS) { totalScore += scores[key] * (this.state.weights[key] / 100); } return totalScore; } getHealthStatus(score) { if (score < 50) return { status: 'At Risk', color: 'var(--health-poor)' }; if (score < 80) return { status: 'Needs Attention', color: 'var(--health-ok)' }; return { status: 'Healthy', color: 'var(--health-good)' }; } // --- Navigation & PDF --- 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)); } const { jsPDF } = window.jspdf; this.dom.downloadPdfBtn.disabled = true; this.dom.downloadPdfBtn.textContent = 'Generating...'; try { 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 pdfHeight = pdf.internal.pageSize.getHeight(); const imgWidth = pdfWidth - margin * 2; const imgHeight = canvas.height * imgWidth / canvas.width; let heightLeft = imgHeight; let position = margin; pdf.addImage(imgData, 'PNG', margin, position, imgWidth, imgHeight); heightLeft -= (pdfHeight - margin * 2); while (heightLeft > 0) { position = -heightLeft - margin; pdf.addPage(); pdf.addImage(imgData, 'PNG', margin, position, imgWidth, imgHeight); heightLeft -= pdfHeight; } pdf.save(`Account_Health_Dashboard.pdf`); } catch (e) { console.error(e); alert('Error generating PDF.'); } finally { this.dom.downloadPdfBtn.disabled = false; this.dom.downloadPdfBtn.textContent = 'Download Dashboard as PDF'; } } } const app = new AccountHealthDashboard();
Scroll to Top