import { useMemo } from 'react'; import Highcharts from 'highcharts'; import HighchartsReact from 'highcharts-react-official'; import type { PlayStats } from '@tracearr/shared'; import { ChartSkeleton } from '@/components/ui/skeleton'; interface PlaysChartProps { data: PlayStats[] | undefined; isLoading?: boolean; height?: number; period?: 'day' | 'week' | 'month' | 'year' | 'all' | 'custom'; } export function PlaysChart({ data, isLoading, height = 200, period = 'month' }: PlaysChartProps) { const options = useMemo(() => { if (!data || data.length === 0) { return {}; } return { chart: { type: 'area', height, backgroundColor: 'transparent', style: { fontFamily: 'inherit', }, reflow: true, }, title: { text: undefined, }, credits: { enabled: false, }, legend: { enabled: false, }, xAxis: { categories: data.map((d) => d.date), labels: { style: { color: 'hsl(var(--muted-foreground))', }, formatter: function () { // this.value could be index (number) or category string depending on Highcharts version const categories = this.axis.categories; const categoryValue = typeof this.value === 'number' ? categories[this.value] : this.value; if (!categoryValue) return ''; const date = new Date(categoryValue); if (isNaN(date.getTime())) return ''; if (period === 'year') { // Short month name for yearly view (Dec, Jan, Feb) return date.toLocaleDateString('en-US', { month: 'short' }); } // M/D format for week/month views return `${date.getMonth() + 1}/${date.getDate()}`; }, step: Math.ceil(data.length / 12), // Show ~12 labels }, lineColor: 'hsl(var(--border))', tickColor: 'hsl(var(--border))', }, yAxis: { title: { text: undefined, }, labels: { style: { color: 'hsl(var(--muted-foreground))', }, }, gridLineColor: 'hsl(var(--border))', min: 0, }, plotOptions: { area: { fillColor: { linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, stops: [ [0, 'hsl(var(--primary) / 0.3)'], [1, 'hsl(var(--primary) / 0.05)'], ], }, marker: { enabled: false, states: { hover: { enabled: true, radius: 4, }, }, }, lineWidth: 2, lineColor: 'hsl(var(--primary))', states: { hover: { lineWidth: 2, }, }, threshold: null, }, }, tooltip: { backgroundColor: 'hsl(var(--popover))', borderColor: 'hsl(var(--border))', style: { color: 'hsl(var(--popover-foreground))', }, formatter: function () { // With categories, this.x is the index. Use this.point.category for the actual value // eslint-disable-next-line @typescript-eslint/no-explicit-any const categoryValue = (this as any).point?.category as string | undefined; const date = categoryValue ? new Date(categoryValue) : null; const dateStr = date && !isNaN(date.getTime()) ? date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }) : 'Unknown'; return `${dateStr}
Plays: ${this.y}`; }, }, series: [ { type: 'area', name: 'Plays', data: data.map((d) => d.count), }, ], responsive: { rules: [ { condition: { maxWidth: 400, }, chartOptions: { xAxis: { labels: { style: { fontSize: '9px', }, step: Math.ceil(data.length / 6), }, }, yAxis: { labels: { style: { fontSize: '9px', }, }, }, }, }, ], }, }; }, [data, height, period]); if (isLoading) { return ; } if (!data || data.length === 0) { return (
No play data available
); } return ( ); }