67 lines · 2.5 KB
import { TONE_SURFACE } from "~/ui/tones";
import { useDemoTimeline } from "~/hooks/use-demo-timeline";
import { StatusDot } from "~/ui/StatusDot";
import { cn } from "~/utils/cn";
import { DemoWindow } from "../DemoWindow";

const STEPS = 6;
const INTERVAL_MS = 900;

type Job = { name: string; runner: string; start: number; end: number; time: string };

const STAGES: ReadonlyArray<ReadonlyArray<Job>> = [
  [{ name: "lint", runner: "managed", start: 0, end: 1, time: "9s" }],
  [
    { name: "test (node 22)", runner: "managed", start: 1, end: 3, time: "38s" },
    { name: "test (node 24)", runner: "managed", start: 1, end: 4, time: "41s" },
  ],
  [{ name: "deploy", runner: "self-hosted", start: 4, end: 6, time: "1m 2s" }],
];

function jobState(step: number, job: Job): string {
  if (step >= job.end) return "success";
  return step >= job.start ? "in_progress" : "queued";
}

export function WorkflowDemo({ className }: { className?: string }) {
  const step = useDemoTimeline(STEPS, INTERVAL_MS);
  return (
    <DemoWindow
      title="acme/web · CI #482 · push to main"
      label="A workflow run where lint, a test matrix and a deploy job finish in order"
      className={className}
    >
      <div className="flex items-stretch gap-3 overflow-x-auto p-5">
        {STAGES.map((stage, index) => (
          <div key={stage[0].name} className="flex items-center gap-3">
            {index > 0 && <span aria-hidden className="h-px w-4 shrink-0 bg-kumo-interact" />}
            <div className="flex flex-col gap-2">
              {stage.map((job) => {
                const state = jobState(step, job);