IntentProbe.tsx
1 // Copyright (c) 2026 VPL Solutions. All rights reserved. 2 // Licensed under the MIT License. See LICENSE for details. 3 4 import { useState } from 'react'; 5 import { ChevronRight, X } from 'lucide-react'; 6 import { 7 INTENT_OPTIONS, 8 TOPOLOGY_OPTIONS, 9 getIntentResponse, 10 type IntentKey, 11 type TopologyKey, 12 } from '../../data/intentResponses'; 13 14 interface IntentProbeProps { 15 onComplete: (intent: IntentKey, topology: TopologyKey) => void; 16 onSkip: () => void; 17 } 18 19 export function IntentProbe({ onComplete, onSkip }: IntentProbeProps) { 20 const [step, setStep] = useState<1 | 2 | 3>(1); 21 const [intent, setIntent] = useState<IntentKey | null>(null); 22 const [topology, setTopology] = useState<TopologyKey | null>(null); 23 24 function selectIntent(key: IntentKey) { 25 setIntent(key); 26 setStep(2); 27 } 28 29 function selectTopology(key: TopologyKey) { 30 setTopology(key); 31 setStep(3); 32 if (intent) { 33 onComplete(intent, key); 34 } 35 } 36 37 return ( 38 <div className="bg-white/[0.04] rounded-xl border border-white/10 p-6 backdrop-blur-sm max-w-sm w-full"> 39 {/* Header with skip */} 40 <div className="flex items-start justify-between mb-4"> 41 <p className="text-xs uppercase tracking-widest text-white/30"> 42 {step === 1 && 'Quick setup'} 43 {step === 2 && 'One more'} 44 {step === 3 && 'Got it'} 45 </p> 46 <button 47 onClick={onSkip} 48 className="text-white/25 hover:text-white/50 transition-colors p-0.5 -mr-1 -mt-1" 49 aria-label="Skip setup" 50 > 51 <X className="w-4 h-4" /> 52 </button> 53 </div> 54 55 {/* Step 1: Intent */} 56 {step === 1 && ( 57 <div className="space-y-3"> 58 <p className="text-sm text-white/70 font-medium"> 59 How are you planning to use Meridian? 60 </p> 61 <div className="space-y-2"> 62 {INTENT_OPTIONS.map(({ key, label }) => ( 63 <button 64 key={key} 65 onClick={() => selectIntent(key)} 66 className="w-full text-left px-3.5 py-2.5 rounded-lg border border-white/10 text-sm text-white/60 hover:text-white/90 hover:border-violet-500/40 hover:bg-violet-500/10 transition-all flex items-center justify-between group" 67 > 68 {label} 69 <ChevronRight className="w-3.5 h-3.5 opacity-0 group-hover:opacity-100 transition-opacity" /> 70 </button> 71 ))} 72 </div> 73 </div> 74 )} 75 76 {/* Step 2: Topology */} 77 {step === 2 && ( 78 <div className="space-y-3"> 79 <p className="text-sm text-white/70 font-medium"> 80 Where will this run? 81 </p> 82 <div className="flex gap-2"> 83 {TOPOLOGY_OPTIONS.map(({ key, label }) => ( 84 <button 85 key={key} 86 onClick={() => selectTopology(key)} 87 className="flex-1 px-3 py-2.5 rounded-lg border border-white/10 text-sm text-white/60 hover:text-white/90 hover:border-teal-500/40 hover:bg-teal-500/10 transition-all text-center" 88 > 89 {label} 90 </button> 91 ))} 92 </div> 93 </div> 94 )} 95 96 {/* Step 3: Response */} 97 {step === 3 && intent && topology && ( 98 <div className="space-y-3"> 99 <p className="text-sm text-white/60 leading-relaxed"> 100 {getIntentResponse(intent, topology)} 101 </p> 102 <div className="flex items-center gap-1.5 text-xs text-white/25"> 103 <span className="inline-block w-1 h-1 rounded-full bg-teal-400/50" /> 104 Selection saved 105 </div> 106 </div> 107 )} 108 109 {/* Progress dots */} 110 <div className="flex items-center justify-center gap-1.5 mt-4"> 111 {[1, 2, 3].map((s) => ( 112 <div 113 key={s} 114 className={`w-1.5 h-1.5 rounded-full transition-colors ${ 115 s === step 116 ? 'bg-violet-400/70' 117 : s < step 118 ? 'bg-teal-400/40' 119 : 'bg-white/10' 120 }`} 121 /> 122 ))} 123 </div> 124 </div> 125 ); 126 }