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)); });