feat (ui): netwerk map functionaliteit verder uitgebreid en polish

This commit is contained in:
kodi
2026-02-24 12:37:17 +01:00
parent 289d222707
commit ec13059437
4 changed files with 100 additions and 13 deletions
+9 -1
View File
@@ -165,7 +165,15 @@ async function buildImage() {
})
});
const data = await res.json();
const ct = (res.headers.get("content-type") || "").toLowerCase();
let data;
if (ct.includes("application/json")) {
data = await res.json();
} else {
const text = await res.text();
data = { ok: res.ok, status: res.status, non_json: true, body: text.slice(0, 4000) };
}
if (!res.ok) {
outputBox.value += "\nERROR:\n" + JSON.stringify(data, null, 2);
+73 -7
View File
@@ -48,6 +48,7 @@
connectedOnly: false,
hideDefaults: true,
sharedOnly: false,
showModes: true,
sort: 'name_asc',
},
};
@@ -126,6 +127,18 @@
return { totalNetworks, usedNetworks, unusedNetworks, connectedContainers, sharedNetns };
}
function buildMapStatus(label, nodesCount, linksCount) {
const f = state.filters || {};
const flags = [];
if (f.connectedOnly) flags.push('connected');
if (f.hideDefaults) flags.push('hide-defaults');
if (f.sharedOnly) flags.push('shared');
if (f.showModes === false) flags.push('modes-off');
const flagTxt = flags.length ? ` | ${flags.join(', ')}` : '';
return `${label} | ${nodesCount} nodes, ${linksCount} links${flagTxt}`;
}
function renderNetworksSummary() {
const host = document.getElementById('networksSummary');
if (!host) return;
@@ -345,6 +358,7 @@
const connectedOnly = !!state.filters.connectedOnly;
const hideDefaults = !!state.filters.hideDefaults;
const sharedOnly = !!state.filters.sharedOnly;
const showModes = state.filters.showModes !== false;
const byMeta = state.usage?.byContainerMeta || {};
@@ -353,6 +367,9 @@
if (hideDefaults) {
out = out.filter(n => !isDefaultNetworkName(n.name));
}
if (!showModes) {
out = out.filter(n => String(n.meta?.driver || '') !== 'mode');
}
if (connectedOnly) {
out = out.filter(n => n.containerCount > 0);
@@ -696,8 +713,8 @@
// simulation
const sim = d3.forceSimulation(model.nodes)
.force('link', d3.forceLink(model.links).id(d => d.id).distance(45))
.force('charge', d3.forceManyBody().strength(-40))
.force('link', d3.forceLink(model.links).id(d => d.id).distance(80))
.force('charge', d3.forceManyBody().strength(-30))
.force('center', d3.forceCenter(w / 2, h / 2).strength(0.15))
.force('collide', d3.forceCollide().radius(d => d.type === 'network' ? 18 : 16))
.on('tick', () => {
@@ -731,7 +748,7 @@
showDetailPanel(networkName);
const s = document.getElementById('networksMapStatus');
if (s) s.textContent = `Detail: ${networkName}`;
if (s) s.textContent = buildMapStatus(`Detail: ${networkName}`, model.nodes.length, model.links.length);
}
function showDetailPanel(networkName) {
@@ -792,7 +809,7 @@
hideDetailPanel();
const s = document.getElementById('networksMapStatus');
if (s) s.textContent = `Kaart: ${model.meta.nodes} nodes, ${model.meta.links} links`;
if (s) s.textContent = buildMapStatus('Global', model.nodes.length, model.links.length);
}
function renderNetworks() {
@@ -927,6 +944,10 @@
resetBtn.dataset.bound = '1';
resetBtn.addEventListener('click', () => {
if (!graphCtx) return;
// wis highlight/dim
graphCtx.g.selectAll('.graphNode').classed('graphDim', false);
graphCtx.g.selectAll('.graphLink').classed('graphDim', false).classed('graphActive', false);
// reset zoom
graphCtx.svg
@@ -990,7 +1011,13 @@
});
const s = document.getElementById('networksMapStatus');
if (s) s.textContent = `Container: ${id.slice(0, 12)}`;
if (s) {
const label = (state.mapMode === 'detail' && state.selectedNetwork)
? `Detail: ${state.selectedNetwork}`
: 'Global';
s.textContent = buildMapStatus(label, graphCtx.model.nodes.length, graphCtx.model.links.length) + ` | container: ${id.slice(0, 12)}`;
}
});
}
@@ -999,13 +1026,24 @@
const fHideDefaults = document.getElementById('networksFilterHideDefaults');
const fShared = document.getElementById('networksFilterShared');
const sort = document.getElementById('networksSort');
const mapShowModes = document.getElementById('networksMapShowModes');
const mapConnectedOnly = document.getElementById('networksMapConnectedOnly');
function rerender() {
renderNetworks();
if (state.view === 'map') {
// als we in detail zitten → detail opnieuw renderen
if (state.mapMode === 'detail' && state.selectedNetwork) {
openNetworkDetail(state.selectedNetwork);
return;
}
// anders global map
const model = buildGlobalGraphModel();
const s = document.getElementById('networksMapStatus');
if (s) s.textContent = `Kaart: ${model.meta.nodes} nodes, ${model.meta.links} links`;
if (s) s.textContent = buildMapStatus('Global', model.nodes.length, model.links.length);
renderGraph(model);
}
}
@@ -1051,8 +1089,36 @@
});
}
if (mapConnectedOnly && !mapConnectedOnly.dataset.bound) {
mapConnectedOnly.dataset.bound = '1';
mapConnectedOnly.checked = !!state.filters.connectedOnly;
mapConnectedOnly.addEventListener('change', () => {
state.filters.connectedOnly = !!mapConnectedOnly.checked;
// sync ook de tabel checkbox (zodat alles hetzelfde blijft)
const tableChk = document.getElementById('networksFilterConnected');
if (tableChk) tableChk.checked = !!state.filters.connectedOnly;
rerender();
});
}
if (mapShowModes && !mapShowModes.dataset.bound) {
mapShowModes.dataset.bound = '1';
mapShowModes.checked = state.filters.showModes !== false;
mapShowModes.addEventListener('change', () => {
state.filters.showModes = !!mapShowModes.checked;
rerender();
});
}
renderNetworksSummary();
setNetworksView(state.view);
// sync kaart-checkbox bij init
if (mapConnectedOnly) {
mapConnectedOnly.checked = !!state.filters.connectedOnly;
}
}
// Expose minimal API
@@ -1066,7 +1132,7 @@
console.table(model.nodes.slice(0, 12));
console.table(model.links.slice(0, 12));
return model;
},
},
};
// Bind when script loads (DOM is already mostly there because script is at end of body)