From 6d42c7b440abb9c767e8fcb174214d603bb8db14 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 1 Jun 2026 03:32:16 +1000 Subject: [PATCH] feat(ui): jobs view stub + sidebar entry Co-Authored-By: Claude Opus 4.7 --- public/app.js | 3 ++- public/components/sidebar.js | 1 + public/router.js | 1 + public/views/jobs.js | 29 +++++++++++++++++++++++++++++ 4 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 public/views/jobs.js diff --git a/public/app.js b/public/app.js index 5f7d11f..8eb0cde 100644 --- a/public/app.js +++ b/public/app.js @@ -19,7 +19,8 @@ const VIEWS = { resource: () => import('./views/resource.js'), search: () => import('./views/search.js'), inbox: () => import('./views/inbox.js'), - 'sacred-valley': () => import('./views/sacred_valley.js') + 'sacred-valley': () => import('./views/sacred_valley.js'), + jobs: () => import('./views/jobs.js') }; async function renderView(ctx) { diff --git a/public/components/sidebar.js b/public/components/sidebar.js index 090155e..c584e79 100644 --- a/public/components/sidebar.js +++ b/public/components/sidebar.js @@ -93,6 +93,7 @@ export function renderSidebar(root) { navItem('Sacred Valley', '/sacred-valley'), navItem('Search', '/search'), inboxItem, + navItem('Jobs', '/jobs'), el('div', { class: 'sb-item muted', title: 'Ships post-Plan-2' }, 'Agents — later'), el('div', { class: 'sb-item muted', title: 'Ships post-Plan-2' }, 'Resources — later') ) diff --git a/public/router.js b/public/router.js index 73840ae..a68700a 100644 --- a/public/router.js +++ b/public/router.js @@ -19,6 +19,7 @@ const ROUTES = [ { name: 'search', re: /^\/search$/, keys: [] }, { name: 'inbox', re: /^\/inbox$/, keys: [] }, { name: 'sacred-valley', re: /^\/sacred-valley$/, keys: [] }, + { name: 'jobs', re: /^\/jobs$/, keys: [] }, { name: 'home', re: /^\/?$/, keys: [] } ]; diff --git a/public/views/jobs.js b/public/views/jobs.js new file mode 100644 index 0000000..f8c9d38 --- /dev/null +++ b/public/views/jobs.js @@ -0,0 +1,29 @@ +// A7 stub — full panel ships in D5. +import { api } from '../api.js'; +import { el, mount } from '../dom.js'; + +function row(j) { + return el('li', {}, + el('span', { class: 'status idle' }, j.state), + ' ', + el('span', { style: { fontFamily: 'var(--font-mono)' } }, j.name), + ' ', + el('span', { class: 'muted' }, (j.id || '').slice(0, 8)) + ); +} + +export async function render(main) { + const wrap = el('div'); + mount(main, + el('h1', { class: 'view-h1' }, 'Jobs'), + el('p', { class: 'view-sub muted' }, 'pg-boss queue — recent jobs across states.'), + wrap + ); + try { + const rows = await api.get('/api/jobs?limit=50'); + if (!rows.length) mount(wrap, el('p', { class: 'muted' }, 'No jobs yet.')); + else mount(wrap, el('ul', { class: 'plain' }, rows.map(row))); + } catch (e) { + mount(wrap, el('p', { class: 'muted' }, 'Could not load: ' + e.message)); + } +}