From e5e6db6dc5a7459cb3cd98cac810037d466a7299 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 28 Jul 2026 13:57:25 +0000 Subject: [PATCH] feat(setup): the datasource list shows the real connect verdict, with the operator-facing reason (framework#3827) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The framework's datasource-admin list now reports a four-state `status` from the retained connect verdict — ok | error | blocked | unvalidated — plus an operator-facing `statusReason` (framework#3827/#3869), and since framework#3869 the primary `default` datasource appears in the list too. The page rendered `status` as an undifferentiated text fragment ("· error"), so a dead datasource read exactly like an untested one and the reason never surfaced. - Status chip per verdict: connected (emerald) / connect failed (destructive) / blocked by policy (amber) / not connected (muted). Unknown values fall back to a muted chip with the raw string, so a future framework state degrades readably instead of disappearing. - `statusReason` shows under the row for error/blocked (truncated, full text via the native title tooltip — the file's existing idiom). This surface is admin-gated; end users never see these strings (framework#3828). Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01TQVM3A9Yd6N2eZS8ZcdnMk --- .../datasource/DatasourceResourcePage.tsx | 50 ++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/packages/app-shell/src/views/metadata-admin/datasource/DatasourceResourcePage.tsx b/packages/app-shell/src/views/metadata-admin/datasource/DatasourceResourcePage.tsx index 230bc39a11..1d9f2da712 100644 --- a/packages/app-shell/src/views/metadata-admin/datasource/DatasourceResourcePage.tsx +++ b/packages/app-shell/src/views/metadata-admin/datasource/DatasourceResourcePage.tsx @@ -70,10 +70,53 @@ interface DatasourceRow { name: string; label?: string; driver?: string; + /** + * Last connect verdict, from the framework's retained connection state + * (framework#3827): `ok` (live driver registered) | `error` (connect + * attempted and failed — see statusReason) | `blocked` (the host's connect + * policy refused it; a decision, not a fault) | `unvalidated` (no connect + * attempted — e.g. a managed datasource the auto-connect gate leaves + * metadata-only). + */ status?: string; + /** + * Operator-facing detail behind `error` / `blocked` — the raw connect error + * or the policy's reason. This surface is admin-gated, so showing it here is + * intended; end users never see it (framework#3828). + */ + statusReason?: string; origin?: string; active?: boolean; } + +/** + * Status chip per verdict. `unvalidated` stays visually quiet — it means + * "nothing is known", not "something is wrong" — while `error`/`blocked` carry + * the operator-facing reason as a native tooltip (the file's existing idiom). + */ +function StatusChip({ status, reason }: { status?: string; reason?: string }) { + if (!status) return null; + const chip = (cls: string, label: string) => ( + + {label} + + ); + switch (status) { + case 'ok': + return chip('bg-emerald-500/10 text-emerald-600 dark:text-emerald-400', 'connected'); + case 'error': + return chip('bg-destructive/10 text-destructive', 'connect failed'); + case 'blocked': + return chip('bg-amber-500/10 text-amber-600 dark:text-amber-400', 'blocked by policy'); + case 'unvalidated': + return chip('bg-muted text-muted-foreground', 'not connected'); + default: + return chip('bg-muted text-muted-foreground', status); + } +} interface RemoteTable { name: string; schema?: string; columnCount?: number } interface JsonProp { @@ -456,9 +499,14 @@ export function DatasourceResourcePage(_props: { type?: string }): React.ReactEl
{ds.driver} - {ds.status && · {ds.status}} + {ds.origin && · {ds.origin}}
+ {ds.statusReason && (ds.status === 'error' || ds.status === 'blocked') && ( +
+ {ds.statusReason} +
+ )}