From dd24925d906dc13d73c52a47fdd07bec89798789 Mon Sep 17 00:00:00 2001 From: Meier Lukas Date: Fri, 12 Dec 2025 19:59:28 +0100 Subject: [PATCH] feat(docker): add widget options to sort containers (#4636) --- packages/translation/src/lang/en.json | 18 +++++++++++++++- packages/widgets/src/docker/component.tsx | 23 +++++++++++++++++++-- packages/widgets/src/docker/index.ts | 25 ++++++++++++++++++++++- 3 files changed, 62 insertions(+), 4 deletions(-) diff --git a/packages/translation/src/lang/en.json b/packages/translation/src/lang/en.json index 82c3bdcfd..517ecfea1 100644 --- a/packages/translation/src/lang/en.json +++ b/packages/translation/src/lang/en.json @@ -1939,7 +1939,23 @@ "dockerContainers": { "name": "Docker stats", "description": "Stats of your containers (This widget can only be added with administrator privileges)", - "option": {}, + "option": { + "enableRowSorting": { + "label": "Enable items sorting" + }, + "defaultSort": { + "label": "Column used for sorting by default", + "option": { + "name": "Name", + "state": "State", + "cpuUsage": "CPU usage", + "memoryUsage": "Memory usage" + } + }, + "descendingDefaultSort": { + "label": "Invert sorting" + } + }, "error": { "internalServerError": "Failed to fetch containers stats" } diff --git a/packages/widgets/src/docker/component.tsx b/packages/widgets/src/docker/component.tsx index 0687b7f18..869b76f0b 100644 --- a/packages/widgets/src/docker/component.tsx +++ b/packages/widgets/src/docker/component.tsx @@ -56,6 +56,7 @@ const createColumns = ( t: ReturnType>, ): MRT_ColumnDef[] => [ { + id: "name", accessorKey: "name", header: t("field.name.label"), Cell({ renderedCellValue, row }) { @@ -70,6 +71,7 @@ const createColumns = ( }, }, { + id: "state", accessorKey: "state", size: 100, header: t("field.state.label"), @@ -78,6 +80,13 @@ const createColumns = ( }, }, { + id: "cpuUsage", + sortingFn: (rowA, rowB) => { + const cpuUsageA = safeValue(rowA.original.cpuUsage); + const cpuUsageB = safeValue(rowB.original.cpuUsage); + + return cpuUsageA - cpuUsageB; + }, accessorKey: "cpuUsage", size: 80, header: t("field.stats.cpu.label"), @@ -92,6 +101,13 @@ const createColumns = ( }, }, { + id: "memoryUsage", + sortingFn: (rowA, rowB) => { + const memoryUsageA = safeValue(rowA.original.memoryUsage); + const memoryUsageB = safeValue(rowB.original.memoryUsage); + + return memoryUsageA - memoryUsageB; + }, accessorKey: "memoryUsage", size: 80, header: t("field.stats.memory.label"), @@ -106,9 +122,11 @@ const createColumns = ( }, }, { + id: "actions", accessorKey: "actions", size: 80, header: t("action.title"), + enableSorting: false, Cell({ row }) { const utils = clientApi.useUtils(); // eslint-disable-next-line no-restricted-syntax @@ -168,7 +186,7 @@ const createColumns = ( }, ]; -export default function DockerWidget({ width }: WidgetComponentProps<"dockerContainers">) { +export default function DockerWidget({ options, width, isEditMode }: WidgetComponentProps<"dockerContainers">) { const t = useScopedI18n("docker"); const isTiny = width <= 256; @@ -192,8 +210,8 @@ export default function DockerWidget({ width }: WidgetComponentProps<"dockerCont enablePagination: false, enableTopToolbar: false, enableBottomToolbar: false, - enableSorting: false, enableColumnActions: false, + enableSorting: options.enableRowSorting && !isEditMode, enableStickyHeader: false, enableColumnOrdering: false, enableRowSelection: false, @@ -203,6 +221,7 @@ export default function DockerWidget({ width }: WidgetComponentProps<"dockerCont enableFilters: false, enableHiding: false, initialState: { + sorting: [{ id: options.defaultSort, desc: options.descendingDefaultSort }], density: "xs", }, mantinePaperProps: { diff --git a/packages/widgets/src/docker/index.ts b/packages/widgets/src/docker/index.ts index fa738023a..1a0964af3 100644 --- a/packages/widgets/src/docker/index.ts +++ b/packages/widgets/src/docker/index.ts @@ -1,12 +1,35 @@ import { IconBrandDocker, IconServerOff } from "@tabler/icons-react"; +import type { RouterOutputs } from "@homarr/api"; + import { createWidgetDefinition } from "../definition"; import { optionsBuilder } from "../options"; +const columnsList = [ + "name", + "state", + "cpuUsage", + "memoryUsage", +] as const satisfies (keyof RouterOutputs["docker"]["getContainers"]["containers"][number])[]; + export const { definition, componentLoader } = createWidgetDefinition("dockerContainers", { icon: IconBrandDocker, createOptions() { - return optionsBuilder.from(() => ({})); + return optionsBuilder.from((factory) => ({ + enableRowSorting: factory.switch({ + defaultValue: false, + }), + defaultSort: factory.select({ + defaultValue: "name", + options: columnsList.map((value) => ({ + value, + label: (t) => t(`widget.dockerContainers.option.defaultSort.option.${value}`), + })), + }), + descendingDefaultSort: factory.switch({ + defaultValue: false, + }), + })); }, errors: { INTERNAL_SERVER_ERROR: {