/** * ComparisonChart Component * Multi-school comparison chart using Chart.js */ 'use client'; import { Line } from 'react-chartjs-2'; import { Chart as ChartJS, CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend, ChartOptions, } from 'chart.js'; import type { ComparisonData } from '@/lib/types'; import { CHART_COLORS, formatAcademicYear } from '@/lib/utils'; // Register Chart.js components ChartJS.register( CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend ); interface ComparisonChartProps { comparisonData: Record; metric: string; metricLabel: string; } export function ComparisonChart({ comparisonData, metric, metricLabel }: ComparisonChartProps) { // Get all schools and their data const schools = Object.entries(comparisonData); if (schools.length === 0) { return
No data available
; } // Get years from first school (assuming all schools have same years) const years = schools[0][1].yearly_data.map((d) => d.year).sort((a, b) => a - b); // Create datasets for each school const datasets = schools.map(([urn, data], index) => { const schoolInfo = data.school_info; const color = CHART_COLORS[index % CHART_COLORS.length]; return { label: schoolInfo.school_name, data: years.map((year) => { const yearData = data.yearly_data.find((d) => d.year === year); if (!yearData) return null; return yearData[metric as keyof typeof yearData] as number | null; }), borderColor: color, backgroundColor: color.replace('rgb', 'rgba').replace(')', ', 0.1)'), tension: 0.3, spanGaps: true, }; }); const chartData = { labels: years.map(formatAcademicYear), datasets, }; // Determine if metric is a progress score or percentage const isProgressScore = metric.includes('progress'); const isPercentage = metric.includes('pct') || metric.includes('rate'); const options: ChartOptions<'line'> = { responsive: true, maintainAspectRatio: false, interaction: { mode: 'index' as const, intersect: false, }, plugins: { legend: { position: 'top' as const, labels: { usePointStyle: true, padding: 15, font: { size: 12, }, }, }, title: { display: true, text: `${metricLabel} - Comparison`, font: { size: 16, weight: 'bold', }, padding: { bottom: 20, }, }, tooltip: { backgroundColor: 'rgba(0, 0, 0, 0.8)', padding: 12, titleFont: { size: 14, }, bodyFont: { size: 13, }, callbacks: { label: function (context) { let label = context.dataset.label || ''; if (label) { label += ': '; } if (context.parsed.y !== null) { if (isProgressScore) { label += context.parsed.y.toFixed(1); } else if (isPercentage) { label += context.parsed.y.toFixed(1) + '%'; } else { label += context.parsed.y.toFixed(1); } } else { label += 'N/A'; } return label; }, }, }, }, scales: { y: { type: 'linear' as const, display: true, title: { display: true, text: isPercentage ? 'Percentage (%)' : isProgressScore ? 'Progress Score' : 'Value', font: { size: 12, weight: 'bold', }, }, ...(isPercentage && { min: 0, max: 100, }), grid: { color: 'rgba(0, 0, 0, 0.05)', }, }, x: { grid: { display: false, }, title: { display: true, text: 'Year', font: { size: 12, weight: 'bold', }, }, }, }, }; return ; }