/** * Chart Management Module * Handles Chart.js initialization and updates */ class ChartManager { constructor(calculator) { this.calculator = calculator; this.charts = {}; } initializeCharts() { // Check if Chart.js is available if (typeof Chart === 'undefined') { console.error('Chart.js library not loaded. Charts will not be available.'); this.showChartError('Chart.js library failed to load. Please refresh the page.'); return; } try { // Instance Growth Chart const instanceCanvas = document.getElementById('instanceGrowthChart'); if (!instanceCanvas) { console.error('Instance growth chart canvas not found'); return; } const instanceCtx = instanceCanvas.getContext('2d'); this.charts.instanceGrowth = new Chart(instanceCtx, { type: 'line', data: { labels: [], datasets: [] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'top' } }, scales: { y: { beginAtZero: true, title: { display: true, text: 'Total Instances' } }, x: { title: { display: true, text: 'Month' } } } } }); // Revenue Chart const revenueCanvas = document.getElementById('revenueChart'); if (!revenueCanvas) { console.error('Revenue chart canvas not found'); return; } const revenueCtx = revenueCanvas.getContext('2d'); this.charts.revenue = new Chart(revenueCtx, { type: 'line', data: { labels: [], datasets: [] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'top' } }, scales: { y: { beginAtZero: true, title: { display: true, text: 'Cumulative Revenue (CHF)' } }, x: { title: { display: true, text: 'Month' } } } } }); // Cash Flow Chart const cashFlowCanvas = document.getElementById('cashFlowChart'); if (!cashFlowCanvas) { console.error('Cash flow chart canvas not found'); return; } const cashFlowCtx = cashFlowCanvas.getContext('2d'); this.charts.cashFlow = new Chart(cashFlowCtx, { type: 'bar', data: { labels: [], datasets: [] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'top' } }, scales: { y: { title: { display: true, text: 'Monthly Cash Flow (CHF)' } }, x: { title: { display: true, text: 'Month' } } } } }); } catch (error) { console.error('Error initializing charts:', error); this.showChartError('Failed to initialize charts. Please refresh the page.'); } } showChartError(message) { // Show error message in place of charts const chartContainers = ['instanceGrowthChart', 'revenueChart', 'cashFlowChart']; chartContainers.forEach(containerId => { const container = document.getElementById(containerId); if (container) { container.style.display = 'flex'; container.style.justifyContent = 'center'; container.style.alignItems = 'center'; container.style.minHeight = '300px'; container.style.backgroundColor = '#f8f9fa'; container.style.border = '1px solid #dee2e6'; container.style.borderRadius = '4px'; container.innerHTML = `

${message}
`; } }); } updateCharts() { try { const scenarios = Object.keys(this.calculator.results); if (scenarios.length === 0 || !this.charts.instanceGrowth) return; const colors = { conservative: '#28a745', moderate: '#ffc107', aggressive: '#dc3545' }; // Get month labels const maxMonths = Math.max(...scenarios.map(s => this.calculator.monthlyData[s].length)); const monthLabels = Array.from({ length: maxMonths }, (_, i) => `M${i + 1}`); // Update Instance Growth Chart this.charts.instanceGrowth.data.labels = monthLabels; this.charts.instanceGrowth.data.datasets = scenarios.map(scenario => ({ label: this.calculator.scenarios[scenario].name, data: this.calculator.monthlyData[scenario].map(d => d.totalInstances), borderColor: colors[scenario], backgroundColor: colors[scenario] + '20', tension: 0.4 })); this.charts.instanceGrowth.update(); // Update Revenue Chart this.charts.revenue.data.labels = monthLabels; this.charts.revenue.data.datasets = scenarios.map(scenario => ({ label: this.calculator.scenarios[scenario].name + ' (CSP)', data: this.calculator.monthlyData[scenario].map(d => d.cumulativeCSPRevenue), borderColor: colors[scenario], backgroundColor: colors[scenario] + '20', tension: 0.4 })); this.charts.revenue.update(); // Update Cash Flow Chart (show average across scenarios) const avgCashFlow = monthLabels.map((_, monthIndex) => { const monthData = scenarios.map(scenario => this.calculator.monthlyData[scenario][monthIndex]?.cspRevenue || 0 ); return monthData.reduce((sum, val) => sum + val, 0) / monthData.length; }); this.charts.cashFlow.data.labels = monthLabels; this.charts.cashFlow.data.datasets = [{ label: 'Average Monthly CSP Revenue', data: avgCashFlow, backgroundColor: '#007bff' }]; this.charts.cashFlow.update(); } catch (error) { console.error('Error updating charts:', error); } } }