diff --git a/frontend/src/pages/status/StatusPage.tsx b/frontend/src/pages/status/StatusPage.tsx index 35fa86d..7df2e39 100644 --- a/frontend/src/pages/status/StatusPage.tsx +++ b/frontend/src/pages/status/StatusPage.tsx @@ -1,7 +1,19 @@ import { useCallback, useEffect, useState } from "react"; import type { ReactNode } from "react"; import { Link } from "react-router-dom"; -import { AlertCircle, RefreshCw } from "lucide-react"; +import { + Activity, + AlertCircle, + AlertTriangle, + CheckCircle2, + Clock, + Cpu, + HelpCircle, + RefreshCw, + Tag, + Terminal, + XCircle, +} from "lucide-react"; import { api } from "@/api/client"; import type { StatusResponse } from "@/api/types"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; @@ -72,31 +84,46 @@ export function StatusPage() { return (
-
-
- +
+
+
+ +
-
-

UmpleOnline Status

+
+

System Status

{status ? : null}
-

- Developer monitoring for the backend, compiler, collaboration, LSP, and execution services. +

+ UmpleOnline Backend • Compiler • Collaboration • LSP • Execution

{status ? ( -

- Last refresh {formatDate(status.generatedAt)}. Auto-refreshes every 30 seconds. -

+
+ + + Last update: {formatDate(status.generatedAt)} + + + Refreshes every 30s +
) : null}
-
- -
@@ -124,18 +151,83 @@ function StatusContent({ status }: { status: StatusResponse }) { const releaseDetail = shortCommit(release.sourceCommit) || shortCommit(status.build?.sourceCommit) || formatValue(status.build?.sourceRefName); const compilerState = formatValue(status.umplesync?.alive) === "true" ? "Running" : "Not running"; const healthRecords = buildHealthRecords(status); + const dockerStats = asRecordArray(legacyDocker.stats); return (
, + }, + { + label: "Release", + value: releaseLabel, + detail: releaseDetail || "No release metadata", + icon: , + }, + { + label: "Compiler", + value: compilerState, + detail: `Port ${formatValue(status.umplesync?.port) || "unknown"}`, + icon: , + }, + { + label: "Health rows", + value: String(healthRecords.length), + detail: "Services, checks, dependencies", + icon: , + }, ]} /> + {dockerStats.length > 0 && ( + +
+ {dockerStats.map((stat, index) => ( +
+
+ {labelize(String(stat.name))} + +
+
+
+ CPU Usage + {formatValue(stat.CPUPerc)} +
+
+
+
+
+ Memory + {formatValue(stat.MemUsage)} +
+
+ Net I/O + + {formatValue(stat.NetIO)} + +
+
+
+ ))} +
+ + )} +
} testId="status-umplesync" > - - -
-          {formatValue(status.umplesync?.log) || "No log output returned."}
-        
+
+
+ + + + {formatValue(status.umplesync?.errors) ? ( + +
+ {formatValue(status.umplesync.errors)} +
+
+ ) : null} +
+
+
+

+ + Compiler logs +

+
+
+
+
+
+
+
+
+ + umplesync.log + +
+
+                  {formatValue(status.umplesync?.log) || (
+                    No log output returned.
+                  )}
+                
+
+
+
+
- - @@ -213,7 +338,7 @@ function StatusContent({ status }: { status: StatusResponse }) { function OverviewStrip({ items, }: { - items: Array<{ label: string; value: string; detail: string }>; + items: Array<{ label: string; value: string; detail: string; icon?: ReactNode }>; }) { return (
@@ -221,14 +346,23 @@ function OverviewStrip({
0 && "border-t border-border sm:border-l sm:border-t-0", index === 2 && "sm:border-l-0 xl:border-l", )} > -

{item.label}

-

{item.value}

-

{item.detail}

+ {item.icon && ( +
+ {item.icon} +
+ )} +
+

+ {item.label} +

+

{item.value}

+

{item.detail}

+
))}
@@ -434,19 +568,34 @@ function FormattedValue({ value }: { value: unknown }) { } function StatusBadge({ value }: { value: string }) { - const normalized = value.toLowerCase(); - const className = cn( - "border font-semibold", - (normalized === "ok" || normalized === "running") && - "border-status-success/30 bg-status-success/10 text-status-success", - (normalized === "degraded" || normalized === "unparsed" || normalized === "not_tracked") && - "border-status-warning/30 bg-status-warning/10 text-status-warning", - (normalized === "unreachable" || normalized === "unavailable") && - "border-status-error/30 bg-status-error/10 text-status-error", - ); + const normalized = (value || "unknown").toLowerCase(); + + let icon = ; + let className = "border-muted-foreground/30 bg-muted/50 text-muted-foreground"; + + if (normalized === "ok" || normalized === "running") { + icon = ; + className = + "border-status-success/30 bg-status-success text-white dark:text-status-success dark:bg-status-success/10"; + } else if (["degraded", "unparsed", "not_tracked"].includes(normalized)) { + icon = ; + className = + "border-status-warning/30 bg-status-warning text-white dark:text-status-warning dark:bg-status-warning/10"; + } else if (["unreachable", "unavailable", "error"].includes(normalized)) { + icon = ; + className = + "border-status-error/30 bg-status-error text-white dark:text-status-error dark:bg-status-error/10"; + } return ( - + + {icon} {value || "unknown"} ); @@ -454,18 +603,33 @@ function StatusBadge({ value }: { value: string }) { function StatusSkeleton() { return ( -
- {Array.from({ length: 6 }).map((_, index) => ( - - - - - - - - - - ))} +
+
+ {Array.from({ length: 4 }).map((_, index) => ( + +
+ +
+ + +
+
+
+ ))} +
+
+ {Array.from({ length: 3 }).map((_, index) => ( + + + + + + + + + + ))} +
); }