diff --git a/services/web-ui/public/screens-jobs.jsx b/services/web-ui/public/screens-jobs.jsx index ebf208f..ba4503c 100644 --- a/services/web-ui/public/screens-jobs.jsx +++ b/services/web-ui/public/screens-jobs.jsx @@ -82,11 +82,19 @@ function Jobs({ navigate }) { .catch(e => alert('Retry failed: ' + e.message)); }, [refresh]); - const handleDelete = React.useCallback((job) => { - if (!window.confirm('Remove this job from the queue?')) return; + // One handler covers cancel (running) AND delete (queued / done / failed). + // BullMQ's job.remove() — what the API calls — works on any state, so a + // stalled-active job (worker died mid-process, holding a concurrency slot) + // gets yanked and the next queued job runs. mode just changes the prompt + // copy so the operator knows what they're doing. + const handleDelete = React.useCallback((job, mode) => { + const msg = mode === 'cancel' + ? 'Cancel this running ' + job.kind + ' job?\n\nThe worker may run a few seconds longer in the background, but its result will be discarded and the queue slot frees up immediately.' + : 'Remove this ' + job.status + ' ' + job.kind + ' job from the queue?'; + if (!window.confirm(msg)) return; window.ZAMPP_API.fetch('/jobs/' + job.id, { method: 'DELETE' }) .then(() => setJobs(prev => prev.filter(j => j.id !== job.id))) - .catch(e => alert('Delete failed: ' + e.message)); + .catch(e => alert((mode === 'cancel' ? 'Cancel' : 'Delete') + ' failed: ' + e.message)); }, []); // Retry every failed job at once. Useful after a transient infra issue @@ -221,12 +229,22 @@ function JobRow({ job, onRetry, onDelete }) { })()}