PageSourceSearch

https://hotokami.jp/assets/controllers/wants_analytics_chart_controller-bec2db3c.js

js hotokami.jp collected 2026-10-01 12:04:25 UTC 3,503 bytes, 178 lines download raw bytes

1import {Controller} from '@hotwired/stimulus';
2import {
3	Chart,
4	BarController,
5	BarElement,
6	LineController,
7	LineElement,
8	PointElement,
9	CategoryScale,
10	LinearScale,
11	Tooltip,
12	Legend,
13	Filler,
14} from 'chart.js';
15
16Chart.register(
17	BarController,
18	BarElement,
19	LineController,
20	LineElement,
21	PointElement,
22	CategoryScale,
23	LinearScale,
24	Tooltip,
25	Legend,
26	Filler,
27);
28
29export default class extends Controller {
30	static values = {
31		trends: Array,
32		periodKey: String,
33	};
34
35	static targets = ['trendsCanvas'];
36
37	connect() {
38		if (this.hasTrendsCanvasTarget && this.trendsValue?.length) {
39			this._renderTrendsChart();
40		}
41	}
42
43	disconnect() {
44		if (this.trendsChart) {
45			this.trendsChart.destroy();
46		}
47	}
48
49	_renderTrendsChart() {
50		const ctx = this.trendsCanvasTarget.getContext('2d');
51		const data = this.trendsValue;
52		const periodKey = this.periodKeyValue || 'date';
53
54		const labels = data.map(row => row[periodKey]);
55		const wantCounts = data.map(row => row.total_wants || 0);
56		const convertedCounts = data.map(row => row.converted || 0);
57		const deletedCounts = data.map(row => row.deleted || 0);
58		const conversionRates = data.map(row => row.conversion_rate || 0);
59
60		this.trendsChart = new Chart(ctx, {
61			type: 'bar',
62			data: {
63				labels,
64				datasets: [
65					{
66						label: 'Want数',
67						data: wantCounts,
68						backgroundColor: 'rgba(59, 130, 246, 0.7)',
69						borderColor: 'rgba(59, 130, 246, 1)',
70						borderWidth: 1,
71						yAxisID: 'y',
72						order: 3,
73					},
74					{
75						label: '転換数',
76						data: convertedCounts,
77						backgroundColor: 'rgba(34, 197, 94, 0.7)',
78						borderColor: 'rgba(34, 197, 94, 1)',
79						borderWidth: 1,
80						yAxisID: 'y',
81						order: 2,
82					},
83					{
84						label: '削除数',
85						data: deletedCounts,
86						backgroundColor: 'rgba(239, 68, 68, 0.5)',
87						borderColor: 'rgba(239, 68, 68, 1)',
88						borderWidth: 1,
89						yAxisID: 'y',
90						order: 4,
91					},
92					{
93						label: '転換率 (%)',
94						data: conversionRates,
95						type: 'line',
96						borderColor: 'rgba(168, 85, 247, 1)',
97						backgroundColor: 'rgba(168, 85, 247, 0.1)',
98						fill: true,
99						tension: 0.3,
100						pointRadius: 3,
101						pointHoverRadius: 5,
102						yAxisID: 'y1',
103						order: 1,
104					},
105				],
106			},
107			options: {
108				responsive: true,
109				maintainAspectRatio: false,
110				interaction: {
111					mode: 'index',
112					intersect: false,
113				},
114				plugins: {
115					legend: {
116						position: 'top',
117						labels: {
118							boxWidth: 12,
119							padding: 15,
120						},
121					},
122					tooltip: {
123						callbacks: {
124							label(context) {
125								const label = context.dataset.label || '';
126								const value = context.parsed.y;
127
128								if (label.includes('%')) {
129									return `${label}: ${value}%`;
130								}
131
132								return `${label}: ${value.toLocaleString()}`;
133							},
134						},
135					},
136				},
137				scales: {
138					x: {
139						grid: {
140							display: false,
141						},
142						ticks: {
143							maxRotation: 45,
144							minRotation: 0,
145						},
146					},
147					y: {
148						type: 'linear',
149						display: true,
150						position: 'left',
151						beginAtZero: true,
152						title: {
153							display: true,
154							text: '件数',
155						},
156						grid: {
157							color: 'rgba(0, 0, 0, 0.05)',
158						},
159					},
160					y1: {
161						type: 'linear',
162						display: true,
163						position: 'right',
164						beginAtZero: true,
165						max: 100,
166						title: {
167							display: true,
168							text: '転換率 (%)',
169						},
170						grid: {
171							drawOnChartArea: false,
172						},
173					},
174				},
175			},
176		});
177	}
178}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.