diff --git a/public/scripts/trends.js b/public/scripts/trends.js
index 8ad064d..e51857b 100644
--- a/public/scripts/trends.js
+++ b/public/scripts/trends.js
@@ -3,9 +3,11 @@ function average(arr) {
return arr.reduce((a, b) => a + b, 0) / arr.length;
}
-// period definitions
+// interval mappings
const periodKeys = ['hourly','daily','weekly','monthly','yearly'];
const periodLabels = ['Hourly','Daily','Weekly','Monthly','Yearly'];
+
+// bucket definitions
const periodConfig = {
hourly: { keyFn: r => r.timestamp.slice(0,13), labelFn: k => k.replace('T',' ') },
daily: { keyFn: r => r.timestamp.slice(0,10), labelFn: k => k },
@@ -19,22 +21,62 @@ const periodConfig = {
};
// global Chart.js instance
-let trendChart;
+let trendChart, dataTable;
-// fetch all readings
+// fetch readings from server
async function fetchReadings() {
const res = await fetch('/api/readings');
return res.ok ? res.json() : [];
}
-// build or update chart
+// determine direction based on dock door #
+function getDirection(dock) {
+ return ( (dock>=124 && dock<=138) || (dock>=202 && dock<=209) )
+ ? 'Inbound'
+ : 'Outbound';
+}
+
+// render DataTable
+function renderTable(allReadings) {
+ const tbody = $('#trendTable tbody').empty();
+ allReadings.forEach(r => {
+ const ts = new Date(r.timestamp).toLocaleString('en-US', { timeZone:'America/New_York' });
+ const dir = getDirection(r.dockDoor);
+ tbody.append(`
+
+ ${ts} |
+ ${r.temperature.toFixed(1)} |
+ ${r.humidity.toFixed(1)} |
+ ${r.heatIndex.toFixed(1)} |
+ ${r.dockDoor} |
+ ${dir} |
+
+ `);
+ });
+
+ // initialize or redraw DataTable
+ if ($.fn.DataTable.isDataTable('#trendTable')) {
+ dataTable.clear().rows.add($('#trendTable tbody tr')).draw();
+ } else {
+ dataTable = $('#trendTable').DataTable({
+ paging: true,
+ pageLength: 25,
+ ordering: true,
+ order: [[0,'desc']],
+ autoWidth: false,
+ scrollX: true
+ });
+ }
+}
+
+// draw or update chart
async function drawTrend() {
const all = await fetchReadings();
const slider = document.getElementById('periodSlider');
const periodKey = periodKeys[slider.value];
const cfg = periodConfig[periodKey];
- // group and compute stats
+ // group & stats
const groups = {};
all.forEach(r => {
const key = cfg.keyFn(r);
@@ -74,10 +116,13 @@ async function drawTrend() {
} else {
trendChart = new Chart(ctx, {
type: 'line',
- data: { labels: labels.map(cfg.labelFn), datasets },
+ data: {
+ labels: labels.map(cfg.labelFn),
+ datasets
+ },
options: {
responsive: true,
- maintainAspectRatio: false, // <<— ensure the canvas fills its container
+ maintainAspectRatio: false,
plugins: {
legend: { display: true },
tooltip: { mode:'index', intersect:false },
@@ -93,17 +138,24 @@ async function drawTrend() {
}
});
}
+
+ // always update table below
+ renderTable(all);
}
+// wire up controls
document.addEventListener('DOMContentLoaded', () => {
+ // initial draw
drawTrend();
+
// slider
document.getElementById('periodSlider')
- .addEventListener('input', e => {
- document.getElementById('periodLabel').textContent = periodLabels[e.target.value];
- drawTrend();
- });
+ .addEventListener('input', e => {
+ document.getElementById('periodLabel').textContent = periodLabels[e.target.value];
+ drawTrend();
+ });
+
// toggles
document.querySelectorAll('#metricToggles input')
- .forEach(chk => chk.addEventListener('change', drawTrend));
+ .forEach(chk => chk.addEventListener('change', drawTrend));
});