1import type { JSX } from "solid-js";
2import type { Health } from "../types/model.ts";
3
4/** A service's health, or routine background work (a scan, a download) that isn't a service changing state. */
5export type StatusKind = Health | "working";
6
7/** Each state has its own shape so it survives without color; the in-between states share a spinner. */
8function shape(health: StatusKind) {
9 switch (health) {
10 case "healthy": return <circle cx="5" cy="5" r="4" fill="currentColor" />;
11 case "degraded": return <path d="M5 1 9.2 9H.8z" fill="currentColor" />;
12 case "down": return <rect x="1.2" y="1.2" width="7.6" height="7.6" rx="1" fill="currentColor" />;
13 case "stopped": return <circle cx="5" cy="5" r="3.4" fill="none" stroke="currentColor" stroke-width="1.6" />;
14 case "deploying": case "restarting": case "starting": case "working":
15 return <path d="M5 1.2a3.8 3.8 0 1 1-3.8 3.8" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" />;
16 }
17}
18
19export function Status(props: { health: StatusKind; label?: string }) {
20 const label = () => props.label ?? props.health;
21 return (
22 <svg class={`status ${props.health}`} viewBox="0 0 10 10" role="img" aria-label={label()} data-tip={label()}>
23 {shape(props.health)}
24 </svg>
25 );
26}
27
28/** The shape beside visible text, which defaults to the state's name. */
29export function StatusLabel(props: { health: StatusKind; children?: JSX.Element }) {
30 return (
31 <span class="status-label">
32 <svg class={`status ${props.health}`} viewBox="0 0 10 10" aria-hidden="true">{shape(props.health)}</svg>
33 {props.children ?? props.health}
34 </span>
35 );
36}