From 3c143d5506ab1f148e69aed567783ca76f272eeb Mon Sep 17 00:00:00 2001 From: infosave2007 Date: Sat, 4 Apr 2026 16:32:14 +0300 Subject: [PATCH] feat: enhance client speed metrics visualization with improved data processing and responsive table layout --- templates/servers/view.twig | 43 ++++++++++++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/templates/servers/view.twig b/templates/servers/view.twig index c4165a0..3546b57 100644 --- a/templates/servers/view.twig +++ b/templates/servers/view.twig @@ -247,7 +247,8 @@ {% if clients|length > 0 %} - +
+
@@ -375,6 +376,7 @@ {% endfor %}
{{ t('clients.name') }}
+ {% else %}
{{ t('clients.no_clients') }}
{% endif %} @@ -878,6 +880,39 @@ if (document.getElementById('cpuSparkline')) { // Update client speeds let clientCharts = {}; +function prepareSparklineSeries(values) { + const cleaned = values.map(v => { + const n = Number(v); + return Number.isFinite(n) && n > 0 ? n : 0; + }); + + const nonZero = cleaned.filter(v => v > 0).sort((a, b) => a - b); + let capped = cleaned; + + // Suppress single extreme spikes that make the mini-chart unreadable. + if (nonZero.length >= 5) { + const p95Index = Math.floor((nonZero.length - 1) * 0.95); + const p95 = nonZero[p95Index] || 0; + const cap = p95 > 0 ? p95 * 2 : 0; + if (cap > 0) { + capped = cleaned.map(v => Math.min(v, cap)); + } + } + + // Small moving average to reduce jitter on tiny sparklines. + return capped.map((_, i, arr) => { + const from = Math.max(0, i - 1); + const to = Math.min(arr.length - 1, i + 1); + let sum = 0; + let count = 0; + for (let j = from; j <= to; j++) { + sum += arr[j]; + count++; + } + return count > 0 ? sum / count : 0; + }); +} + async function updateClientSpeeds() { const clientRows = document.querySelectorAll('[id^="client-speed-"]'); @@ -899,8 +934,10 @@ async function updateClientSpeeds() { // 1. Render/Update Chart if (canvas) { const labels = metrics.map((_, i) => i); - const dataUp = metrics.map(m => (parseFloat(m.speed_up_kbps) / 1000)); // Mbps - const dataDown = metrics.map(m => (parseFloat(m.speed_down_kbps) / 1000)); // Mbps + const rawUp = metrics.map(m => (parseFloat(m.speed_up_kbps) / 1000)); // Mbps + const rawDown = metrics.map(m => (parseFloat(m.speed_down_kbps) / 1000)); // Mbps + const dataUp = prepareSparklineSeries(rawUp); + const dataDown = prepareSparklineSeries(rawDown); if (clientCharts[clientId]) { // Update existing chart