import React, { useState, useCallback, useEffect } from 'react'; import { useForm, Controller } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { useFormPersistence } from '../../hooks/useFormPersistence'; import { Button } from '../ui/Button'; import { Input } from '../ui/input'; import { Textarea } from '../ui/textarea'; import { Label } from '../ui/label'; import Typography from '../ui/Typography'; import { AgentLogoSelector } from './AgentLogoSelector'; import { CapabilitiesSelector } from './CapabilitiesSelector'; import { ProgressBar } from './ProgressBar'; import { type HCS10ProfileFormData, HCS10ProfileSchema, } from '../../../shared/schemas/hcs10'; import { Loader2, User, Bot, Sparkles, Globe, Twitter, Github, ChevronRight } from 'lucide-react'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '../ui/select'; import { cn } from '../../lib/utils'; interface ProfileRegistrationFormProps { onSubmit: (data: HCS10ProfileFormData) => void | Promise; isSubmitting?: boolean; existingData?: Partial; progress?: { message: string; percent: number; stage?: string; }; network?: 'mainnet' | 'testnet'; } /** * Form component for HCS-10 profile registration */ export function ProfileRegistrationForm({ onSubmit, isSubmitting = false, existingData, progress, network = 'testnet', }: ProfileRegistrationFormProps) { const [currentStep, setCurrentStep] = useState(1); const totalSteps = 3; const { register, control, handleSubmit, formState: { errors }, setValue, watch, reset, } = useForm({ resolver: zodResolver(HCS10ProfileSchema), defaultValues: { name: existingData?.name || '', description: existingData?.description || '', profileType: existingData?.profileType || 'aiAgent', alias: existingData?.alias || '', creator: existingData?.creator || '', version: existingData?.version || '1.0.0', agentType: existingData?.agentType || 'manual', capabilities: existingData?.capabilities || [], socials: existingData?.socials || { twitter: '', github: '', website: '', }, profileImage: existingData?.profileImage || undefined, logo: existingData?.logo || '', feeConfiguration: existingData?.feeConfiguration || undefined, customProperties: existingData?.customProperties || {}, }, }); const capabilities = watch('capabilities'); const socials = watch('socials'); const logo = watch('logo'); const profileType = watch('profileType'); const name = watch('name'); const description = watch('description'); const creator = watch('creator'); const alias = watch('alias'); const descriptionLength = (description?.length ?? 0); const descriptionTooShort = descriptionLength > 0 && descriptionLength < 10; const { saveToStorage, clearPersistedData } = useFormPersistence( 'hcs10_profile_form', watch, setValue, ['name', 'description', 'creator', 'alias', 'profileType', 'agentType', 'capabilities', 'socials', 'logo', 'profileImage'] ); useEffect(() => { const timer = setTimeout(() => { saveToStorage(); }, 1000); return () => clearTimeout(timer); }, [name, description, creator, alias, profileType, watch('agentType'), capabilities, socials, logo, saveToStorage]); /** * Handle form submission with persistence cleanup */ const handleFormSubmit = useCallback(async (data: HCS10ProfileFormData) => { try { await onSubmit(data); clearPersistedData(); } catch (error) { throw error; } }, [onSubmit, clearPersistedData]); /** * Handle profile type change */ const handleProfileTypeChange = useCallback( (value: 'person' | 'aiAgent') => { setValue('profileType', value); if (value === 'person') { setValue('agentType', undefined); setValue('capabilities', []); } }, [setValue] ); /** * Handle social links change */ const handleSocialChange = useCallback( (key: 'twitter' | 'github' | 'website', value: string) => { setValue(`socials.${key}` as any, value); }, [setValue] ); /** * Handle logo change from AgentLogoSelector */ const handleLogoChange = useCallback( (value: string) => { setValue('logo', value); setValue('profileImage', value); }, [setValue] ); /** * Check if current step is valid */ const isStepValid = useCallback((step: number) => { switch (step) { case 1: return name?.length >= 3 && description?.length >= 10 && creator?.length >= 2 && alias?.length >= 3 && alias?.length <= 20 && /^[a-zA-Z0-9_-]+$/.test(alias || ''); case 2: if (profileType === 'person') return true; return capabilities?.length > 0 && capabilities.length <= 5; case 3: return true; default: return false; } }, [name, description, creator, alias, profileType, capabilities]); /** * Check if form is valid */ const isFormValid = React.useMemo(() => { const basicValid = name?.length >= 3 && description?.length >= 10 && creator?.length >= 2 && alias?.length >= 3 && alias?.length <= 20 && /^[a-zA-Z0-9_-]+$/.test(alias || ''); if (profileType === 'person') { return basicValid; } else { const capabilitiesValid = capabilities?.length > 0 && capabilities.length <= 5; return basicValid && capabilitiesValid; } }, [name, description, creator, alias, profileType, capabilities]); const handleNextStep = () => { if (currentStep < totalSteps && isStepValid(currentStep)) { setCurrentStep(currentStep + 1); } }; const handlePreviousStep = () => { if (currentStep > 1) { setCurrentStep(currentStep - 1); } }; return (
{progress && ( )}
{[1, 2, 3].map((step) => (
= step ? 'bg-gradient-to-br from-[#5599fe] to-[#48df7b] text-white shadow-lg shadow-[#48df7b]/25' : 'bg-muted text-muted-foreground' )} > {step}
{step < 3 && (
step ? 'translate-x-0' : '-translate-x-full' )} />
)}
))}
{currentStep === 1 && (
Let's start with the basics Tell us about yourself or your agent
{errors.name && ( {errors.name.message} )}
{errors.creator && ( {errors.creator.message} )}