Skip to content
Notra logoNotra UI
Esc
↑↓navigate↵open⌘Jpreview
On this page

Workflow Decision Tree

A content refresh workflow that routes a visibility drop to one of four fixes.

"use client";

import { useState } from "react";

import { Badge } from "@/components/ui/badge";

import { WorkflowCanvas } from "../components/workflow-canvas";
import { WorkflowRunButton } from "../components/workflow-run-button";
import { WorkflowRunLog } from "../components/workflow-run-log";
import {
  WorkflowShell,
  WorkflowShellCard,
  WorkflowShellHeader,
} from "../components/workflow-shell";
import { WorkflowTimer } from "../components/workflow-timer";
import { CONTENT_REFRESH_WORKFLOW } from "../constants/workflow-templates";
import { useWorkflowGraph } from "../hooks/use-workflow-graph";
import { useWorkflowRun } from "../hooks/use-workflow-run";
import { getOutputPorts, getRunLog } from "../lib/workflow";

const ROUTE_ID = "route";

export default function WorkflowDecisionTree() {
  const graph = useWorkflowGraph(CONTENT_REFRESH_WORKFLOW);
  const run = useWorkflowRun(graph.nodes, graph.edges);
  const route = graph.nodes.find((node) => node.id === ROUTE_ID);
  const [cause, setCause] = useState(route?.branch ?? "");
  const log = getRunLog(graph.nodes, run.schedule, run.phase, run.elapsedMs);

  const pickCause = (id: string) => {
    run.stop();
    setCause(id);
    graph.updateNode(ROUTE_ID, { branch: id });
  };

  return (
    <WorkflowShell className="w-full">
      <WorkflowShellHeader>
        <span className="text-muted-foreground text-xs font-normal">
          Diagnosis
        </span>
        {route &&
          getOutputPorts(route).map((port) => (
            <button
              aria-pressed={cause === port.id}
              className="focus-visible:ring-ring/50 rounded-full outline-none focus-visible:ring-2"
              key={port.id}
              onClick={() => pickCause(port.id)}
              type="button"
            >
              <Badge variant={cause === port.id ? "default" : "outline"}>
                {port.label}
              </Badge>
            </button>
          ))}
        <div className="ml-auto flex items-center gap-3">
          <WorkflowTimer ms={run.elapsedMs} running={run.phase === "running"} />
          <WorkflowRunButton
            onPlay={run.play}
            onStop={run.stop}
            phase={run.phase}
            progress={run.elapsedMs / run.totalMs}
          />
        </div>
      </WorkflowShellHeader>
      <WorkflowShellCard className="overflow-hidden">
        <WorkflowCanvas
          className="h-[30rem] rounded-none border-0"
          edgeState={run.edgeState}
          edges={graph.edges}
          nodeState={run.nodeState}
          nodes={graph.nodes}
          onNodeMove={graph.moveNode}
        />
      </WorkflowShellCard>
      <WorkflowShellHeader>Run log</WorkflowShellHeader>
      <WorkflowShellCard>
        <WorkflowRunLog className="h-48 overflow-y-auto py-2" entries={log} />
      </WorkflowShellCard>
    </WorkflowShell>
  );
}

When a page loses AI visibility, Gemini diagnoses why and a decision routes it to the matching fix: Claude rewrites stale sections, a task generates schema, a GitHub PR unblocks the crawler or GPT writes a comparison page. A merge waits for the fix and Webflow publishes it. Pick a diagnosis in the toolbar and press Run to watch the other branches get skipped.

Installation

$ bunx --bun shadcn@latest add @notra/workflow-decision-tree