import React, { useState, useRef, useEffect } from 'react'; import { AnimatePresence } from 'framer-motion'; import { toast } from 'react-toastify'; import { classNames } from '~/utils/classNames'; import { Switch } from '~/components/ui/Switch'; import type { UserProfile } from '~/components/settings/settings.types'; import { themeStore, kTheme } from '~/lib/stores/theme'; 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 [currentTimezone, setCurrentTimezone] = useState(''); const [profile, setProfile] = useState(() => { const saved = localStorage.getItem('bolt_user_profile'); return saved ? JSON.parse(saved) : { name: '', email: '', theme: 'system', notifications: true, language: 'en', timezone: Intl.DateTimeFormat().resolvedOptions().timeZone, password: '', bio: '', }; }); useEffect(() => { setCurrentTimezone(Intl.DateTimeFormat().resolvedOptions().timeZone); }, []); // Apply theme when profile changes useEffect(() => { if (profile.theme === 'system') { // Remove theme override localStorage.removeItem(kTheme); const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; document.querySelector('html')?.setAttribute('data-theme', prefersDark ? 'dark' : 'light'); } else { // Set specific theme localStorage.setItem(kTheme, profile.theme); document.querySelector('html')?.setAttribute('data-theme', profile.theme); themeStore.set(profile.theme); } }, [profile.theme]); 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 { localStorage.setItem('bolt_user_profile', JSON.stringify(profile)); 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', )} />
{/* Theme & Language */}
Appearance
{(['light', 'dark', 'system'] as const).map((theme) => ( ))}
{profile.notifications ? 'Notifications are enabled' : 'Notifications are disabled'} setProfile((prev) => ({ ...prev, notifications: checked }))} />
{/* Timezone */}
Time Settings
{/* Save Button */}
); }