nodelist now also works with API

This commit is contained in:
Pablo Revilla
2025-09-12 11:46:20 -07:00
parent 168763357a
commit fd57310b0e

View File

@@ -115,6 +115,24 @@
<div id="total_portnum_71" class="total-count">Total: 0</div>
<div id="chart_portnum_71" class="chart"></div>
</div>
<!-- Hardware breakdown -->
<div class="card-section">
<p class="section-header">Hardware Breakdown</p>
<div id="chart_hw_model" class="chart"></div>
</div>
<!-- Role breakdown -->
<div class="card-section">
<p class="section-header">Role Breakdown</p>
<div id="chart_role" class="chart"></div>
</div>
<!-- Channel breakdown -->
<div class="card-section">
<p class="section-header">Channel Breakdown</p>
<div id="chart_channel" class="chart"></div>
</div>
</div>
<script>
@@ -137,6 +155,30 @@
}
}
async function fetchNodes() {
try {
const res = await fetch("/api/nodes");
if (!res.ok) {
console.error("Failed to fetch nodes:", res.status, res.statusText);
return [];
}
const json = await res.json();
return json.nodes || [];
} catch (err) {
console.error("Error fetching nodes:", err);
return [];
}
}
function processCountField(nodes, field) {
const counts = {};
nodes.forEach(n => {
const key = n[field] || "Unknown";
counts[key] = (counts[key] || 0) + 1;
});
return Object.entries(counts).map(([name, value]) => ({ name, value }));
}
function updateTotalCount(domId, data) {
const el = document.getElementById(domId);
if (!el || !data.length) return;
@@ -144,15 +186,21 @@
el.textContent = `Total: ${total.toLocaleString()}`;
}
let chartHourlyAll = null;
let chartPortnum1 = null;
let chartPortnum3 = null;
let chartPortnum4 = null;
let chartPortnum67 = null;
let chartPortnum70 = null;
let chartPortnum71 = null;
let chartDailyAll = null;
let chartDailyPortnum1 = null;
// Chart variables
let chartHourlyAll, chartPortnum1, chartPortnum3, chartPortnum4, chartPortnum67, chartPortnum70, chartPortnum71;
let chartDailyAll, chartDailyPortnum1;
let chartHwModel, chartRole, chartChannel;
// Utility function for top N + "Other"
function prepareTopN(data, n = 20) {
data.sort((a, b) => b.value - a.value);
let top = data.slice(0, n);
if (data.length > n) {
const otherValue = data.slice(n).reduce((sum, item) => sum + item.value, 0);
top.push({ name: "Other", value: otherValue });
}
return top;
}
function renderChart(domId, data, type, color, isHourly) {
const el = document.getElementById(domId);
@@ -208,6 +256,35 @@
chart.setOption(option);
}
function renderPieChart(elId, data, name) {
const el = document.getElementById(elId);
if (!el) return;
const chart = echarts.init(el);
const top20 = prepareTopN(data, 20);
const option = {
backgroundColor: "#272b2f",
tooltip: { trigger: "item", formatter: "{b}: {d}% ({c})" },
series: [
{
name: name,
type: "pie",
radius: ["30%", "70%"],
center: ["50%", "50%"],
avoidLabelOverlap: true,
itemStyle: { borderRadius: 6, borderColor: "#272b2f", borderWidth: 2 },
label: { show: true, formatter: "{b}\n{d}%", color: "#ccc", fontSize: 10 },
labelLine: { show: true, length: 10, length2: 6 },
data: top20
}
]
};
chart.setOption(option);
return chart;
}
async function init() {
// Daily all ports
const dailyAllData = await fetchStats('day', 14);
@@ -227,41 +304,25 @@
// Hourly by port
const portnums = [1, 3, 4, 67, 70, 71];
const colors = ['#ff5722', '#2196f3', '#9c27b0', '#ffeb3b', '#795548', '#4caf50'];
const domIds = [
'chart_portnum_1',
'chart_portnum_3',
'chart_portnum_4',
'chart_portnum_67',
'chart_portnum_70',
'chart_portnum_71'
];
const totalIds = [
'total_portnum_1',
'total_portnum_3',
'total_portnum_4',
'total_portnum_67',
'total_portnum_70',
'total_portnum_71'
];
const domIds = ['chart_portnum_1','chart_portnum_3','chart_portnum_4','chart_portnum_67','chart_portnum_70','chart_portnum_71'];
const totalIds = ['total_portnum_1','total_portnum_3','total_portnum_4','total_portnum_67','total_portnum_70','total_portnum_71'];
const allData = await Promise.all(portnums.map(pn => fetchStats('hour', 24, pn)));
for (let i = 0; i < portnums.length; i++) {
updateTotalCount(totalIds[i], allData[i]);
renderChart(domIds[i], allData[i], 'bar', colors[i], true);
}
// Nodes breakdown charts
const nodes = await fetchNodes();
renderPieChart("chart_hw_model", processCountField(nodes, "hw_model"), "Hardware");
renderPieChart("chart_role", processCountField(nodes, "role"), "Role");
renderPieChart("chart_channel", processCountField(nodes, "channel"), "Channel");
}
window.addEventListener('resize', () => {
if (chartHourlyAll) chartHourlyAll.resize();
if (chartPortnum1) chartPortnum1.resize();
if (chartPortnum3) chartPortnum3.resize();
if (chartPortnum4) chartPortnum4.resize();
if (chartPortnum67) chartPortnum67.resize();
if (chartPortnum70) chartPortnum70.resize();
if (chartPortnum71) chartPortnum71.resize();
if (chartDailyAll) chartDailyAll.resize();
if (chartDailyPortnum1) chartDailyPortnum1.resize();
[chartHourlyAll, chartPortnum1, chartPortnum3, chartPortnum4, chartPortnum67, chartPortnum70, chartPortnum71, chartDailyAll, chartDailyPortnum1, chartHwModel, chartRole, chartChannel]
.forEach(c => c?.resize());
});
init();