import { Chart, ChartConfiguration, registerables } from 'chart.js'; import type { MetricACWRData } from '@/types'; // Register all Chart.js components Chart.register(...registerables); let distanceChart: Chart | null = null; let durationChart: Chart | null = null; let tssChart: Chart | null = null; // Common chart styling inspired by Garmin const commonOptions = { responsive: true, maintainAspectRatio: true, plugins: { legend: { display: true, position: 'top' as const, labels: { usePointStyle: true, padding: 15, font: { size: 12, }, }, }, tooltip: { mode: 'index' as const, intersect: false, backgroundColor: 'rgba(255, 255, 255, 0.95)', titleColor: '#1e293b', bodyColor: '#475569', borderColor: '#e2e8f0', borderWidth: 1, padding: 12, displayColors: true, }, }, scales: { x: { grid: { display: false, drawBorder: false, }, ticks: { maxRotation: 45, minRotation: 45, padding: 8, color: '#64748b', font: { size: 10, }, autoSkip: true, maxTicksLimit: 20, }, }, }, }; function createDualAxisChart( canvasId: string, data: MetricACWRData, metricLabel: string, metricUnit: string, barColor: string ): Chart { const canvas = document.getElementById(canvasId) as HTMLCanvasElement; if (!canvas) throw new Error(`Canvas ${canvasId} not found`); const config: ChartConfiguration = { type: 'bar', data: { labels: data.dates, datasets: [ { type: 'bar', label: `Daily ${metricLabel}`, data: data.values, backgroundColor: barColor, borderWidth: 0, borderRadius: 2, barPercentage: 0.8, yAxisID: 'y', }, { type: 'line', label: '7-Day Average', data: data.average7d, borderColor: 'rgba(239, 68, 68, 0.9)', backgroundColor: 'rgba(239, 68, 68, 0.1)', borderWidth: 2, pointRadius: 0, pointHoverRadius: 4, tension: 0.4, fill: false, yAxisID: 'y', }, { type: 'line', label: '28-Day Average', data: data.average28d, borderColor: 'rgba(249, 115, 22, 0.9)', backgroundColor: 'rgba(249, 115, 22, 0.1)', borderWidth: 2, pointRadius: 0, pointHoverRadius: 4, tension: 0.4, fill: false, yAxisID: 'y', }, { type: 'line', label: 'ACWR', data: data.acwr, borderColor: 'rgba(139, 92, 246, 1)', backgroundColor: 'rgba(139, 92, 246, 0.1)', borderWidth: 3, pointRadius: 0, pointHoverRadius: 5, tension: 0.4, fill: false, yAxisID: 'y1', }, ], }, options: { ...commonOptions, scales: { ...commonOptions.scales, y: { type: 'linear', position: 'left', beginAtZero: true, border: { display: false, }, grid: { color: 'rgba(148, 163, 184, 0.1)', }, ticks: { padding: 8, color: '#64748b', font: { size: 11, }, }, title: { display: true, text: `${metricLabel} ${metricUnit}`, color: '#64748b', font: { size: 12, weight: 500, }, }, }, y1: { type: 'linear', position: 'right', beginAtZero: true, suggestedMin: 0, suggestedMax: 2, grid: { drawOnChartArea: false, }, ticks: { padding: 8, color: '#8b5cf6', font: { size: 11, }, }, title: { display: true, text: 'ACWR', color: '#8b5cf6', font: { size: 12, weight: 500, }, }, }, }, interaction: { mode: 'index' as const, intersect: false, }, }, }; return new Chart(canvas, config); } export function createDistanceChart(data: MetricACWRData): void { if (distanceChart) { distanceChart.destroy(); } distanceChart = createDualAxisChart( 'distance-chart', data, 'Distance', '(km)', 'rgba(59, 130, 246, 0.6)' ); } export function createDurationChart(data: MetricACWRData): void { if (durationChart) { durationChart.destroy(); } durationChart = createDualAxisChart( 'duration-chart', data, 'Duration', '(min)', 'rgba(16, 185, 129, 0.6)' ); } export function createTSSChart(data: MetricACWRData): void { if (tssChart) { tssChart.destroy(); } tssChart = createDualAxisChart( 'tss-chart', data, 'TSS', '', 'rgba(245, 158, 11, 0.6)' ); } export function destroyAllCharts(): void { if (distanceChart) { distanceChart.destroy(); distanceChart = null; } if (durationChart) { durationChart.destroy(); durationChart = null; } if (tssChart) { tssChart.destroy(); tssChart = null; } }