diff --git a/webui/html/index.html b/webui/html/index.html index ab2efb5..36708d8 100644 --- a/webui/html/index.html +++ b/webui/html/index.html @@ -220,6 +220,14 @@ overflow:auto; } .hint{font-size:12px;color:var(--muted);margin-top:8px;line-height:1.35} + .pod-group-row td { + background: rgba(255,255,255,0.04); + border-top: 1px solid rgba(255,255,255,0.08); + font-weight: 600; + } + .pod-group-row:hover td { + background: rgba(255,255,255,0.07); + } @@ -623,42 +631,131 @@ } // ---- Containers ---- + + function _podKey(c) { + return (c.PodName && String(c.PodName).trim()) ? String(c.PodName).trim() : '(geen pod)'; + } + + function _cmpStr(a, b) { + return String(a).localeCompare(String(b), undefined, { numeric: true, sensitivity: 'base' }); + } + + function _isCollapsed(pod) { + return localStorage.getItem('pod_group_collapsed:' + pod) === '1'; + } + + function _setCollapsed(pod, v) { + localStorage.setItem('pod_group_collapsed:' + pod, v ? '1' : '0'); + } + + function renderContainerRow(c) { + const name = (c.Names && c.Names[0]) ? c.Names[0] : (c.Names || c.Name || c.name || ''); + const status = c.Status || c.State || c.state || ''; + const podName = c.PodName || '-'; + const image = c.Image || c.image || ''; + const managed = c._dashboard_source || 'podman'; + const ports = (c._dashboard_published_ports || []).join(", ") + || (c.Ports || []).map(p => `${p.host_port}:${p.container_port}`).join(", "); + + return ` + + ${esc(name)} + ${badgeFromStatus(status)} + ${podName} + ${esc(image)} + ${badgeFromStatus(managed)} + - + - + ${ports || '-'} + +
+ + + + + +
+ + + `; + } + + function renderContainersGrouped(list, tbody) { + const groups = new Map(); + for (const c of (list || [])) { + const k = _podKey(c); + if (!groups.has(k)) groups.set(k, []); + groups.get(k).push(c); + } + + const keys = Array.from(groups.keys()).sort((a, b) => { + if (a === '(geen pod)' && b !== '(geen pod)') return 1; + if (b === '(geen pod)' && a !== '(geen pod)') return -1; + return _cmpStr(a, b); + }); + + const table = tbody.closest('table'); + const colCount = table ? table.querySelectorAll('thead th').length : 9; + + let html = ''; + + for (const pod of keys) { + const items = groups.get(pod) || []; + items.sort((a, b) => + _cmpStr( + (a.Names && a.Names[0]) ? a.Names[0] : (a.Name || ''), + (b.Names && b.Names[0]) ? b.Names[0] : (b.Name || '') + ) + ); + + const collapsed = _isCollapsed(pod); + + html += ` + + + + ${collapsed ? '▶' : '▼'} ${esc(pod)} (${items.length}) + + + + `; + + for (const c of items) { + const row = renderContainerRow(c).replace( + '