Vectrum
Sign In
88 lines · 3.0 KB · TypeScript
import { Label as MuLabel, StatusBadge } from "@unlocalhosted/metalui";
import { ConfirmButton } from "@/components/confirm-button";
import { ServerIcon } from "@/components/icons";
import { SettingsSection } from "@/components/metal";
import { ListRow, PlateSection, RowGlyph } from "@/components/settings-plate";
import { Blankslate, RefChip } from "@/components/ui";
import { timeAgo } from "@/lib/format";
import { RUNNER_TOKEN_PREFIX, type Runner } from "@/lib/runners";

/** Registered runners: online, busy or offline, with their labels and machine, each removable. */
export function RunnerList({ runners, deleteAction }: { runners: Runner[]; deleteAction: (runnerId: string) => Promise<void> }) {
  if (runners.length === 0) {
    return (
      <PlateSection title="Runners">
        <Blankslate icon={<ServerIcon />} title="No runners yet">
          Register a runner below, then start it on the machine that should run your jobs.
        </Blankslate>
      </PlateSection>
    );
  }
  return (
    <SettingsSection title={`Runners · ${runners.length}`}>
      {runners.map((r) => (
        <ListRow
          key={r.id}
          lead={
            <RowGlyph>
              <ServerIcon />
            </RowGlyph>
          }
          title={
            <>
              <span className="truncate">{r.name}</span>
              {r.busy ? (
                <StatusBadge led="waiting">Busy</StatusBadge>
              ) : r.online ? (
                <StatusBadge led="live">Idle</StatusBadge>
              ) : (
                <StatusBadge led="off">Offline</StatusBadge>
              )}
              <RefChip>
                {RUNNER_TOKEN_PREFIX}…{r.hint}
              </RefChip>
            </>
          }
          meta={
            <>
              <span className="flex flex-wrap gap-1">
                {[...r.systemLabels, ...r.labels].map((l) => (
                  <RefChip key={l} className="text-ink2">
                    {l}
                  </RefChip>
                ))}
              </span>
              <MuLabel variant="engraved">
                {r.busy ? (
                  <>
                    Running <b>{r.busy.jobName}</b> in <b>{r.busy.repo}</b> #{r.busy.runNumber}
                  </>
                ) : r.lastSeenAt ? (
                  <>
                    {r.hostname && (
                      <>
                        <b>{r.hostname}</b> ·{" "}
                      </>
                    )}
                    Last seen <b>{timeAgo(r.lastSeenAt)}</b>
                    {r.version && <> · v{r.version}</>}
                  </>
                ) : (
                  "Never connected"
                )}
              </MuLabel>
            </>
          }
        >
          <ConfirmButton
            action={deleteAction.bind(null, r.id)}
            message={`Remove the runner “${r.name}”? It stops picking up jobs, and its token stops working.`}
            variant="default"
          >
            Remove
          </ConfirmButton>
        </ListRow>
      ))}
    </SettingsSection>
  );
}