/**
* javascript/containers.js
*
* Containers panel: table of all containers with lifecycle actions
* (start/stop/restart/remove), backed entirely by ajax/containers.php.
*/
(function () {
'use strict';
const P = window.Podman;
let allContainers = [];
let filter = 'all';
let searchTerm = '';
function iconLabel(name) {
return P.escapeHtml(name.slice(0, 2).toUpperCase());
}
function rowHtml(c) {
const cpuMem = c.state === 'running'
? 'running'
: '—';
return '' +
'
' +
'| ' + P.escapeHtml(c.health || c.state) + ' | ' +
'' + iconLabel(c.name) + '' + P.escapeHtml(c.name) + ' | ' +
'' + P.escapeHtml(c.image) + ' | ' +
'' + cpuMem + ' | ' +
'' + P.escapeHtml(c.ports.join(', ') || '—') + ' | ' +
'' + P.formatDuration(c.uptimeSeconds) + ' | ' +
'' + actionButtons(c) + ' | ' +
'
';
}
function actionButtons(c) {
if (c.state === 'running') {
return '' +
'' +
'' +
'';
}
return '' +
'' +
'';
}
function applyFilters() {
return allContainers.filter(function (c) {
if (filter === 'running' && c.state !== 'running') return false;
if (filter === 'stopped' && c.state === 'running') return false;
if (searchTerm && c.name.toLowerCase().indexOf(searchTerm) === -1 && c.image.toLowerCase().indexOf(searchTerm) === -1) return false;
return true;
});
}
function renderTable() {
const tbody = P.el('containers-tbody');
const visible = applyFilters();
tbody.innerHTML = visible.length
? visible.map(rowHtml).join('')
: '| No containers match. |
';
}
function renderCounts() {
const running = allContainers.filter(function (c) { return c.state === 'running'; }).length;
P.el('containers-count-all').textContent = 'All ' + allContainers.length;
P.el('containers-count-running').textContent = 'Running ' + running;
P.el('containers-count-stopped').textContent = 'Stopped ' + (allContainers.length - running);
}
function load() {
const tbody = P.el('containers-tbody');
tbody.innerHTML = P.loadingRow(7);
return P.get('containers', 'list').then(function (data) {
allContainers = data;
renderCounts();
renderTable();
}).catch(function (err) {
tbody.innerHTML = P.errorRow(7, err.message);
});
}
function handleAction(id, action, btn) {
const doIt = function (extra) {
btn.disabled = true;
return P.post('containers', action, Object.assign({ id: id }, extra)).then(load).catch(function (err) {
alert('Action failed: ' + err.message);
btn.disabled = false;
});
};
if (action === 'remove') {
if (!confirm('Remove this container? This does not remove its volumes.')) return;
doIt({ force: true });
} else {
doIt({});
}
}
function init() {
P.el('containers-search').addEventListener('input', function (e) {
searchTerm = e.target.value.trim().toLowerCase();
renderTable();
});
document.querySelectorAll('#containers-filterset button').forEach(function (btn) {
btn.addEventListener('click', function () {
document.querySelectorAll('#containers-filterset button').forEach(function (b) { b.classList.remove('active'); });
btn.classList.add('active');
filter = btn.dataset.filter;
renderTable();
});
});
P.el('containers-tbody').addEventListener('click', function (e) {
const btn = e.target.closest('button[data-action]');
if (!btn || btn.disabled) return;
const row = btn.closest('tr');
handleAction(row.dataset.id, btn.dataset.action, btn);
});
return load();
}
P.registerPanel('containers', { init: init, refresh: load });
})();