171 lines
5.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// public/scripts/trends.js
// Timeframe configurations
const tfConfig = [
{ unit: 'hours', count: 24, label: 'Last 24 Hours' },
{ unit: 'days', count: 7, label: 'Last 7 Days' },
{ unit: 'weeks', count: 4, label: 'Last 4 Weeks' },
{ unit: 'months', count: 12, label: 'Last 12 Months' },
{ unit: 'years', count: 1, label: 'All Time' }
];
let readings = [];
let chart;
// Subtract helper
function subtract(date, count, unit) {
const d = new Date(date);
switch (unit) {
case 'hours': d.setHours(d.getHours() - count); break;
case 'days': d.setDate(d.getDate() - count); break;
case 'weeks': d.setDate(d.getDate() - 7 * count); break;
case 'months': d.setMonth(d.getMonth() - count); break;
case 'years': d.setFullYear(d.getFullYear() - count); break;
}
return d;
}
document.addEventListener('DOMContentLoaded', () => {
// 1) Use serverinjected data
const data = window.__INITIAL_READINGS__ || [];
readings = data.map(r => {
const dt = new Date(r.epoch_ms);
return {
stationDockDoor: r.stationDockDoor,
location: r.location,
temperature: r.temperature,
humidity: r.humidity,
heatIndex: r.heatIndex,
dateObj: dt,
formattedTs: dt.toLocaleString('en-US', {
timeZone: 'America/New_York',
month: 'numeric',
day: 'numeric',
year: '2-digit',
hour12: false,
hour: '2-digit',
minute: '2-digit'
}).replace(',', ' @')
};
});
setupUI();
initChart();
updateView();
});
function setupUI() {
const slider = document.getElementById('timeframe-slider');
const label = document.getElementById('timeframe-label');
slider.addEventListener('input', () => {
label.textContent = tfConfig[slider.value].label;
updateView();
});
label.textContent = tfConfig[slider.value].label;
document.getElementsByName('metric').forEach(cb => {
cb.addEventListener('change', updateView);
});
document.querySelectorAll('#trends-table thead th.sortable')
.forEach(th => {
th.addEventListener('click', () => {
const asc = !th.classList.contains('asc');
document.querySelectorAll('#trends-table thead th')
.forEach(h => h.classList.remove('asc','desc'));
th.classList.add(asc ? 'asc' : 'desc');
const idx = th.cellIndex;
const tbody = document.getElementById('trends-table-body');
const rows = Array.from(tbody.rows);
rows.sort((a, b) => {
const av = a.cells[idx].textContent;
const bv = b.cells[idx].textContent;
return asc
? av.localeCompare(bv, undefined, { numeric: true })
: bv.localeCompare(av, undefined, { numeric: true });
});
rows.forEach(r => tbody.appendChild(r));
});
});
}
function initChart() {
const ctx = document.getElementById('trend-chart').getContext('2d');
chart = new Chart(ctx, {
type: 'line',
data: { labels: [], datasets: [] },
options: {
scales: {
x: { title: { display: true, text: 'Time (EST)' } },
y: { title: { display: true, text: 'Value' } }
},
interaction: { mode: 'index', intersect: false },
plugins: { legend: { position: 'top' } },
maintainAspectRatio: false
}
});
}
function updateView() {
if (!chart) return;
// Use latest reading time as “now”
const maxEpoch = readings.reduce((m, r) => Math.max(m, r.dateObj.getTime()), 0);
const now = new Date(maxEpoch);
const idx = +document.getElementById('timeframe-slider').value;
const { unit, count } = tfConfig[idx];
const cutoff = (idx === tfConfig.length - 1)
? new Date(0)
: subtract(now, count, unit);
const filtered = readings.filter(r => r.dateObj >= cutoff);
const selected = Array.from(document.getElementsByName('metric'))
.filter(cb => cb.checked)
.map(cb => cb.value);
chart.data.labels = filtered.map(r => r.formattedTs);
chart.data.datasets = [];
if (selected.includes('temperature')) {
chart.data.datasets.push({
label: 'Temperature (°F)',
data: filtered.map(r => r.temperature),
tension: 0.3
});
}
if (selected.includes('humidity')) {
chart.data.datasets.push({
label: 'Humidity (%)',
data: filtered.map(r => r.humidity),
tension: 0.3
});
}
if (selected.includes('heatIndex')) {
chart.data.datasets.push({
label: 'Heat Index (°F)',
data: filtered.map(r => r.heatIndex),
tension: 0.3
});
}
chart.update();
const tbody = document.getElementById('trends-table-body');
tbody.innerHTML = '';
filtered.forEach(r => {
const tr = document.createElement('tr');
tr.innerHTML = `
<td>${r.formattedTs}</td>
<td>${r.temperature.toFixed(1)}</td>
<td>${r.humidity.toFixed(1)}</td>
<td>${r.heatIndex.toFixed(2)}</td>
<td>${r.stationDockDoor}</td>
<td>${r.location}</td>
`;
tbody.appendChild(tr);
});
}