174 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.

// Timeframes
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) Grab the serverinjected readings
const data = window.__INITIAL_READINGS__ || [];
if (!data.length) {
console.warn('No initial readings found.');
}
// 2) Map into local array with Date objs & formatted strings
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',
hour: '2-digit',
minute: '2-digit',
hour12: false
}).replace(',', ' @')
};
});
setupUI();
initChart();
updateView();
});
function setupUI() {
// Slider + label
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;
// Metric checkboxes
document.getElementsByName('metric').forEach(cb => {
cb.addEventListener('change', updateView);
});
// Table sorting
document.querySelectorAll('#trends-table thead th.sortable').forEach(th => {
th.addEventListener('click', () => {
const idx = th.cellIndex;
const asc = !th.classList.contains('asc');
const tbody = document.getElementById('trends-table-body');
Array.from(tbody.rows)
.sort((a,b) => asc
? a.cells[idx].textContent.localeCompare(b.cells[idx].textContent, undefined, {numeric:true})
: b.cells[idx].textContent.localeCompare(a.cells[idx].textContent, undefined, {numeric:true})
)
.forEach(r => tbody.appendChild(r));
th.classList.toggle('asc', asc);
});
});
}
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;
// Determine "now" from the latest reading, not the browser clock
const maxEpoch = Math.max(...readings.map(r => r.dateObj.getTime()));
const nowDate = new Date(maxEpoch);
// Compute cutoff
const idx = +document.getElementById('timeframe-slider').value;
const { unit, count } = tfConfig[idx];
const cutoff = (idx === 4)
? new Date(0) // "All Time"
: subtract(nowDate, count, unit);
// Filter
const filtered = readings.filter(r => r.dateObj >= cutoff);
// Which metrics?
const selected = Array.from(document.getElementsByName('metric'))
.filter(cb => cb.checked)
.map(cb => cb.value);
// Update chart
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();
// Populate table
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);
});
}