import { Button } from '@/ui/Button'; import { Icon } from '@/ui/Icon'; import { Image } from '@/ui/Image'; import { Grid } from '@/ui/Grid'; import { Stack } from '@/ui/Stack'; import { Surface } from '@/ui/Surface'; import { Text } from '@/ui/Text'; import { Check, Loader2, Palette, Sparkles, Upload, User } from 'lucide-react'; import { ChangeEvent, useRef } from 'react'; export type RacingSuitColor = | 'red' | 'blue' | 'green' | 'yellow' | 'orange' | 'purple' | 'black' | 'white' | 'pink' | 'cyan'; export interface AvatarInfo { facePhoto: string | null; suitColor: RacingSuitColor; generatedAvatars: string[]; selectedAvatarIndex: number | null; isGenerating: boolean; isValidating: boolean; } interface FormErrors { [key: string]: string | undefined; } interface AvatarStepProps { avatarInfo: AvatarInfo; setAvatarInfo: (info: AvatarInfo) => void; errors: FormErrors; setErrors: (errors: FormErrors) => void; onGenerateAvatars: () => void; } const SUIT_COLORS: { value: RacingSuitColor; label: string; hex: string }[] = [ { value: 'red', label: 'Racing Red', hex: '#EF4444' }, { value: 'blue', label: 'Motorsport Blue', hex: '#3B82F6' }, { value: 'green', label: 'Racing Green', hex: '#22C55E' }, { value: 'yellow', label: 'Championship Yellow', hex: '#EAB308' }, { value: 'orange', label: 'Papaya Orange', hex: '#F97316' }, { value: 'purple', label: 'Royal Purple', hex: '#A855F7' }, { value: 'black', label: 'Stealth Black', hex: '#1F2937' }, { value: 'white', label: 'Clean White', hex: '#F9FAFB' }, { value: 'pink', label: 'Hot Pink', hex: '#EC4899' }, { value: 'cyan', label: 'Electric Cyan', hex: '#06B6D4' }, ]; export function AvatarStep({ avatarInfo, setAvatarInfo, errors, setErrors, onGenerateAvatars }: AvatarStepProps) { const fileInputRef = useRef(null); const handleFileSelect = async (e: ChangeEvent) => { const file = e.target.files?.[0]; if (!file) return; // Validate file type if (!file.type.startsWith('image/')) { setErrors({ ...errors, facePhoto: 'Please upload an image file' }); return; } // Validate file size (max 5MB) if (file.size > 5 * 1024 * 1024) { setErrors({ ...errors, facePhoto: 'Image must be less than 5MB' }); return; } // Convert to base64 const reader = new FileReader(); reader.onload = async (event) => { const base64 = event.target?.result as string; setAvatarInfo({ ...avatarInfo, facePhoto: base64, generatedAvatars: [], selectedAvatarIndex: null, }); const newErrors = { ...errors }; delete newErrors.facePhoto; setErrors(newErrors); }; reader.readAsDataURL(file); }; return ( {/* Photo Upload */} Upload Your Photo * {/* Upload Area */} fileInputRef.current?.click()} flex={1} display="flex" flexDirection="column" alignItems="center" justifyContent="center" p={6} rounded="xl" border borderStyle="dashed" borderWidth="2px" borderColor={avatarInfo.facePhoto ? 'border-performance-green' : errors.facePhoto ? 'border-racing-red' : 'border-charcoal-outline'} bg={avatarInfo.facePhoto ? 'bg-performance-green/10' : errors.facePhoto ? 'bg-racing-red/10' : 'transparent'} cursor="pointer" transition hoverBorderColor="border-primary-blue" hoverBg="bg-primary-blue/5" position="relative" > {avatarInfo.isValidating ? ( Validating photo... ) : avatarInfo.facePhoto ? ( Your photo Photo uploaded Click to change ) : ( Drop your photo here or click to upload JPEG or PNG, max 5MB )} {/* Preview area */} {(() => { const selectedAvatarUrl = avatarInfo.selectedAvatarIndex !== null ? avatarInfo.generatedAvatars[avatarInfo.selectedAvatarIndex] : undefined; if (!selectedAvatarUrl) { return ; } return ( Selected avatar ); })()} Your avatar {errors.facePhoto && ( {errors.facePhoto} )} {/* Suit Color Selection */} Racing Suit Color {SUIT_COLORS.map((color) => ( ))} Selected: {SUIT_COLORS.find(c => c.value === avatarInfo.suitColor)?.label} {/* Generate Button */} {avatarInfo.facePhoto && !errors.facePhoto && ( )} {/* Generated Avatars */} {avatarInfo.generatedAvatars.length > 0 && ( Choose Your Avatar * {avatarInfo.generatedAvatars.map((url, index) => ( ))} {errors.avatar && ( {errors.avatar} )} )} ); }