2026-02-17 13:24:33 -06:00
import { useState } from "react" ;
import { useNavigate } from "react-router-dom" ;
2026-02-17 20:07:41 -06:00
import { useQuery , useQueryClient } from "@tanstack/react-query" ;
2026-02-17 13:24:33 -06:00
import { useDialog } from "../context/DialogContext" ;
import { useCompany } from "../context/CompanyContext" ;
import { companiesApi } from "../api/companies" ;
import { goalsApi } from "../api/goals" ;
import { agentsApi } from "../api/agents" ;
import { issuesApi } from "../api/issues" ;
import { queryKeys } from "../lib/queryKeys" ;
import { Dialog , DialogContent } from "@/components/ui/dialog" ;
2026-02-17 20:07:41 -06:00
import {
Popover ,
PopoverContent ,
PopoverTrigger ,
} from "@/components/ui/popover" ;
2026-02-17 13:24:33 -06:00
import { Button } from "@/components/ui/button" ;
import { cn } from "../lib/utils" ;
2026-02-18 13:53:03 -06:00
import { getUIAdapter } from "../adapters" ;
import { defaultCreateValues } from "./AgentConfigForm" ;
2026-02-17 13:24:33 -06:00
import {
Building2 ,
Bot ,
ListTodo ,
Rocket ,
ArrowLeft ,
ArrowRight ,
Terminal ,
Globe ,
Sparkles ,
Check ,
Loader2 ,
2026-02-17 20:07:41 -06:00
FolderOpen ,
ChevronDown ,
2026-02-17 13:24:33 -06:00
} from "lucide-react" ;
type Step = 1 | 2 | 3 | 4 ;
type AdapterType = "claude_local" | "process" | "http" ;
export function OnboardingWizard() {
const { onboardingOpen , closeOnboarding } = useDialog ( ) ;
const { setSelectedCompanyId } = useCompany ( ) ;
const queryClient = useQueryClient ( ) ;
const navigate = useNavigate ( ) ;
const [ step , setStep ] = useState < Step > ( 1 ) ;
const [ loading , setLoading ] = useState ( false ) ;
const [ error , setError ] = useState < string | null > ( null ) ;
2026-02-17 20:07:41 -06:00
const [ modelOpen , setModelOpen ] = useState ( false ) ;
2026-02-17 13:24:33 -06:00
// Step 1
const [ companyName , setCompanyName ] = useState ( "" ) ;
const [ companyGoal , setCompanyGoal ] = useState ( "" ) ;
// Step 2
const [ agentName , setAgentName ] = useState ( "CEO" ) ;
const [ adapterType , setAdapterType ] = useState < AdapterType > ( "claude_local" ) ;
const [ cwd , setCwd ] = useState ( "" ) ;
const [ model , setModel ] = useState ( "" ) ;
const [ command , setCommand ] = useState ( "" ) ;
const [ args , setArgs ] = useState ( "" ) ;
const [ url , setUrl ] = useState ( "" ) ;
2026-02-18 13:02:23 -06:00
const [ cwdPickerNotice , setCwdPickerNotice ] = useState < string | null > ( null ) ;
2026-02-17 13:24:33 -06:00
// Step 3
2026-02-18 11:45:43 -06:00
const [ taskTitle , setTaskTitle ] = useState ( "Create your CEO HEARTBEAT.md" ) ;
const [ taskDescription , setTaskDescription ] = useState ( "You're the CEO of the company, make sure you have a file agents/ceo/HEARTBEAT.md that tells you your core loop. You MUST use the Paperclip SKILL." ) ;
2026-02-17 13:24:33 -06:00
// Created entity IDs
const [ createdCompanyId , setCreatedCompanyId ] = useState < string | null > ( null ) ;
const [ createdAgentId , setCreatedAgentId ] = useState < string | null > ( null ) ;
2026-02-17 20:07:41 -06:00
const { data : adapterModels } = useQuery ( {
queryKey : [ "adapter-models" , adapterType ] ,
queryFn : ( ) = > agentsApi . adapterModels ( adapterType ) ,
enabled : onboardingOpen && step === 2 ,
} ) ;
const selectedModel = ( adapterModels ? ? [ ] ) . find ( ( m ) = > m . id === model ) ;
2026-02-17 13:24:33 -06:00
function reset() {
setStep ( 1 ) ;
setLoading ( false ) ;
setError ( null ) ;
setCompanyName ( "" ) ;
setCompanyGoal ( "" ) ;
setAgentName ( "CEO" ) ;
setAdapterType ( "claude_local" ) ;
setCwd ( "" ) ;
setModel ( "" ) ;
setCommand ( "" ) ;
setArgs ( "" ) ;
setUrl ( "" ) ;
2026-02-18 13:02:23 -06:00
setCwdPickerNotice ( null ) ;
2026-02-18 11:45:43 -06:00
setTaskTitle ( "Create your CEO HEARTBEAT.md" ) ;
setTaskDescription ( "You're the CEO of the company, make sure you have a file agents/ceo/HEARTBEAT.md that tells you your core loop. You MUST use the Paperclip SKILL." ) ;
2026-02-17 13:24:33 -06:00
setCreatedCompanyId ( null ) ;
setCreatedAgentId ( null ) ;
}
function buildAdapterConfig ( ) : Record < string , unknown > {
2026-02-18 13:53:03 -06:00
const adapter = getUIAdapter ( adapterType ) ;
return adapter . buildAdapterConfig ( {
. . . defaultCreateValues ,
adapterType ,
cwd ,
model ,
command ,
args ,
url ,
dangerouslySkipPermissions : adapterType === "claude_local" ,
} ) ;
2026-02-17 13:24:33 -06:00
}
async function handleStep1Next() {
setLoading ( true ) ;
setError ( null ) ;
try {
const company = await companiesApi . create ( { name : companyName.trim ( ) } ) ;
setCreatedCompanyId ( company . id ) ;
setSelectedCompanyId ( company . id ) ;
queryClient . invalidateQueries ( { queryKey : queryKeys.companies.all } ) ;
if ( companyGoal . trim ( ) ) {
await goalsApi . create ( company . id , {
title : companyGoal.trim ( ) ,
level : "company" ,
status : "active" ,
} ) ;
queryClient . invalidateQueries ( { queryKey : queryKeys.goals.list ( company . id ) } ) ;
}
setStep ( 2 ) ;
} catch ( err ) {
setError ( err instanceof Error ? err . message : "Failed to create company" ) ;
} finally {
setLoading ( false ) ;
}
}
async function handleStep2Next() {
if ( ! createdCompanyId ) return ;
setLoading ( true ) ;
setError ( null ) ;
try {
const agent = await agentsApi . create ( createdCompanyId , {
name : agentName.trim ( ) ,
role : "ceo" ,
adapterType ,
adapterConfig : buildAdapterConfig ( ) ,
runtimeConfig : {
heartbeat : {
enabled : true ,
intervalSec : 300 ,
wakeOnAssignment : true ,
wakeOnOnDemand : true ,
wakeOnAutomation : true ,
cooldownSec : 10 ,
} ,
} ,
} ) ;
setCreatedAgentId ( agent . id ) ;
queryClient . invalidateQueries ( {
queryKey : queryKeys.agents.list ( createdCompanyId ) ,
} ) ;
setStep ( 3 ) ;
} catch ( err ) {
setError ( err instanceof Error ? err . message : "Failed to create agent" ) ;
} finally {
setLoading ( false ) ;
}
}
async function handleStep3Next() {
if ( ! createdCompanyId || ! createdAgentId ) return ;
setLoading ( true ) ;
setError ( null ) ;
try {
await issuesApi . create ( createdCompanyId , {
title : taskTitle.trim ( ) ,
. . . ( taskDescription . trim ( ) ? { description : taskDescription.trim ( ) } : { } ) ,
assigneeAgentId : createdAgentId ,
status : "todo" ,
} ) ;
queryClient . invalidateQueries ( {
queryKey : queryKeys.issues.list ( createdCompanyId ) ,
} ) ;
setStep ( 4 ) ;
} catch ( err ) {
setError ( err instanceof Error ? err . message : "Failed to create task" ) ;
} finally {
setLoading ( false ) ;
}
}
async function handleLaunch() {
if ( ! createdAgentId ) return ;
setLoading ( true ) ;
setError ( null ) ;
try {
await agentsApi . invoke ( createdAgentId ) ;
} catch {
// Agent may already be running from auto-wake — that's fine
}
setLoading ( false ) ;
reset ( ) ;
closeOnboarding ( ) ;
navigate ( ` /agents/ ${ createdAgentId } ` ) ;
}
function handleKeyDown ( e : React.KeyboardEvent ) {
if ( e . key === "Enter" && ( e . metaKey || e . ctrlKey ) ) {
e . preventDefault ( ) ;
if ( step === 1 && companyName . trim ( ) ) handleStep1Next ( ) ;
else if ( step === 2 && agentName . trim ( ) ) handleStep2Next ( ) ;
else if ( step === 3 && taskTitle . trim ( ) ) handleStep3Next ( ) ;
else if ( step === 4 ) handleLaunch ( ) ;
}
}
const stepIcons = [ Building2 , Bot , ListTodo , Rocket ] ;
return (
< Dialog
open = { onboardingOpen }
onOpenChange = { ( open ) = > {
if ( ! open ) {
reset ( ) ;
closeOnboarding ( ) ;
}
} }
>
< DialogContent
showCloseButton = { false }
className = "p-0 gap-0 overflow-hidden sm:max-w-lg"
onKeyDown = { handleKeyDown }
>
{ /* Header */ }
< div className = "flex items-center justify-between px-4 py-2.5 border-b border-border" >
< div className = "flex items-center gap-2 text-sm" >
< Sparkles className = "h-4 w-4 text-muted-foreground" / >
< span className = "font-medium" > Get Started < / span >
< span className = "text-muted-foreground/60" >
Step { step } of 4
< / span >
< / div >
< div className = "flex items-center gap-1.5" >
{ [ 1 , 2 , 3 , 4 ] . map ( ( s ) = > (
< div
key = { s }
className = { cn (
"h-1.5 w-6 rounded-full transition-colors" ,
s < step
? "bg-green-500"
: s === step
? "bg-foreground"
: "bg-muted"
) }
/ >
) ) }
< / div >
< / div >
{ /* Content */ }
< div className = "overflow-y-auto max-h-[60vh]" >
{ step === 1 && (
< div className = "p-4 space-y-4" >
< div className = "flex items-center gap-3 mb-2" >
Polish UI components and rework AgentConfigForm
Major AgentConfigForm rework with improved adapter configuration
fields and layout. Refine sidebar, breadcrumbs, and card/tab
components for visual consistency. Clean up page layouts across
Activity, Agents, Approvals, Costs, Dashboard, Goals, Inbox,
Issues, Org, and Projects pages. Minor heartbeat-run CLI fix.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 10:43:25 -06:00
< div className = "bg-muted/50 p-2" >
2026-02-17 13:24:33 -06:00
< Building2 className = "h-5 w-5 text-muted-foreground" / >
< / div >
< div >
< h3 className = "font-medium" > Name your company < / h3 >
< p className = "text-xs text-muted-foreground" >
This is the organization your agents will work for .
< / p >
< / div >
< / div >
< div >
< label className = "text-xs text-muted-foreground mb-1 block" >
Company name
< / label >
< input
className = "w-full rounded-md border border-border bg-transparent px-3 py-2 text-sm outline-none focus:ring-1 focus:ring-ring placeholder:text-muted-foreground/50"
placeholder = "Acme Corp"
value = { companyName }
onChange = { ( e ) = > setCompanyName ( e . target . value ) }
autoFocus
/ >
< / div >
< div >
< label className = "text-xs text-muted-foreground mb-1 block" >
Mission / goal ( optional )
< / label >
< textarea
className = "w-full rounded-md border border-border bg-transparent px-3 py-2 text-sm outline-none focus:ring-1 focus:ring-ring placeholder:text-muted-foreground/50 resize-none min-h-[60px]"
placeholder = "What is this company trying to achieve?"
value = { companyGoal }
onChange = { ( e ) = > setCompanyGoal ( e . target . value ) }
/ >
< / div >
< / div >
) }
{ step === 2 && (
< div className = "p-4 space-y-4" >
< div className = "flex items-center gap-3 mb-2" >
Polish UI components and rework AgentConfigForm
Major AgentConfigForm rework with improved adapter configuration
fields and layout. Refine sidebar, breadcrumbs, and card/tab
components for visual consistency. Clean up page layouts across
Activity, Agents, Approvals, Costs, Dashboard, Goals, Inbox,
Issues, Org, and Projects pages. Minor heartbeat-run CLI fix.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 10:43:25 -06:00
< div className = "bg-muted/50 p-2" >
2026-02-17 13:24:33 -06:00
< Bot className = "h-5 w-5 text-muted-foreground" / >
< / div >
< div >
< h3 className = "font-medium" > Create your first agent < / h3 >
< p className = "text-xs text-muted-foreground" >
Choose how this agent will run tasks .
< / p >
< / div >
< / div >
< div >
< label className = "text-xs text-muted-foreground mb-1 block" >
Agent name
< / label >
< input
className = "w-full rounded-md border border-border bg-transparent px-3 py-2 text-sm outline-none focus:ring-1 focus:ring-ring placeholder:text-muted-foreground/50"
placeholder = "CEO"
value = { agentName }
onChange = { ( e ) = > setAgentName ( e . target . value ) }
autoFocus
/ >
< / div >
{ /* Adapter type radio cards */ }
< div >
< label className = "text-xs text-muted-foreground mb-2 block" >
Adapter type
< / label >
< div className = "grid grid-cols-3 gap-2" >
{ ( [
{
value : "claude_local" as const ,
label : "Claude Code" ,
icon : Sparkles ,
desc : "Local Claude agent" ,
} ,
{
value : "process" as const ,
label : "Shell Command" ,
icon : Terminal ,
desc : "Run a process" ,
} ,
{
value : "http" as const ,
label : "HTTP Webhook" ,
icon : Globe ,
desc : "Call an endpoint" ,
} ,
] as const ) . map ( ( opt ) = > (
< button
key = { opt . value }
className = { cn (
"flex flex-col items-center gap-1.5 rounded-md border p-3 text-xs transition-colors" ,
adapterType === opt . value
? "border-foreground bg-accent"
: "border-border hover:bg-accent/50"
) }
onClick = { ( ) = > setAdapterType ( opt . value ) }
>
< opt.icon className = "h-4 w-4" / >
< span className = "font-medium" > { opt . label } < / span >
< span className = "text-muted-foreground text-[10px]" >
{ opt . desc }
< / span >
< / button >
) ) }
< / div >
< / div >
{ /* Conditional adapter fields */ }
{ adapterType === "claude_local" && (
< div className = "space-y-3" >
< div >
< label className = "text-xs text-muted-foreground mb-1 block" >
Working directory
< / label >
2026-02-17 20:07:41 -06:00
< div className = "flex items-center gap-2 rounded-md border border-border px-2.5 py-1.5" >
< FolderOpen className = "h-3.5 w-3.5 text-muted-foreground shrink-0" / >
< input
className = "w-full bg-transparent outline-none text-sm font-mono placeholder:text-muted-foreground/50"
placeholder = "/path/to/project"
value = { cwd }
onChange = { ( e ) = > setCwd ( e . target . value ) }
/ >
< button
type = "button"
className = "inline-flex items-center rounded-md border border-border px-2 py-0.5 text-xs text-muted-foreground hover:bg-accent/50 transition-colors shrink-0"
onClick = { async ( ) = > {
try {
2026-02-18 13:02:23 -06:00
setCwdPickerNotice ( null ) ;
2026-02-17 20:07:41 -06:00
// @ts-expect-error -- showDirectoryPicker is not in all TS lib defs yet
const handle = await window . showDirectoryPicker ( { mode : "read" } ) ;
2026-02-18 13:02:23 -06:00
const pickedPath =
typeof handle === "object" &&
handle !== null &&
typeof ( handle as { path? : unknown } ) . path === "string"
? String ( ( handle as { path : string } ) . path )
: "" ;
if ( pickedPath ) {
setCwd ( pickedPath ) ;
return ;
}
const selectedName =
typeof handle === "object" &&
handle !== null &&
typeof ( handle as { name? : unknown } ) . name === "string"
? String ( ( handle as { name : string } ) . name )
: "selected folder" ;
setCwdPickerNotice (
` Directory picker only exposed " ${ selectedName } ". Paste the absolute path manually. ` ,
) ;
2026-02-17 20:07:41 -06:00
} catch {
// user cancelled or API unsupported
}
} }
>
Choose
< / button >
< / div >
2026-02-18 13:02:23 -06:00
{ cwdPickerNotice && (
< p className = "mt-1 text-xs text-amber-400" > { cwdPickerNotice } < / p >
) }
2026-02-17 13:24:33 -06:00
< / div >
< div >
< label className = "text-xs text-muted-foreground mb-1 block" >
Model
< / label >
2026-02-17 20:07:41 -06:00
< Popover open = { modelOpen } onOpenChange = { setModelOpen } >
< PopoverTrigger asChild >
< button className = "inline-flex items-center gap-1.5 rounded-md border border-border px-2.5 py-1.5 text-sm hover:bg-accent/50 transition-colors w-full justify-between" >
< span className = { cn ( ! model && "text-muted-foreground" ) } >
{ selectedModel ? selectedModel.label : model || "Default" }
< / span >
< ChevronDown className = "h-3 w-3 text-muted-foreground" / >
< / button >
< / PopoverTrigger >
< PopoverContent className = "w-[var(--radix-popover-trigger-width)] p-1" align = "start" >
< button
className = { cn (
"flex items-center gap-2 w-full px-2 py-1.5 text-sm rounded hover:bg-accent/50" ,
! model && "bg-accent"
) }
onClick = { ( ) = > { setModel ( "" ) ; setModelOpen ( false ) ; } }
>
Default
< / button >
{ ( adapterModels ? ? [ ] ) . map ( ( m ) = > (
< button
key = { m . id }
className = { cn (
"flex items-center justify-between w-full px-2 py-1.5 text-sm rounded hover:bg-accent/50" ,
m . id === model && "bg-accent"
) }
onClick = { ( ) = > { setModel ( m . id ) ; setModelOpen ( false ) ; } }
>
< span > { m . label } < / span >
< span className = "text-xs text-muted-foreground font-mono" > { m . id } < / span >
< / button >
) ) }
< / PopoverContent >
< / Popover >
2026-02-17 13:24:33 -06:00
< / div >
< / div >
) }
{ adapterType === "process" && (
< div className = "space-y-3" >
< div >
< label className = "text-xs text-muted-foreground mb-1 block" >
Command
< / label >
< input
className = "w-full rounded-md border border-border bg-transparent px-3 py-2 text-sm font-mono outline-none focus:ring-1 focus:ring-ring placeholder:text-muted-foreground/50"
placeholder = "e.g. node, python"
value = { command }
onChange = { ( e ) = > setCommand ( e . target . value ) }
/ >
< / div >
< div >
< label className = "text-xs text-muted-foreground mb-1 block" >
Args ( comma - separated )
< / label >
< input
className = "w-full rounded-md border border-border bg-transparent px-3 py-2 text-sm font-mono outline-none focus:ring-1 focus:ring-ring placeholder:text-muted-foreground/50"
placeholder = "e.g. script.js, --flag"
value = { args }
onChange = { ( e ) = > setArgs ( e . target . value ) }
/ >
< / div >
< / div >
) }
{ adapterType === "http" && (
< div >
< label className = "text-xs text-muted-foreground mb-1 block" >
Webhook URL
< / label >
< input
className = "w-full rounded-md border border-border bg-transparent px-3 py-2 text-sm font-mono outline-none focus:ring-1 focus:ring-ring placeholder:text-muted-foreground/50"
placeholder = "https://..."
value = { url }
onChange = { ( e ) = > setUrl ( e . target . value ) }
/ >
< / div >
) }
< / div >
) }
{ step === 3 && (
< div className = "p-4 space-y-4" >
< div className = "flex items-center gap-3 mb-2" >
Polish UI components and rework AgentConfigForm
Major AgentConfigForm rework with improved adapter configuration
fields and layout. Refine sidebar, breadcrumbs, and card/tab
components for visual consistency. Clean up page layouts across
Activity, Agents, Approvals, Costs, Dashboard, Goals, Inbox,
Issues, Org, and Projects pages. Minor heartbeat-run CLI fix.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 10:43:25 -06:00
< div className = "bg-muted/50 p-2" >
2026-02-17 13:24:33 -06:00
< ListTodo className = "h-5 w-5 text-muted-foreground" / >
< / div >
< div >
< h3 className = "font-medium" > Give it something to do < / h3 >
< p className = "text-xs text-muted-foreground" >
Give your agent a small task to start with — a bug fix , a
research question , writing a script .
< / p >
< / div >
< / div >
< div >
< label className = "text-xs text-muted-foreground mb-1 block" >
Task title
< / label >
< input
className = "w-full rounded-md border border-border bg-transparent px-3 py-2 text-sm outline-none focus:ring-1 focus:ring-ring placeholder:text-muted-foreground/50"
placeholder = "e.g. Research competitor pricing"
value = { taskTitle }
onChange = { ( e ) = > setTaskTitle ( e . target . value ) }
autoFocus
/ >
< / div >
< div >
< label className = "text-xs text-muted-foreground mb-1 block" >
Description ( optional )
< / label >
< textarea
className = "w-full rounded-md border border-border bg-transparent px-3 py-2 text-sm outline-none focus:ring-1 focus:ring-ring placeholder:text-muted-foreground/50 resize-none min-h-[80px]"
placeholder = "Add more detail about what the agent should do..."
value = { taskDescription }
onChange = { ( e ) = > setTaskDescription ( e . target . value ) }
/ >
< / div >
< / div >
) }
{ step === 4 && (
< div className = "p-4 space-y-4" >
< div className = "flex items-center gap-3 mb-2" >
Polish UI components and rework AgentConfigForm
Major AgentConfigForm rework with improved adapter configuration
fields and layout. Refine sidebar, breadcrumbs, and card/tab
components for visual consistency. Clean up page layouts across
Activity, Agents, Approvals, Costs, Dashboard, Goals, Inbox,
Issues, Org, and Projects pages. Minor heartbeat-run CLI fix.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 10:43:25 -06:00
< div className = "bg-muted/50 p-2" >
2026-02-17 13:24:33 -06:00
< Rocket className = "h-5 w-5 text-muted-foreground" / >
< / div >
< div >
< h3 className = "font-medium" > Ready to launch < / h3 >
< p className = "text-xs text-muted-foreground" >
Everything is set up . Launch your agent and watch it work .
< / p >
< / div >
< / div >
Polish UI components and rework AgentConfigForm
Major AgentConfigForm rework with improved adapter configuration
fields and layout. Refine sidebar, breadcrumbs, and card/tab
components for visual consistency. Clean up page layouts across
Activity, Agents, Approvals, Costs, Dashboard, Goals, Inbox,
Issues, Org, and Projects pages. Minor heartbeat-run CLI fix.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 10:43:25 -06:00
< div className = "border border-border divide-y divide-border" >
2026-02-17 13:24:33 -06:00
< div className = "flex items-center gap-3 px-3 py-2.5" >
< Building2 className = "h-4 w-4 text-muted-foreground shrink-0" / >
< div className = "flex-1 min-w-0" >
< p className = "text-sm font-medium truncate" > { companyName } < / p >
< p className = "text-xs text-muted-foreground" > Company < / p >
< / div >
< Check className = "h-4 w-4 text-green-500 shrink-0" / >
< / div >
< div className = "flex items-center gap-3 px-3 py-2.5" >
< Bot className = "h-4 w-4 text-muted-foreground shrink-0" / >
< div className = "flex-1 min-w-0" >
< p className = "text-sm font-medium truncate" > { agentName } < / p >
< p className = "text-xs text-muted-foreground" >
2026-02-18 13:53:03 -06:00
{ getUIAdapter ( adapterType ) . label }
2026-02-17 13:24:33 -06:00
< / p >
< / div >
< Check className = "h-4 w-4 text-green-500 shrink-0" / >
< / div >
< div className = "flex items-center gap-3 px-3 py-2.5" >
< ListTodo className = "h-4 w-4 text-muted-foreground shrink-0" / >
< div className = "flex-1 min-w-0" >
< p className = "text-sm font-medium truncate" > { taskTitle } < / p >
< p className = "text-xs text-muted-foreground" > Task < / p >
< / div >
< Check className = "h-4 w-4 text-green-500 shrink-0" / >
< / div >
< / div >
< / div >
) }
< / div >
{ /* Error */ }
{ error && (
< div className = "px-4 pb-2" >
< p className = "text-xs text-destructive" > { error } < / p >
< / div >
) }
{ /* Footer */ }
< div className = "flex items-center justify-between px-4 py-2.5 border-t border-border" >
< div >
{ step > 1 && (
< Button
variant = "ghost"
size = "sm"
onClick = { ( ) = > setStep ( ( step - 1 ) as Step ) }
disabled = { loading }
>
< ArrowLeft className = "h-3.5 w-3.5 mr-1" / >
Back
< / Button >
) }
< / div >
< div className = "flex items-center gap-2" >
{ step === 1 && (
< Button
size = "sm"
disabled = { ! companyName . trim ( ) || loading }
onClick = { handleStep1Next }
>
{ loading ? (
< Loader2 className = "h-3.5 w-3.5 mr-1 animate-spin" / >
) : (
< ArrowRight className = "h-3.5 w-3.5 mr-1" / >
) }
{ loading ? "Creating..." : "Next" }
< / Button >
) }
{ step === 2 && (
< Button
size = "sm"
disabled = { ! agentName . trim ( ) || loading }
onClick = { handleStep2Next }
>
{ loading ? (
< Loader2 className = "h-3.5 w-3.5 mr-1 animate-spin" / >
) : (
< ArrowRight className = "h-3.5 w-3.5 mr-1" / >
) }
{ loading ? "Creating..." : "Next" }
< / Button >
) }
{ step === 3 && (
< Button
size = "sm"
disabled = { ! taskTitle . trim ( ) || loading }
onClick = { handleStep3Next }
>
{ loading ? (
< Loader2 className = "h-3.5 w-3.5 mr-1 animate-spin" / >
) : (
< ArrowRight className = "h-3.5 w-3.5 mr-1" / >
) }
{ loading ? "Creating..." : "Next" }
< / Button >
) }
{ step === 4 && (
< Button size = "sm" disabled = { loading } onClick = { handleLaunch } >
{ loading ? (
< Loader2 className = "h-3.5 w-3.5 mr-1 animate-spin" / >
) : (
< Rocket className = "h-3.5 w-3.5 mr-1" / >
) }
{ loading ? "Launching..." : "Launch Agent" }
< / Button >
) }
< / div >
< / div >
< / DialogContent >
< / Dialog >
) ;
}