status colors for swarm resources

This commit is contained in:
mbecker20
2025-12-13 11:12:26 -08:00
parent 01df3add41
commit e1d745aee0
3 changed files with 139 additions and 18 deletions

View File

@@ -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 }) => (
<Diamond className={cn(`w-${size} h-${size}`, className)} />
),
Service: ({ size, className }) => (
<FolderCode className={cn(`w-${size} h-${size}`, className)} />
),
Task: ({ size, className }) => (
<ListTodo className={cn(`w-${size} h-${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 (
<Diamond
className={cn(
`w-${size} h-${size}`,
stroke_color_class_by_intention(swarm_node_state_intention(state)),
className
)}
/>
);
},
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 (
<SquareStack
className={cn(
`w-${size} h-${size}`,
stroke_color_class_by_intention(swarm_state_intention(state)),
className
)}
/>
);
},
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 (
<FolderCode
className={cn(
`w-${size} h-${size}`,
stroke_color_class_by_intention(swarm_state_intention(state)),
className
)}
/>
);
},
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 (
<ListTodo
className={cn(
`w-${size} h-${size}`,
stroke_color_class_by_intention(
swarm_task_state_intention(task?.State, task?.DesiredState)
),
className
)}
/>
);
},
Secret: ({ size, className }) => (
<KeyRound className={cn(`w-${size} h-${size}`, className)} />
),
Config: ({ size, className }) => (
<Settings className={cn(`w-${size} h-${size}`, className)} />
),
Stack: ({ size, className }) => (
<SquareStack className={cn(`w-${size} h-${size}`, className)} />
),
};
export const SwarmResourceLink = ({
@@ -180,10 +245,15 @@ export const SwarmResourceLink = ({
return (
<Link
to={`/swarms/${swarm_id}/swarm-${type.toLowerCase()}/${resource_id}`}
className="flex gap-2 items-center hover:underline"
className="flex items-center gap-2 text-sm hover:underline py-1"
>
<Icon size={4} />
{name ?? "Unknown"}
<Icon swarm_id={swarm_id} resource_id={resource_id} size={4} />
<div
title={name}
className="max-w-[250px] lg:max-w-[300px] overflow-hidden overflow-ellipsis break-words text-nowrap"
>
{name ?? "Unknown"}
</div>
</Link>
);
};

View File

@@ -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 = ({
<Icon server_id={server_id} name={type === "image" ? id : name} />
<div
title={name}
className="max-w-[250px] lg:max-w-[300px] overflow-hidden overflow-ellipsis break-words"
className="max-w-[250px] lg:max-w-[300px] overflow-hidden overflow-ellipsis break-words text-nowrap"
>
{name}
</div>

View File

@@ -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";
}