88 lines
3.5 KiB
JavaScript
88 lines
3.5 KiB
JavaScript
import Echo from 'laravel-echo';
|
|
import Pusher from 'pusher-js';
|
|
import { Chart, registerables } from 'chart.js';
|
|
import Sortable from 'sortablejs';
|
|
|
|
Chart.register(...registerables);
|
|
|
|
// Drag-to-reorder tile grid → persists order via $wire.reorder([uuid, …]).
|
|
document.addEventListener('alpine:init', () => {
|
|
window.Alpine.data('sortableGrid', () => ({
|
|
init() {
|
|
Sortable.create(this.$refs.grid, {
|
|
handle: '.drag-handle',
|
|
animation: 150,
|
|
ghostClass: 'opacity-40',
|
|
onEnd: () => {
|
|
const order = [...this.$refs.grid.children].map((c) => c.dataset.uuid).filter(Boolean);
|
|
this.$wire.reorder(order);
|
|
},
|
|
});
|
|
},
|
|
}));
|
|
});
|
|
|
|
// Themed line-chart island (Livewire bundles Alpine). Usage:
|
|
// <div x-data="lineChart(@js($config))"><canvas x-ref="canvas"></canvas></div>
|
|
// where $config = { labels: [...], series: [{ key, label, color, data, axis }], units: {...} }
|
|
document.addEventListener('alpine:init', () => {
|
|
window.Alpine.data('lineChart', (config) => ({
|
|
chart: null,
|
|
init() {
|
|
const css = getComputedStyle(document.documentElement);
|
|
const token = (name, fallback) => (css.getPropertyValue(name).trim() || fallback);
|
|
const grid = token('--color-line', '#1C2846');
|
|
const ink = token('--color-ink-3', '#5E6C8A');
|
|
|
|
this.chart = new Chart(this.$refs.canvas, {
|
|
type: 'line',
|
|
data: {
|
|
labels: config.labels,
|
|
datasets: config.series.map((s) => ({
|
|
label: s.label,
|
|
data: s.data,
|
|
borderColor: token(s.color, '#4FC1FF'),
|
|
backgroundColor: 'transparent',
|
|
borderWidth: 2,
|
|
tension: 0.35,
|
|
pointRadius: 0,
|
|
yAxisID: s.axis || 'y',
|
|
})),
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
interaction: { mode: 'index', intersect: false },
|
|
plugins: { legend: { labels: { color: ink, boxWidth: 10, boxHeight: 10, usePointStyle: true } } },
|
|
scales: {
|
|
x: { ticks: { color: ink, maxTicksLimit: 6 }, grid: { color: grid } },
|
|
y: { position: 'left', ticks: { color: ink }, grid: { color: grid }, beginAtZero: true },
|
|
y1: { position: 'right', ticks: { color: ink }, grid: { drawOnChartArea: false }, beginAtZero: true, max: 100 },
|
|
},
|
|
},
|
|
});
|
|
},
|
|
destroy() {
|
|
this.chart?.destroy();
|
|
},
|
|
}));
|
|
});
|
|
|
|
// Reverb (Pusher protocol), proxied same-origin through nginx (/app, /apps).
|
|
// The browser connects to the page's own host/port — works on localhost and over the LAN
|
|
// with no separate WS port and no host mismatch. Every channel is PRIVATE (/broadcasting/auth).
|
|
const loc = window.location;
|
|
const isHttps = loc.protocol === 'https:';
|
|
const port = loc.port ? Number(loc.port) : (isHttps ? 443 : 80);
|
|
|
|
window.Pusher = Pusher;
|
|
window.Echo = new Echo({
|
|
broadcaster: 'reverb',
|
|
key: import.meta.env.VITE_REVERB_APP_KEY,
|
|
wsHost: loc.hostname,
|
|
wsPort: port,
|
|
wssPort: port,
|
|
forceTLS: isHttps,
|
|
enabledTransports: ['ws', 'wss'],
|
|
});
|