`;
};
const renderSavingsBreakdownChart = (results) => {
const ctx = document.getElementById('savingsBreakdownChart').getContext('2d');
if (savingsBreakdownChart) savingsBreakdownChart.destroy();
savingsBreakdownChart = new Chart(ctx, {
type: 'pie',
data: {
labels: ['Patient Travel', 'Reduced No-Shows', 'Admin Efficiency'],
datasets: [{
data: [results.patientTravelSavings, results.noShowSavings, results.adminEfficiencySavings],
backgroundColor: ['#3b82f6', '#10b981', '#f59e0b'],
}]
},
options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'bottom' } } }
});
};
const renderCumulativeRoiChart = (results) => {
const ctx = document.getElementById('cumulativeRoiChart').getContext('2d');
const year1Cum = results.year1Net;
const year2Cum = year1Cum + results.netAnnualSavings;
const year3Cum = year2Cum + results.netAnnualSavings;
if (cumulativeRoiChart) cumulativeRoiChart.destroy();
cumulativeRoiChart = new Chart(ctx, {
type: 'line',
data: {
labels: ['Year 1', 'Year 2', 'Year 3'],
datasets: [{
label: 'Cumulative Net Savings ($)',
data: [year1Cum, year2Cum, year3Cum],
borderColor: '#2563eb',
backgroundColor: 'rgba(37, 99, 235, 0.1)',
fill: true,
tension: 0.1
}]
},
options: { responsive: true, maintainAspectRatio: false, scales: { y: { ticks: { callback: value => formatCurrency(value) } } } }
});
};
const renderRoiTable = (results) => {
const container = document.getElementById('roi-table-container');
const tableData = [
{ category: 'Savings', item: 'Patient Travel Savings', value: formatCurrency(results.patientTravelSavings) },
{ category: 'Savings', item: 'Reduced No-Show Savings', value: formatCurrency(results.noShowSavings) },
{ category: 'Savings', item: 'Admin Efficiency Savings', value: formatCurrency(results.adminEfficiencySavings) },
{ category: 'Savings', item: '
Total Annual Savings', value: `
${formatCurrency(results.totalAnnualSavings)}` },
{ category: 'Costs', item: 'One-Time Implementation Costs', value: formatCurrency(roiInputs.oneTimeCosts) },
{ category: 'Costs', item: 'Annual Operating Costs', value: formatCurrency(results.totalAnnualOperatingCosts) },
{ category: 'Costs', item: '
Total Year 1 Costs', value: `
${formatCurrency(results.totalYear1Costs)}` },
{ category: 'Results', item: 'Net Annual Savings (Year 2+)', value: formatCurrency(results.netAnnualSavings) },
{ category: 'Results', item: '
Net Year 1 Result', value: `
${formatCurrency(results.year1Net)}` },
];
let tableHtml = `
| Category |
Line Item |
Annual Value |
`;
tableData.forEach(row => {
tableHtml += `
| ${row.category} |
${row.item} |
${row.value} |
`;
});
tableHtml += `
`;
container.innerHTML = tableHtml;
};
// --- EVENT HANDLERS ---
document.getElementById('recalculate-btn').addEventListener('click', () => {
renderAll();
switchTab('dashboard');
});
document.getElementById('download-pdf-btn').addEventListener('click', () => {
const exportArea = document.getElementById('dashboard-export-area');
const mainTitleEl = document.querySelector('#roi-tool-container h1');
if (!exportArea || !mainTitleEl) return;
const btn = document.getElementById('download-pdf-btn');
const originalBtnText = btn.innerHTML;
btn.disabled = true;
btn.innerHTML = `
Processing...`;
html2canvas(exportArea, { scale: 2, useCORS: true, windowWidth: exportArea.scrollWidth, windowHeight: exportArea.scrollHeight })
.then(canvas => {
const { jsPDF } = window.jspdf;
const imgData = canvas.toDataURL('image/png');
const pdf = new jsPDF({ orientation: 'landscape', unit: 'mm', format: 'a4' });
const pageMargin = 15;
const pdfPageWidth = pdf.internal.pageSize.getWidth();
const pdfPageHeight = pdf.internal.pageSize.getHeight();
const pdfContentWidth = pdfPageWidth - (2 * pageMargin);
const canvasAspectRatio = canvas.width / canvas.height;
const pdfImgHeight = pdfContentWidth / canvasAspectRatio;
let yPos = pageMargin;
pdf.setFontSize(18).setFont('helvetica', 'bold');
pdf.text(mainTitleEl.innerText, pdfPageWidth / 2, yPos, { align: 'center' });
yPos += 12;
let heightLeft = pdfImgHeight;
let positionOnCanvas = 0;
pdf.addImage(imgData, 'PNG', pageMargin, yPos, pdfContentWidth, pdfImgHeight);
heightLeft -= (pdfPageHeight - yPos - pageMargin);
while (heightLeft > 0) {
positionOnCanvas -= (pdfPageHeight - (2 * pageMargin));
pdf.addPage();
pdf.addImage(imgData, 'PNG', pageMargin, positionOnCanvas + pageMargin, pdfContentWidth, pdfImgHeight);
heightLeft -= (pdfPageHeight - (2 * pageMargin));
}
pdf.save(`telehealth-roi-dashboard-${new Date().toISOString().slice(0,10)}.pdf`);
})
.catch(err => { console.error("PDF generation failed:", err); alert("Error generating PDF."); })
.finally(() => {
btn.disabled = false;
btn.innerHTML = originalBtnText;
});
});
// --- INITIALIZATION ---
const switchTab = (tabName) => {
const tabDashboardBtn = document.getElementById('tab-dashboard-btn');
const tabConfigBtn = document.getElementById('tab-config-btn');
const tabDashboardContent = document.getElementById('tab-dashboard-content');
const tabConfigContent = document.getElementById('tab-config-content');
const prevTabBtn = document.getElementById('prev-tab-btn');
const nextTabBtn = document.getElementById('next-tab-btn');
if (tabName === 'dashboard') {
tabDashboardBtn.classList.add('active');
tabConfigBtn.classList.remove('active');
tabDashboardContent.classList.remove('hidden');
tabConfigContent.classList.add('hidden');
prevTabBtn.disabled = true;
nextTabBtn.disabled = false;
} else {
tabDashboardBtn.classList.remove('active');
tabConfigBtn.classList.add('active');
tabDashboardContent.classList.add('hidden');
tabConfigContent.classList.remove('hidden');
prevTabBtn.disabled = false;
nextTabBtn.disabled = true;
}
};
document.getElementById('tab-dashboard-btn').addEventListener('click', () => switchTab('dashboard'));
document.getElementById('tab-config-btn').addEventListener('click', () => switchTab('config'));
document.getElementById('prev-tab-btn').addEventListener('click', () => switchTab('dashboard'));
document.getElementById('next-tab-btn').addEventListener('click', () => switchTab('config'));
switchTab('dashboard');
renderAll();
});