Upload files to "static/js/pages"
This commit is contained in:
parent
8de446191c
commit
62d506b7de
2 changed files with 228 additions and 4 deletions
166
static/js/pages/dhcpv6_leases.js
Normal file
166
static/js/pages/dhcpv6_leases.js
Normal file
|
|
@ -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 = '<tr><td colspan="8" class="text-center text-danger">No pool selected or available.</td></tr>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshButton.disabled = true;
|
||||||
|
refreshIcon.classList.add('d-none');
|
||||||
|
refreshSpinner.classList.remove('d-none');
|
||||||
|
|
||||||
|
const loadingHTML = `
|
||||||
|
<tr id="lease-loading-row">
|
||||||
|
<td colspan="8" class="text-center">
|
||||||
|
<div class="spinner-border text-primary" role="status">
|
||||||
|
<span class="visually-hidden">Loading...</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>`;
|
||||||
|
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 = `
|
||||||
|
<td>${escapeHTML(lease.prefix)}</td>
|
||||||
|
<td>${escapeHTML(lease.state)}</td>
|
||||||
|
<td>${escapeHTML(lease.last_communication)}</td>
|
||||||
|
<td>${escapeHTML(lease.lease_expiration)}</td>
|
||||||
|
<td>${escapeHTML(lease.remaining)}</td>
|
||||||
|
<td>${escapeHTML(lease.type)}</td>
|
||||||
|
<td>${escapeHTML(lease.pool)}</td>
|
||||||
|
<td>${escapeHTML(lease.duid)}</td>
|
||||||
|
`;
|
||||||
|
tableBody.appendChild(row);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
tableBody.innerHTML = '<tr><td colspan="8" class="text-center">No active leases found for this pool.</td></tr>';
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error fetching DHCPv6 leases:', error);
|
||||||
|
tableBody.innerHTML = `<tr><td colspan="8" class="text-center text-danger">Failed to retrieve data: ${error.message}</td></tr>`;
|
||||||
|
})
|
||||||
|
.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, '"')
|
||||||
|
.replace(/'/g, ''');
|
||||||
|
}
|
||||||
|
|
@ -5,16 +5,66 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||||
let currentRange = '24h';
|
let currentRange = '24h';
|
||||||
|
|
||||||
const timeRangeButtons = document.querySelectorAll('.btn-group .btn');
|
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 => {
|
timeRangeButtons.forEach(button => {
|
||||||
button.addEventListener('click', () => {
|
button.addEventListener('click', () => {
|
||||||
currentRange = button.getAttribute('data-range');
|
currentRange = button.getAttribute('data-range');
|
||||||
timeRangeButtons.forEach(btn => btn.classList.remove('active'));
|
timeRangeButtons.forEach(btn => btn.classList.remove('active'));
|
||||||
button.classList.add('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) {
|
function updateChartScale(chartInstance) {
|
||||||
const dataset = chartInstance.data.datasets[0];
|
const dataset = chartInstance.data.datasets[0];
|
||||||
const validData = (dataset.data || []).filter(val => typeof val === 'number');
|
const validData = (dataset.data || []).filter(val => typeof val === 'number');
|
||||||
|
|
@ -38,9 +88,16 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||||
chartInstance.update();
|
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 {
|
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}`);
|
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
renderCharts(data);
|
renderCharts(data);
|
||||||
|
|
@ -167,5 +224,6 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||||
updateChartScale(ipv6Chart);
|
updateChartScale(ipv6Chart);
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchDataAndRenderCharts(currentRange);
|
updateDateInputs(currentRange);
|
||||||
|
fetchDataAndRenderCharts({ range: currentRange });
|
||||||
});
|
});
|
||||||
Loading…
Add table
Add a link
Reference in a new issue