diff --git a/app/(dashboard)/_components/performance-infrastructure-card.tsx b/app/(dashboard)/_components/performance-infrastructure-card.tsx index 1c85a7ff..a1c988f6 100644 --- a/app/(dashboard)/_components/performance-infrastructure-card.tsx +++ b/app/(dashboard)/_components/performance-infrastructure-card.tsx @@ -2,7 +2,53 @@ import { Badge } from "@/components/ui/badge" import { Card, CardContent, CardDescription, CardHeader } from "@/components/ui/card" -import type { RunningStatusTopology } from "@/lib/performance-data" +import type { RunningStatusTopology, StatusDiagnostic } from "@/lib/performance-data" + +export type InfrastructureHealthState = "healthy" | "attention" | "unknown" + +export function getInfrastructureHealthState({ + onlineServers, + offlineServers, + degradedServers, + initializingServers, + unknownServers, + onlineDisks, + offlineDisks, + unknownDisks, + topology, + peerHealth, + storageReadiness, +}: { + onlineServers?: number + offlineServers?: number + degradedServers?: number + initializingServers?: number + unknownServers?: number + onlineDisks?: number + offlineDisks?: number + unknownDisks?: number + topology: RunningStatusTopology + peerHealth?: StatusDiagnostic + storageReadiness?: StatusDiagnostic +}): InfrastructureHealthState { + const resourceNeedsAttention = Boolean((offlineServers ?? 0) + (degradedServers ?? 0) + (offlineDisks ?? 0)) + const diagnosticNeedsAttention = [peerHealth, storageReadiness].some((diagnostic) => diagnostic?.state === "degraded") + if (resourceNeedsAttention || diagnosticNeedsAttention) return "attention" + + const resourceUncertain = Boolean((initializingServers ?? 0) + (unknownServers ?? 0) + (unknownDisks ?? 0)) + const diagnosticUncertain = [peerHealth, storageReadiness].some( + (diagnostic) => diagnostic?.state === "stale" || diagnostic?.state === "unknown", + ) + const serverCounts = [onlineServers, offlineServers, degradedServers, initializingServers, unknownServers] + const diskCounts = [onlineDisks, offlineDisks, unknownDisks] + const dataUnavailable = + serverCounts.some((value) => value === undefined) || + diskCounts.some((value) => value === undefined) || + serverCounts.reduce((total, value) => total + (value ?? 0), 0) === 0 || + diskCounts.reduce((total, value) => total + (value ?? 0), 0) === 0 + + return resourceUncertain || diagnosticUncertain || dataUnavailable || topology.incomplete ? "unknown" : "healthy" +} function HealthMetric({ label, value, unavailableLabel }: { label: string; value?: number; unavailableLabel: string }) { return ( @@ -23,6 +69,8 @@ export function PerformanceInfrastructureCard({ offlineDisks, unknownDisks, topology, + peerHealth, + storageReadiness, t, }: { onlineServers?: number @@ -34,30 +82,34 @@ export function PerformanceInfrastructureCard({ offlineDisks?: number unknownDisks?: number topology: RunningStatusTopology + peerHealth?: StatusDiagnostic + storageReadiness?: StatusDiagnostic t: (key: string) => string }) { - const needsAttention = Boolean((offlineServers ?? 0) + (degradedServers ?? 0) + (offlineDisks ?? 0)) - const hasUnknownState = Boolean((initializingServers ?? 0) + (unknownServers ?? 0) + (unknownDisks ?? 0)) - const serverCounts = [onlineServers, offlineServers, degradedServers, initializingServers, unknownServers] - const diskCounts = [onlineDisks, offlineDisks, unknownDisks] - const dataUnavailable = - serverCounts.some((value) => value === undefined) || - diskCounts.some((value) => value === undefined) || - serverCounts.reduce((total, value) => total + (value ?? 0), 0) === 0 || - diskCounts.reduce((total, value) => total + (value ?? 0), 0) === 0 const hasIncompleteTopology = topology.incomplete - const status = needsAttention - ? t("Needs attention") - : hasUnknownState || dataUnavailable || hasIncompleteTopology - ? t("Unknown") - : t("Healthy") - const description = needsAttention - ? t("Offline or degraded resources require attention.") - : hasIncompleteTopology - ? t("The reported health rows do not cover the full cluster topology.") - : hasUnknownState || dataUnavailable - ? t("Some resource health data is unavailable or still initializing.") - : t("All reported servers and disks are online.") + const healthState = getInfrastructureHealthState({ + onlineServers, + offlineServers, + degradedServers, + initializingServers, + unknownServers, + onlineDisks, + offlineDisks, + unknownDisks, + topology, + peerHealth, + storageReadiness, + }) + const status = + healthState === "attention" ? t("Needs attention") : healthState === "unknown" ? t("Unknown") : t("Healthy") + const description = + healthState === "attention" + ? t("Offline or degraded resources require attention.") + : hasIncompleteTopology + ? t("The reported health rows do not cover the full cluster topology.") + : healthState === "unknown" + ? t("Some resource health data is unavailable or still initializing.") + : t("All reported servers and disks are online.") return ( @@ -70,13 +122,7 @@ export function PerformanceInfrastructureCard({ {description} {status} diff --git a/app/(dashboard)/_components/performance-status-sources.tsx b/app/(dashboard)/_components/performance-status-sources.tsx index 5d41a447..2e1734eb 100644 --- a/app/(dashboard)/_components/performance-status-sources.tsx +++ b/app/(dashboard)/_components/performance-status-sources.tsx @@ -1,8 +1,11 @@ "use client" import * as React from "react" +import { RiArrowDownSLine } from "@remixicon/react" import { Badge } from "@/components/ui/badge" +import { Button } from "@/components/ui/button" import { Card, CardContent, CardDescription, CardHeader } from "@/components/ui/card" +import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible" import { Separator } from "@/components/ui/separator" import type { ClusterDiagnostics, OperationalStatus, StatusDiagnostic } from "@/lib/performance-data" @@ -62,131 +65,208 @@ function DiagnosticRow({ scope?.set ? `${t("Set")}: ${scope.set}` : undefined, scope?.timeout ? `${t("Timeout")}: ${scope.timeout}` : undefined, ].filter(Boolean) + const hasSupportingDetail = Boolean( + diagnostic.lastSuccessfulUpdate || + showLastSuccessfulUpdate || + diagnostic.lastError || + scopeParts.length || + diagnostic.source || + diagnostic.historicalStallTimeouts !== undefined || + diagnostic.hint || + troubleshooting || + troubleshootingHref, + ) return ( -
-
- {label} +
+
{label}
+
{getStatusLabel(diagnostic.state, t)} - -
+
+

{diagnostic.reason ?? getDefaultDescription(diagnostic.state, t)}

- {diagnostic.lastSuccessfulUpdate || showLastSuccessfulUpdate ? ( -

- {t("Last successful update")}:{" "} - {diagnostic.lastSuccessfulUpdate ? formatTimestamp(diagnostic.lastSuccessfulUpdate, locale) : t("Unknown")} -

- ) : null} - {diagnostic.lastError ? ( -

- {t("Last error")}: {diagnostic.lastError} -

- ) : null} - {scopeParts.length ? ( -

{scopeParts.join(" · ")}

- ) : null} - {diagnostic.source ? ( -

- {t("Source")}: {diagnostic.source} -

- ) : null} - {diagnostic.historicalStallTimeouts !== undefined ? ( -
-

- {t("Historical internode stall timeouts")}: {diagnostic.historicalStallTimeouts} -

-

- {t("This lifetime counter has no sampling window and does not indicate current degradation by itself.")} -

+ {hasSupportingDetail ? ( +
+ {diagnostic.lastSuccessfulUpdate || showLastSuccessfulUpdate ? ( +

+ {t("Last successful update")}:{" "} + {diagnostic.lastSuccessfulUpdate + ? formatTimestamp(diagnostic.lastSuccessfulUpdate, locale) + : t("Unknown")} +

+ ) : null} + {diagnostic.lastError ? ( +

+ {t("Last error")}: {diagnostic.lastError} +

+ ) : null} + {scopeParts.length ? ( +

{scopeParts.join(" · ")}

+ ) : null} + {diagnostic.source ? ( +

+ {t("Source")}: {diagnostic.source} +

+ ) : null} + {diagnostic.historicalStallTimeouts !== undefined ? ( + <> +

+ {t("Historical internode stall timeouts")}: {diagnostic.historicalStallTimeouts} +

+

+ {t( + "This lifetime counter has no sampling window and does not indicate current degradation by itself.", + )} +

+ + ) : null} + {diagnostic.hint ? ( +

+ {t("Backend guidance")}: {diagnostic.hint} +

+ ) : null} + {troubleshooting ? ( +

+ {t("Troubleshooting")}: {troubleshooting} +

+ ) : null} + {troubleshootingHref ? ( + + {t("Open the real multi-node metrics verification guide")} + + ) : null}
) : null} - {diagnostic.hint ? ( -

- {t("Backend guidance")}: {diagnostic.hint} -

- ) : null} - {troubleshooting ? ( -

- {t("Troubleshooting")}: {troubleshooting} -

- ) : null} - {troubleshootingHref ? ( - - {t("Open the real multi-node metrics verification guide")} - - ) : null}
) } -export function PerformanceStatusSources({ +function PerformanceStatusSourcesContent({ diagnostics, usageFreshness, t, locale, }: { - diagnostics?: ClusterDiagnostics + diagnostics: ClusterDiagnostics usageFreshness?: StatusDiagnostic t: Translate locale?: string }) { - const notReported: StatusDiagnostic = { state: "not_reported" } - const peerHealth = diagnostics?.peerHealth ?? notReported - const storageReadiness = diagnostics?.storageReadiness ?? notReported - const resolvedUsageFreshness = usageFreshness ?? diagnostics?.usageFreshness ?? notReported - const listingHealth = diagnostics?.listingHealth ?? notReported - const workloadAdmission = diagnostics?.workloadAdmission ?? notReported const rows = [ - { label: t("Peer Health"), diagnostic: peerHealth }, - { label: t("Storage Readiness"), diagnostic: storageReadiness }, - { label: t("Usage Freshness"), diagnostic: resolvedUsageFreshness, showLastSuccessfulUpdate: true }, + { label: t("Peer Health"), diagnostic: diagnostics.peerHealth }, + { label: t("Storage Readiness"), diagnostic: diagnostics.storageReadiness }, + { + label: t("Usage Freshness"), + diagnostic: usageFreshness ?? diagnostics.usageFreshness, + showLastSuccessfulUpdate: true, + }, { label: t("Listing and Metacache"), - diagnostic: listingHealth, + diagnostic: diagnostics.listingHealth, troubleshooting: t( "Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.", ), troubleshootingHref: "https://github.com/rustfs/backlog/issues/1392#issuecomment-5040442761", }, - { label: t("Workload Admission"), diagnostic: workloadAdmission }, + { label: t("Workload Admission"), diagnostic: diagnostics.workloadAdmission }, ] + const hasAttention = rows.some(({ diagnostic }) => ["degraded", "stale", "unknown"].includes(diagnostic.state)) + const reportedRows = rows.filter(({ diagnostic }) => diagnostic.state !== "not_reported") + const attentionCount = rows.filter(({ diagnostic }) => + ["degraded", "stale", "unknown"].includes(diagnostic.state), + ).length + const [open, setOpen] = React.useState(hasAttention) + const previousHasAttention = React.useRef(hasAttention) + + React.useEffect(() => { + if (hasAttention && !previousHasAttention.current) setOpen(true) + previousHasAttention.current = hasAttention + }, [hasAttention]) + + return ( + + + +
+

+ {t("Diagnostic Details")} +

+ + {hasAttention + ? `${attentionCount} ${t("Diagnostic items need confirmation")}` + : reportedRows.length + ? t("All reported diagnostics are healthy.") + : t("No diagnostic sources were reported by the server.")} + +
+ + {t("Expand")} + {t("Collapse")} + + + } + /> +
+ + + +
+ {rows.map((row, index) => ( + + {index ? : null} + + + ))} +
+
+
+
+
+ ) +} + +export function PerformanceStatusSources({ + diagnostics, + usageFreshness, + t, + locale, +}: { + diagnostics?: ClusterDiagnostics + usageFreshness?: StatusDiagnostic + t: Translate + locale?: string +}) { + if (!diagnostics) return null return ( - - -

- {t("Status Sources")} -

- - {t("Review peer, storage, usage, listing, and workload admission health independently.")} - -
- -
- {rows.map((row, index) => ( - - {index ? : null} - - - ))} -
-
-
+ ) } diff --git a/app/(dashboard)/_components/performance-status-summary.tsx b/app/(dashboard)/_components/performance-status-summary.tsx new file mode 100644 index 00000000..6c621d39 --- /dev/null +++ b/app/(dashboard)/_components/performance-status-summary.tsx @@ -0,0 +1,75 @@ +"use client" + +import { RiCheckboxCircleLine, RiErrorWarningLine, RiQuestionLine } from "@remixicon/react" +import type { RunningStatusTopology, StatusDiagnostic } from "@/lib/performance-data" +import { getInfrastructureHealthState } from "./performance-infrastructure-card" + +type Translate = (key: string) => string + +export function PerformanceStatusSummary({ + onlineServers, + offlineServers, + degradedServers, + initializingServers, + unknownServers, + onlineDisks, + offlineDisks, + unknownDisks, + topology, + peerHealth, + storageReadiness, + t, +}: { + onlineServers?: number + offlineServers?: number + degradedServers?: number + initializingServers?: number + unknownServers?: number + onlineDisks?: number + offlineDisks?: number + unknownDisks?: number + topology: RunningStatusTopology + peerHealth?: StatusDiagnostic + storageReadiness?: StatusDiagnostic + t: Translate +}) { + const healthState = getInfrastructureHealthState({ + onlineServers, + offlineServers, + degradedServers, + initializingServers, + unknownServers, + onlineDisks, + offlineDisks, + unknownDisks, + topology, + peerHealth, + storageReadiness, + }) + const Icon = + healthState === "attention" ? RiErrorWarningLine : healthState === "unknown" ? RiQuestionLine : RiCheckboxCircleLine + const label = + healthState === "attention" + ? t("Cluster needs attention") + : healthState === "unknown" + ? t("Cluster status is incomplete") + : t("Cluster is healthy") + + return ( +
+
+ +

+ {label} +

+
+

+ {t("Servers")}: {onlineServers ?? t("Unknown")} {t("Online")} · {t("Disks")}: {onlineDisks ?? t("Unknown")}{" "} + {t("Online")} +

+
+ ) +} diff --git a/app/(dashboard)/_components/performance-usage-card.tsx b/app/(dashboard)/_components/performance-usage-card.tsx index 9c0e737b..668719bb 100644 --- a/app/(dashboard)/_components/performance-usage-card.tsx +++ b/app/(dashboard)/_components/performance-usage-card.tsx @@ -2,8 +2,10 @@ import { RiDatabase2Line } from "@remixicon/react" import { Card, CardContent, CardDescription, CardHeader } from "@/components/ui/card" +import { Badge } from "@/components/ui/badge" import { Progress } from "@/components/ui/progress" import { niceBytes } from "@/lib/functions" +import type { StatusDiagnostic } from "@/lib/performance-data" export interface UsageStat { label: string @@ -16,6 +18,7 @@ export function PerformanceUsageCard({ totalUsedCapacity, usedPercent, usageStats, + usageFreshness, t, }: { totalCapacity?: number @@ -23,9 +26,11 @@ export function PerformanceUsageCard({ totalUsedCapacity?: number usedPercent?: number usageStats: UsageStat[] + usageFreshness?: StatusDiagnostic t: (key: string) => string }) { const formatCapacity = (value?: number) => (value === undefined ? t("Unknown") : niceBytes(String(value))) + const freshnessNeedsAttention = usageFreshness?.state === "degraded" || usageFreshness?.state === "stale" return ( @@ -73,6 +78,17 @@ export function PerformanceUsageCard({ + {freshnessNeedsAttention ? ( +
+ + {usageFreshness.state === "degraded" ? t("Degraded") : t("Stale")} + +

+ {usageFreshness.reason ?? t("Previously reported data may be out of date.")} +

+
+ ) : null} +
{usageStats.map((item) => (
diff --git a/app/(dashboard)/status/page.tsx b/app/(dashboard)/status/page.tsx index 12262807..314027d0 100644 --- a/app/(dashboard)/status/page.tsx +++ b/app/(dashboard)/status/page.tsx @@ -24,6 +24,7 @@ import { PerformanceInfrastructureCard } from "../_components/performance-infras import { PerformanceServerList } from "../_components/performance-server-list" import { PerformanceSummaryCards } from "../_components/performance-summary-cards" import { PerformanceStatusSources } from "../_components/performance-status-sources" +import { PerformanceStatusSummary } from "../_components/performance-status-summary" import { PerformanceUsageCard } from "../_components/performance-usage-card" function formatDuration(seconds: number | undefined, t: (key: string) => string) { @@ -258,11 +259,19 @@ export default function PerformancePage() {
) : null} -
@@ -277,6 +286,8 @@ export default function PerformancePage() { offlineDisks={systemInfo.backend?.offlineDisks} unknownDisks={systemInfo.backend?.unknownDisks} topology={runningStatus.topology} + peerHealth={diagnosticsInfo?.peerHealth} + storageReadiness={diagnosticsInfo?.storageReadiness} t={t} />
@@ -288,6 +299,7 @@ export default function PerformancePage() { totalUsedCapacity={totalUsedCapacity} usedPercent={usedPercent} usageStats={usageStats} + usageFreshness={diagnosticsInfo ? usageFreshness : undefined} t={t} /> @@ -305,6 +317,13 @@ export default function PerformancePage() { + + ) diff --git a/i18n/locales/ar-MA.json b/i18n/locales/ar-MA.json index a90f46bc..5ba2df41 100644 --- a/i18n/locales/ar-MA.json +++ b/i18n/locales/ar-MA.json @@ -1549,5 +1549,14 @@ "Backend guidance": "Backend guidance", "Open the real multi-node metrics verification guide": "Open the real multi-node metrics verification guide", "Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.": "Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.", - "Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently." + "Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently.", + "Diagnostic Details": "Diagnostic Details", + "Diagnostic items need confirmation": "diagnostic items need confirmation", + "All reported diagnostics are healthy.": "All reported diagnostics are healthy.", + "No diagnostic sources were reported by the server.": "No diagnostic sources were reported by the server.", + "Expand": "Expand", + "Collapse": "Collapse", + "Cluster is healthy": "Cluster is healthy", + "Cluster needs attention": "Cluster needs attention", + "Cluster status is incomplete": "Cluster status is incomplete" } diff --git a/i18n/locales/de-DE.json b/i18n/locales/de-DE.json index cd8bdf17..7987aa89 100644 --- a/i18n/locales/de-DE.json +++ b/i18n/locales/de-DE.json @@ -1549,5 +1549,14 @@ "Backend guidance": "Backend guidance", "Open the real multi-node metrics verification guide": "Open the real multi-node metrics verification guide", "Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.": "Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.", - "Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently." + "Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently.", + "Diagnostic Details": "Diagnostic Details", + "Diagnostic items need confirmation": "diagnostic items need confirmation", + "All reported diagnostics are healthy.": "All reported diagnostics are healthy.", + "No diagnostic sources were reported by the server.": "No diagnostic sources were reported by the server.", + "Expand": "Expand", + "Collapse": "Collapse", + "Cluster is healthy": "Cluster is healthy", + "Cluster needs attention": "Cluster needs attention", + "Cluster status is incomplete": "Cluster status is incomplete" } diff --git a/i18n/locales/en-US.json b/i18n/locales/en-US.json index e86e82f2..9d258a4f 100644 --- a/i18n/locales/en-US.json +++ b/i18n/locales/en-US.json @@ -1549,5 +1549,14 @@ "Backend guidance": "Backend guidance", "Open the real multi-node metrics verification guide": "Open the real multi-node metrics verification guide", "Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.": "Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.", - "Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently." + "Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently.", + "Diagnostic Details": "Diagnostic Details", + "Diagnostic items need confirmation": "diagnostic items need confirmation", + "All reported diagnostics are healthy.": "All reported diagnostics are healthy.", + "No diagnostic sources were reported by the server.": "No diagnostic sources were reported by the server.", + "Expand": "Expand", + "Collapse": "Collapse", + "Cluster is healthy": "Cluster is healthy", + "Cluster needs attention": "Cluster needs attention", + "Cluster status is incomplete": "Cluster status is incomplete" } diff --git a/i18n/locales/es-ES.json b/i18n/locales/es-ES.json index b5224fd7..a8fe5a86 100644 --- a/i18n/locales/es-ES.json +++ b/i18n/locales/es-ES.json @@ -1549,5 +1549,14 @@ "Backend guidance": "Backend guidance", "Open the real multi-node metrics verification guide": "Open the real multi-node metrics verification guide", "Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.": "Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.", - "Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently." + "Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently.", + "Diagnostic Details": "Diagnostic Details", + "Diagnostic items need confirmation": "diagnostic items need confirmation", + "All reported diagnostics are healthy.": "All reported diagnostics are healthy.", + "No diagnostic sources were reported by the server.": "No diagnostic sources were reported by the server.", + "Expand": "Expand", + "Collapse": "Collapse", + "Cluster is healthy": "Cluster is healthy", + "Cluster needs attention": "Cluster needs attention", + "Cluster status is incomplete": "Cluster status is incomplete" } diff --git a/i18n/locales/fr-FR.json b/i18n/locales/fr-FR.json index 36861bee..db973152 100644 --- a/i18n/locales/fr-FR.json +++ b/i18n/locales/fr-FR.json @@ -1549,5 +1549,14 @@ "Backend guidance": "Backend guidance", "Open the real multi-node metrics verification guide": "Open the real multi-node metrics verification guide", "Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.": "Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.", - "Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently." + "Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently.", + "Diagnostic Details": "Diagnostic Details", + "Diagnostic items need confirmation": "diagnostic items need confirmation", + "All reported diagnostics are healthy.": "All reported diagnostics are healthy.", + "No diagnostic sources were reported by the server.": "No diagnostic sources were reported by the server.", + "Expand": "Expand", + "Collapse": "Collapse", + "Cluster is healthy": "Cluster is healthy", + "Cluster needs attention": "Cluster needs attention", + "Cluster status is incomplete": "Cluster status is incomplete" } diff --git a/i18n/locales/id-ID.json b/i18n/locales/id-ID.json index c7563c4d..1640d4a2 100644 --- a/i18n/locales/id-ID.json +++ b/i18n/locales/id-ID.json @@ -1549,5 +1549,14 @@ "Backend guidance": "Backend guidance", "Open the real multi-node metrics verification guide": "Open the real multi-node metrics verification guide", "Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.": "Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.", - "Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently." + "Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently.", + "Diagnostic Details": "Diagnostic Details", + "Diagnostic items need confirmation": "diagnostic items need confirmation", + "All reported diagnostics are healthy.": "All reported diagnostics are healthy.", + "No diagnostic sources were reported by the server.": "No diagnostic sources were reported by the server.", + "Expand": "Expand", + "Collapse": "Collapse", + "Cluster is healthy": "Cluster is healthy", + "Cluster needs attention": "Cluster needs attention", + "Cluster status is incomplete": "Cluster status is incomplete" } diff --git a/i18n/locales/it-IT.json b/i18n/locales/it-IT.json index 7c63edae..8846e8bc 100644 --- a/i18n/locales/it-IT.json +++ b/i18n/locales/it-IT.json @@ -1549,5 +1549,14 @@ "Backend guidance": "Backend guidance", "Open the real multi-node metrics verification guide": "Open the real multi-node metrics verification guide", "Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.": "Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.", - "Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently." + "Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently.", + "Diagnostic Details": "Diagnostic Details", + "Diagnostic items need confirmation": "diagnostic items need confirmation", + "All reported diagnostics are healthy.": "All reported diagnostics are healthy.", + "No diagnostic sources were reported by the server.": "No diagnostic sources were reported by the server.", + "Expand": "Expand", + "Collapse": "Collapse", + "Cluster is healthy": "Cluster is healthy", + "Cluster needs attention": "Cluster needs attention", + "Cluster status is incomplete": "Cluster status is incomplete" } diff --git a/i18n/locales/ja-JP.json b/i18n/locales/ja-JP.json index ffa11198..e31a4a82 100644 --- a/i18n/locales/ja-JP.json +++ b/i18n/locales/ja-JP.json @@ -1549,5 +1549,14 @@ "Backend guidance": "Backend guidance", "Open the real multi-node metrics verification guide": "Open the real multi-node metrics verification guide", "Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.": "Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.", - "Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently." + "Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently.", + "Diagnostic Details": "Diagnostic Details", + "Diagnostic items need confirmation": "diagnostic items need confirmation", + "All reported diagnostics are healthy.": "All reported diagnostics are healthy.", + "No diagnostic sources were reported by the server.": "No diagnostic sources were reported by the server.", + "Expand": "Expand", + "Collapse": "Collapse", + "Cluster is healthy": "Cluster is healthy", + "Cluster needs attention": "Cluster needs attention", + "Cluster status is incomplete": "Cluster status is incomplete" } diff --git a/i18n/locales/ko-KR.json b/i18n/locales/ko-KR.json index 3eef1089..04fc893b 100644 --- a/i18n/locales/ko-KR.json +++ b/i18n/locales/ko-KR.json @@ -1549,5 +1549,14 @@ "Backend guidance": "Backend guidance", "Open the real multi-node metrics verification guide": "Open the real multi-node metrics verification guide", "Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.": "Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.", - "Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently." + "Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently.", + "Diagnostic Details": "Diagnostic Details", + "Diagnostic items need confirmation": "diagnostic items need confirmation", + "All reported diagnostics are healthy.": "All reported diagnostics are healthy.", + "No diagnostic sources were reported by the server.": "No diagnostic sources were reported by the server.", + "Expand": "Expand", + "Collapse": "Collapse", + "Cluster is healthy": "Cluster is healthy", + "Cluster needs attention": "Cluster needs attention", + "Cluster status is incomplete": "Cluster status is incomplete" } diff --git a/i18n/locales/pt-BR.json b/i18n/locales/pt-BR.json index ae9f542c..ef37a4f1 100644 --- a/i18n/locales/pt-BR.json +++ b/i18n/locales/pt-BR.json @@ -1549,5 +1549,14 @@ "Backend guidance": "Backend guidance", "Open the real multi-node metrics verification guide": "Open the real multi-node metrics verification guide", "Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.": "Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.", - "Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently." + "Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently.", + "Diagnostic Details": "Diagnostic Details", + "Diagnostic items need confirmation": "diagnostic items need confirmation", + "All reported diagnostics are healthy.": "All reported diagnostics are healthy.", + "No diagnostic sources were reported by the server.": "No diagnostic sources were reported by the server.", + "Expand": "Expand", + "Collapse": "Collapse", + "Cluster is healthy": "Cluster is healthy", + "Cluster needs attention": "Cluster needs attention", + "Cluster status is incomplete": "Cluster status is incomplete" } diff --git a/i18n/locales/ru-RU.json b/i18n/locales/ru-RU.json index c3327886..8a538932 100644 --- a/i18n/locales/ru-RU.json +++ b/i18n/locales/ru-RU.json @@ -1549,5 +1549,14 @@ "Backend guidance": "Backend guidance", "Open the real multi-node metrics verification guide": "Open the real multi-node metrics verification guide", "Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.": "Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.", - "Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently." + "Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently.", + "Diagnostic Details": "Diagnostic Details", + "Diagnostic items need confirmation": "diagnostic items need confirmation", + "All reported diagnostics are healthy.": "All reported diagnostics are healthy.", + "No diagnostic sources were reported by the server.": "No diagnostic sources were reported by the server.", + "Expand": "Expand", + "Collapse": "Collapse", + "Cluster is healthy": "Cluster is healthy", + "Cluster needs attention": "Cluster needs attention", + "Cluster status is incomplete": "Cluster status is incomplete" } diff --git a/i18n/locales/tr-TR.json b/i18n/locales/tr-TR.json index 46f68ab4..d699f487 100644 --- a/i18n/locales/tr-TR.json +++ b/i18n/locales/tr-TR.json @@ -1549,5 +1549,14 @@ "Backend guidance": "Backend guidance", "Open the real multi-node metrics verification guide": "Open the real multi-node metrics verification guide", "Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.": "Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.", - "Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently." + "Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently.", + "Diagnostic Details": "Diagnostic Details", + "Diagnostic items need confirmation": "diagnostic items need confirmation", + "All reported diagnostics are healthy.": "All reported diagnostics are healthy.", + "No diagnostic sources were reported by the server.": "No diagnostic sources were reported by the server.", + "Expand": "Expand", + "Collapse": "Collapse", + "Cluster is healthy": "Cluster is healthy", + "Cluster needs attention": "Cluster needs attention", + "Cluster status is incomplete": "Cluster status is incomplete" } diff --git a/i18n/locales/vi-VN.json b/i18n/locales/vi-VN.json index 73a83b1d..8162496d 100644 --- a/i18n/locales/vi-VN.json +++ b/i18n/locales/vi-VN.json @@ -1549,5 +1549,14 @@ "Backend guidance": "Backend guidance", "Open the real multi-node metrics verification guide": "Open the real multi-node metrics verification guide", "Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.": "Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.", - "Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently." + "Review peer, storage, usage, listing, and workload admission health independently.": "Review peer, storage, usage, listing, and workload admission health independently.", + "Diagnostic Details": "Diagnostic Details", + "Diagnostic items need confirmation": "diagnostic items need confirmation", + "All reported diagnostics are healthy.": "All reported diagnostics are healthy.", + "No diagnostic sources were reported by the server.": "No diagnostic sources were reported by the server.", + "Expand": "Expand", + "Collapse": "Collapse", + "Cluster is healthy": "Cluster is healthy", + "Cluster needs attention": "Cluster needs attention", + "Cluster status is incomplete": "Cluster status is incomplete" } diff --git a/i18n/locales/zh-CN.json b/i18n/locales/zh-CN.json index 65376f6c..adaffa24 100644 --- a/i18n/locales/zh-CN.json +++ b/i18n/locales/zh-CN.json @@ -1549,5 +1549,14 @@ "Backend guidance": "后端提示", "Open the real multi-node metrics verification guide": "打开真实多节点指标确认指南", "Correlate time-windowed walk_dir metrics and metacache logs before treating listing symptoms as a disk failure.": "在将列表症状判断为磁盘故障前,请关联时间窗口内的 walk_dir 指标和元数据缓存日志。", - "Review peer, storage, usage, listing, and workload admission health independently.": "分别检查节点、存储、用量、列表服务和工作负载准入状态。" + "Review peer, storage, usage, listing, and workload admission health independently.": "分别检查节点、存储、用量、列表服务和工作负载准入状态。", + "Diagnostic Details": "诊断详情", + "Diagnostic items need confirmation": "项诊断状态需要确认", + "All reported diagnostics are healthy.": "所有已报告的诊断均正常。", + "No diagnostic sources were reported by the server.": "服务器未报告诊断来源。", + "Expand": "展开", + "Collapse": "收起", + "Cluster is healthy": "集群运行正常", + "Cluster needs attention": "集群需要关注", + "Cluster status is incomplete": "集群状态信息不完整" } diff --git a/tests/lib/performance-status-source.test.js b/tests/lib/performance-status-source.test.js index 327f3852..17df4f80 100644 --- a/tests/lib/performance-status-source.test.js +++ b/tests/lib/performance-status-source.test.js @@ -22,6 +22,10 @@ test("performance infrastructure card renders unknown, degraded, and initializin assert.match(source, /initializingServers\?: number/) assert.match(source, /unknownDisks\?: number/) assert.match(source, /topology: RunningStatusTopology/) + assert.match(source, /peerHealth\?: StatusDiagnostic/) + assert.match(source, /storageReadiness\?: StatusDiagnostic/) + assert.match(source, /diagnosticNeedsAttention/) + assert.match(source, /diagnosticUncertain/) assert.match(source, /hasIncompleteTopology/) assert.match(source, /t\("Unreported"\)/) assert.match(source, /\{t\("Unknown"\)\}/) @@ -30,6 +34,18 @@ test("performance infrastructure card renders unknown, degraded, and initializin assert.match(source, /value \?\? unavailableLabel/) }) +test("status page routes diagnostics into the relevant operational surfaces", () => { + const source = fs.readFileSync("app/(dashboard)/status/page.tsx", "utf8") + const usageSource = fs.readFileSync("app/(dashboard)/_components/performance-usage-card.tsx", "utf8") + + assert.match(source, /peerHealth=\{diagnosticsInfo\?\.peerHealth\}/) + assert.match(source, /storageReadiness=\{diagnosticsInfo\?\.storageReadiness\}/) + assert.match(source, /usageFreshness=\{diagnosticsInfo \? usageFreshness : undefined\}/) + assert.match(usageSource, /usageFreshness\?: StatusDiagnostic/) + assert.match(usageSource, /usageFreshness\?\.state === "degraded"/) + assert.match(usageSource, /usageFreshness\?\.state === "stale"/) +}) + test("performance server list treats every health state as a first-class filter", () => { const source = fs.readFileSync("app/(dashboard)/_components/performance-server-list.tsx", "utf8") diff --git a/tests/lib/running-status-safety.test.js b/tests/lib/running-status-safety.test.js index 886ee88c..4c19952b 100644 --- a/tests/lib/running-status-safety.test.js +++ b/tests/lib/running-status-safety.test.js @@ -88,6 +88,16 @@ test("status sections expose semantic headings and named progress", () => { assert.match(backendSource, /

{ assert.match(statusSourcesSource, / {