Job Tray Updates
Release rolling / release (push) Successful in 9m12s

This commit is contained in:
Raven Scott
2026-07-16 04:20:26 -04:00
parent 6866e83439
commit 2c35f5cb82
6 changed files with 1051 additions and 100 deletions
+233 -50
View File
@@ -2124,6 +2124,24 @@ async function pruneResource(kind) {
});
if (!confirmed) return;
if (typeof window.peardockOps?.pruneJob === 'function') {
try {
await window.peardockOps.pruneJob({
kind,
label: conf.label,
method: conf.method,
});
if (kind === 'containers') sendCommand('listContainers');
if (kind === 'images' || kind === 'builder') sendCommand('listImages');
if (kind === 'networks') sendCommand('listNetworks');
if (kind === 'volumes') sendCommand('listVolumes');
sendCommand('getSystemDf');
} catch (err) {
if (!err?.viaJob) showAlert('danger', err.message || 'Prune failed');
}
return;
}
showStatusIndicator(`Pruning ${conf.label}`);
try {
const response = await sendCommand(conf.method, {});
@@ -2177,6 +2195,24 @@ async function systemPrune(opts = {}) {
});
if (!confirmed) return;
if (typeof window.peardockOps?.pruneJob === 'function') {
try {
await window.peardockOps.pruneJob({
kind: 'system',
label: volumes ? 'system (+ volumes)' : 'system',
method: Methods.systemPrune || 'systemPrune',
args: { volumes, all: false },
});
sendCommand('listContainers');
sendCommand('listImages');
sendCommand('listNetworks');
sendCommand('getSystemDf');
} catch (err) {
if (!err?.viaJob) showAlert('danger', err.message || 'System prune failed');
}
return;
}
showStatusIndicator('Running docker system prune…');
try {
const response = await sendCommand('systemPrune', { volumes, all: false });
@@ -2329,6 +2365,20 @@ async function recreateContainerAction(container) {
});
if (!confirmed) return;
if (typeof window.peardockOps?.recreateContainersJob === 'function') {
try {
await window.peardockOps.recreateContainersJob({
ids: [container.Id],
names: [name],
start: true,
});
sendCommand('listContainers');
} catch (err) {
if (!err?.viaJob) showAlert('danger', err.message || 'Recreate failed');
}
return;
}
showStatusIndicator(`Recreating ${name}`);
try {
const response = await sendCommand('recreateContainer', {
@@ -3513,6 +3563,18 @@ function renderStacks(stacks) {
}
});
if (!confirmed) return;
if (typeof window.peardockOps?.removeStackJob === 'function') {
try {
await window.peardockOps.removeStackJob({ stackName });
loadStacks();
} catch (error) {
if (!error?.viaJob) {
showAlert('danger', error.message || 'Failed to remove stack');
}
}
return;
}
showStatusIndicator(`Removing stack "${stackName}"...`);
sendCommand('removeStack', { stackName });
@@ -3827,6 +3889,28 @@ function setupBuildImageHandler() {
return;
}
if (typeof window.peardockOps?.buildImageJob === 'function') {
try {
const modal = bootstrap.Modal.getInstance(document.getElementById('buildImageModal'));
if (modal) modal.hide();
await window.peardockOps.buildImageJob({
tag,
dockerfile,
buildFn: async () => {
sendCommand('buildImage', { tag, dockerfile });
return waitForPeerResponse('Image built successfully');
},
});
buildImageForm.reset();
sendCommand('listImages');
} catch (error) {
if (!error?.viaJob) {
showAlert('danger', error.message || 'Failed to build image');
}
}
return;
}
showStatusIndicator(`Building image "${tag}"...`);
sendCommand('buildImage', { tag, dockerfile });
@@ -3957,6 +4041,19 @@ function createVolume() {
const modal = bootstrap.Modal.getInstance(document.getElementById('createVolumeModal'));
if (modal) modal.hide();
if (typeof window.peardockOps?.createVolumeJob === 'function') {
window.peardockOps
.createVolumeJob({ name, driver: driver || undefined })
.then(() => {
if (typeof loadVolumes === 'function') loadVolumes();
else sendCommand('listVolumes');
})
.catch((err) => {
if (!err?.viaJob) showAlert('danger', err.message || 'Failed to create volume');
});
return;
}
showStatusIndicator(`Creating volume "${name}"...`);
sendCommand('createVolume', { name, driver });
@@ -4303,6 +4400,41 @@ async function runContainerDetailAction(action, container, btn) {
if (!spec) return;
if (btn) btn.disabled = true;
if (typeof window.peardockOps?.containerActionJob === 'function') {
try {
await window.peardockOps.containerActionJob({
action,
id,
name,
force: true,
});
sendCommand('listContainers');
if (action === 'remove') {
currentContainerDetails = null;
navigateToView('containers');
return;
}
const nextState =
action === 'start' || action === 'resume' || action === 'restart'
? 'running'
: action === 'stop' || action === 'kill'
? 'exited'
: action === 'pause'
? 'paused'
: container.State;
const updated = { ...container, State: nextState, Status: nextState };
currentContainerDetails = updated;
if (typeof window !== 'undefined') window.currentContainerDetails = updated;
updateContainerDetailsActions(updated);
} catch (err) {
if (!err?.viaJob) presentError(err, spec.method, { showAlert });
} finally {
if (btn) btn.disabled = false;
}
return;
}
showStatusIndicator(`${spec.label}ing ${name}`);
try {
const payload =
@@ -7145,6 +7277,29 @@ async function runBulkContainerOperation(operation, meta) {
if (!ok) return;
}
if (typeof window.peardockOps?.bulkContainerJob === 'function') {
try {
const names = selected.map((id) => {
const row = document.querySelector(`.container-checkbox[value="${id}"]`);
const tr = row?.closest('tr');
const nameCell = tr?.querySelector('[data-container-name], .container-name, td:nth-child(2)');
return (nameCell?.textContent || '').trim().replace(/^\//, '') || id.slice(0, 12);
});
await window.peardockOps.bulkContainerJob({
operation,
label: meta.label,
containerIds: selected,
names,
force: true,
});
clearContainerSelection();
sendCommand('listContainers');
} catch (err) {
if (!err?.viaJob) presentError(err, 'bulkContainerOperation', { showAlert });
}
return;
}
showStatusIndicator(`${meta.label}ing ${selected.length} container(s)…`);
try {
const res = await manager.request(Methods.bulkContainerOperation, {
@@ -7300,6 +7455,20 @@ async function bulkRecreateContainers() {
}
if (!ok) return;
if (typeof window.peardockOps?.recreateContainersJob === 'function') {
try {
await window.peardockOps.recreateContainersJob({
ids: selected,
start: true,
});
clearContainerSelection();
sendCommand('listContainers');
} catch (err) {
if (!err?.viaJob) presentError(err, 'recreateContainer', { showAlert });
}
return;
}
showStatusIndicator(`Recreating ${selected.length} container(s)…`);
let successCount = 0;
let failCount = 0;
@@ -7361,6 +7530,20 @@ async function bulkRemoveImages() {
}
});
if (!confirmed) return;
if (typeof window.peardockOps?.removeImagesJob === 'function') {
try {
await window.peardockOps.removeImagesJob({
ids: selected,
force: true,
});
clearImageSelection();
setTimeout(() => loadImages(), 400);
} catch (err) {
if (!err?.viaJob) showAlert('danger', err.message || 'Failed to remove images');
}
return;
}
showStatusIndicator(`Removing ${selected.length} image(s)...`);
let completed = 0;
@@ -10739,66 +10922,66 @@ function addActionListeners(row, container) {
const killBtn = row.querySelector('.action-kill');
const topBtn = row.querySelector('.action-top');
// Start Button (primary row actions only — more actions use the modal)
if (!startBtn) return;
startBtn.addEventListener('click', async () => {
showStatusIndicator(`Starting container "${container.Names[0]}"...`);
sendCommand('startContainer', { id: container.Id });
const expectedMessageFragment = `Container ${container.Id} started`;
try {
const response = await waitForPeerResponse(expectedMessageFragment);
console.log('[DEBUG] Start container response:', response);
showAlert('success', response.message);
// Refresh the container list to update states
sendCommand('listContainers');
// Restart stats interval
startStatsInterval();
} catch (error) {
console.error('[ERROR] Failed to start container:', error.message);
showAlert('danger', error.message || 'Failed to start container.');
} finally {
console.log('[DEBUG] Hiding status indicator in startBtn finally block');
hideStatusIndicator();
}
});
if (stopBtn) {
stopBtn.addEventListener('click', async () => {
showStatusIndicator(`Stopping container "${container.Names[0]}"...`);
sendCommand('stopContainer', { id: container.Id });
const expectedMessageFragment = `Container ${container.Id} stopped`;
const cname = (container.Names?.[0] || '').replace(/^\//, '') || container.Id.slice(0, 12);
/** @param {string} action */
async function runRowContainerAction(action) {
if (typeof window.peardockOps?.containerActionJob === 'function') {
try {
const response = await waitForPeerResponse(expectedMessageFragment);
console.log('[DEBUG] Stop container response:', response);
showAlert('success', response.message);
// Refresh the container list to update states
await window.peardockOps.containerActionJob({
action,
id: container.Id,
name: cname,
force: true,
});
sendCommand('listContainers');
// Restart stats interval
startStatsInterval();
} catch (error) {
console.error('[ERROR] Failed to stop container:', error.message);
showAlert('danger', error.message || 'Failed to stop container.');
} finally {
console.log('[DEBUG] Hiding status indicator in stopBtn finally block');
hideStatusIndicator();
if (!error?.viaJob) {
showAlert('danger', error.message || `Failed to ${action} container.`);
}
}
});
return;
}
// Legacy fallback
const methodMap = {
start: 'startContainer',
stop: 'stopContainer',
restart: 'restartContainer',
kill: 'killContainer',
pause: 'pauseContainer',
resume: 'unpauseContainer',
};
const method = methodMap[action];
if (!method) return;
showStatusIndicator(`${action} container "${cname}"…`);
sendCommand(method, { id: container.Id });
try {
const response = await waitForPeerResponse(`Container ${container.Id}`);
showAlert('success', response.message);
sendCommand('listContainers');
startStatsInterval();
} catch (error) {
showAlert('danger', error.message || `Failed to ${action} container.`);
} finally {
hideStatusIndicator();
}
}
// Start Button (primary row actions only — more actions use the modal)
if (!startBtn) return;
startBtn.addEventListener('click', () => runRowContainerAction('start'));
if (stopBtn) {
stopBtn.addEventListener('click', () => runRowContainerAction('stop'));
}
// Restart / kill / pause / etc. live in the more-actions modal when not on the row
if (restartBtn) {
restartBtn.addEventListener('click', async () => {
if (typeof window.peardockOps?.containerActionJob === 'function') {
await runRowContainerAction('restart');
return;
}
showStatusIndicator(`Restarting container "${container.Names[0]}"...`);
sendCommand('restartContainer', { id: container.Id });