/ src / components / auth / setup-wizard / step-profile.tsx
step-profile.tsx
  1  'use client'
  2  
  3  import { useCallback, useId, useState } from 'react'
  4  import { AgentAvatar } from '@/components/agents/agent-avatar'
  5  import { StepShell } from './shared'
  6  
  7  export interface StepProfileProps {
  8    onContinue: (userName: string, avatarSeed: string) => void
  9    onSkip: () => void
 10  }
 11  
 12  export function StepProfile({ onContinue, onSkip }: StepProfileProps) {
 13    const [name, setName] = useState('')
 14    const defaultAvatarSeed = useId().replace(/:/g, '')
 15    const [avatarSeed, setAvatarSeed] = useState(defaultAvatarSeed)
 16    const [seedOpen, setSeedOpen] = useState(false)
 17  
 18    const hasName = name.trim().length > 0
 19  
 20    const randomizeSeed = useCallback(() => {
 21      setAvatarSeed(Math.random().toString(36).slice(2, 10))
 22    }, [])
 23  
 24    const handleSubmit = (e: React.FormEvent) => {
 25      e.preventDefault()
 26      if (!hasName) return
 27      onContinue(name.trim().toLowerCase(), avatarSeed.trim() || defaultAvatarSeed)
 28    }
 29  
 30    return (
 31      <StepShell>
 32        {/* Avatar hero */}
 33        <div className="flex justify-center mb-8">
 34          <div className="relative group">
 35            <div
 36              className="absolute inset-[-12px] rounded-full transition-all duration-700"
 37              style={{
 38                background: hasName
 39                  ? 'conic-gradient(from 0deg, rgba(99,102,241,0.15), rgba(236,72,153,0.1), rgba(52,211,153,0.1), rgba(99,102,241,0.15))'
 40                  : 'conic-gradient(from 0deg, rgba(99,102,241,0.06), transparent, rgba(99,102,241,0.06))',
 41                animation: 'sparkle-spin 8s linear infinite',
 42                filter: 'blur(8px)',
 43              }}
 44            />
 45            <div
 46              className="absolute inset-[-4px] rounded-full transition-all duration-500"
 47              style={{
 48                border: '1px solid',
 49                borderColor: hasName ? 'rgba(99,102,241,0.2)' : 'rgba(255,255,255,0.06)',
 50              }}
 51            />
 52            <div className="relative">
 53              <AgentAvatar seed={avatarSeed || null} name={name || '?'} size={96} />
 54            </div>
 55  
 56            <button
 57              type="button"
 58              onClick={randomizeSeed}
 59              className="absolute -bottom-1 -right-1 w-7 h-7 rounded-full bg-surface border border-white/[0.08]
 60                flex items-center justify-center cursor-pointer
 61                hover:bg-white/[0.08] hover:border-accent-bright/30 active:scale-90
 62                transition-all duration-200 z-10"
 63              title="Randomize avatar"
 64            >
 65              <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="text-text-3">
 66                <rect x="2" y="2" width="20" height="20" rx="3" />
 67                <circle cx="8" cy="8" r="1.5" fill="currentColor" stroke="none" />
 68                <circle cx="16" cy="8" r="1.5" fill="currentColor" stroke="none" />
 69                <circle cx="8" cy="16" r="1.5" fill="currentColor" stroke="none" />
 70                <circle cx="16" cy="16" r="1.5" fill="currentColor" stroke="none" />
 71              </svg>
 72            </button>
 73          </div>
 74        </div>
 75  
 76        <h1 className="font-display text-[36px] font-800 leading-[1.05] tracking-[-0.04em] mb-2">
 77          Welcome
 78        </h1>
 79        <p className="text-[15px] text-text-2 mb-8">
 80          What should we call you?
 81        </p>
 82  
 83        <form onSubmit={handleSubmit} className="flex flex-col items-center gap-5">
 84          <input
 85            type="text"
 86            value={name}
 87            onChange={(e) => setName(e.target.value)}
 88            placeholder="Your name"
 89            autoFocus
 90            className="w-full max-w-[300px] px-6 py-4 rounded-[16px] border border-white/[0.08] bg-surface
 91              text-text text-[18px] text-center font-display font-600 outline-none
 92              transition-all duration-200 placeholder:text-text-3/70
 93              focus:border-accent-bright/30 focus:shadow-[0_0_30px_rgba(99,102,241,0.1)]"
 94          />
 95  
 96          {!seedOpen ? (
 97            <button
 98              type="button"
 99              onClick={() => setSeedOpen(true)}
100              className="bg-transparent border-none text-[12px] text-text-3 cursor-pointer hover:text-text-2 transition-colors"
101            >
102              Customize avatar seed
103            </button>
104          ) : (
105            <div className="flex items-center gap-2">
106              <input
107                type="text"
108                value={avatarSeed}
109                onChange={(e) => setAvatarSeed(e.target.value)}
110                placeholder="Avatar seed"
111                className="w-[160px] px-3 py-2 rounded-[10px] border border-white/[0.08] bg-surface
112                  text-text text-[13px] text-center outline-none transition-all
113                  focus:border-accent-bright/30"
114              />
115              <button
116                type="button"
117                onClick={randomizeSeed}
118                className="px-3 py-2 rounded-[10px] border border-white/[0.08] bg-transparent text-text-3 text-[12px] font-600
119                  cursor-pointer transition-all hover:bg-white/[0.04] shrink-0"
120              >
121                Randomize
122              </button>
123            </div>
124          )}
125  
126          <div className="flex items-center gap-3">
127            <button
128              type="button"
129              onClick={onSkip}
130              className="px-6 py-3.5 rounded-[14px] border border-white/[0.08] bg-transparent text-text-2 text-[14px]
131                font-display font-500 cursor-pointer hover:bg-white/[0.03] transition-all duration-200"
132            >
133              Skip for now
134            </button>
135            <button
136              type="submit"
137              disabled={!hasName}
138              className="px-10 py-3.5 rounded-[14px] border-none bg-accent-bright text-white text-[15px] font-display font-600
139                cursor-pointer hover:brightness-110 active:scale-[0.97] transition-all duration-200
140                shadow-[0_6px_28px_rgba(99,102,241,0.3)] disabled:opacity-30"
141            >
142              Continue
143            </button>
144          </div>
145        </form>
146      </StepShell>
147    )
148  }