feat(containers-dashboard): groeperen containers toegevoegd

This commit is contained in:
kodi
2026-02-18 15:46:31 +01:00
parent eecf4ad9f2
commit 35e5682b91
+104 -7
View File
@@ -220,6 +220,14 @@
overflow:auto; overflow:auto;
} }
.hint{font-size:12px;color:var(--muted);margin-top:8px;line-height:1.35} .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);
}
</style> </style>
</head> </head>
@@ -623,13 +631,24 @@
} }
// ---- Containers ---- // ---- Containers ----
async function fetchContainers() {
const containers = await api('/containers-dashboard', 'GET');
const list = Array.isArray(containers) ? containers : (containers?.containers || []);
document.getElementById('countContainers').textContent = list.length;
const tbody = document.getElementById('containersTbody'); function _podKey(c) {
tbody.innerHTML = list.map(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 name = (c.Names && c.Names[0]) ? c.Names[0] : (c.Names || c.Name || c.name || '');
const status = c.Status || c.State || c.state || ''; const status = c.Status || c.State || c.state || '';
const podName = c.PodName || '-'; const podName = c.PodName || '-';
@@ -637,6 +656,7 @@
const managed = c._dashboard_source || 'podman'; const managed = c._dashboard_source || 'podman';
const ports = (c._dashboard_published_ports || []).join(", ") const ports = (c._dashboard_published_ports || []).join(", ")
|| (c.Ports || []).map(p => `${p.host_port}:${p.container_port}`).join(", "); || (c.Ports || []).map(p => `${p.host_port}:${p.container_port}`).join(", ");
return ` return `
<tr> <tr>
<td><strong>${esc(name)}</strong></td> <td><strong>${esc(name)}</strong></td>
@@ -658,7 +678,84 @@
</td> </td>
</tr> </tr>
`; `;
}).join(''); }
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 += `
<tr class="pod-group-row">
<td colspan="${colCount}">
<span class="pod-toggle" data-pod="${pod}" style="cursor:pointer; user-select:none;">
${collapsed ? '▶' : '▼'} <b>${esc(pod)}</b> <span style="opacity:.7;">(${items.length})</span>
</span>
</td>
</tr>
`;
for (const c of items) {
const row = renderContainerRow(c).replace(
'<tr',
`<tr class="pod-item-row" data-pod="${esc(pod)}"${collapsed ? ' style="display:none;"' : ''}`
);
html += row;
}
}
tbody.innerHTML = html;
tbody.onclick = (ev) => {
const t = ev.target.closest('.pod-toggle');
if (!t) return;
const pod = t.getAttribute('data-pod');
const isNowCollapsed = !_isCollapsed(pod);
_setCollapsed(pod, isNowCollapsed);
tbody.querySelectorAll(`.pod-item-row[data-pod="${CSS.escape(pod)}"]`).forEach(r => {
r.style.display = isNowCollapsed ? 'none' : '';
});
t.innerHTML =
`${isNowCollapsed ? '▶' : '▼'} <b>${pod}</b> ` +
`<span style="opacity:.7;">(${tbody.querySelectorAll(`.pod-item-row[data-pod="${CSS.escape(pod)}"]`).length})</span>`;
};
}
async function fetchContainers() {
const containers = await api('/containers-dashboard', 'GET');
const list = Array.isArray(containers) ? containers : (containers?.containers || []);
document.getElementById('countContainers').textContent = list.length;
const tbody = document.getElementById('containersTbody');
renderContainersGrouped(list, tbody);
} }
let containersStatsES = null; let containersStatsES = null;