diff --git a/public/views/cards/jobs.js b/public/views/cards/jobs.js new file mode 100644 index 0000000..5f1d02f --- /dev/null +++ b/public/views/cards/jobs.js @@ -0,0 +1,28 @@ +// public/views/cards/jobs.js +import { el, mount } from '../../dom.js'; +import { api } from '../../api.js'; + +let body, timer; +async function load() { + if (!body) return; + try { + const jobs = await api.get('/api/jobs?limit=100'); // flat array of {name,state,...} + const counts = {}; + for (const j of jobs) counts[j.state] = (counts[j.state] || 0) + 1; + const rows = ['active', 'created', 'retry', 'completed', 'failed'] + .filter(s => counts[s]) + .map(s => el('div', { class: 'sv-row' }, el('span', { class: 'k' }, s), el('span', {}, String(counts[s])))); + mount(body, + rows.length ? rows : el('span', { class: 'muted' }, 'No jobs'), + el('a', { href: '#/jobs', class: 'k', style: { display: 'block', marginTop: '8px' } }, 'Open Jobs →') + ); + } catch (e) { + mount(body, el('span', { class: 'muted' }, e.status === 403 ? 'Owner only' : 'Jobs unavailable')); + } +} +export default { + id: 'jobs', title: 'Capture Queue', size: 'm', + mount(el_) { body = el_; load(); }, + start() { timer = setInterval(load, 10000); }, + stop() { clearInterval(timer); body = null; } +}; diff --git a/public/views/sacred_valley.js b/public/views/sacred_valley.js index bee6261..7876cb9 100644 --- a/public/views/sacred_valley.js +++ b/public/views/sacred_valley.js @@ -6,8 +6,9 @@ import { orderCards } from './cards/registry.js'; import clock from './cards/clock.js'; import weather from './cards/weather.js'; import hostPerf from './cards/host_perf.js'; +import jobs from './cards/jobs.js'; -const CARD_MODULES = [clock, weather, hostPerf]; // grows in later tasks +const CARD_MODULES = [clock, weather, hostPerf, jobs]; // grows in later tasks let active = []; // mounted cards needing stop() export async function render(main) {