From e564c9283da3cb3edf1ba1be681de7d8c0659a8b Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Wed, 13 May 2026 23:14:56 +0200 Subject: [PATCH] feat(nodes): mobile card list, info modal, and tighter summary layout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit NodeList now branches on isMobile: a vertical card list mirrors the inbound mobile redesign — status dot + name + an Info icon that opens an a-modal with the full per-node stats (address, status, CPU/mem, xray version, uptime, latency, last heartbeat). The card head expands to surface NodeHistoryPanel inline (parity with the desktop expandable row), and the more-dropdown carries probe/edit/delete. NodesPage also gets two layout fixes: an 8px vertical gutter between the summary card and the node list on mobile (was 0), and a 2x2 grid for the four summary statistics on phones via :xs="12" plus a 16px inner vertical gutter, so Total/Online/Offline/Avg Latency no longer crowd each other. --- frontend/src/pages/nodes/NodeList.vue | 224 ++++++++++++++++++++++++- frontend/src/pages/nodes/NodesPage.vue | 12 +- 2 files changed, 229 insertions(+), 7 deletions(-) diff --git a/frontend/src/pages/nodes/NodeList.vue b/frontend/src/pages/nodes/NodeList.vue index bd84ecec..73dc6236 100644 --- a/frontend/src/pages/nodes/NodeList.vue +++ b/frontend/src/pages/nodes/NodeList.vue @@ -9,6 +9,9 @@ import { ExclamationCircleOutlined, EyeOutlined, EyeInvisibleOutlined, + InfoCircleOutlined, + MoreOutlined, + RightOutlined, } from '@ant-design/icons-vue'; import NodeHistoryPanel from './NodeHistoryPanel.vue'; @@ -72,6 +75,25 @@ function formatPct(p) { if (typeof p !== 'number' || isNaN(p)) return '-'; return `${p.toFixed(1)}%`; } + +const statsNode = ref(null); +function openStats(node) { + statsNode.value = node; +} +function closeStats() { + statsNode.value = null; +} + +const expandedIds = ref(new Set()); +function toggleExpanded(id) { + const next = new Set(expandedIds.value); + if (next.has(id)) next.delete(id); + else next.add(id); + expandedIds.value = next; +} +function isExpanded(id) { + return expandedIds.value.has(id); +}