forked from PinSpace-Org/GistPin
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDailyGistsChart.tsx
More file actions
104 lines (94 loc) 路 3.2 KB
/
Copy pathDailyGistsChart.tsx
File metadata and controls
104 lines (94 loc) 路 3.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
'use client';
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
PointElement,
LineElement,
Filler,
Tooltip,
} from 'chart.js';
import type { Plugin, TooltipItem } from 'chart.js';
import { Line } from 'react-chartjs-2';
import { memo, useMemo } from 'react';
import { baseOptions } from '@/lib/chart-config';
import ChartWrapper from './ChartWrapper';
ChartJS.register(CategoryScale, LinearScale, PointElement, LineElement, Filler, Tooltip);
interface DayPoint {
label: string;
fullLabel: string;
count: number;
}
function generateLast30Days(): DayPoint[] {
const now = Date.now();
return Array.from({ length: 30 }, (_, i) => {
const d = new Date(now - (29 - i) * 86_400_000);
const isWeekend = d.getDay() === 0 || d.getDay() === 6;
const count = Math.round(Math.min(200, Math.max(50, 100 + i * 1.5 + (Math.random() - 0.5) * 50 - (isWeekend ? 20 : 0))));
return {
label: d.toLocaleDateString('en-GB', { day: 'numeric', month: 'short' }),
fullLabel: d.toLocaleDateString('en-GB', { weekday: 'short', day: 'numeric', month: 'short' }),
count,
};
});
}
const gradientPlugin: Plugin<'line'> = {
id: 'dailyGistsGradient',
afterLayout(chart) {
const { ctx, chartArea } = chart;
if (!chartArea) return;
const grad = ctx.createLinearGradient(0, chartArea.top, 0, chartArea.bottom);
grad.addColorStop(0, 'rgba(99,102,241,0.45)');
grad.addColorStop(1, 'rgba(99,102,241,0.02)');
chart.data.datasets[0].backgroundColor = grad;
},
};
function DailyGistsChart() {
const points = useMemo(() => generateLast30Days(), []);
const labels = points.map((p) => p.label);
const fullLabels = points.map((p) => p.fullLabel);
const data = {
labels: labels.map((l, i) => (i % 5 === 0 || i === 29 ? l : null)),
datasets: [{
label: 'Gists created',
data: points.map((p) => p.count),
borderColor: 'rgba(99,102,241,0.9)',
borderWidth: 2.5,
backgroundColor: 'rgba(99,102,241,0.35)',
fill: true,
tension: 0.42,
pointRadius: 0,
pointHoverRadius: 6,
pointHoverBackgroundColor: '#6366f1',
pointHoverBorderColor: '#ffffff',
pointHoverBorderWidth: 2,
}],
};
const options = baseOptions({
scales: {
x: { grid: { display: false }, ticks: { maxRotation: 0, color: '#9ca3af', font: { size: 11 } }, border: { color: '#e5e7eb' } },
y: { beginAtZero: false, suggestedMin: 30, grid: { color: 'rgba(0,0,0,0.05)' }, ticks: { color: '#9ca3af', font: { size: 11 }, stepSize: 50 }, border: { display: false } },
},
plugins: {
legend: { display: false },
tooltip: {
backgroundColor: 'rgba(17,24,39,0.9)',
titleColor: '#f9fafb',
bodyColor: '#c7d2fe',
padding: 12,
cornerRadius: 8,
displayColors: false,
callbacks: {
title: (items: TooltipItem<'line'>[]) => fullLabels[items[0].dataIndex],
label: (item: TooltipItem<'line'>) => ` ${(item.raw as number).toLocaleString()} gists`,
},
},
},
});
return (
<ChartWrapper title="Daily Gists (Last 30 Days)">
<Line data={data} options={options} plugins={[gradientPlugin]} />
</ChartWrapper>
);
}
export default memo(DailyGistsChart);