// screens-jobs.jsx
function Jobs({ navigate }) {
const [tab, setTab] = React.useState('all');
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 i = setInterval(poll, 5000);
return () => clearInterval(i);
}, []);
const counts = {
all: jobs.length,
running: jobs.filter(j => j.status === 'running').length,
queued: jobs.filter(j => j.status === 'queued').length,
done: jobs.filter(j => j.status === 'done').length,
failed: jobs.filter(j => j.status === 'failed').length,
};
const filtered = tab === 'all' ? jobs : jobs.filter(j => j.status === tab);
return (
Jobs
Proxy generation, transcoding, and processing queue
Running
{counts.running}
{counts.queued} queued
Completed
{counts.done}
Total done
Failed
{counts.failed}
0 ? 'var(--warning)' : '' }}>
{counts.failed > 0 ? 'Needs attention' : 'All clear'}
Total jobs
{counts.all}
Updated {Math.round((Date.now()-lastFetch)/1000)}s ago
{[
{ id: 'all', label: 'All · ' + counts.all },
{ id: 'running', label: 'Running · ' + counts.running },
{ id: 'queued', label: 'Queued · ' + counts.queued },
{ id: 'done', label: 'Done · ' + counts.done },
{ id: 'failed', label: 'Failed · ' + counts.failed },
].map(t => (
))}
Job
Asset
Node
Progress
ETA
Priority
{filtered.length === 0
?
No jobs in this category.
: filtered.map(j =>
)}
);
}
function JobRow({ job }) {
const iconMap = { Proxy: 'proxy', Transcode: 'film', Thumbnail: 'image', Conform: 'layers' };
return (
{job.kind}
{job.asset}
{job.node}
{job.status === 'running' && (
{Math.round(job.progress)}%
)}
{job.status === 'done' &&
Complete}
{job.status === 'queued' &&
Waiting…}
{job.status === 'failed' &&
{job.error || 'Failed'}}
{job.eta}
{job.priority}
{job.status === 'failed' && }
{(job.status === 'queued' || job.status === 'done') && }
);
}
window.Jobs = Jobs;