← back to Ventura Claw
Wizard: bulk health strip — see all saved connectors auth status at a glance
1ad784f8160a44aedf062eb2b075e0f9fc6b68d9 · 2026-05-06 16:58:34 -0700 · SteveStudio2
- New strip at top of wizard shows one pill per filled connector (✓ ok / ⊘ catalog-only / ✗ rejected)
- Pulls from existing /api/connectors/health-all (30s cached, no rate hit)
- Click a pill to insert that connector into the wizard queue at current cursor
(handy for fixing a broken one without leaving the page)
- Re-test button refreshes the strip on demand
- Connectors absent from /health-all (catalog-only filled) render as ⊘ skipped, not stuck-in-testing
- Summary line: 'N authenticate · M no test handler · K rejected'
Files touched
M server/public/admin-wizard.html
Diff
commit 1ad784f8160a44aedf062eb2b075e0f9fc6b68d9
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date: Wed May 6 16:58:34 2026 -0700
Wizard: bulk health strip — see all saved connectors auth status at a glance
- New strip at top of wizard shows one pill per filled connector (✓ ok / ⊘ catalog-only / ✗ rejected)
- Pulls from existing /api/connectors/health-all (30s cached, no rate hit)
- Click a pill to insert that connector into the wizard queue at current cursor
(handy for fixing a broken one without leaving the page)
- Re-test button refreshes the strip on demand
- Connectors absent from /health-all (catalog-only filled) render as ⊘ skipped, not stuck-in-testing
- Summary line: 'N authenticate · M no test handler · K rejected'
---
server/public/admin-wizard.html | 93 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 93 insertions(+)
diff --git a/server/public/admin-wizard.html b/server/public/admin-wizard.html
index 309f779..73837bc 100644
--- a/server/public/admin-wizard.html
+++ b/server/public/admin-wizard.html
@@ -83,6 +83,34 @@
.done-card .checkmark { font-size:64px; color:var(--good); margin-bottom:16px; }
.done-card .stat { font-family:var(--serif); font-size:36px; color:var(--gold); margin:18px 0 8px 0; }
.done-card .stat-label { font-family:var(--mono); font-size:11px; color:var(--ink-mute); letter-spacing:0.14em; text-transform:uppercase; }
+
+ /* Health strip — bulk-test view of every filled connector */
+ .health-strip {
+ margin-bottom: 22px; padding: 14px 18px;
+ background: var(--bg-2); border: 1px solid var(--rule); border-radius: 4px;
+ }
+ .health-strip-header {
+ display: flex; align-items: center; gap: 10px; margin-bottom: 10px;
+ font-family: var(--mono); font-size: 10px; letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-mute);
+ }
+ .health-strip-header .retest {
+ margin-left: auto; padding: 5px 12px; border-radius: 2px; cursor: pointer;
+ font-family: var(--mono); font-size: 9px; letter-spacing: 0.12em; text-transform: uppercase;
+ background: transparent; color: var(--gold); border: 1px solid var(--gold);
+ }
+ .health-strip-header .retest:hover { background: rgba(212,160,74,0.10); }
+ .health-pills { display: flex; flex-wrap: wrap; gap: 6px; }
+ .health-pill {
+ padding: 5px 10px; border-radius: 12px; font-family: var(--mono); font-size: 11px;
+ border: 1px solid var(--rule); display: inline-flex; align-items: center; gap: 6px;
+ cursor: pointer; transition: filter 120ms ease;
+ }
+ .health-pill:hover { filter: brightness(1.15); }
+ .health-pill.ok { background: rgba(143,184,154,0.10); border-color: var(--good); color: var(--good); }
+ .health-pill.skipped { background: rgba(212,160,74,0.06); border-color: var(--ink-faint); color: var(--ink-mute); }
+ .health-pill.bad { background: rgba(200,122,110,0.12); border-color: var(--bad); color: var(--bad); }
+ .health-pill.testing { background: var(--bg-3); color: var(--ink-mute); }
+ .health-pill .pill-icon { font-weight: 700; }
</style>
</head><body>
@@ -97,6 +125,15 @@
<div class="progress-text"><span id="pos">0/0</span><span id="filled">0 connected</span></div>
</div>
+ <div class="health-strip" id="health-strip" style="display:none">
+ <div class="health-strip-header">
+ <span>✦ Already saved · live health</span>
+ <span id="health-summary" style="color:var(--ink-faint);text-transform:none;letter-spacing:0">…</span>
+ <button class="retest" id="health-retest">Re-test</button>
+ </div>
+ <div class="health-pills" id="health-pills"></div>
+ </div>
+
<div id="card" class="card">
<div class="step-tag" id="step-tag">loading…</div>
<h2 id="conn-name"></h2>
@@ -133,18 +170,72 @@ function toast(msg, isError) {
toast._t = setTimeout(() => t.style.display = 'none', 3500);
}
+let allFilled = []; // connections that already have credentials saved (for the health strip)
+
async function load() {
const r = await fetch('/api/me/connections');
const d = await r.json();
totalCatalog = d.connections.length;
allConnectors = d.connections.filter(c => !c.filled);
+ allFilled = d.connections.filter(c => c.filled);
// Already-connected count
const filled = totalCatalog - allConnectors.length;
$('#filled').textContent = filled + ' / ' + totalCatalog + ' connected';
+ // Show health strip if any are saved
+ if (allFilled.length) {
+ $('#health-strip').style.display = 'block';
+ renderHealthStrip(); // initial pass: testing… state
+ refreshHealth(); // hit /api/connectors/health-all in background
+ }
if (!allConnectors.length) return showDone(filled, totalCatalog);
render();
}
+function renderHealthStrip(healthData) {
+ const wrap = $('#health-pills');
+ wrap.innerHTML = '';
+ for (const c of allFilled) {
+ const h = healthData ? healthData[c.id] : undefined;
+ let cls = 'testing', icon = '…', title = 'testing…';
+ if (healthData) {
+ // /health-all only probes real-impl connectors; absence means catalog-only
+ if (!h) { cls = 'skipped'; icon = '⊘'; title = 'catalog-only — no test handler yet'; }
+ else if (h.ok) { cls = 'ok'; icon = '✓'; title = h.label || h.account || h.business || h.email || 'authenticated'; }
+ else if (h.reason === 'not_implemented') { cls = 'skipped'; icon = '⊘'; title = 'no test handler — credentials stored'; }
+ else { cls = 'bad'; icon = '✗'; title = h.reason || h.error || 'failed'; }
+ }
+ const pill = document.createElement('span');
+ pill.className = 'health-pill ' + cls;
+ pill.title = c.name + ' · ' + title;
+ pill.innerHTML = '<span class="pill-icon">' + icon + '</span> ' + esc(c.name);
+ pill.addEventListener('click', () => {
+ // Click pill → push that connector to the queue at the current cursor and re-render
+ if (!allConnectors.find(x => x.id === c.id)) {
+ allConnectors.splice(cursor, 0, c);
+ }
+ render();
+ });
+ wrap.appendChild(pill);
+ }
+ if (healthData) {
+ const ok = allFilled.filter(c => healthData[c.id]?.ok).length;
+ const skipped = allFilled.filter(c => !healthData[c.id] || healthData[c.id]?.reason === 'not_implemented').length;
+ const bad = allFilled.length - ok - skipped;
+ $('#health-summary').textContent = ok + ' authenticate · ' + skipped + ' no test handler · ' + bad + ' rejected';
+ }
+}
+
+async function refreshHealth() {
+ $('#health-summary').textContent = 'testing all…';
+ try {
+ const r = await fetch('/api/connectors/health-all');
+ const j = await r.json();
+ renderHealthStrip(j.data || {});
+ } catch (e) {
+ $('#health-summary').textContent = 'health probe failed: ' + e.message;
+ }
+}
+
function render() {
const c = allConnectors[cursor];
if (!c) return showDone(totalCatalog - allConnectors.length + cursor, totalCatalog);
@@ -292,6 +383,8 @@ $('#btn-vault').addEventListener('click', async () => {
}
});
+$('#health-retest').addEventListener('click', refreshHealth);
+
// Cmd/Ctrl-Enter to save
document.addEventListener('keydown', e => {
if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {
← 437cf54 Wizard: Test button — saves + validates against connector he
·
back to Ventura Claw
·
ENV_KEY_TO_CONNECTOR: align field names with what the handle a6a5067 →