experiment: redesign onboarding wizard with 6-step mission-driven flow
Replaces the old 4-step wizard (Company → Agent → Task → Launch) with a new 6-step flow that puts the user in control of key moments: 1. Define your mission (required, with questionnaire or direct input) 2. Launch your company! (celebration moment) 3. Bring your CEO to life (agent creation, reframed) 4. Chat with your CEO (placeholder for hiring plan chat) 5. Review hiring plan (placeholder for editable role cards) 6. Make your first hires (summary + task creation) Key changes: - Mission/goal is now mandatory (was optional) - Two paths to define mission: "I know my mission" or "Help me figure it out" - Prompt chips for inspiration - Questionnaire generates a draft mission from 4 questions - Company launch is a celebrated moment before CEO creation - Step type expanded from 1-4 to 1-6 in DialogContext - Agent creation step reframed as "giving the CEO a heartbeat" - Steps 4-5 are placeholders for the chat and plan review components Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
42c0ca669b
commit
a35fac7281
2 changed files with 349 additions and 105 deletions
|
|
@ -53,7 +53,7 @@ import {
|
||||||
X
|
X
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
|
|
||||||
type Step = 1 | 2 | 3 | 4;
|
type Step = 1 | 2 | 3 | 4 | 5 | 6;
|
||||||
type AdapterType =
|
type AdapterType =
|
||||||
| "claude_local"
|
| "claude_local"
|
||||||
| "codex_local"
|
| "codex_local"
|
||||||
|
|
@ -65,6 +65,21 @@ type AdapterType =
|
||||||
| "http"
|
| "http"
|
||||||
| "openclaw_gateway";
|
| "openclaw_gateway";
|
||||||
|
|
||||||
|
const MISSION_PROMPT_CHIPS = [
|
||||||
|
"Build a SaaS product",
|
||||||
|
"Scale a content business",
|
||||||
|
"Launch a marketplace"
|
||||||
|
];
|
||||||
|
|
||||||
|
function buildMissionFromQuestionnaire(q1: string, q2: string, q3: string, q4: string): string {
|
||||||
|
const parts: string[] = [];
|
||||||
|
if (q1.trim()) parts.push(q1.trim());
|
||||||
|
if (q2.trim()) parts.push(`We serve ${q2.trim().toLowerCase()}.`);
|
||||||
|
if (q3.trim()) parts.push(`Our biggest challenge is ${q3.trim().toLowerCase()}.`);
|
||||||
|
if (q4.trim()) parts.push(`Success looks like ${q4.trim().toLowerCase()}.`);
|
||||||
|
return parts.join(" ");
|
||||||
|
}
|
||||||
|
|
||||||
const DEFAULT_TASK_DESCRIPTION = `Setup yourself as the CEO. Use the ceo persona found here:
|
const DEFAULT_TASK_DESCRIPTION = `Setup yourself as the CEO. Use the ceo persona found here:
|
||||||
|
|
||||||
https://github.com/paperclipai/companies/blob/main/default/ceo/AGENTS.md
|
https://github.com/paperclipai/companies/blob/main/default/ceo/AGENTS.md
|
||||||
|
|
@ -91,6 +106,13 @@ export function OnboardingWizard() {
|
||||||
// Step 1
|
// Step 1
|
||||||
const [companyName, setCompanyName] = useState("");
|
const [companyName, setCompanyName] = useState("");
|
||||||
const [companyGoal, setCompanyGoal] = useState("");
|
const [companyGoal, setCompanyGoal] = useState("");
|
||||||
|
const [missionPath, setMissionPath] = useState<"direct" | "questionnaire" | null>(null);
|
||||||
|
const [missionConfirmed, setMissionConfirmed] = useState(false);
|
||||||
|
// Questionnaire answers
|
||||||
|
const [q1, setQ1] = useState(""); // What do you do?
|
||||||
|
const [q2, setQ2] = useState(""); // Who do you serve?
|
||||||
|
const [q3, setQ3] = useState(""); // Biggest bottleneck?
|
||||||
|
const [q4, setQ4] = useState(""); // What would success look like?
|
||||||
|
|
||||||
// Step 2
|
// Step 2
|
||||||
const [agentName, setAgentName] = useState("CEO");
|
const [agentName, setAgentName] = useState("CEO");
|
||||||
|
|
@ -158,8 +180,8 @@ export function OnboardingWizard() {
|
||||||
|
|
||||||
// Resize textarea when step 3 is shown or description changes
|
// Resize textarea when step 3 is shown or description changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (step === 3) autoResizeTextarea();
|
// Auto-resize removed — task description textarea no longer used in onboarding
|
||||||
}, [step, taskDescription, autoResizeTextarea]);
|
}, [step, autoResizeTextarea]);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: adapterModels,
|
data: adapterModels,
|
||||||
|
|
@ -171,7 +193,7 @@ export function OnboardingWizard() {
|
||||||
? queryKeys.agents.adapterModels(createdCompanyId, adapterType)
|
? queryKeys.agents.adapterModels(createdCompanyId, adapterType)
|
||||||
: ["agents", "none", "adapter-models", adapterType],
|
: ["agents", "none", "adapter-models", adapterType],
|
||||||
queryFn: () => agentsApi.adapterModels(createdCompanyId!, adapterType),
|
queryFn: () => agentsApi.adapterModels(createdCompanyId!, adapterType),
|
||||||
enabled: Boolean(createdCompanyId) && onboardingOpen && step === 2
|
enabled: Boolean(createdCompanyId) && onboardingOpen && step === 3
|
||||||
});
|
});
|
||||||
const isLocalAdapter =
|
const isLocalAdapter =
|
||||||
adapterType === "claude_local" ||
|
adapterType === "claude_local" ||
|
||||||
|
|
@ -192,7 +214,7 @@ export function OnboardingWizard() {
|
||||||
: "claude");
|
: "claude");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (step !== 2) return;
|
if (step !== 3) return;
|
||||||
setAdapterEnvResult(null);
|
setAdapterEnvResult(null);
|
||||||
setAdapterEnvError(null);
|
setAdapterEnvError(null);
|
||||||
}, [step, adapterType, cwd, model, command, args, url]);
|
}, [step, adapterType, cwd, model, command, args, url]);
|
||||||
|
|
@ -249,6 +271,12 @@ export function OnboardingWizard() {
|
||||||
setError(null);
|
setError(null);
|
||||||
setCompanyName("");
|
setCompanyName("");
|
||||||
setCompanyGoal("");
|
setCompanyGoal("");
|
||||||
|
setMissionPath(null);
|
||||||
|
setMissionConfirmed(false);
|
||||||
|
setQ1("");
|
||||||
|
setQ2("");
|
||||||
|
setQ3("");
|
||||||
|
setQ4("");
|
||||||
setAgentName("CEO");
|
setAgentName("CEO");
|
||||||
setAdapterType("claude_local");
|
setAdapterType("claude_local");
|
||||||
setCwd("");
|
setCwd("");
|
||||||
|
|
@ -351,7 +379,6 @@ export function OnboardingWizard() {
|
||||||
setSelectedCompanyId(company.id);
|
setSelectedCompanyId(company.id);
|
||||||
queryClient.invalidateQueries({ queryKey: queryKeys.companies.all });
|
queryClient.invalidateQueries({ queryKey: queryKeys.companies.all });
|
||||||
|
|
||||||
if (companyGoal.trim()) {
|
|
||||||
const parsedGoal = parseOnboardingGoalInput(companyGoal);
|
const parsedGoal = parseOnboardingGoalInput(companyGoal);
|
||||||
await goalsApi.create(company.id, {
|
await goalsApi.create(company.id, {
|
||||||
title: parsedGoal.title,
|
title: parsedGoal.title,
|
||||||
|
|
@ -364,7 +391,6 @@ export function OnboardingWizard() {
|
||||||
queryClient.invalidateQueries({
|
queryClient.invalidateQueries({
|
||||||
queryKey: queryKeys.goals.list(company.id)
|
queryKey: queryKeys.goals.list(company.id)
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
setStep(2);
|
setStep(2);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
@ -436,7 +462,7 @@ export function OnboardingWizard() {
|
||||||
queryClient.invalidateQueries({
|
queryClient.invalidateQueries({
|
||||||
queryKey: queryKeys.agents.list(createdCompanyId)
|
queryKey: queryKeys.agents.list(createdCompanyId)
|
||||||
});
|
});
|
||||||
setStep(3);
|
setStep(4);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError(err instanceof Error ? err.message : "Failed to create agent");
|
setError(err instanceof Error ? err.message : "Failed to create agent");
|
||||||
} finally {
|
} finally {
|
||||||
|
|
@ -539,10 +565,12 @@ export function OnboardingWizard() {
|
||||||
function handleKeyDown(e: React.KeyboardEvent) {
|
function handleKeyDown(e: React.KeyboardEvent) {
|
||||||
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
|
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (step === 1 && companyName.trim()) handleStep1Next();
|
if (step === 1 && companyName.trim() && companyGoal.trim()) handleStep1Next();
|
||||||
else if (step === 2 && agentName.trim()) handleStep2Next();
|
else if (step === 2) setStep(3);
|
||||||
else if (step === 3 && taskTitle.trim()) handleStep3Next();
|
else if (step === 3 && agentName.trim()) handleStep2Next();
|
||||||
else if (step === 4) handleLaunch();
|
else if (step === 4) setStep(5);
|
||||||
|
else if (step === 5) setStep(6);
|
||||||
|
else if (step === 6) handleLaunch();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -582,10 +610,12 @@ export function OnboardingWizard() {
|
||||||
<div className="flex items-center gap-0 mb-8 border-b border-border">
|
<div className="flex items-center gap-0 mb-8 border-b border-border">
|
||||||
{(
|
{(
|
||||||
[
|
[
|
||||||
{ step: 1 as Step, label: "Company", icon: Building2 },
|
{ step: 1 as Step, label: "Mission", icon: Building2 },
|
||||||
{ step: 2 as Step, label: "Agent", icon: Bot },
|
{ step: 2 as Step, label: "Launch", icon: Rocket },
|
||||||
{ step: 3 as Step, label: "Task", icon: ListTodo },
|
{ step: 3 as Step, label: "CEO", icon: Bot },
|
||||||
{ step: 4 as Step, label: "Launch", icon: Rocket }
|
{ step: 4 as Step, label: "Chat", icon: Sparkles },
|
||||||
|
{ step: 5 as Step, label: "Plan", icon: ListTodo },
|
||||||
|
{ step: 6 as Step, label: "Hire", icon: Bot }
|
||||||
] as const
|
] as const
|
||||||
).map(({ step: s, label, icon: Icon }) => (
|
).map(({ step: s, label, icon: Icon }) => (
|
||||||
<button
|
<button
|
||||||
|
|
@ -593,7 +623,7 @@ export function OnboardingWizard() {
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setStep(s)}
|
onClick={() => setStep(s)}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex items-center gap-1.5 px-3 py-2 text-xs font-medium border-b-2 -mb-px transition-colors cursor-pointer",
|
"flex items-center gap-1.5 px-2 py-2 text-xs font-medium border-b-2 -mb-px transition-colors cursor-pointer",
|
||||||
s === step
|
s === step
|
||||||
? "border-foreground text-foreground"
|
? "border-foreground text-foreground"
|
||||||
: "border-transparent text-muted-foreground hover:text-foreground/70 hover:border-border"
|
: "border-transparent text-muted-foreground hover:text-foreground/70 hover:border-border"
|
||||||
|
|
@ -613,9 +643,10 @@ export function OnboardingWizard() {
|
||||||
<Building2 className="h-5 w-5 text-muted-foreground" />
|
<Building2 className="h-5 w-5 text-muted-foreground" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h3 className="font-medium">Name your company</h3>
|
<h3 className="font-medium">Define your mission</h3>
|
||||||
<p className="text-xs text-muted-foreground">
|
<p className="text-xs text-muted-foreground">
|
||||||
This is the organization your agents will work for.
|
Your mission drives everything — your CEO, your hires,
|
||||||
|
and the work your company will do.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -638,6 +669,41 @@ export function OnboardingWizard() {
|
||||||
autoFocus
|
autoFocus
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Mission path selector */}
|
||||||
|
{!missionPath && (
|
||||||
|
<div className="space-y-3">
|
||||||
|
<label className="text-xs text-foreground block">
|
||||||
|
How would you like to define your mission?
|
||||||
|
</label>
|
||||||
|
<div className="grid grid-cols-2 gap-2">
|
||||||
|
<button
|
||||||
|
className="flex flex-col items-center gap-1.5 rounded-md border border-border p-3 text-xs hover:bg-accent/50 transition-colors"
|
||||||
|
onClick={() => setMissionPath("direct")}
|
||||||
|
>
|
||||||
|
<Sparkles className="h-4 w-4" />
|
||||||
|
<span className="font-medium">I know my mission</span>
|
||||||
|
<span className="text-muted-foreground text-[10px]">
|
||||||
|
Type it directly
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="flex flex-col items-center gap-1.5 rounded-md border border-border p-3 text-xs hover:bg-accent/50 transition-colors"
|
||||||
|
onClick={() => setMissionPath("questionnaire")}
|
||||||
|
>
|
||||||
|
<ListTodo className="h-4 w-4" />
|
||||||
|
<span className="font-medium">Help me figure it out</span>
|
||||||
|
<span className="text-muted-foreground text-[10px]">
|
||||||
|
Answer a few questions
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Direct mission input */}
|
||||||
|
{missionPath === "direct" && (
|
||||||
|
<div className="space-y-3">
|
||||||
<div className="group">
|
<div className="group">
|
||||||
<label
|
<label
|
||||||
className={cn(
|
className={cn(
|
||||||
|
|
@ -647,28 +713,175 @@ export function OnboardingWizard() {
|
||||||
: "text-muted-foreground group-focus-within:text-foreground"
|
: "text-muted-foreground group-focus-within:text-foreground"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
Mission / goal (optional)
|
Mission
|
||||||
</label>
|
</label>
|
||||||
<textarea
|
<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]"
|
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?"
|
placeholder="What is this company trying to achieve?"
|
||||||
value={companyGoal}
|
value={companyGoal}
|
||||||
onChange={(e) => setCompanyGoal(e.target.value)}
|
onChange={(e) => setCompanyGoal(e.target.value)}
|
||||||
|
autoFocus
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
{/* Prompt chips for inspiration */}
|
||||||
|
<div className="flex flex-wrap gap-1.5">
|
||||||
|
{MISSION_PROMPT_CHIPS.map((chip) => (
|
||||||
|
<button
|
||||||
|
key={chip}
|
||||||
|
className={cn(
|
||||||
|
"rounded-full border px-2.5 py-1 text-[11px] transition-colors",
|
||||||
|
companyGoal === chip
|
||||||
|
? "border-foreground bg-accent text-foreground"
|
||||||
|
: "border-border text-muted-foreground hover:text-foreground hover:border-foreground/50"
|
||||||
|
)}
|
||||||
|
onClick={() => setCompanyGoal(chip)}
|
||||||
|
>
|
||||||
|
{chip}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
className="text-[11px] text-muted-foreground hover:text-foreground transition-colors"
|
||||||
|
onClick={() => { setMissionPath(null); setCompanyGoal(""); }}
|
||||||
|
>
|
||||||
|
← Choose a different path
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Questionnaire path */}
|
||||||
|
{missionPath === "questionnaire" && !missionConfirmed && (
|
||||||
|
<div className="space-y-3">
|
||||||
|
<div className="group">
|
||||||
|
<label className="text-xs text-muted-foreground mb-1 block">
|
||||||
|
What does your company do?
|
||||||
|
</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. We create educational YouTube content about AI"
|
||||||
|
value={q1}
|
||||||
|
onChange={(e) => setQ1(e.target.value)}
|
||||||
|
autoFocus
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="group">
|
||||||
|
<label className="text-xs text-muted-foreground mb-1 block">
|
||||||
|
Who do you serve?
|
||||||
|
</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. Non-technical professionals curious about AI tools"
|
||||||
|
value={q2}
|
||||||
|
onChange={(e) => setQ2(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="group">
|
||||||
|
<label className="text-xs text-muted-foreground mb-1 block">
|
||||||
|
What's your biggest bottleneck right now?
|
||||||
|
</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. Can't produce content fast enough across multiple channels"
|
||||||
|
value={q3}
|
||||||
|
onChange={(e) => setQ3(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="group">
|
||||||
|
<label className="text-xs text-muted-foreground mb-1 block">
|
||||||
|
What would success look like in 6 months?
|
||||||
|
</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. Publishing daily content across 4 platforms with a team of AI agents"
|
||||||
|
value={q4}
|
||||||
|
onChange={(e) => setQ4(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{q1.trim() && (
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => {
|
||||||
|
setCompanyGoal(buildMissionFromQuestionnaire(q1, q2, q3, q4));
|
||||||
|
setMissionConfirmed(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Generate my mission
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<button
|
||||||
|
className="text-[11px] text-muted-foreground hover:text-foreground transition-colors block"
|
||||||
|
onClick={() => { setMissionPath(null); setQ1(""); setQ2(""); setQ3(""); setQ4(""); }}
|
||||||
|
>
|
||||||
|
← Choose a different path
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Questionnaire result — editable mission */}
|
||||||
|
{missionPath === "questionnaire" && missionConfirmed && (
|
||||||
|
<div className="space-y-3">
|
||||||
|
<div className="group">
|
||||||
|
<label className="text-xs text-foreground mb-1 block">
|
||||||
|
Here's your draft mission — edit it however you like:
|
||||||
|
</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]"
|
||||||
|
value={companyGoal}
|
||||||
|
onChange={(e) => setCompanyGoal(e.target.value)}
|
||||||
|
autoFocus
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
className="text-[11px] text-muted-foreground hover:text-foreground transition-colors"
|
||||||
|
onClick={() => { setMissionConfirmed(false); setCompanyGoal(""); }}
|
||||||
|
>
|
||||||
|
← Back to questions
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Confirm mission note */}
|
||||||
|
{companyGoal.trim() && companyName.trim() && (
|
||||||
|
<p className="text-[11px] text-muted-foreground italic">
|
||||||
|
You can always change your mission later in settings.
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Step 2: Launch celebration */}
|
||||||
{step === 2 && (
|
{step === 2 && (
|
||||||
|
<div className="space-y-6 text-center py-4">
|
||||||
|
<div className="text-5xl">🚀</div>
|
||||||
|
<div>
|
||||||
|
<h3 className="text-xl font-semibold">{companyName} is live!</h3>
|
||||||
|
<p className="text-sm text-muted-foreground mt-2">
|
||||||
|
Your company has been created with the mission:
|
||||||
|
</p>
|
||||||
|
<p className="text-sm font-medium mt-1 italic">
|
||||||
|
"{companyGoal}"
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
Next, let's bring your CEO to life.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Step 3: Create your CEO (was step 2) */}
|
||||||
|
{step === 3 && (
|
||||||
<div className="space-y-5">
|
<div className="space-y-5">
|
||||||
<div className="flex items-center gap-3 mb-1">
|
<div className="flex items-center gap-3 mb-1">
|
||||||
<div className="bg-muted/50 p-2">
|
<div className="bg-muted/50 p-2">
|
||||||
<Bot className="h-5 w-5 text-muted-foreground" />
|
<Bot className="h-5 w-5 text-muted-foreground" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h3 className="font-medium">Create your first agent</h3>
|
<h3 className="font-medium">Bring your CEO to life</h3>
|
||||||
<p className="text-xs text-muted-foreground">
|
<p className="text-xs text-muted-foreground">
|
||||||
Choose how this agent will run tasks.
|
Give your CEO a heartbeat. They'll lead{" "}
|
||||||
|
<span className="font-medium text-foreground">{companyName}</span>{" "}
|
||||||
|
toward its mission.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -1129,58 +1342,64 @@ export function OnboardingWizard() {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{step === 3 && (
|
{/* Step 4: Chat with CEO — placeholder for OnboardingChat component */}
|
||||||
|
{step === 4 && (
|
||||||
|
<div className="space-y-5">
|
||||||
|
<div className="flex items-center gap-3 mb-1">
|
||||||
|
<div className="bg-muted/50 p-2">
|
||||||
|
<Sparkles className="h-5 w-5 text-muted-foreground" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 className="font-medium">Chat with your CEO</h3>
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
Work with your CEO to build a hiring plan for{" "}
|
||||||
|
<span className="font-medium text-foreground">{companyName}</span>.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="rounded-md border border-border p-4 min-h-[200px] flex items-center justify-center">
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
Chat component coming soon — skip to review a sample hiring plan.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Step 5: Review hiring plan — placeholder */}
|
||||||
|
{step === 5 && (
|
||||||
<div className="space-y-5">
|
<div className="space-y-5">
|
||||||
<div className="flex items-center gap-3 mb-1">
|
<div className="flex items-center gap-3 mb-1">
|
||||||
<div className="bg-muted/50 p-2">
|
<div className="bg-muted/50 p-2">
|
||||||
<ListTodo className="h-5 w-5 text-muted-foreground" />
|
<ListTodo className="h-5 w-5 text-muted-foreground" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h3 className="font-medium">Give it something to do</h3>
|
<h3 className="font-medium">Review your hiring plan</h3>
|
||||||
<p className="text-xs text-muted-foreground">
|
<p className="text-xs text-muted-foreground">
|
||||||
Give your agent a small task to start with — a bug fix,
|
Select which roles to hire. You can edit, add, or remove
|
||||||
a research question, writing a script.
|
roles before approving.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div className="rounded-md border border-border p-4 min-h-[200px] flex items-center justify-center">
|
||||||
<label className="text-xs text-muted-foreground mb-1 block">
|
<p className="text-sm text-muted-foreground">
|
||||||
Task title
|
Hiring plan review component coming soon.
|
||||||
</label>
|
</p>
|
||||||
<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
|
|
||||||
ref={textareaRef}
|
|
||||||
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-[120px] max-h-[300px] overflow-y-auto"
|
|
||||||
placeholder="Add more detail about what the agent should do..."
|
|
||||||
value={taskDescription}
|
|
||||||
onChange={(e) => setTaskDescription(e.target.value)}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{step === 4 && (
|
{/* Step 6: Make your first hires */}
|
||||||
|
{step === 6 && (
|
||||||
<div className="space-y-5">
|
<div className="space-y-5">
|
||||||
<div className="flex items-center gap-3 mb-1">
|
<div className="flex items-center gap-3 mb-1">
|
||||||
<div className="bg-muted/50 p-2">
|
<div className="bg-muted/50 p-2">
|
||||||
<Rocket className="h-5 w-5 text-muted-foreground" />
|
<Rocket className="h-5 w-5 text-muted-foreground" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h3 className="font-medium">Ready to launch</h3>
|
<h3 className="font-medium">Make your first hires</h3>
|
||||||
<p className="text-xs text-muted-foreground">
|
<p className="text-xs text-muted-foreground">
|
||||||
Everything is set up. Launching now will create the
|
Everything is set up. Approving will create hire tasks
|
||||||
starter task, wake the agent, and open the issue.
|
for your CEO to act on.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -1202,7 +1421,7 @@ export function OnboardingWizard() {
|
||||||
{agentName}
|
{agentName}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-xs text-muted-foreground">
|
<p className="text-xs text-muted-foreground">
|
||||||
{getUIAdapter(adapterType).label}
|
CEO · {getUIAdapter(adapterType).label}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Check className="h-4 w-4 text-green-500 shrink-0" />
|
<Check className="h-4 w-4 text-green-500 shrink-0" />
|
||||||
|
|
@ -1211,9 +1430,11 @@ export function OnboardingWizard() {
|
||||||
<ListTodo className="h-4 w-4 text-muted-foreground shrink-0" />
|
<ListTodo className="h-4 w-4 text-muted-foreground shrink-0" />
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<p className="text-sm font-medium truncate">
|
<p className="text-sm font-medium truncate">
|
||||||
{taskTitle}
|
Hiring plan approved
|
||||||
|
</p>
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
{companyGoal}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-xs text-muted-foreground">Task</p>
|
|
||||||
</div>
|
</div>
|
||||||
<Check className="h-4 w-4 text-green-500 shrink-0" />
|
<Check className="h-4 w-4 text-green-500 shrink-0" />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -1247,7 +1468,7 @@ export function OnboardingWizard() {
|
||||||
{step === 1 && (
|
{step === 1 && (
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
disabled={!companyName.trim() || loading}
|
disabled={!companyName.trim() || !companyGoal.trim() || loading}
|
||||||
onClick={handleStep1Next}
|
onClick={handleStep1Next}
|
||||||
>
|
>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
|
|
@ -1255,10 +1476,19 @@ export function OnboardingWizard() {
|
||||||
) : (
|
) : (
|
||||||
<ArrowRight className="h-3.5 w-3.5 mr-1" />
|
<ArrowRight className="h-3.5 w-3.5 mr-1" />
|
||||||
)}
|
)}
|
||||||
{loading ? "Creating..." : "Next"}
|
{loading ? "Creating..." : "Confirm mission"}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{step === 2 && (
|
{step === 2 && (
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
onClick={() => setStep(3)}
|
||||||
|
>
|
||||||
|
<ArrowRight className="h-3.5 w-3.5 mr-1" />
|
||||||
|
Hire your CEO
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{step === 3 && (
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
disabled={
|
disabled={
|
||||||
|
|
@ -1271,31 +1501,45 @@ export function OnboardingWizard() {
|
||||||
) : (
|
) : (
|
||||||
<ArrowRight className="h-3.5 w-3.5 mr-1" />
|
<ArrowRight className="h-3.5 w-3.5 mr-1" />
|
||||||
)}
|
)}
|
||||||
{loading ? "Creating..." : "Next"}
|
{loading ? "Bringing to life..." : "Give it a heartbeat"}
|
||||||
</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>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{step === 4 && (
|
{step === 4 && (
|
||||||
|
<>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => setStep(5)}
|
||||||
|
>
|
||||||
|
Skip chat
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
disabled={loading}
|
||||||
|
onClick={() => setStep(5)}
|
||||||
|
>
|
||||||
|
<ArrowRight className="h-3.5 w-3.5 mr-1" />
|
||||||
|
Next
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{step === 5 && (
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
onClick={() => setStep(6)}
|
||||||
|
>
|
||||||
|
<ArrowRight className="h-3.5 w-3.5 mr-1" />
|
||||||
|
Approve hiring plan
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{step === 6 && (
|
||||||
<Button size="sm" disabled={loading} onClick={handleLaunch}>
|
<Button size="sm" disabled={loading} onClick={handleLaunch}>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<Loader2 className="h-3.5 w-3.5 mr-1 animate-spin" />
|
<Loader2 className="h-3.5 w-3.5 mr-1 animate-spin" />
|
||||||
) : (
|
) : (
|
||||||
<ArrowRight className="h-3.5 w-3.5 mr-1" />
|
<Rocket className="h-3.5 w-3.5 mr-1" />
|
||||||
)}
|
)}
|
||||||
{loading ? "Creating..." : "Create & Open Issue"}
|
{loading ? "Creating tasks..." : "Make your first hires"}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ interface NewGoalDefaults {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface OnboardingOptions {
|
interface OnboardingOptions {
|
||||||
initialStep?: 1 | 2 | 3 | 4;
|
initialStep?: 1 | 2 | 3 | 4 | 5 | 6;
|
||||||
companyId?: string;
|
companyId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue