fix: wire Jobs Retry (POST /jobs/:id/retry) and Delete (DELETE /jobs/:id) buttons
This commit is contained in:
parent
506ee2d695
commit
fea0f2962b
1 changed files with 52 additions and 33 deletions
|
|
@ -5,35 +5,50 @@ function Jobs({ navigate }) {
|
|||
const [jobs, setJobs] = React.useState(window.ZAMPP_DATA.JOBS);
|
||||
const [lastFetch, setLastFetch] = React.useState(Date.now());
|
||||
|
||||
// Poll for job updates every 5s
|
||||
React.useEffect(() => {
|
||||
const poll = () => {
|
||||
window.ZAMPP_API.fetch('/jobs')
|
||||
.then(raw => {
|
||||
const normalized = (raw || []).map(j => {
|
||||
const statusMap = { waiting: 'queued', active: 'running', completed: 'done', failed: 'failed' };
|
||||
const kindMap = { proxy: 'Proxy', thumbnail: 'Thumbnail', conform: 'Conform', transcode: 'Transcode' };
|
||||
const meta = j.metadata || {};
|
||||
return {
|
||||
...j,
|
||||
status: statusMap[j.status] || j.status,
|
||||
kind: kindMap[j.type] || j.type || 'Job',
|
||||
asset: j.asset_name || meta.filename || '—',
|
||||
eta: '—',
|
||||
node: meta.node || '—',
|
||||
priority: meta.priority || 'normal',
|
||||
error: j.error || null,
|
||||
progress: j.progress || 0,
|
||||
};
|
||||
});
|
||||
window.ZAMPP_DATA.JOBS = normalized;
|
||||
setJobs(normalized);
|
||||
setLastFetch(Date.now());
|
||||
})
|
||||
.catch(() => {});
|
||||
const normalizeJob = (j) => {
|
||||
const statusMap = { waiting: 'queued', active: 'running', completed: 'done', failed: 'failed' };
|
||||
const kindMap = { proxy: 'Proxy', thumbnail: 'Thumbnail', conform: 'Conform', transcode: 'Transcode' };
|
||||
const meta = j.metadata || {};
|
||||
return {
|
||||
...j,
|
||||
status: statusMap[j.status] || j.status,
|
||||
kind: kindMap[j.type] || j.type || 'Job',
|
||||
asset: j.asset_name || meta.filename || '—',
|
||||
eta: '—',
|
||||
node: meta.node || '—',
|
||||
priority: meta.priority || 'normal',
|
||||
error: j.error || null,
|
||||
progress: j.progress || 0,
|
||||
};
|
||||
const i = setInterval(poll, 5000);
|
||||
};
|
||||
|
||||
const refresh = React.useCallback(() => {
|
||||
window.ZAMPP_API.fetch('/jobs')
|
||||
.then(raw => {
|
||||
const norm = (raw || []).map(normalizeJob);
|
||||
window.ZAMPP_DATA.JOBS = norm;
|
||||
setJobs(norm);
|
||||
setLastFetch(Date.now());
|
||||
})
|
||||
.catch(() => {});
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
const i = setInterval(refresh, 5000);
|
||||
return () => clearInterval(i);
|
||||
}, [refresh]);
|
||||
|
||||
const handleRetry = React.useCallback((job) => {
|
||||
window.ZAMPP_API.fetch('/jobs/' + job.id + '/retry', { method: 'POST' })
|
||||
.then(() => refresh())
|
||||
.catch(e => alert('Retry failed: ' + e.message));
|
||||
}, [refresh]);
|
||||
|
||||
const handleDelete = React.useCallback((job) => {
|
||||
if (!window.confirm('Remove this job from the queue?')) 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));
|
||||
}, []);
|
||||
|
||||
const counts = {
|
||||
|
|
@ -51,7 +66,7 @@ function Jobs({ navigate }) {
|
|||
<h1>Jobs</h1>
|
||||
<span className="subtitle">Proxy generation, transcoding, and processing queue</span>
|
||||
<div className="spacer" />
|
||||
<button className="btn ghost sm" onClick={() => window.ZAMPP_API.fetch('/jobs').then(raw => { const norm = (raw||[]).map(j => ({ ...j, status: ({waiting:'queued',active:'running',completed:'done',failed:'failed'})[j.status]||j.status, kind: ({proxy:'Proxy',thumbnail:'Thumbnail',conform:'Conform',transcode:'Transcode'})[j.type]||j.type||'Job', asset: j.asset_name||'—', eta:'—', node:(j.metadata||{}).node||'—', priority:(j.metadata||{}).priority||'normal', error:j.error||null, progress:j.progress||0 })); setJobs(norm); })}>
|
||||
<button className="btn ghost sm" onClick={refresh}>
|
||||
<Icon name="refresh" />Refresh
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -100,14 +115,14 @@ function Jobs({ navigate }) {
|
|||
</div>
|
||||
{filtered.length === 0
|
||||
? <div style={{ padding: '24px', textAlign: 'center', color: 'var(--text-3)' }}>No jobs in this category.</div>
|
||||
: filtered.map(j => <JobRow key={j.id} job={j} />)}
|
||||
: filtered.map(j => <JobRow key={j.id} job={j} onRetry={handleRetry} onDelete={handleDelete} />)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function JobRow({ job }) {
|
||||
function JobRow({ job, onRetry, onDelete }) {
|
||||
const iconMap = { Proxy: 'proxy', Transcode: 'film', Thumbnail: 'image', Conform: 'layers' };
|
||||
return (
|
||||
<div className="job-row">
|
||||
|
|
@ -131,9 +146,13 @@ function JobRow({ job }) {
|
|||
</div>
|
||||
<div className="mono" style={{ fontSize: 12, color: 'var(--text-3)' }}>{job.eta}</div>
|
||||
<div><span className={'badge ' + (job.priority === 'high' ? 'warning' : 'outline')}>{job.priority}</span></div>
|
||||
<div>
|
||||
{job.status === 'failed' && <button className="btn ghost sm"><Icon name="refresh" />Retry</button>}
|
||||
{(job.status === 'queued' || job.status === 'done') && <button className="icon-btn"><Icon name="more" /></button>}
|
||||
<div style={{ display: 'flex', gap: 4 }}>
|
||||
{job.status === 'failed' && (
|
||||
<button className="btn ghost sm" onClick={() => onRetry(job)}><Icon name="refresh" />Retry</button>
|
||||
)}
|
||||
{(job.status === 'queued' || job.status === 'done') && (
|
||||
<button className="icon-btn" title="Remove job" onClick={() => onDelete(job)}><Icon name="x" /></button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
Loading…
Reference in a new issue