From 1c2cb767020bd58ac6cc097e9139df2fb6ee38ef Mon Sep 17 00:00:00 2001
From: shamoon <4887959+shamoon@users.noreply.github.com>
Date: Sat, 5 Sep 2026 10:24:59 -0700
Subject: [PATCH] Enhancement: render more docker states (#7099)
---
public/locales/en/common.json | 7 ++-
src/components/services/status.jsx | 57 +++++++++++++++----------
src/components/services/status.test.jsx | 40 +++++++++++++++++
3 files changed, 81 insertions(+), 23 deletions(-)
diff --git a/public/locales/en/common.json b/public/locales/en/common.json
index e355c2efd..ba4ee5f55 100644
--- a/public/locales/en/common.json
+++ b/public/locales/en/common.json
@@ -99,7 +99,12 @@
"unhealthy": "Unhealthy",
"not_found": "Not Found",
"exited": "Exited",
- "partial": "Partial"
+ "partial": "Partial",
+ "created": "Created",
+ "paused": "Paused",
+ "restarting": "Restarting",
+ "removing": "Removing",
+ "dead": "Dead"
},
"ping": {
"error": "Error",
diff --git a/src/components/services/status.jsx b/src/components/services/status.jsx
index 88ff9096f..594a0a190 100644
--- a/src/components/services/status.jsx
+++ b/src/components/services/status.jsx
@@ -1,6 +1,22 @@
import { useTranslation } from "next-i18next/pages";
import useSWR from "swr";
+const WARNING_CLASS = "text-orange-400/50 dark:text-orange-400/80";
+
+// docker container states other than running
+const STATE_LABELS = {
+ created: "docker.created",
+ dead: "docker.dead",
+ exited: "docker.exited",
+ "not found": "docker.not_found",
+ paused: "docker.paused",
+ removing: "docker.removing",
+ restarting: "docker.restarting",
+};
+
+// states that mean something is wrong rather than merely not started
+const WARNING_STATES = new Set(["dead", "exited", "not found", "restarting"]);
+
export default function Status({ service, style }) {
const { t } = useTranslation();
@@ -16,33 +32,30 @@ export default function Status({ service, style }) {
if (statusError) {
statusLabel = t("docker.error");
colorClass = "text-rose-500/80";
- } else if (data) {
- if (data.status?.includes("running")) {
- colorClass = "text-emerald-500/80";
+ } else if (data?.status?.includes("running")) {
+ colorClass = "text-emerald-500/80";
- if (!data.health) {
- statusLabel = data.status.replace("running", t("docker.running"));
- } else {
- statusLabel = data.health === "healthy" ? t("docker.healthy") : data.health;
+ if (!data.health) {
+ statusLabel = data.status.replace("running", t("docker.running"));
+ } else {
+ statusLabel = data.health === "healthy" ? t("docker.healthy") : data.health;
- if (data.health === "starting") {
- statusLabel = t("docker.starting");
- colorClass = "text-blue-500/80";
- }
+ if (data.health === "starting") {
+ statusLabel = t("docker.starting");
+ colorClass = "text-blue-500/80";
+ }
- if (data.health === "unhealthy") {
- statusLabel = t("docker.unhealthy");
- colorClass = "text-orange-400/50 dark:text-orange-400/80";
- }
+ if (data.health === "unhealthy") {
+ statusLabel = t("docker.unhealthy");
+ colorClass = WARNING_CLASS;
}
}
-
- if (data.status === "not found" || data.status === "exited" || data.status?.startsWith("partial")) {
- if (data.status === "not found") statusLabel = t("docker.not_found");
- else if (data.status === "exited") statusLabel = t("docker.exited");
- else statusLabel = data.status.replace("partial", t("docker.partial"));
- colorClass = "text-orange-400/50 dark:text-orange-400/80";
- }
+ } else if (data?.status?.startsWith("partial")) {
+ statusLabel = data.status.replace("partial", t("docker.partial"));
+ colorClass = WARNING_CLASS;
+ } else if (data && STATE_LABELS[data.status]) {
+ statusLabel = t(STATE_LABELS[data.status]);
+ if (WARNING_STATES.has(data.status)) colorClass = WARNING_CLASS;
}
if (style === "dot") {
diff --git a/src/components/services/status.test.jsx b/src/components/services/status.test.jsx
index 54364f78c..75ff2b654 100644
--- a/src/components/services/status.test.jsx
+++ b/src/components/services/status.test.jsx
@@ -75,6 +75,46 @@ describe("components/services/status", () => {
expect(screen.getByText("docker.starting")).toBeInTheDocument();
});
+ it("renders the remaining container states instead of falling through to unknown", () => {
+ const states = {
+ created: "docker.created",
+ paused: "docker.paused",
+ restarting: "docker.restarting",
+ removing: "docker.removing",
+ dead: "docker.dead",
+ };
+
+ Object.entries(states).forEach(([status, label]) => {
+ useSWR.mockReturnValue({ data: { statuses: { c: { status } } }, error: undefined });
+ render();
+ expect(screen.getByText(label)).toBeInTheDocument();
+ });
+
+ expect(screen.queryByText("docker.unknown")).not.toBeInTheDocument();
+ });
+
+ it("colors problem states as warnings and merely stopped states neutrally", () => {
+ useSWR.mockReturnValue({ data: { statuses: { c: { status: "restarting" } } }, error: undefined });
+ const { container: restarting } = render();
+ expect(restarting.querySelector(".bg-orange-400")).toBeInTheDocument();
+
+ useSWR.mockReturnValue({ data: { statuses: { c: { status: "paused" } } }, error: undefined });
+ const { container: paused } = render();
+ expect(paused.querySelector(".bg-orange-400")).not.toBeInTheDocument();
+ });
+
+ it("does not surface health for containers that are not running", () => {
+ useSWR.mockReturnValue({
+ data: { statuses: { c: { status: "paused", health: "unhealthy" } } },
+ error: undefined,
+ });
+
+ render();
+
+ expect(screen.getByText("docker.paused")).toBeInTheDocument();
+ expect(screen.queryByText("docker.unhealthy")).not.toBeInTheDocument();
+ });
+
it("renders a dot when style is dot", () => {
useSWR.mockReturnValue({ data: { statuses: { c: { status: "running" } } }, error: undefined });