From 62d506b7de8047de808d7edff8ef352ff76386db Mon Sep 17 00:00:00 2001 From: Blackwhitebear8 Date: Fri, 17 Oct 2025 20:16:49 +0200 Subject: [PATCH] Upload files to "static/js/pages" --- static/js/pages/dhcpv6_leases.js | 166 +++++++++++++++++++++++++++++++ static/js/pages/history.js | 66 +++++++++++- 2 files changed, 228 insertions(+), 4 deletions(-) create mode 100644 static/js/pages/dhcpv6_leases.js diff --git a/static/js/pages/dhcpv6_leases.js b/static/js/pages/dhcpv6_leases.js new file mode 100644 index 0000000..81ae498 --- /dev/null +++ b/static/js/pages/dhcpv6_leases.js @@ -0,0 +1,166 @@ +document.addEventListener('DOMContentLoaded', function() { + if (document.getElementById('poolSelect')) { + refreshLeaseTable(); + } +}); + +function refreshLeaseTable() { + const tableBody = document.getElementById('leaseTableBody'); + const refreshButton = document.getElementById('refreshButton'); + const refreshIcon = document.getElementById('refreshIcon'); + const refreshSpinner = document.getElementById('refreshSpinner'); + const poolSelect = document.getElementById('poolSelect'); + + if (!tableBody || !poolSelect || !refreshButton || !refreshIcon || !refreshSpinner) { + console.error("Not all elements required for DHCPv6 leases were found."); + return; + } + + const selectedPool = poolSelect.value; + if (!selectedPool) { + tableBody.innerHTML = 'No pool selected or available.'; + return; + } + + refreshButton.disabled = true; + refreshIcon.classList.add('d-none'); + refreshSpinner.classList.remove('d-none'); + + const loadingHTML = ` + + +
+ Loading... +
+ + `; + tableBody.innerHTML = loadingHTML; + + fetch(`/dhcpv6-leases/json?pool=${selectedPool}`) + .then(response => { + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + return response.json(); + }) + .then(data => { + tableBody.innerHTML = ''; + + if (data.error) { + throw new Error(data.error); + } + + if (data.data && data.data.length > 0) { + data.data.forEach((lease, index) => { + const row = document.createElement('tr'); + row.setAttribute('data-index', index); + + row.innerHTML = ` + ${escapeHTML(lease.prefix)} + ${escapeHTML(lease.state)} + ${escapeHTML(lease.last_communication)} + ${escapeHTML(lease.lease_expiration)} + ${escapeHTML(lease.remaining)} + ${escapeHTML(lease.type)} + ${escapeHTML(lease.pool)} + ${escapeHTML(lease.duid)} + `; + tableBody.appendChild(row); + }); + } else { + tableBody.innerHTML = 'No active leases found for this pool.'; + } + }) + .catch(error => { + console.error('Error fetching DHCPv6 leases:', error); + tableBody.innerHTML = `Failed to retrieve data: ${error.message}`; + }) + .finally(() => { + refreshButton.disabled = false; + refreshIcon.classList.remove('d-none'); + refreshSpinner.classList.add('d-none'); + }); +} + +function filterTable(searchInputId, tableId) { + const input = document.getElementById(searchInputId); + if (!input) return; + const filter = input.value.toUpperCase(); + const table = document.getElementById(tableId); + if (!table) return; + + const tr = table.getElementsByTagName("tr"); + + for (let i = 1; i < tr.length; i++) { + if (tr[i].parentNode.tagName.toLowerCase() !== 'tbody') { + continue; + } + + tr[i].style.display = "none"; + const td = tr[i].getElementsByTagName("td"); + + for (let j = 0; j < td.length; j++) { + if (td[j]) { + const txtValue = td[j].textContent || td[j].innerText; + if (txtValue.toUpperCase().indexOf(filter) > -1) { + tr[i].style.display = ""; + break; + } + } + } + } +} + +function sortTable(tableId, columnIndex, th) { + const table = document.getElementById(tableId); + if (!table || !table.tBodies[0]) return; + + const tbody = table.tBodies[0]; + const rows = Array.from(tbody.rows); + const headers = Array.from(th.parentNode.children); + + headers.forEach(header => { + if (header !== th) { + header.classList.remove('asc', 'desc'); + header.setAttribute('data-sort-state', 'none'); + } + }); + + let currentState = th.getAttribute('data-sort-state') || 'none'; + let newState = currentState === 'none' ? 'asc' : currentState === 'asc' ? 'desc' : 'none'; + + th.classList.remove('asc', 'desc'); + if (newState !== 'none') th.classList.add(newState); + th.setAttribute('data-sort-state', newState); + + if (newState === 'none') { + rows.sort((a, b) => parseInt(a.getAttribute('data-index')) - parseInt(b.getAttribute('data-index'))); + } else { + rows.sort((a, b) => { + const aText = a.cells[columnIndex]?.textContent.trim() || ''; + const bText = b.cells[columnIndex]?.textContent.trim() || ''; + + const aNum = parseFloat(aText.replace(/[^0-9.-]/g, '')); + const bNum = parseFloat(bText.replace(/[^0-9.-]/g, '')); + const isNumeric = !isNaN(aNum) && !isNaN(bNum) && aText.match(/^[0-9.-]/) && bText.match(/^[0-9.-]/); + + return newState === 'asc' + ? (isNumeric ? aNum - bNum : aText.localeCompare(bText)) + : (isNumeric ? bNum - aNum : bText.localeCompare(aText)); + }); + } + + rows.forEach(row => tbody.appendChild(row)); +} + +function escapeHTML(str) { + if (str === null || str === undefined) { + return 'N/A'; + } + return str.toString() + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); +} \ No newline at end of file diff --git a/static/js/pages/history.js b/static/js/pages/history.js index 74e9dc0..054ff13 100644 --- a/static/js/pages/history.js +++ b/static/js/pages/history.js @@ -5,16 +5,66 @@ document.addEventListener('DOMContentLoaded', function () { let currentRange = '24h'; const timeRangeButtons = document.querySelectorAll('.btn-group .btn'); + const customRangeBtn = document.getElementById('customRangeBtn'); + const startDateInput = document.getElementById('startDate'); + const endDateInput = document.getElementById('endDate'); + + function formatDateForInput(date) { + const pad = (num) => num.toString().padStart(2, '0'); + const year = date.getFullYear(); + const month = pad(date.getMonth() + 1); + const day = pad(date.getDate()); + const hours = pad(date.getHours()); + const minutes = pad(date.getMinutes()); + return `${year}-${month}-${day}T${hours}:${minutes}`; + } + + function updateDateInputs(range) { + const endDate = new Date(); + let startDate = new Date(); + + switch (range) { + case '24h': + startDate.setHours(startDate.getHours() - 24); + break; + case '7d': + startDate.setDate(startDate.getDate() - 7); + break; + case '30d': + startDate.setDate(startDate.getDate() - 30); + break; + case '90d': + startDate.setDate(startDate.getDate() - 90); + break; + } + + startDateInput.value = formatDateForInput(startDate); + endDateInput.value = formatDateForInput(endDate); + } timeRangeButtons.forEach(button => { button.addEventListener('click', () => { currentRange = button.getAttribute('data-range'); timeRangeButtons.forEach(btn => btn.classList.remove('active')); button.classList.add('active'); - fetchDataAndRenderCharts(currentRange); + + updateDateInputs(currentRange); + fetchDataAndRenderCharts({ range: currentRange }); }); }); + customRangeBtn.addEventListener('click', () => { + const startDate = startDateInput.value; + const endDate = endDateInput.value; + + if (startDate && endDate) { + timeRangeButtons.forEach(btn => btn.classList.remove('active')); + fetchDataAndRenderCharts({ startDate, endDate }); + } else { + alert('Please select both a start and end date.'); + } + }); + function updateChartScale(chartInstance) { const dataset = chartInstance.data.datasets[0]; const validData = (dataset.data || []).filter(val => typeof val === 'number'); @@ -38,9 +88,16 @@ document.addEventListener('DOMContentLoaded', function () { chartInstance.update(); } - async function fetchDataAndRenderCharts(range) { + async function fetchDataAndRenderCharts(params) { + let url = '/history/api/total-routes'; + if (params.range) { + url += `?range=${params.range}`; + } else if (params.startDate && params.endDate) { + url += `?start_date=${params.startDate}&end_date=${params.endDate}`; + } + try { - const response = await fetch(`/history/api/total-routes?range=${range}`); + const response = await fetch(url); if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); const data = await response.json(); renderCharts(data); @@ -167,5 +224,6 @@ document.addEventListener('DOMContentLoaded', function () { updateChartScale(ipv6Chart); } - fetchDataAndRenderCharts(currentRange); + updateDateInputs(currentRange); + fetchDataAndRenderCharts({ range: currentRange }); }); \ No newline at end of file