mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-24 08:02:14 +03:00
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:
95
package-lock.json
generated
95
package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
29
tests/unit/health-page-static.test.ts
Normal file
29
tests/unit/health-page-static.test.ts
Normal 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"\)/);
|
||||
});
|
||||
Reference in New Issue
Block a user