Getting Started
Components
- Agent Status
- Agent Template Card
- API Key Card
- Audio Player
- Bar Visualizer
- Channel Status Card
- Chat Composer
- Code Editor
- Condition Builder
- Confirm Dialog
- Conversation
- Conversation Bar
- Dataset Card
- Document Status Badge
- Extraction Strategy Select
- File Icon
- File Upload
- Indexing Progress
- Live Waveform
- Matrix
- Message
- Mic Selector
- Model Diff View
- Model Selector
- Model Type Filter
- Node Catalog
- Option Editor
- Orb
- Output Variables View
- Package Card
- Prompt Editor
- Provider Status Card
- Radio Card
- Resource Card
- Resource Sidebar
- Response
- Run Node List
- Scrub Bar
- Segment Card
- Shimmering Text
- Speech Input
- Token Trend Chart
- Tool Call Card
- Transcript Viewer
- Usage Stat Card
- Variable Binding Editor
- Variable Selector
- Voice Button
- Voice Picker
- Waveform
- Workflow Node Card
- Workspace Selector
Classify intent
LLMClassify the user request before routing it to a workflow branch.
Inputs
conversation.message
stringcustomer.plan
stringOutputs
intent
enumconfidence
numberZGI Agent Pro · temperature 0.2
Retrieve knowledge
RAGSearch approved product docs and attach citations to the answer.
Inputs
intent
stringOutputs
documents
arraycitations
arrayEscalation branch
If / ElseRoute low confidence cases to a human review queue.
Inputs
confidence
numberOutputs
self_serve
booleanneeds_handoff
booleanimport { BrainCircuitIcon, DatabaseIcon, GitBranchIcon } from "lucide-react"
import { WorkflowNodeCard } from "@/components/ui/workflow-node-card"
export function WorkflowNodeCardDemo() {
return (
<div className="grid w-full max-w-4xl gap-3 lg:grid-cols-2">
<WorkflowNodeCard
icon={<BrainCircuitIcon className="size-5" />}
title="Classify intent"
type="LLM"
status="running"
description="Classify the user request before routing it to a workflow branch."
inputs={[
{ label: "conversation.message", type: "string" },
{ label: "customer.plan", type: "string" },
]}
outputs={[
{ label: "intent", type: "enum" },
{ label: "confidence", type: "number" },
]}
footer="ZGI Agent Pro · temperature 0.2"
/>
<WorkflowNodeCard
icon={<DatabaseIcon className="size-5" />}
title="Retrieve knowledge"
type="RAG"
status="success"
description="Search approved product docs and attach citations to the answer."
inputs={[{ label: "intent", type: "string" }]}
outputs={[
{ label: "documents", type: "array" },
{ label: "citations", type: "array" },
]}
/>
<WorkflowNodeCard
icon={<GitBranchIcon className="size-5" />}
title="Escalation branch"
type="If / Else"
status="waiting"
description="Route low confidence cases to a human review queue."
inputs={[{ label: "confidence", type: "number" }]}
outputs={[
{ label: "self_serve", type: "boolean" },
{ label: "needs_handoff", type: "boolean" },
]}
/>
</div>
)
}
Installation
pnpm dlx shadcn@latest add https://ui.zgi.ai/r/workflow-node-card.jsonpnpm dlx shadcn@latest add https://ui.zgi.ai/r/workflow-node-card.json
yarn dlx shadcn@latest add https://ui.zgi.ai/r/workflow-node-card.jsonComponent
"use client"
import * as React from "react"
import {
AlertCircleIcon,
CheckCircle2Icon,
CircleIcon,
Clock3Icon,
Loader2Icon,
} from "lucide-react"
import { cn } from "@/lib/utils"
import { Badge } from "@/components/ui/badge"
export type WorkflowNodeStatus =
| "idle"
| "running"
| "success"
| "error"
| "waiting"
export interface WorkflowNodePort {
label: React.ReactNode
type?: React.ReactNode
}
export interface WorkflowNodeCardProps
extends Omit<React.ComponentProps<"div">, "title"> {
icon?: React.ReactNode
title: React.ReactNode
type?: React.ReactNode
description?: React.ReactNode
status?: WorkflowNodeStatus
inputs?: WorkflowNodePort[]
outputs?: WorkflowNodePort[]
footer?: React.ReactNode
}
const statusConfig: Record<
WorkflowNodeStatus,
{
label: string
icon: React.ComponentType<{ className?: string }>
className: string
}
> = {
idle: {
label: "Idle",
icon: CircleIcon,
className: "border-border bg-muted text-muted-foreground",
},
running: {
label: "Running",
icon: Loader2Icon,
className:
"border-blue-500/20 bg-blue-500/10 text-blue-700 dark:text-blue-300",
},
success: {
label: "Success",
icon: CheckCircle2Icon,
className:
"border-emerald-500/20 bg-emerald-500/10 text-emerald-700 dark:text-emerald-300",
},
error: {
label: "Error",
icon: AlertCircleIcon,
className: "border-destructive/20 bg-destructive/10 text-destructive",
},
waiting: {
label: "Waiting",
icon: Clock3Icon,
className:
"border-amber-500/25 bg-amber-500/10 text-amber-700 dark:text-amber-300",
},
}
function PortList({
label,
ports = [],
}: {
label: string
ports?: WorkflowNodePort[]
}) {
if (!ports.length) return null
return (
<div className="space-y-1.5">
<p className="text-muted-foreground text-[11px] font-medium tracking-normal uppercase">
{label}
</p>
<div className="space-y-1">
{ports.map((port, index) => (
<div
key={index}
className="border-border bg-muted/40 flex min-h-7 items-center justify-between gap-2 rounded-md border px-2 text-xs"
>
<span className="truncate">{port.label}</span>
{port.type ? (
<code className="text-muted-foreground shrink-0 font-mono text-[11px]">
{port.type}
</code>
) : null}
</div>
))}
</div>
</div>
)
}
export function WorkflowNodeCard({
icon,
title,
type,
description,
status = "idle",
inputs,
outputs,
footer,
className,
...props
}: WorkflowNodeCardProps) {
const config = statusConfig[status]
const StatusIcon = config.icon
return (
<div
className={cn(
"border-border bg-card text-card-foreground w-full overflow-hidden rounded-lg border shadow-sm",
className
)}
{...props}
>
<div className="border-border flex items-start gap-3 border-b p-3">
{icon ? (
<div className="bg-muted text-muted-foreground flex size-9 shrink-0 items-center justify-center rounded-md">
{icon}
</div>
) : null}
<div className="min-w-0 flex-1">
<div className="flex min-w-0 items-center gap-2">
<p className="truncate text-sm font-medium">{title}</p>
{type ? (
<Badge variant="secondary" className="h-6 rounded-md px-2">
{type}
</Badge>
) : null}
</div>
{description ? (
<p className="text-muted-foreground mt-1 line-clamp-2 text-xs leading-5">
{description}
</p>
) : null}
</div>
<Badge
variant="outline"
className={cn("h-6 gap-1 rounded-md px-2", config.className)}
>
<StatusIcon
className={cn("size-3", status === "running" && "animate-spin")}
/>
{config.label}
</Badge>
</div>
<div className="grid gap-3 p-3 sm:grid-cols-2">
<PortList label="Inputs" ports={inputs} />
<PortList label="Outputs" ports={outputs} />
</div>
{footer ? (
<div className="border-border bg-muted/20 border-t px-3 py-2 text-xs">
{footer}
</div>
) : null}
</div>
)
}
Usage
import { BrainCircuitIcon } from "lucide-react"
import { WorkflowNodeCard } from "@/components/ui/workflow-node-card"
export function Example() {
return (
<WorkflowNodeCard
icon={<BrainCircuitIcon className="size-5" />}
title="Classify intent"
type="LLM"
status="running"
inputs={[{ label: "conversation.message", type: "string" }]}
outputs={[{ label: "intent", type: "enum" }]}
/>
)
}Props
| Prop | Type | Default |
|---|---|---|
| title | ReactNode | required |
| icon | ReactNode | - |
| type | ReactNode | - |
| description | ReactNode | - |
| status | WorkflowNodeStatus | "idle" |
| inputs | WorkflowNodePort[] | [] |
| outputs | WorkflowNodePort[] | [] |
| footer | ReactNode | - |
Deploy and Scale Agents with ZGI
ZGI delivers the infrastructure and developer experience you need to ship reliable audio & agent applications at scale.
Talk to an expert