import React, { useState, useRef } from 'react'; import { AnimatePresence } from 'framer-motion'; import { toast } from 'react-toastify'; import { classNames } from '~/utils/classNames'; import type { UserProfile } from '~/components/settings/settings.types'; import { motion } from 'framer-motion'; const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB const ALLOWED_FILE_TYPES = ['image/jpeg', 'image/png', 'image/gif']; const MIN_PASSWORD_LENGTH = 8; export default function ProfileTab() { const fileInputRef = useRef(null); const [isLoading, setIsLoading] = useState(false); const [showPassword, setShowPassword] = useState(false); const [profile, setProfile] = useState(() => { const saved = localStorage.getItem('bolt_user_profile'); return saved ? JSON.parse(saved) : { name: '', email: '', password: '', bio: '', }; }); const handleAvatarUpload = async (event: React.ChangeEvent) => { const file = event.target.files?.[0]; if (!file) { return; } if (!ALLOWED_FILE_TYPES.includes(file.type)) { toast.error('Please upload a valid image file (JPEG, PNG, or GIF)'); return; } if (file.size > MAX_FILE_SIZE) { toast.error('File size must be less than 5MB'); return; } setIsLoading(true); try { const reader = new FileReader(); reader.onloadend = () => { setProfile((prev) => ({ ...prev, avatar: reader.result as string })); setIsLoading(false); }; reader.readAsDataURL(file); } catch (error) { console.error('Error uploading avatar:', error); toast.error('Failed to upload avatar'); setIsLoading(false); } }; const handleSave = async () => { if (!profile.name.trim()) { toast.error('Name is required'); return; } if (!profile.email.trim() || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(profile.email)) { toast.error('Please enter a valid email address'); return; } if (profile.password && profile.password.length < MIN_PASSWORD_LENGTH) { toast.error(`Password must be at least ${MIN_PASSWORD_LENGTH} characters long`); return; } setIsLoading(true); try { // Get existing profile data to preserve settings const existingProfile = JSON.parse(localStorage.getItem('bolt_user_profile') || '{}'); // Merge with new profile data const updatedProfile = { ...existingProfile, name: profile.name, email: profile.email, password: profile.password, bio: profile.bio, avatar: profile.avatar, }; localStorage.setItem('bolt_user_profile', JSON.stringify(updatedProfile)); // Dispatch a storage event to notify other components window.dispatchEvent( new StorageEvent('storage', { key: 'bolt_user_profile', newValue: JSON.stringify(updatedProfile), }), ); toast.success('Profile settings saved successfully'); } catch (error) { console.error('Error saving profile:', error); toast.error('Failed to save profile settings'); } finally { setIsLoading(false); } }; return (
{/* Profile Information */}
Personal Information
{/* Avatar */}
{isLoading ? (
) : profile.avatar ? ( Profile ) : (
)}
{/* Profile Fields */}
setProfile((prev) => ({ ...prev, name: e.target.value }))} placeholder="Enter your name" className={classNames( 'w-full px-3 py-1.5 rounded-lg text-sm', 'pl-10', 'bg-[#F5F5F5] dark:bg-[#1A1A1A] border border-[#E5E5E5] dark:border-[#333333]', 'text-bolt-elements-textPrimary placeholder-bolt-elements-textTertiary', 'focus:outline-none focus:ring-1 focus:ring-purple-500', )} />
setProfile((prev) => ({ ...prev, email: e.target.value }))} placeholder="Enter your email" className={classNames( 'w-full px-3 py-1.5 rounded-lg text-sm', 'pl-10', 'bg-[#F5F5F5] dark:bg-[#1A1A1A] border border-[#E5E5E5] dark:border-[#333333]', 'text-bolt-elements-textPrimary placeholder-bolt-elements-textTertiary', 'focus:outline-none focus:ring-1 focus:ring-purple-500', )} />
setProfile((prev) => ({ ...prev, password: e.target.value }))} placeholder="Enter new password" className={classNames( 'w-full px-3 py-1.5 rounded-lg text-sm', 'bg-[#F5F5F5] dark:bg-[#1A1A1A] border border-[#E5E5E5] dark:border-[#333333]', 'text-bolt-elements-textPrimary placeholder-bolt-elements-textTertiary', 'focus:outline-none focus:ring-1 focus:ring-purple-500', )} />