/ src / components / shared / profile-sheet.tsx
profile-sheet.tsx
  1  'use client'
  2  
  3  import { useState, useEffect } from 'react'
  4  import { useAppStore } from '@/stores/use-app-store'
  5  import { BottomSheet } from '@/components/shared/bottom-sheet'
  6  import { AgentAvatar } from '@/components/agents/agent-avatar'
  7  import { api } from '@/lib/app/api-client'
  8  import { toast } from 'sonner'
  9  
 10  interface Props {
 11    open: boolean
 12    onClose: () => void
 13  }
 14  
 15  export function ProfileSheet({ open, onClose }: Props) {
 16    const appSettings = useAppStore((s) => s.appSettings)
 17    const loadSettings = useAppStore((s) => s.loadSettings)
 18    const setUser = useAppStore((s) => s.setUser)
 19    const currentUser = useAppStore((s) => s.currentUser)
 20  
 21    const [name, setName] = useState('')
 22    const [avatarSeed, setAvatarSeed] = useState('')
 23    const [saving, setSaving] = useState(false)
 24  
 25    useEffect(() => {
 26      if (open) {
 27        setName(appSettings.userName || currentUser || '')
 28        setAvatarSeed(appSettings.userAvatarSeed || '')
 29      }
 30    }, [open, appSettings.userName, appSettings.userAvatarSeed, currentUser])
 31  
 32    const handleSave = async () => {
 33      const trimmed = name.trim()
 34      if (!trimmed || saving) return
 35      setSaving(true)
 36      try {
 37        await api('PUT', '/settings', {
 38          userName: trimmed.toLowerCase(),
 39          userAvatarSeed: avatarSeed.trim() || undefined,
 40        })
 41        setUser(trimmed.toLowerCase())
 42        await loadSettings()
 43        toast.success('Profile updated')
 44        onClose()
 45      } catch (err: unknown) {
 46        toast.error(err instanceof Error ? err.message : 'Failed to update profile')
 47      } finally {
 48        setSaving(false)
 49      }
 50    }
 51  
 52    const handleSignOut = () => {
 53      setUser(null)
 54      onClose()
 55    }
 56  
 57    return (
 58      <BottomSheet open={open} onClose={onClose}>
 59        <div className="p-6 max-w-[400px] mx-auto">
 60          <h2 className="font-display text-[18px] font-700 text-text mb-6 text-center">Profile</h2>
 61  
 62          {/* Avatar preview */}
 63          <div className="flex justify-center mb-6">
 64            <AgentAvatar seed={avatarSeed || null} name={name || '?'} size={72} />
 65          </div>
 66  
 67          {/* Avatar seed */}
 68          <div className="mb-4">
 69            <label className="block text-[12px] font-600 text-text-2 mb-1.5">Avatar</label>
 70            <div className="flex items-center gap-2">
 71              <input
 72                type="text"
 73                value={avatarSeed}
 74                onChange={(e) => setAvatarSeed(e.target.value)}
 75                placeholder="Avatar seed (any text)"
 76                className="flex-1 px-3 py-2 rounded-[8px] bg-white/[0.06] border border-white/[0.08] text-[13px] text-text placeholder:text-text-3 focus:outline-none focus:border-accent-bright/40"
 77              />
 78              <button
 79                type="button"
 80                onClick={() => setAvatarSeed(Math.random().toString(36).slice(2, 10))}
 81                className="px-3 py-2 rounded-[8px] border border-white/[0.08] bg-transparent text-text-3 text-[12px] font-600 cursor-pointer transition-all hover:bg-white/[0.04] shrink-0"
 82              >
 83                Randomize
 84              </button>
 85            </div>
 86          </div>
 87  
 88          {/* Name */}
 89          <div className="mb-6">
 90            <label className="block text-[12px] font-600 text-text-2 mb-1.5">Name</label>
 91            <input
 92              type="text"
 93              value={name}
 94              onChange={(e) => setName(e.target.value)}
 95              placeholder="Your name"
 96              className="w-full px-3 py-2 rounded-[8px] bg-white/[0.06] border border-white/[0.08] text-[13px] text-text placeholder:text-text-3 focus:outline-none focus:border-accent-bright/40"
 97            />
 98          </div>
 99  
100          {/* Save */}
101          <button
102            onClick={handleSave}
103            disabled={!name.trim() || saving}
104            className="w-full py-2.5 rounded-[8px] text-[13px] font-600 bg-accent-bright text-white hover:bg-accent-bright/90 transition-all disabled:opacity-50 cursor-pointer mb-4"
105          >
106            {saving ? 'Saving...' : 'Save'}
107          </button>
108  
109          {/* Sign out */}
110          <button
111            onClick={handleSignOut}
112            className="w-full text-center text-[12px] text-text-3 hover:text-text-2 transition-all cursor-pointer bg-transparent border-none"
113          >
114            Sign in as different user
115          </button>
116        </div>
117      </BottomSheet>
118    )
119  }