fix(webui): sp8 review round, an organization page starts its workspace read beside its own reads, a Dashboard tile with no figures read says so, the Organizations heading gets its jump target from secH (#1873, lane sp8-kpis)

This commit was merged in pull request #1972.
This commit is contained in:
jason.woltje committed 2026-11-15 07:56:00 -06:00
1 parent 9400e0d94d
commit c66f1ec00c
1 file changed
+13 -9
+13 -9
View File
@@ -156,7 +156,7 @@
const titleCase = s => String(s || '').replace(/(^|[-_\s])(\w)/g, (_, sep, c) => `${sep ? ' ' : ''}${c.toUpperCase()}`); const titleCase = s => String(s || '').replace(/(^|[-_\s])(\w)/g, (_, sep, c) => `${sep ? ' ' : ''}${c.toUpperCase()}`);
const clock = () => new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' }); const clock = () => new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' });
const plural = (n, one, many = `${one}s`) => `${n} ${n === 1 ? one : many}`; const plural = (n, one, many = `${one}s`) => `${n} ${n === 1 ? one : many}`;
const secH = (title, note = '', link = '') => `<div class="ws-sec-h"><h2>${title}</h2>${note ? `<span class="note">${note}</span>` : ''}${link}</div>`; const secH = (title, note = '', link = '', attrs = '') => `<div class="ws-sec-h"><h2${attrs ? ` ${attrs}` : ''}>${title}</h2>${note ? `<span class="note">${note}</span>` : ''}${link}</div>`;
function settingsList(org, ctx) { function settingsList(org, ctx) {
const { business, settings, instances, orgCount } = ctx; const { business, settings, instances, orgCount } = ctx;
@@ -256,15 +256,17 @@
if (!org) return `<article class="ws ws-dash" data-org-dashboard data-org-unavailable><div class="ws-vh"><h1 tabindex="-1">Organization unavailable</h1><p class="sub">${list ? 'This organization is not on your list.' : 'The organization list could not be read.'}</p></div></article>`; if (!org) return `<article class="ws ws-dash" data-org-dashboard data-org-unavailable><div class="ws-vh"><h1 tabindex="-1">Organization unavailable</h1><p class="sub">${list ? 'This organization is not on your list.' : 'The organization list could not be read.'}</p></div></article>`;
// E2E6 P2-8: a stopped bus is not read for the bus-backed editors; each says the bus is stopped. // E2E6 P2-8: a stopped bus is not read for the bus-backed editors; each says the bus is stopped.
const stopped = !org.busHealthy && org.busStopped === true; const stopped = !org.busHealthy && org.busStopped === true;
// SP8 (#1873): the cross-organization read scoped to this one gives the tiles' figures and, SP1,
// while the bus is down, the reason the banner above everything else says. It runs beside the
// organization's own reads, not after them.
const wsRead = data.workspace({ org: id, sections: ['sessions', 'projects', 'metrics'] }).catch(() => null);
const reads = await Promise.allSettled([data.business(id), data.settings(id), data.instances(id), data.threads(id), data.projects(id), data.queue(id), data.agents(id), data.claims(id), ...(stopped ? [] : [window.MosaicAutonomy?.load(id), window.MosaicPacks?.load(id), window.MosaicWorkflows?.load(id)])]); const reads = await Promise.allSettled([data.business(id), data.settings(id), data.instances(id), data.threads(id), data.projects(id), data.queue(id), data.agents(id), data.claims(id), ...(stopped ? [] : [window.MosaicAutonomy?.load(id), window.MosaicPacks?.load(id), window.MosaicWorkflows?.load(id)])]);
const value = i => (reads[i].status === 'fulfilled' ? reads[i].value : null); const value = i => (reads[i].status === 'fulfilled' ? reads[i].value : null);
const business = value(0), settings = value(1), instances = instancesOf(value(2)); const business = value(0), settings = value(1), instances = instancesOf(value(2));
const threads = rowsOf(value(3)), projectRows = rowsOf(value(4)), queue = rowsOf(value(5)); const threads = rowsOf(value(3)), projectRows = rowsOf(value(4)), queue = rowsOf(value(5));
const agents = rowsOf(value(6)), claims = rowsOf(value(7)); const agents = rowsOf(value(6)), claims = rowsOf(value(7));
const docRead = business?.business ? await docs(id, business.business.projects).catch(() => ({ rows: [], failed: true })) : null; const docRead = business?.business ? await docs(id, business.business.projects).catch(() => ({ rows: [], failed: true })) : null;
// SP8 (#1873): the cross-organization read scoped to this one gives the tiles' figures and, SP1, const ws = await wsRead;
// while the bus is down, the reason the banner above everything else says.
const ws = await data.workspace({ org: id, sections: ['sessions', 'projects', 'metrics'] }).catch(() => null);
const wsRow = arr(ws?.orgs).find(r => r?.org === id) ?? null; const wsRow = arr(ws?.orgs).find(r => r?.org === id) ?? null;
const downRow = org.busHealthy ? null : wsRow; const downRow = org.busHealthy ? null : wsRow;
await window.MosaicConsole?.ready; await window.MosaicConsole?.ready;
@@ -653,6 +655,8 @@
const parts = (key, f) => { const ok = readers(o, SRC[key]); return per.filter(x => ok.has(x.org)).map(x => ({ id: x.org, n: num(f(x)) })); }; const parts = (key, f) => { const ok = readers(o, SRC[key]); return per.filter(x => ok.has(x.org)).map(x => ({ id: x.org, n: num(f(x)) })); };
const sum = ps => ps.reduce((a, p) => a + p.n, 0); const sum = ps => ps.reduce((a, p) => a + p.n, 0);
const fig = (key, ps) => (m && readers(o, SRC[key]).size ? sum(ps) : null); const fig = (key, ps) => (m && readers(o, SRC[key]).size ? sum(ps) : null);
// A response with no metrics section leaves every figure tile quiet; it says so rather than a bare "Not read".
const noFig = m ? {} : { none: 'Not read: no figures were read' };
const answered = o.rows.filter(r => r.state === 'ok').length; const answered = o.rows.filter(r => r.state === 'ok').length;
// Agents working of all agents: the system Agents page's rows, read the way that page reads them. // Agents working of all agents: the system Agents page's rows, read the way that page reads them.
// An organization whose agent read failed is named, never counted as having none. // An organization whose agent read failed is named, never counted as having none.
@@ -666,10 +670,10 @@
const tiles = !o.rows.length ? '<p class="ws-panel tight note" data-ws-tiles>No organizations yet, so there is nothing to count.</p>' : `<div class="ws-tiles six" data-ws-tiles role="group" aria-label="Key figures"> const tiles = !o.rows.length ? '<p class="ws-panel tight note" data-ws-tiles>No organizations yet, so there is nothing to count.</p>' : `<div class="ws-tiles six" data-ws-tiles role="group" aria-label="Key figures">
${kpiTile(o, { key: 'agents', name: 'Agents working', href: '#/agents?state=working', big: agentOk.length ? sum(agentParts) : null, small: `of ${agentAll.length}`, parts: agentParts, delta: waitingOn ? `${waitingOn} waiting on you` : '', miss: [...notIn(['agents']), ...agentReads.filter(r => !r.inst).map(r => `${esc(o.name(r.id))} (${esc(instWhy(r))})`)] })} ${kpiTile(o, { key: 'agents', name: 'Agents working', href: '#/agents?state=working', big: agentOk.length ? sum(agentParts) : null, small: `of ${agentAll.length}`, parts: agentParts, delta: waitingOn ? `${waitingOn} waiting on you` : '', miss: [...notIn(['agents']), ...agentReads.filter(r => !r.inst).map(r => `${esc(o.name(r.id))} (${esc(instWhy(r))})`)] })}
${kpiTile(o, { key: 'orgs', name: 'Organizations', href: '#/dashboard', big: answered, small: `of ${o.rows.length} running`, delta: o.unread.length ? andList(o.unread.map(r => `${esc(o.name(r.org))} ${offWord(r)}`)) : 'every bus answered', attrs: ' data-ws-jump="orgs"' })} ${kpiTile(o, { key: 'orgs', name: 'Organizations', href: '#/dashboard', big: answered, small: `of ${o.rows.length} running`, delta: o.unread.length ? andList(o.unread.map(r => `${esc(o.name(r.org))} ${offWord(r)}`)) : 'every bus answered', attrs: ' data-ws-jump="orgs"' })}
${kpiTile(o, { key: 'needs', name: 'Needs you', href: '#/inbox', big: needsN, small: needsN === 1 ? 'item' : 'items', parts: ps.needs, delta: needsN && t?.oldestNeedsYouAt ? `oldest ${span(t.oldestNeedsYouAt)}` : 'nothing waiting', miss: notIn(SRC.needs) })} ${kpiTile(o, { key: 'needs', name: 'Needs you', href: '#/inbox', big: needsN, small: needsN === 1 ? 'item' : 'items', parts: ps.needs, delta: needsN && t?.oldestNeedsYouAt ? `oldest ${span(t.oldestNeedsYouAt)}` : 'nothing waiting', miss: notIn(SRC.needs), ...noFig })}
${kpiTile(o, { key: 'tasks', name: 'Open tasks', href: '#/tasks', big: fig('tasks', ps.tasks), parts: ps.tasks, miss: notIn(SRC.tasks) })} ${kpiTile(o, { key: 'tasks', name: 'Open tasks', href: '#/tasks', big: fig('tasks', ps.tasks), parts: ps.tasks, miss: notIn(SRC.tasks), ...noFig })}
${kpiTile(o, { key: 'done', name: 'Done this week', href: '#/tasks', big: fig('done', ps.done), parts: ps.done, miss: notIn(SRC.done) })} ${kpiTile(o, { key: 'done', name: 'Done this week', href: '#/tasks', big: fig('done', ps.done), parts: ps.done, miss: notIn(SRC.done), ...noFig })}
${kpiTile(o, { key: 'projects', name: 'Projects', href: selHref('/projects'), big: fig('projects', ps.projects), small: 'active', parts: ps.projects, delta: `${archived} archived`, miss: notIn(SRC.projects) })} ${kpiTile(o, { key: 'projects', name: 'Projects', href: selHref('/projects'), big: fig('projects', ps.projects), small: 'active', parts: ps.projects, delta: `${archived} archived`, miss: notIn(SRC.projects), ...noFig })}
</div>`; </div>`;
// SP1 (sample r2-01): one row per organization. A figure shows only when its read gave it; an // SP1 (sample r2-01): one row per organization. A figure shows only when its read gave it; an
@@ -726,7 +730,7 @@
${tiles} ${tiles}
<div class="ws-split ws-home-split"> <div class="ws-split ws-home-split">
<section aria-label="Organizations, projects and tasks"> <section aria-label="Organizations, projects and tasks">
${secH('Organizations', `read ${esc(hm(body.readAt))}`).replace('<h2>', '<h2 tabindex="-1" data-ws-orgs-h data-focus-key="orgs-h">')} ${secH('Organizations', `read ${esc(hm(body.readAt))}`, '', 'tabindex="-1" data-ws-orgs-h data-focus-key="orgs-h"')}
${orgList} ${orgList}
${secH('Recent project activity', 'latest change per project; there is no activity feed yet', `<a href="${esc(selHref('/projects'))}" data-focus-key="home-projects">All projects</a>`)} ${secH('Recent project activity', 'latest change per project; there is no activity feed yet', `<a href="${esc(selHref('/projects'))}" data-focus-key="home-projects">All projects</a>`)}
${trail}${missLine(o, ['projects'], 'recent')} ${trail}${missLine(o, ['projects'], 'recent')}