From 6f6a19064b44c8a18a86864bb0efc72f39a14e30 Mon Sep 17 00:00:00 2001
From: shamoon <4887959+shamoon@users.noreply.github.com>
Date: Fri, 23 Jan 2026 12:57:19 -0800
Subject: [PATCH] more fields
---
docs/widgets/services/dockhand.md | 2 ++
public/locales/en/common.json | 8 ++++-
src/widgets/dockhand/component.jsx | 56 ++++++++++++++++++++++++++++--
3 files changed, 63 insertions(+), 3 deletions(-)
diff --git a/docs/widgets/services/dockhand.md b/docs/widgets/services/dockhand.md
index 152c9ccef..710c2ec0f 100644
--- a/docs/widgets/services/dockhand.md
+++ b/docs/widgets/services/dockhand.md
@@ -7,6 +7,8 @@ Learn more about [Dockhand](https://dockhand.pro/).
The widget reads `/api/dashboard/stats` from your Dockhand instance. It currently supports Dockhand's **local** authentication only.
+Available fields (max 4): `running`, `stopped`, `cpu`, `memory`, `images`, `volumes`, `events_today`, `pending_updates`, `stacks`, `paused`.
+
```yaml
widget:
type: dockhand
diff --git a/public/locales/en/common.json b/public/locales/en/common.json
index 8d6feaf5d..f75e7adc2 100644
--- a/public/locales/en/common.json
+++ b/public/locales/en/common.json
@@ -1141,6 +1141,12 @@
"running": "Running",
"stopped": "Stopped",
"cpu": "CPU",
- "memory": "Memory"
+ "memory": "Memory",
+ "images": "Images",
+ "volumes": "Volumes",
+ "events_today": "Events Today",
+ "pending_updates": "Pending Updates",
+ "stacks": "Stacks",
+ "paused": "Paused"
}
}
diff --git a/src/widgets/dockhand/component.jsx b/src/widgets/dockhand/component.jsx
index 443343b9b..c9b9b12d6 100644
--- a/src/widgets/dockhand/component.jsx
+++ b/src/widgets/dockhand/component.jsx
@@ -4,10 +4,18 @@ import { useTranslation } from "next-i18next";
import useWidgetAPI from "utils/proxy/use-widget-api";
+const MAX_FIELDS = 4;
+
export default function Component({ service }) {
const { t } = useTranslation();
const { widget } = service;
+ if (!widget.fields) {
+ widget.fields = ["running", "stopped", "cpu", "memory"];
+ } else if (widget.fields.length > MAX_FIELDS) {
+ widget.fields = widget.fields.slice(0, MAX_FIELDS);
+ }
+
const { data: stats, error: statsError } = useWidgetAPI(widget, "dashboard/stats");
if (statsError) {
@@ -19,45 +27,89 @@ export default function Component({ service }) {
+
+
+
+
+
+
);
}
let running;
let stopped;
+ let paused;
+ let pendingUpdates;
let cpuPercent;
let memoryPercent;
+ let imagesTotal;
+ let volumesTotal;
+ let stacksRunning;
+ let stacksTotal;
+ let eventsToday;
if (widget?.environment) {
const environment = stats.find(
- (env) => env?.name === widget.environment || env?.id?.toString() === widget.environment.toString(),
+ (env) =>
+ env?.name?.toString().toLowerCase() === widget?.environment.toString().toLowerCase() ||
+ env?.id?.toString() === widget?.environment.toString(),
);
if (environment) {
running = environment?.containers?.running;
stopped = environment?.containers?.stopped ?? (environment?.containers?.total ?? 0) - (running ?? 0);
+ paused = environment?.containers?.paused;
+ pendingUpdates = environment?.containers?.pendingUpdates;
cpuPercent = environment?.metrics?.cpuPercent;
memoryPercent = environment?.metrics?.memoryPercent;
+ imagesTotal = environment?.images?.total;
+ volumesTotal = environment?.volumes?.total;
+ stacksRunning = environment?.stacks?.running;
+ stacksTotal = environment?.stacks?.total;
+ eventsToday = environment?.events?.today;
}
- } else {
+ }
+
+ if (running === undefined) {
// Aggregate across all environments
running = stats.reduce((sum, env) => sum + (env?.containers?.running ?? 0), 0);
const total = stats.reduce((sum, env) => sum + (env?.containers?.total ?? 0), 0);
stopped = total - running;
+ paused = stats.reduce((sum, env) => sum + (env?.containers?.paused ?? 0), 0);
+ pendingUpdates = stats.reduce((sum, env) => sum + (env?.containers?.pendingUpdates ?? 0), 0);
const totalCpu = stats.reduce((sum, env) => sum + (env?.metrics?.cpuPercent ?? 0), 0);
const totalMemory = stats.reduce((sum, env) => sum + (env?.metrics?.memoryPercent ?? 0), 0);
const envCount = stats.length;
cpuPercent = envCount > 0 ? totalCpu / envCount : 0;
memoryPercent = envCount > 0 ? totalMemory / envCount : 0;
+ imagesTotal = stats.reduce((sum, env) => sum + (env?.images?.total ?? 0), 0);
+ volumesTotal = stats.reduce((sum, env) => sum + (env?.volumes?.total ?? 0), 0);
+ stacksRunning = stats.reduce((sum, env) => sum + (env?.stacks?.running ?? 0), 0);
+ stacksTotal = stats.reduce((sum, env) => sum + (env?.stacks?.total ?? 0), 0);
+ eventsToday = stats.reduce((sum, env) => sum + (env?.events?.today ?? 0), 0);
}
return (
+
+
+
+
+
+
);
}