From d1e9e189a47fbec2ce386574b5f1967bbaaf619d Mon Sep 17 00:00:00 2001 From: Blackwhitebear8 Date: Wed, 17 Sep 2025 20:05:01 +0200 Subject: [PATCH] Add static/js/pages/history.js --- static/js/pages/history.js | 167 +++++++++++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 static/js/pages/history.js diff --git a/static/js/pages/history.js b/static/js/pages/history.js new file mode 100644 index 0000000..7422f9f --- /dev/null +++ b/static/js/pages/history.js @@ -0,0 +1,167 @@ +document.addEventListener('DOMContentLoaded', function () { + const ctx = document.getElementById('totalRoutesChart').getContext('2d'); + let chart; + let currentRange = '24h'; + + const timeRangeButtons = document.querySelectorAll('.btn-group .btn'); + + timeRangeButtons.forEach(button => { + button.addEventListener('click', () => { + currentRange = button.getAttribute('data-range'); + timeRangeButtons.forEach(btn => btn.classList.remove('active')); + button.classList.add('active'); + fetchDataAndRenderChart(currentRange); + }); + }); + + function updateChartScale(chartInstance) { + const visibleDatasets = chartInstance.data.datasets.filter(dataset => !dataset.hidden); + + let pointsForScaling = []; + visibleDatasets.forEach(dataset => { + const validData = (dataset.data || []).filter(val => typeof val === 'number'); + pointsForScaling.push(...validData); + }); + + let suggestedMin, suggestedMax; + + if (pointsForScaling.length > 0) { + const minValue = Math.min(...pointsForScaling); + const maxValue = Math.max(...pointsForScaling); + const padding = (maxValue - minValue) * 0.1 || 200; + + suggestedMin = Math.floor(minValue - padding); + suggestedMax = Math.ceil(maxValue + padding); + } else { + suggestedMin = 0; + suggestedMax = 1000; + } + + chartInstance.options.scales.y.min = suggestedMin; + chartInstance.options.scales.y.max = suggestedMax; + chartInstance.update(); + } + + async function fetchDataAndRenderChart(range) { + try { + const response = await fetch(`/history/api/total-routes?range=${range}`); + if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); + const data = await response.json(); + renderChart(data); + } catch (error) { + console.error('Error fetching chart data:', error); + const chartContainer = document.querySelector('.achart-wrapper-chart'); + if (chartContainer) { + chartContainer.innerHTML = '

Could not load chart data.

'; + } + } + } + + function renderChart(data) { + if (chart) { + chart.destroy(); + } + + const findLastValue = (arr) => { + for (let i = arr.length - 1; i >= 0; i--) { + if (arr[i] !== null && typeof arr[i] !== 'undefined') { + return arr[i]; + } + } + return 'N/A'; + }; + + const ipv4LastValue = findLastValue(data.ipv4_routes); + const ipv6LastValue = findLastValue(data.ipv6_routes); + + const ipv4Label = `Total IPv4 Routes: ${ipv4LastValue.toLocaleString()}`; + const ipv6Label = `Total IPv6 Routes: ${ipv6LastValue.toLocaleString()}`; + + chart = new Chart(ctx, { + type: 'line', + data: { + labels: data.labels, + datasets: [ + { + label: ipv4Label, + data: data.ipv4_routes, + borderColor: 'rgb(54, 162, 235)', + borderWidth: 1.5, + pointRadius: 2, + pointHoverRadius: 5, + spanGaps: true, + fill: true, + backgroundColor: 'rgba(54, 162, 235, 0.1)', + }, + { + label: ipv6Label, + data: data.ipv6_routes, + borderColor: 'rgb(255, 99, 132)', + borderWidth: 1.5, + pointRadius: 2, + pointHoverRadius: 5, + spanGaps: true, + fill: true, + backgroundColor: 'rgba(255, 99, 132, 0.1)', + } + ] + }, + options: { + responsive: true, + maintainAspectRatio: false, + interaction: { + intersect: false, + mode: 'index', + }, + scales: { + x: { + type: 'time', + time: { + unit: 'minute', + stepSize: 30, + displayFormats: { + minute: 'HH:mm', + hour: 'HH:mm' + } + } + }, + y: { } + }, + plugins: { + legend: { + position: 'top', + onClick: (e, legendItem, legend) => { + const chartInstance = legend.chart; + const index = legendItem.datasetIndex; + chartInstance.data.datasets[index].hidden = !chartInstance.data.datasets[index].hidden; + updateChartScale(chartInstance); + } + }, + tooltip: { + callbacks: { + title: function(context) { + if (!context[0]) return ''; + return new Date(context[0].parsed.x).toLocaleString([], { + year: 'numeric', month: 'short', day: 'numeric', + hour: '2-digit', minute: '2-digit', hour12: false + }); + }, + label: function(context) { + let label = context.dataset.label.split(':')[0] || ''; + if (label) label += ': '; + if (context.parsed.y !== null) { + label += context.parsed.y.toLocaleString(); + } + return label; + } + } + } + } + } + }); + + updateChartScale(chart); + } + + fetchDataAndRenderChart(currentRange); +}); \ No newline at end of file