From 571f627b3bc47af26c93ccba8a73e0bbe991ff47 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Wed, 4 Oct 2023 22:12:57 -0700 Subject: [PATCH] Enhancement: statusStyle improvements (#2119) --- docs/configs/services.md | 9 +-- docs/configs/settings.md | 24 ++++++++ public/locales/en/common.json | 4 +- src/components/services/item.jsx | 9 +-- src/components/services/kubernetes-status.jsx | 47 ++++++++------- src/components/services/ping.jsx | 25 ++++---- src/components/services/status.jsx | 59 ++++++++----------- src/utils/config/service-helpers.js | 3 + 8 files changed, 100 insertions(+), 80 deletions(-) diff --git a/docs/configs/services.md b/docs/configs/services.md index 834b98cee..9f47cbbf2 100644 --- a/docs/configs/services.md +++ b/docs/configs/services.md @@ -123,14 +123,7 @@ Services may have an optional `ping` property that allows you to monitor the ava Ping -You can also apply different styles to the ping indicator by using the `statusStyle` property. The default is no value, and displays the response time in ms, but you can also use `dot` or `basic`. `dot` shows a green dot for a successful ping, and `basic` shows either ONLINE or OFFLINE to match the status style of Docker containers. For example: - -```yaml - - Sonarr: - ... - ping: http://sonarr.host/ - statusStyle: dot -``` +You can also apply different styles to the ping indicator by using the `statusStyle` property, see [settings](settings.md#status-style). ## Docker Integration diff --git a/docs/configs/settings.md b/docs/configs/settings.md index e9f9c7f65..06b6b40a7 100644 --- a/docs/configs/settings.md +++ b/docs/configs/settings.md @@ -380,6 +380,30 @@ or per-service (`services.yaml`) with: If you have both set the per-service settings take precedence. +## Status Style + +You can choose from the following styles for docker or k8s status and ping: `dot` or `basic` + +- The default is no value, and displays the ping response time in ms and the docker / k8s container status +- `dot` shows a green dot for a successful ping or healthy status. +- `basic` shows either UP or DOWN for ping + +For example: + +```yaml +statusStyle: 'dot' +``` + +or per-service (`services.yaml`) with: + +```yaml +- Example Service: + ... + statusStyle: 'dot' +``` + +If you have both set, the per-service settings take precedence. + ## Hide Widget Error Messages Hide the visible API error messages either globally in `settings.yaml`: diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 813c8e2a5..a4a7a32f0 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -80,7 +80,9 @@ }, "ping": { "error": "Error", - "ping": "Ping" + "ping": "Ping", + "down": "Down", + "up": "Up" }, "emby": { "playing": "Playing", diff --git a/src/components/services/item.jsx b/src/components/services/item.jsx index 056d5cfc3..d3dfedcbd 100644 --- a/src/components/services/item.jsx +++ b/src/components/services/item.jsx @@ -15,6 +15,7 @@ export default function Item({ service, group }) { const hasLink = service.href && service.href !== "#"; const { settings } = useContext(SettingsContext); const showStats = (service.showStats === false) ? false : settings.showStats; + const statusStyle = (service.statusStyle !== undefined) ? service.statusStyle : settings.statusStyle; const [statsOpen, setStatsOpen] = useState(service.showStats); const [statsClosing, setStatsClosing] = useState(false); @@ -76,10 +77,10 @@ export default function Item({ service, group }) { )} -
+
{service.ping && (
- + Ping status
)} @@ -90,7 +91,7 @@ export default function Item({ service, group }) { onClick={() => (statsOpen ? closeStats() : setStatsOpen(true))} className="flex-shrink-0 flex items-center justify-center cursor-pointer service-tag service-container-stats" > - + View container stats )} @@ -100,7 +101,7 @@ export default function Item({ service, group }) { onClick={() => (statsOpen ? closeStats() : setStatsOpen(true))} className="flex-shrink-0 flex items-center justify-center cursor-pointer service-tag service-app" > - + View container stats )} diff --git a/src/components/services/kubernetes-status.jsx b/src/components/services/kubernetes-status.jsx index 413029947..0618e5741 100644 --- a/src/components/services/kubernetes-status.jsx +++ b/src/components/services/kubernetes-status.jsx @@ -1,35 +1,42 @@ import useSWR from "swr"; import { t } from "i18next"; -export default function KubernetesStatus({ service }) { +export default function KubernetesStatus({ service, style }) { const podSelectorString = service.podSelector !== undefined ? `podSelector=${service.podSelector}` : ""; const { data, error } = useSWR(`/api/kubernetes/status/${service.namespace}/${service.app}?${podSelectorString}`); - if (error) { -
-
{t("docker.error")}
-
- } + let statusLabel = t("docker.unknown"); + let statusTitle = ""; + let backgroundClass = "px-1.5 py-0.5 bg-theme-500/10 dark:bg-theme-900/50"; + let colorClass = "text-black/20 dark:text-white/40 "; - if (data && data.status === "running") { - return ( -
-
{data.health ?? data.status}
-
- ); + if (error) { + statusTitle = t("docker.error"); + statusLabel = statusTitle; + colorClass = "text-rose-500/80"; + } else if (data) { + if (data.status === "running") { + statusTitle = data.health ?? data.status; + statusLabel = statusTitle; + colorClass = "text-emerald-500/80"; + } + + if (data.status === "not found" || data.status === "down" || data.status === "partial") { + statusTitle = data.status; + statusLabel = statusTitle; + colorClass = "text-orange-400/50 dark:text-orange-400/80"; + } } - if (data && (data.status === "not found" || data.status === "down" || data.status === "partial")) { - return ( -
-
{data.status}
-
- ); + if (style === 'dot') { + colorClass = colorClass.replace('text-', 'bg-').replace(/\/\d\d$/, ''); + backgroundClass = "p-3 hover:bg-theme-500/10 dark:hover:bg-theme-900/20"; } return ( -
-
{t("docker.unknown")}
+
+ {style !== 'dot' &&
{statusLabel}
} + {style === 'dot' &&
}
); } diff --git a/src/components/services/ping.jsx b/src/components/services/ping.jsx index de56ce516..2d89012f2 100644 --- a/src/components/services/ping.jsx +++ b/src/components/services/ping.jsx @@ -7,9 +7,8 @@ export default function Ping({ group, service, style }) { refreshInterval: 30000 }); - let textSize = "text-[8px]"; let colorClass = "" - let backgroundClass = "bg-theme-500/10 dark:bg-theme-900/50"; + let backgroundClass = "bg-theme-500/10 dark:bg-theme-900/50 px-1.5 py-0.5"; let statusTitle = "HTTP status"; let statusText; @@ -26,11 +25,7 @@ export default function Ping({ group, service, style }) { statusTitle += ` ${data.status}` if (style === "basic") { - statusText = t("docker.offline") - } else if (style === "dot") { - statusText = "◉" - textSize = "text-[14px]" - backgroundClass = "" + statusText = t("ping.down") } else { statusText = data.status } @@ -40,19 +35,21 @@ export default function Ping({ group, service, style }) { colorClass = "text-emerald-500/80" if (style === "basic") { - statusText = t("docker.running") - } else if (style === "dot") { - statusText = "◉" - textSize = "text-[14px]" - backgroundClass = "" + statusText = t("ping.up") } else { statusText = ping } } + if (style === "dot") { + backgroundClass = 'p-3'; + colorClass = colorClass.replace('text-', 'bg-').replace(/\/\d\d$/, ''); + } + return ( -
-
{statusText}
+
+ {style !== 'dot' &&
{statusText}
} + {style === 'dot' &&
}
); } diff --git a/src/components/services/status.jsx b/src/components/services/status.jsx index 18b871b82..04160a5ba 100644 --- a/src/components/services/status.jsx +++ b/src/components/services/status.jsx @@ -1,65 +1,58 @@ import { useTranslation } from "react-i18next"; import useSWR from "swr"; -export default function Status({ service }) { +export default function Status({ service, style }) { const { t } = useTranslation(); const { data, error } = useSWR(`/api/docker/status/${service.container}/${service.server || ""}`); - if (error) { -
-
{t("docker.error")}
-
- } - - if (data) { - let statusLabel = ""; + let statusLabel = t("docker.unknown"); + let statusTitle = ""; + let backgroundClass = "px-1.5 py-0.5 bg-theme-500/10 dark:bg-theme-900/50"; + let colorClass = "text-black/20 dark:text-white/40 "; + if (error) { + statusTitle = t("docker.error"); + colorClass = "text-rose-500/80"; + } else if (data) { if (data.status?.includes("running")) { if (data.health === "starting") { - return ( -
-
{t("docker.starting")}
-
- ); + statusTitle = t("docker.starting"); + colorClass = "text-blue-500/80"; } if (data.health === "unhealthy") { - return ( -
-
{t("docker.unhealthy")}
-
- ); + statusTitle = t("docker.unhealthy"); + colorClass = "text-orange-400/50 dark:text-orange-400/80"; } if (!data.health) { - statusLabel = data.status.replace("running", t("docker.running")) + statusLabel = data.status.replace("running", t("docker.running")); } else { - statusLabel = data.health === "healthy" ? t("docker.healthy") : data.health + statusLabel = data.health === "healthy" ? t("docker.healthy") : data.health; } - return ( -
-
{statusLabel}
-
- ); + statusTitle = statusLabel; + colorClass = "text-emerald-500/80"; } 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")) - return ( -
-
{statusLabel}
-
- ); + colorClass = "text-orange-400/50 dark:text-orange-400/80"; } } + if (style === 'dot') { + colorClass = colorClass.replace('text-', 'bg-').replace(/\/\d\d$/, ''); + backgroundClass = "p-3 hover:bg-theme-500/10 dark:hover:bg-theme-900/20"; + } + return ( -
-
{t("docker.unknown")}
+
+ {style !== 'dot' &&
{statusLabel}
} + {style === 'dot' &&
}
); } diff --git a/src/utils/config/service-helpers.js b/src/utils/config/service-helpers.js index 91e5b75c0..e96d50e53 100644 --- a/src/utils/config/service-helpers.js +++ b/src/utils/config/service-helpers.js @@ -259,6 +259,9 @@ export async function servicesFromKubernetes() { if (ingress.metadata.annotations[`${ANNOTATION_BASE}/ping`]) { constructedService.ping = ingress.metadata.annotations[`${ANNOTATION_BASE}/ping`]; } + if (ingress.metadata.annotations[`${ANNOTATION_BASE}/statusStyle`]) { + constructedService.statusStyle = ingress.metadata.annotations[`${ANNOTATION_BASE}/statusStyle`]; + } Object.keys(ingress.metadata.annotations).forEach((annotation) => { if (annotation.startsWith(ANNOTATION_WIDGET_BASE)) { shvl.set(