Beginner UX: interpret health status with plain-language verdict (#11224)

Validated on the combined batch board (health-page static test green, new testing-library deps installed for the component test) + this branch: i18n suites 16/16 with Vietnamese translations of the verdict/diagnostics strings added here. Plain-language verdict leads; advanced diagnostics collapse behind a toggle. Thank you @ignamiranda!
This commit is contained in:
ignamiranda
2026-08-23 05:44:29 -04:00
committed by GitHub
parent dfe73f4609
commit ef8414b022
6 changed files with 176 additions and 7 deletions

95
package-lock.json generated
View File

@@ -104,8 +104,10 @@
"@stryker-mutator/core": "^10.0.0",
"@stryker-mutator/tap-runner": "^10.0.0",
"@tailwindcss/postcss": "^4.3.0",
"@testing-library/dom": "^10.4.1",
"@testing-library/jest-dom": "^7.0.1",
"@testing-library/react": "^16.3.2",
"@testing-library/user-event": "^14.6.6",
"@types/better-sqlite3": "^9.6.0",
"@types/bun": "latest",
"@types/node": "^26.2.0",
@@ -12178,6 +12180,26 @@
"tailwindcss": "4.3.3"
}
},
"node_modules/@testing-library/dom": {
"version": "10.4.1",
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz",
"integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.10.4",
"@babel/runtime": "^7.12.5",
"@types/aria-query": "^5.0.1",
"aria-query": "5.3.0",
"dom-accessibility-api": "^0.5.9",
"lz-string": "^1.5.0",
"picocolors": "1.1.1",
"pretty-format": "^27.0.2"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@testing-library/jest-dom": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-7.0.1.tgz",
@@ -12242,6 +12264,20 @@
}
}
},
"node_modules/@testing-library/user-event": {
"version": "14.6.6",
"resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.6.tgz",
"integrity": "sha512-Jbs9FpkkIDw8FgSc6kOVsOv8JuuqGAL7J4X1oot77JxAoDlkNn2GRkd0aYRVuQ+pVQAiHWVkE4rX/dkF5fBiCw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12",
"npm": ">=6"
},
"peerDependencies": {
"@testing-library/dom": ">=7.21.4"
}
},
"node_modules/@tokenizer/inflate": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.4.1.tgz",
@@ -12326,6 +12362,13 @@
"tslib": "^2.4.0"
}
},
"node_modules/@types/aria-query": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz",
"integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/better-sqlite3": {
"version": "9.6.0",
"resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-9.6.0.tgz",
@@ -18138,6 +18181,13 @@
"dev": true,
"license": "MIT"
},
"node_modules/dom-accessibility-api": {
"version": "0.5.16",
"resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz",
"integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==",
"dev": true,
"license": "MIT"
},
"node_modules/dompurify": {
"version": "3.4.13",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.13.tgz",
@@ -26532,6 +26582,16 @@
"node": ">=12"
}
},
"node_modules/lz-string": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz",
"integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==",
"dev": true,
"license": "MIT",
"bin": {
"lz-string": "bin/bin.js"
}
},
"node_modules/magic-string": {
"version": "0.30.21",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
@@ -31360,6 +31420,41 @@
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/pretty-format": {
"version": "27.5.1",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz",
"integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1",
"ansi-styles": "^5.0.0",
"react-is": "^17.0.1"
},
"engines": {
"node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
}
},
"node_modules/pretty-format/node_modules/ansi-styles": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/pretty-format/node_modules/react-is": {
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
"dev": true,
"license": "MIT"
},
"node_modules/pretty-ms": {
"version": "9.3.0",
"resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.3.0.tgz",

View File

@@ -358,8 +358,10 @@
"@stryker-mutator/core": "^10.0.0",
"@stryker-mutator/tap-runner": "^10.0.0",
"@tailwindcss/postcss": "^4.3.0",
"@testing-library/dom": "^10.4.1",
"@testing-library/jest-dom": "^7.0.1",
"@testing-library/react": "^16.3.2",
"@testing-library/user-event": "^14.6.6",
"@types/better-sqlite3": "^9.6.0",
"@types/bun": "latest",
"@types/node": "^26.2.0",

View File

@@ -13,6 +13,7 @@
*/
import { useState, useEffect, useCallback } from "react";
import { Card } from "@/shared/components";
import { AI_PROVIDERS } from "@/shared/constants/providers";
import { getProviderDisplayName } from "@/lib/display/names";
@@ -74,6 +75,7 @@ export default function HealthPage() {
const [repairingDb, setRepairingDb] = useState(false);
const [unblocking, setUnblocking] = useState(false);
const [unblockingKey, setUnblockingKey] = useState<string | null>(null);
const [showAdvanced, setShowAdvanced] = useState(false);
const fetchHealth = useCallback(async () => {
try {
@@ -266,6 +268,21 @@ export default function HealthPage() {
</div>
{/* Status Banner */}
{/* Verdict Header */}
<div className="mb-8">
<h1 className="text-3xl font-bold mb-2">
{
data.status === "healthy"
? t("healthVerdictReady")
: data.status === "cooling"
? t("healthVerdictCoolingDown")
: t("healthVerdictActionRequired")
}
</h1>
<p className="text-text-muted text-lg">{t("healthSubtitle")}</p>
</div>
{/* Status Details */}
<div
role="status"
aria-live="polite"
@@ -283,15 +300,29 @@ export default function HealthPage() {
{data.status === "healthy" ? "check_circle" : "error"}
</span>
<span className={data.status === "healthy" ? "text-green-400" : "text-red-400"}>
{data.status === "healthy" ? t("allOperational") : t("issuesDetected")}
{data.status === "healthy"
? t("allOperational")
: t("issuesDetected")}
</span>
</div>
<TelemetryCard />
<ProviderHealthAutopilotCard />
<ProviderHealthMatrixCard />
{/* Advanced Diagnostics Section */}
<div className="mt-8">
<div className="flex justify-between items-center mb-4">
<h2 className="text-xl font-semibold">{t("advancedDiagnosticsTitle")}</h2>
<button
onClick={() => setShowAdvanced(!showAdvanced)}
className="text-primary hover:underline"
>
{showAdvanced ? t("hide") : t("show")}
</button>
</div>
<div className={showAdvanced ? "block" : "hidden"}>
<TelemetryCard />
<ProviderHealthAutopilotCard />
<ProviderHealthMatrixCard />
</div>
</div>
<Card className="p-5">
<div className="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">

View File

@@ -1214,6 +1214,12 @@
"consoleLogsSubtitle": "Console output",
"logsActivitySubtitle": "User activity log",
"healthSubtitle": "System health check",
"healthVerdictReady": "OmniRoute is ready",
"healthVerdictActionRequired": "Action required to restore full operation",
"healthVerdictCoolingDown": "Cooling down after recent changes",
"advancedDiagnosticsTitle": "Advanced diagnostics",
"hide": "Hide",
"show": "Show",
"costsPricingSubtitle": "Per-model pricing rules",
"costsBudgetSubtitle": "Budget limits",
"costsQuotaShareSubtitle": "Share provider quotas across keys",

View File

@@ -1293,7 +1293,13 @@
"open": "mở",
"close": "đóng"
},
"noResults": "Không có kết quả"
"noResults": "Không có kết quả",
"healthVerdictReady": "OmniRoute đã sẵn sàng",
"healthVerdictActionRequired": "Cần hành động để khôi phục hoạt động đầy đủ",
"healthVerdictCoolingDown": "Đang nguội sau các thay đổi gần đây",
"advancedDiagnosticsTitle": "Chẩn đoán nâng cao",
"hide": "Ẩn",
"show": "Hiện"
},
"webhooks": {
"title": "Webhook",

View File

@@ -0,0 +1,29 @@
import test from "node:test";
import assert from "node:assert/strict";
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
const pagePath = path.join(repoRoot, "src/app/(dashboard)/dashboard/health/page.tsx");
function readPage() {
return fs.readFileSync(pagePath, "utf8");
}
test("health page leads with a plain-language verdict and a collapsible advanced section", () => {
const source = readPage();
// Verdict header with plain-language states
assert.match(source, /healthVerdictReady/);
assert.match(source, /healthVerdictCoolingDown/);
assert.match(source, /healthVerdictActionRequired/);
// No hardcoded English outcomes in the verdict header
assert.doesNotMatch(source, /OmniRoute is ready/);
// Collapsible "Advanced diagnostics" section
assert.match(source, /advancedDiagnosticsTitle/);
assert.match(source, /setShowAdvanced/);
assert.match(source, /showAdvanced \? t\("hide"\) : t\("show"\)/);
});