step-path.tsx
1 'use client' 2 3 import { ONBOARDING_PATHS } from '@/lib/setup-defaults' 4 import type { StepPathProps } from './types' 5 import { StepShell, SkipLink } from './shared' 6 import { formatAgentCount, getStarterKitsForPath } from './utils' 7 8 export function StepPath({ 9 onboardingPath, 10 starterKitId, 11 intentText, 12 onPathChange, 13 onStarterKitChange, 14 onIntentTextChange, 15 onContinue, 16 onBack, 17 onSkip, 18 }: StepPathProps) { 19 const visibleStarterKits = getStarterKitsForPath(onboardingPath) 20 21 return ( 22 <StepShell wide> 23 <h1 className="font-display text-[36px] font-800 leading-[1.05] tracking-[-0.04em] mb-3"> 24 Choose Your Start 25 </h1> 26 <p className="text-[15px] text-text-2 mb-2"> 27 Pick the setup path that matches how much guidance you want. 28 </p> 29 <p className="text-[13px] text-text-3 mb-7"> 30 You can still edit providers, prompts, tools, and agent details before finishing setup. 31 </p> 32 33 <div className="grid gap-3 md:grid-cols-3 text-left mb-6"> 34 {ONBOARDING_PATHS.map((path) => { 35 const active = path.id === onboardingPath 36 return ( 37 <button 38 key={path.id} 39 type="button" 40 onClick={() => onPathChange(path.id)} 41 className={`rounded-[18px] border px-5 py-4 text-left transition-all duration-200 cursor-pointer ${ 42 active 43 ? 'border-accent-bright/35 bg-accent-soft shadow-[0_0_24px_rgba(99,102,241,0.12)]' 44 : 'border-white/[0.08] bg-surface hover:border-accent-bright/20 hover:bg-white/[0.04]' 45 }`} 46 > 47 <div className="flex items-start justify-between gap-3"> 48 <div className="text-[15px] font-display font-700 text-text">{path.title}</div> 49 {path.badge ? ( 50 <span className={`rounded-full px-2 py-1 text-[10px] font-700 uppercase tracking-[0.12em] ${ 51 active ? 'bg-accent-bright text-black' : 'bg-white/[0.05] text-text-3/80' 52 }`}> 53 {path.badge} 54 </span> 55 ) : null} 56 </div> 57 <p className="mt-2 text-[13px] leading-relaxed text-text-2">{path.description}</p> 58 <p className="mt-3 text-[12px] leading-relaxed text-text-3/72">{path.detail}</p> 59 </button> 60 ) 61 })} 62 </div> 63 64 {onboardingPath === 'intent' && ( 65 <div className="mb-6 rounded-[18px] border border-white/[0.08] bg-surface px-5 py-4 text-left"> 66 <label className="block text-[12px] font-700 uppercase tracking-[0.12em] text-text-3/60 mb-2"> 67 What Are You Setting Up SwarmClaw To Do? 68 </label> 69 <textarea 70 value={intentText} 71 onChange={(event) => onIntentTextChange(event.target.value)} 72 rows={3} 73 placeholder="e.g. Help me run product research every week, summarize findings, and turn them into follow-up tasks." 74 className="w-full rounded-[14px] border border-white/[0.08] bg-bg px-4 py-3 text-[14px] text-text outline-none transition-all duration-200 resize-none placeholder:text-text-3/45 focus:border-accent-bright/30 focus:shadow-[0_0_30px_rgba(99,102,241,0.1)]" 75 /> 76 <p className="mt-2 text-[12px] leading-relaxed text-text-3/72"> 77 This is used only to seed the starter prompts. It does not auto-classify your workflow. 78 </p> 79 </div> 80 )} 81 82 <div className="rounded-[20px] border border-white/[0.08] bg-surface p-5 text-left"> 83 <div className="flex flex-wrap items-start justify-between gap-3"> 84 <div> 85 <div className="text-[11px] font-700 uppercase tracking-[0.12em] text-text-3/55">Starting Shape</div> 86 <div className="mt-1 text-[13px] text-text-3/72"> 87 Start from a broad team shape instead of a niche preset. You can still edit every agent before setup finishes. 88 </div> 89 </div> 90 <div className="rounded-full border border-white/[0.08] bg-white/[0.03] px-3 py-1 text-[11px] font-700 uppercase tracking-[0.12em] text-text-3/70"> 91 {visibleStarterKits.length} options 92 </div> 93 </div> 94 95 <div className="mt-4 grid gap-3 md:grid-cols-2 xl:grid-cols-3"> 96 {visibleStarterKits.map((kit) => { 97 const active = starterKitId === kit.id 98 return ( 99 <button 100 key={kit.id} 101 type="button" 102 onClick={() => onStarterKitChange(kit.id)} 103 className={`rounded-[18px] border px-4 py-4 text-left transition-all duration-200 cursor-pointer ${ 104 active 105 ? 'border-accent-bright/35 bg-accent-soft shadow-[0_0_24px_rgba(99,102,241,0.12)]' 106 : 'border-white/[0.08] bg-white/[0.02] hover:border-accent-bright/20 hover:bg-white/[0.04]' 107 }`} 108 > 109 <div className="flex items-start justify-between gap-3"> 110 <div className="text-[15px] font-display font-700 text-text">{kit.name}</div> 111 <span className={`rounded-full px-2 py-1 text-[10px] font-700 uppercase tracking-[0.12em] ${ 112 active ? 'bg-accent-bright text-black' : 'bg-white/[0.05] text-text-3/80' 113 }`}> 114 {kit.badge || formatAgentCount(kit.agents.length)} 115 </span> 116 </div> 117 <p className="mt-2 text-[13px] leading-relaxed text-text-2">{kit.description}</p> 118 <p className="mt-3 text-[12px] leading-relaxed text-text-3/72">{kit.detail}</p> 119 {kit.agents.length > 0 ? ( 120 <div className="mt-4 flex flex-wrap gap-2"> 121 {kit.agents.map((agent) => ( 122 <span 123 key={agent.id} 124 className="rounded-full border border-white/[0.08] bg-white/[0.03] px-2.5 py-1 text-[11px] text-text-2" 125 > 126 {agent.name} 127 </span> 128 ))} 129 </div> 130 ) : ( 131 <div className="mt-4 rounded-[12px] border border-dashed border-white/[0.06] bg-white/[0.02] px-3 py-2 text-[11px] text-text-3/70"> 132 Finish setup without starter agents. 133 </div> 134 )} 135 </button> 136 ) 137 })} 138 </div> 139 </div> 140 141 <div className="mt-6 flex items-center justify-center gap-3"> 142 <button 143 onClick={onBack} 144 className="px-6 py-3.5 rounded-[14px] border border-white/[0.08] bg-transparent text-text-2 text-[14px] font-display font-500 cursor-pointer hover:bg-white/[0.03] transition-all duration-200" 145 > 146 Back 147 </button> 148 <button 149 onClick={onContinue} 150 className="px-8 py-3.5 rounded-[14px] border-none bg-accent-bright text-white text-[15px] font-display font-600 cursor-pointer hover:brightness-110 active:scale-[0.97] transition-all duration-200 shadow-[0_6px_28px_rgba(99,102,241,0.3)]" 151 > 152 Continue to Providers 153 </button> 154 </div> 155 156 <SkipLink onClick={onSkip} /> 157 </StepShell> 158 ) 159 }