feat(ui): image tabblad toegevoegd
This commit is contained in:
@@ -390,4 +390,31 @@ pre{
|
|||||||
|
|
||||||
.file-folder-files{
|
.file-folder-files{
|
||||||
margin-left: 18px;
|
margin-left: 18px;
|
||||||
|
}
|
||||||
|
.data-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-table th,
|
||||||
|
.data-table td {
|
||||||
|
padding: 8px;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge {
|
||||||
|
padding: 4px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-green {
|
||||||
|
background: #2ecc71;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-yellow {
|
||||||
|
background: #f1c40f;
|
||||||
|
color: black;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
async function loadImages() {
|
||||||
|
const res = await fetch("/api/images");
|
||||||
|
const images = await res.json();
|
||||||
|
renderImages(images);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderImages(images) {
|
||||||
|
const tbody = document.getElementById("images-tbody");
|
||||||
|
tbody.innerHTML = "";
|
||||||
|
|
||||||
|
images.forEach(img => {
|
||||||
|
const tr = document.createElement("tr");
|
||||||
|
|
||||||
|
const repoTag = (img.RepoTags && img.RepoTags.length > 0)
|
||||||
|
? img.RepoTags[0]
|
||||||
|
: "<none>";
|
||||||
|
|
||||||
|
const shortId = img.Id.substring(0, 12);
|
||||||
|
const sizeMB = (img.Size / 1024 / 1024).toFixed(1);
|
||||||
|
const containers = img.Containers || 0;
|
||||||
|
const fullId = img.Id;
|
||||||
|
|
||||||
|
const status = containers > 0
|
||||||
|
? `<span class="badge badge-green">In use</span>`
|
||||||
|
: `<span class="badge badge-yellow">Unused</span>`;
|
||||||
|
|
||||||
|
const disabled = containers > 0 ? "disabled" : "";
|
||||||
|
|
||||||
|
tr.innerHTML = `
|
||||||
|
<td>
|
||||||
|
<input type="checkbox" class="image-checkbox" value="${fullId}" ${disabled}>
|
||||||
|
</td>
|
||||||
|
<td>${repoTag}</td>
|
||||||
|
<td>${shortId}</td>
|
||||||
|
<td>${sizeMB} MB</td>
|
||||||
|
<td>${containers}</td>
|
||||||
|
<td>${status}</td>
|
||||||
|
<td>
|
||||||
|
<button class="btn small bad" onclick="removeSingleImage('${fullId}')" ${disabled}>
|
||||||
|
Remove
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
`;
|
||||||
|
|
||||||
|
tbody.appendChild(tr);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleSelectAllImages(master) {
|
||||||
|
document.querySelectorAll(".image-checkbox:not(:disabled)")
|
||||||
|
.forEach(cb => cb.checked = master.checked);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function removeSingleImage(id) {
|
||||||
|
if (!confirm("Image verwijderen?")) return;
|
||||||
|
|
||||||
|
await fetch("/api/images/" + encodeURIComponent(id), {
|
||||||
|
method: "DELETE"
|
||||||
|
});
|
||||||
|
|
||||||
|
await loadImages();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function removeSelectedImages() {
|
||||||
|
const selected = Array.from(document.querySelectorAll(".image-checkbox:checked"))
|
||||||
|
.map(cb => cb.value);
|
||||||
|
|
||||||
|
if (!selected.length) {
|
||||||
|
alert("Geen images geselecteerd.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!confirm("Geselecteerde images verwijderen?")) return;
|
||||||
|
|
||||||
|
await fetch("/api/images/remove", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ images: selected })
|
||||||
|
});
|
||||||
|
|
||||||
|
await loadImages();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function pruneUnusedImages() {
|
||||||
|
if (!confirm("Alle unused images verwijderen?")) return;
|
||||||
|
|
||||||
|
await fetch("/api/images/prune?all=true", {
|
||||||
|
method: "POST"
|
||||||
|
});
|
||||||
|
|
||||||
|
await loadImages();
|
||||||
|
}
|
||||||
+38
-1
@@ -51,6 +51,9 @@
|
|||||||
<div class="tab" id="tab-networks" onclick="setTab('networks')" title="Netwerk">
|
<div class="tab" id="tab-networks" onclick="setTab('networks')" title="Netwerk">
|
||||||
<span class="navIcon">🌐</span><span class="navLabel">Netwerk</span>
|
<span class="navIcon">🌐</span><span class="navLabel">Netwerk</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="tab" id="tab-images" onclick="setTab('images')" title="Images">
|
||||||
|
<span class="navIcon">📦</span><span class="navLabel">Images</span>
|
||||||
|
</div>
|
||||||
<div class="tab" id="tab-files" onclick="setTab('files')" title="Files">
|
<div class="tab" id="tab-files" onclick="setTab('files')" title="Files">
|
||||||
<span class="navIcon">📁</span><span class="navLabel">Files</span>
|
<span class="navIcon">📁</span><span class="navLabel">Files</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -142,6 +145,37 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="view-images" class="grid" style="display:none">
|
||||||
|
<div class="card" style="grid-column: 1 / -1;">
|
||||||
|
<div class="cardHeader">
|
||||||
|
<div class="cardTitle">Images</div>
|
||||||
|
<div class="flex">
|
||||||
|
<button class="btn" onclick="loadImages()">Ververs</button>
|
||||||
|
<button class="btn bad" onclick="removeSelectedImages()">Remove selected</button>
|
||||||
|
<button class="btn warn" onclick="pruneUnusedImages()">Prune unused</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="cardBody">
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="width:30px;">
|
||||||
|
<input type="checkbox" id="imagesSelectAll" onclick="toggleSelectAllImages(this)">
|
||||||
|
</th>
|
||||||
|
<th>Repo / Tag</th>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>Size</th>
|
||||||
|
<th>Containers</th>
|
||||||
|
<th>Status</th>
|
||||||
|
<th style="width:100px;">Acties</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="images-tbody"></tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="view-files" class="grid" style="display:none">
|
<div id="view-files" class="grid" style="display:none">
|
||||||
<div class="card" style="grid-column: 1 / -1;">
|
<div class="card" style="grid-column: 1 / -1;">
|
||||||
<div class="cardHeader">
|
<div class="cardHeader">
|
||||||
@@ -302,7 +336,9 @@
|
|||||||
if (tab === 'networks') {
|
if (tab === 'networks') {
|
||||||
networksRefresh();
|
networksRefresh();
|
||||||
}
|
}
|
||||||
|
if (tab === "images") {
|
||||||
|
loadImages();
|
||||||
|
}
|
||||||
// Start/stop live stats alleen in Containers tab
|
// Start/stop live stats alleen in Containers tab
|
||||||
if (tab === 'containers') startContainersStatsStream();
|
if (tab === 'containers') startContainersStatsStream();
|
||||||
else stopContainersStatsStream();
|
else stopContainersStatsStream();
|
||||||
@@ -1227,5 +1263,6 @@
|
|||||||
setInterval(() => { pingApi(); }, 20000);
|
setInterval(() => { pingApi(); }, 20000);
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
<script src="assets/js/tabs/images.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user