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 PaymentMethodsSection from '../components/PaymentMethodsSection'; import { useNavigate } from 'react-router-dom'; import useDirectoryConfig from '../hooks/use-directory-config'; 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 { isFieldEnabled, loading: directoryConfigLoading } = useDirectoryConfig(); 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...

); } // Account & Privacy Tab Content const AccountPrivacyContent = () => (

Account & Privacy

Login Email

{profileData.email}

Password

••••••••

Membership Status

{profileData.status?.replace('_', ' ') || 'Active'}

{/* Payment Methods Section */}
); // My Bio & Directory Tab Content const BioDirectoryContent = () => (

My Bio & Directory

{/* Profile Photo Section */}

Profile Photo

{profileData?.first_name?.charAt(0)}{profileData?.last_name?.charAt(0)}
{profilePhotoUrl && ( )}

Max {maxFileSizeMB}MB

{/* Personal Information */}

Personal Information

{/* Member Directory Settings */} {isFieldEnabled('show_in_directory') && (

Member Directory Settings

Control your visibility and information in the member directory.

{formData.show_in_directory && (
{isFieldEnabled('directory_email') && (
)} {isFieldEnabled('directory_bio') && (