Avg. Period 1 Retention
${avgRetentionP1.toFixed(1)}%
Best Performing Cohort (P1)
${bestCohort.name || 'N/A'}
`;
}
renderCohortHeatmap(processedData) {
let table = `
| Cohort | Initial Size | `;
this.state.periods.forEach(p => table += `${p} | `);
table += `
`;
processedData.forEach(cohort => {
table += `| ${cohort.name} | ${cohort.initialSize.toLocaleString()} | `;
cohort.retentionRates.forEach((rate, i) => {
if (cohort.retained[i] === 0 && i > 0 && cohort.retained[i-1] === 0) {
table += `- | `;
} else {
const color = this.getColorForPercentage(rate);
table += `
${rate.toFixed(1)}%
(${cohort.retained[i].toLocaleString()})
| `;
}
});
table += `
`;
});
table += `
`;
this.dom.cohortTableContainer.innerHTML = table;
}
getColorForPercentage(pct) {
if (pct === 0) return '#ffffff';
// HSL: Hue=140 (green), Saturation=60%, Lightness varies from 90% (low) to 40% (high)
const lightness = 90 - (pct * 0.5); // Maps 0-100 to 90-40
return `hsl(140, 60%, ${lightness}%)`;
}
// --- 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(`${this.state.reportTitle.replace(/\s+/g, '_')}.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 CohortAnalysisDashboard();