From e1d745aee0b92e9a156a6290ca5f282c54c16865 Mon Sep 17 00:00:00 2001 From: mbecker20 Date: Sat, 13 Dec 2025 11:12:26 -0800 Subject: [PATCH] status colors for swarm resources --- .../src/components/resources/swarm/index.tsx | 102 +++++++++++++++--- frontend/src/components/util.tsx | 4 +- frontend/src/lib/color.ts | 51 +++++++++ 3 files changed, 139 insertions(+), 18 deletions(-) diff --git a/frontend/src/components/resources/swarm/index.tsx b/frontend/src/components/resources/swarm/index.tsx index d88e6c0fc..c45cd8361 100644 --- a/frontend/src/components/resources/swarm/index.tsx +++ b/frontend/src/components/resources/swarm/index.tsx @@ -14,6 +14,8 @@ import { SwarmTable } from "./table"; import { swarm_state_intention, stroke_color_class_by_intention, + swarm_node_state_intention, + swarm_task_state_intention, } from "@lib/color"; import { cn, updateLogToHtml } from "@lib/utils"; import { Types } from "komodo_client"; @@ -143,26 +145,89 @@ export type SwarmResourceType = | "Stack"; export const SWARM_ICONS: { - [type in SwarmResourceType]: React.FC<{ size?: number; className?: string }>; + [type in SwarmResourceType]: React.FC<{ + swarm_id?: string; + resource_id?: string; + size?: number; + className?: string; + }>; } = { - Node: ({ size, className }) => ( - - ), - Service: ({ size, className }) => ( - - ), - Task: ({ size, className }) => ( - - ), + Node: ({ swarm_id, resource_id, size, className }) => { + const state = useRead( + "ListSwarmNodes", + { swarm: swarm_id! }, + { enabled: !!swarm_id } + ).data?.find((node) => resource_id && node.ID === resource_id)?.State; + return ( + + ); + }, + Stack: ({ swarm_id, resource_id, size, className }) => { + const state = useRead( + "ListSwarmStacks", + { swarm: swarm_id! }, + { enabled: !!swarm_id } + ).data?.find((stack) => resource_id && stack.Name === resource_id)?.State; + return ( + + ); + }, + Service: ({ swarm_id, resource_id, size, className }) => { + const state = useRead( + "ListSwarmServices", + { swarm: swarm_id! }, + { enabled: !!swarm_id } + ).data?.find( + (service) => + resource_id && + (service.ID === resource_id || service.Name === resource_id) + )?.State; + return ( + + ); + }, + Task: ({ swarm_id, resource_id, size, className }) => { + const task = useRead( + "ListSwarmTasks", + { swarm: swarm_id! }, + { enabled: !!swarm_id } + ).data?.find((task) => resource_id && task.ID === resource_id); + return ( + + ); + }, Secret: ({ size, className }) => ( ), Config: ({ size, className }) => ( ), - Stack: ({ size, className }) => ( - - ), }; export const SwarmResourceLink = ({ @@ -180,10 +245,15 @@ export const SwarmResourceLink = ({ return ( - - {name ?? "Unknown"} + +
+ {name ?? "Unknown"} +
); }; diff --git a/frontend/src/components/util.tsx b/frontend/src/components/util.tsx index 33f8b3647..caa1398f8 100644 --- a/frontend/src/components/util.tsx +++ b/frontend/src/components/util.tsx @@ -726,7 +726,7 @@ export const DockerResourceLink = ({ server_id: string; name: string | undefined; id?: string; - type: "container" | "network" | "image" | "volume"; + type: DockerResourceType; extra?: ReactNode; muted?: boolean; }) => { @@ -745,7 +745,7 @@ export const DockerResourceLink = ({
{name}
diff --git a/frontend/src/lib/color.ts b/frontend/src/lib/color.ts index 7a3c76134..d406d68e6 100644 --- a/frontend/src/lib/color.ts +++ b/frontend/src/lib/color.ts @@ -144,6 +144,55 @@ export const swarm_state_intention: ( } }; +export const swarm_node_state_intention: ( + state?: Types.NodeState +) => ColorIntention = (state) => { + switch (state) { + case Types.NodeState.READY: + return "Good"; + case Types.NodeState.DOWN: + return "Warning"; + case Types.NodeState.DISCONNECTED: + return "Critical"; + case Types.NodeState.UNKNOWN: + return "Neutral"; + case undefined: + return "None"; + } +}; + +export const swarm_task_state_intention: ( + state?: Types.TaskState, + desired?: Types.TaskState +) => ColorIntention = (state, desired) => { + // Case when its desired running + if (desired === Types.TaskState.RUNNING) { + if (state === Types.TaskState.RUNNING) { + return "Good"; + } else { + return "Critical"; + } + } + + // Case when its desired shutdown + if (desired === Types.TaskState.SHUTDOWN) { + // If you want it shutdown, then running is critical. + if (state === Types.TaskState.RUNNING) { + return "Critical"; + } else { + // Otherwise, it is "Down", give neutral color + return "Neutral"; + } + } + + // Others + if (state === desired) { + return "Good"; + } else { + return "Critical"; + } +}; + export const server_state_intention: ( state?: Types.ServerState, hasVersionMismatch?: boolean @@ -217,6 +266,8 @@ export const container_state_intention: ( return "Warning"; case Types.ContainerStateStatusEnum.Empty: return "Unknown"; + case undefined: + return "Unknown"; default: return "Critical"; }