import React, { useState, useEffect, useRef } from 'react'; import { useAuth } from '../context/AuthContext'; import api from '../utils/api'; import { Card } from '../components/ui/card'; import { Button } from '../components/ui/button'; import { Input } from '../components/ui/input'; import { Label } from '../components/ui/label'; import { Textarea } from '../components/ui/textarea'; import { toast } from 'sonner'; import Navbar from '../components/Navbar'; import { User, Lock, Heart, Users, Mail, BookUser, Camera, Upload, Trash2, Eye, CreditCard, Handshake, ArrowLeft } from 'lucide-react'; import { Avatar, AvatarImage, AvatarFallback } from '../components/ui/avatar'; import ChangePasswordDialog from '../components/ChangePasswordDialog'; import { useNavigate } from 'react-router-dom'; const Profile = () => { const { user } = useAuth(); const [loading, setLoading] = useState(false); const [profileData, setProfileData] = useState(null); const [passwordDialogOpen, setPasswordDialogOpen] = useState(false); const [profilePhotoUrl, setProfilePhotoUrl] = useState(null); const [previewImage, setPreviewImage] = useState(null); const [uploadingPhoto, setUploadingPhoto] = useState(false); const fileInputRef = useRef(null); const [maxFileSizeMB, setMaxFileSizeMB] = useState(50); const [maxFileSizeBytes, setMaxFileSizeBytes] = useState(52428800); const [activeTab, setActiveTab] = useState('account'); const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false); const [initialFormData, setInitialFormData] = useState(null); const navigate = useNavigate(); const [formData, setFormData] = useState({ first_name: '', last_name: '', phone: '', address: '', city: '', state: '', zipcode: '', partner_first_name: '', partner_last_name: '', partner_is_member: false, partner_plan_to_become_member: false, newsletter_publish_name: false, newsletter_publish_photo: false, newsletter_publish_birthday: false, newsletter_publish_none: false, volunteer_interests: [], show_in_directory: false, directory_email: '', directory_bio: '', directory_address: '', directory_phone: '', directory_dob: '', directory_partner_name: '' }); useEffect(() => { fetchConfig(); fetchProfile(); }, []); // Track unsaved changes useEffect(() => { if (initialFormData) { const hasChanges = JSON.stringify(formData) !== JSON.stringify(initialFormData); setHasUnsavedChanges(hasChanges); } }, [formData, initialFormData]); const fetchConfig = async () => { try { const response = await api.get('/config'); setMaxFileSizeMB(response.data.max_file_size_mb); setMaxFileSizeBytes(response.data.max_file_size_bytes); } catch (error) { console.error('Failed to fetch config, using defaults:', error); } }; const fetchProfile = async () => { try { const response = await api.get('/users/profile'); setProfileData(response.data); setProfilePhotoUrl(response.data.profile_photo_url); setPreviewImage(response.data.profile_photo_url); const newFormData = { first_name: response.data.first_name || '', last_name: response.data.last_name || '', phone: response.data.phone || '', address: response.data.address || '', city: response.data.city || '', state: response.data.state || '', zipcode: response.data.zipcode || '', partner_first_name: response.data.partner_first_name || '', partner_last_name: response.data.partner_last_name || '', partner_is_member: response.data.partner_is_member || false, partner_plan_to_become_member: response.data.partner_plan_to_become_member || false, newsletter_publish_name: response.data.newsletter_publish_name || false, newsletter_publish_photo: response.data.newsletter_publish_photo || false, newsletter_publish_birthday: response.data.newsletter_publish_birthday || false, newsletter_publish_none: response.data.newsletter_publish_none || false, volunteer_interests: response.data.volunteer_interests || [], show_in_directory: response.data.show_in_directory || false, directory_email: response.data.directory_email || '', directory_bio: response.data.directory_bio || '', directory_address: response.data.directory_address || '', directory_phone: response.data.directory_phone || '', directory_dob: response.data.directory_dob || '', directory_partner_name: response.data.directory_partner_name || '' }; setFormData(newFormData); setInitialFormData(newFormData); } catch (error) { toast.error('Failed to load profile'); } }; const handleInputChange = (e) => { const { name, value } = e.target; setFormData(prev => ({ ...prev, [name]: value })); }; const handleCheckboxChange = (e) => { const { name, checked } = e.target; setFormData(prev => ({ ...prev, [name]: checked })); }; const handleVolunteerToggle = (interest) => { setFormData(prev => ({ ...prev, volunteer_interests: prev.volunteer_interests.includes(interest) ? prev.volunteer_interests.filter(i => i !== interest) : [...prev.volunteer_interests, interest] })); }; const volunteerOptions = [ 'Event Planning', 'Social Media', 'Newsletter', 'Fundraising', 'Community Outreach', 'Graphic Design', 'Photography', 'Writing/Editing', 'Tech Support', 'Hospitality' ]; const handlePhotoUpload = async (e) => { const file = e.target.files[0]; if (!file) return; if (!file.type.startsWith('image/')) { toast.error('Please select an image file'); return; } if (file.size > maxFileSizeBytes) { toast.error(`File size must be less than ${maxFileSizeMB}MB`); return; } setUploadingPhoto(true); try { const formData = new FormData(); formData.append('file', file); const response = await api.post('/members/profile/upload-photo', formData, { headers: { 'Content-Type': 'multipart/form-data' } }); setProfilePhotoUrl(response.data.profile_photo_url); setPreviewImage(response.data.profile_photo_url); toast.success('Profile photo updated successfully!'); } catch (error) { toast.error('Failed to upload photo'); } finally { setUploadingPhoto(false); } }; const handlePhotoDelete = async () => { if (!profilePhotoUrl) return; setUploadingPhoto(true); try { await api.delete('/members/profile/delete-photo'); setProfilePhotoUrl(null); setPreviewImage(null); toast.success('Profile photo deleted successfully!'); } catch (error) { toast.error('Failed to delete photo'); } finally { setUploadingPhoto(false); } }; const handleSubmit = async (e) => { e.preventDefault(); setLoading(true); try { await api.put('/users/profile', formData); toast.success('Profile updated successfully!'); setInitialFormData(formData); setHasUnsavedChanges(false); fetchProfile(); } catch (error) { toast.error('Failed to update profile'); } finally { setLoading(false); } }; const tabs = [ { id: 'account', label: 'Account & Privacy', shortLabel: 'Account', icon: Lock }, { id: 'bio', label: 'My Bio & Directory', shortLabel: 'Bio & Directory', icon: User }, { id: 'engagement', label: 'Engagement', shortLabel: 'Engagement', icon: Handshake } ]; if (!profileData) { return (
Loading profile...
Login Email
{profileData.email}
Password
••••••••
Membership Status
{profileData.status?.replace('_', ' ') || 'Active'}
Payment Method
Max {maxFileSizeMB}MB
Control your visibility and information in the member directory.
Choose what information you'd like published in our member newsletter.
Select areas where you'd like to volunteer and help our community.
Update your personal information below.