Update:- Membership Plan- Donation- Member detail for Member Directory
This commit is contained in:
@@ -5,10 +5,11 @@ 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 MemberFooter from '../components/MemberFooter';
|
||||
import { User, Save, Lock } from 'lucide-react';
|
||||
import { User, Save, Lock, Heart, Users, Mail, BookUser } from 'lucide-react';
|
||||
import ChangePasswordDialog from '../components/ChangePasswordDialog';
|
||||
|
||||
const Profile = () => {
|
||||
@@ -17,13 +18,34 @@ const Profile = () => {
|
||||
const [profileData, setProfileData] = useState(null);
|
||||
const [passwordDialogOpen, setPasswordDialogOpen] = useState(false);
|
||||
const [formData, setFormData] = useState({
|
||||
// Personal Information
|
||||
first_name: '',
|
||||
last_name: '',
|
||||
phone: '',
|
||||
address: '',
|
||||
city: '',
|
||||
state: '',
|
||||
zipcode: ''
|
||||
zipcode: '',
|
||||
// Partner Information
|
||||
partner_first_name: '',
|
||||
partner_last_name: '',
|
||||
partner_is_member: false,
|
||||
partner_plan_to_become_member: false,
|
||||
// Newsletter Preferences
|
||||
newsletter_publish_name: false,
|
||||
newsletter_publish_photo: false,
|
||||
newsletter_publish_birthday: false,
|
||||
newsletter_publish_none: false,
|
||||
// Volunteer Interests (array)
|
||||
volunteer_interests: [],
|
||||
// Member Directory Settings
|
||||
show_in_directory: false,
|
||||
directory_email: '',
|
||||
directory_bio: '',
|
||||
directory_address: '',
|
||||
directory_phone: '',
|
||||
directory_dob: '',
|
||||
directory_partner_name: ''
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@@ -35,13 +57,34 @@ const Profile = () => {
|
||||
const response = await api.get('/users/profile');
|
||||
setProfileData(response.data);
|
||||
setFormData({
|
||||
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
|
||||
// Personal Information
|
||||
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 Information
|
||||
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 Preferences
|
||||
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
|
||||
volunteer_interests: response.data.volunteer_interests || [],
|
||||
// Member Directory Settings
|
||||
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 || ''
|
||||
});
|
||||
} catch (error) {
|
||||
toast.error('Failed to load profile');
|
||||
@@ -53,6 +96,34 @@ const Profile = () => {
|
||||
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]
|
||||
}));
|
||||
};
|
||||
|
||||
// Volunteer interest options
|
||||
const volunteerOptions = [
|
||||
'Event Planning',
|
||||
'Social Media',
|
||||
'Newsletter',
|
||||
'Fundraising',
|
||||
'Community Outreach',
|
||||
'Graphic Design',
|
||||
'Photography',
|
||||
'Writing/Editing',
|
||||
'Tech Support',
|
||||
'Hospitality'
|
||||
];
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
@@ -226,15 +297,279 @@ const Profile = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
className="bg-[#DDD8EB] text-[#422268] hover:bg-white rounded-full px-8 py-6 text-lg font-medium shadow-lg disabled:opacity-50"
|
||||
data-testid="save-profile-button"
|
||||
>
|
||||
<Save className="h-5 w-5 mr-2" />
|
||||
{loading ? 'Saving...' : 'Save Changes'}
|
||||
</Button>
|
||||
{/* Section 2: Partner Information */}
|
||||
<div className="pt-8 mt-8 border-t border-[#ddd8eb]">
|
||||
<h2 className="text-2xl font-semibold text-[#422268] mb-6 flex items-center gap-2" style={{ fontFamily: "'Inter', sans-serif" }}>
|
||||
<Heart className="h-6 w-6 text-[#ff9e77]" />
|
||||
Partner Information
|
||||
</h2>
|
||||
<div className="space-y-6">
|
||||
<div className="grid md:grid-cols-2 gap-6">
|
||||
<div>
|
||||
<Label htmlFor="partner_first_name">Partner First Name</Label>
|
||||
<Input
|
||||
id="partner_first_name"
|
||||
name="partner_first_name"
|
||||
value={formData.partner_first_name}
|
||||
onChange={handleInputChange}
|
||||
className="h-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]"
|
||||
placeholder="Optional"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="partner_last_name">Partner Last Name</Label>
|
||||
<Input
|
||||
id="partner_last_name"
|
||||
name="partner_last_name"
|
||||
value={formData.partner_last_name}
|
||||
onChange={handleInputChange}
|
||||
className="h-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]"
|
||||
placeholder="Optional"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="partner_is_member"
|
||||
name="partner_is_member"
|
||||
checked={formData.partner_is_member}
|
||||
onChange={handleCheckboxChange}
|
||||
className="w-5 h-5 text-[#664fa3] border-2 border-[#ddd8eb] rounded focus:ring-[#664fa3]"
|
||||
/>
|
||||
<Label htmlFor="partner_is_member" className="cursor-pointer text-[#422268]">
|
||||
My partner is a current member
|
||||
</Label>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="partner_plan_to_become_member"
|
||||
name="partner_plan_to_become_member"
|
||||
checked={formData.partner_plan_to_become_member}
|
||||
onChange={handleCheckboxChange}
|
||||
className="w-5 h-5 text-[#664fa3] border-2 border-[#ddd8eb] rounded focus:ring-[#664fa3]"
|
||||
/>
|
||||
<Label htmlFor="partner_plan_to_become_member" className="cursor-pointer text-[#422268]">
|
||||
My partner plans to become a member
|
||||
</Label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Section 3: Newsletter Preferences */}
|
||||
<div className="pt-8 mt-8 border-t border-[#ddd8eb]">
|
||||
<h2 className="text-2xl font-semibold text-[#422268] mb-6 flex items-center gap-2" style={{ fontFamily: "'Inter', sans-serif" }}>
|
||||
<Mail className="h-6 w-6 text-[#81B29A]" />
|
||||
Newsletter Preferences
|
||||
</h2>
|
||||
<p className="text-[#664fa3] mb-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
|
||||
Choose what information you'd like published in our member newsletter.
|
||||
</p>
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="newsletter_publish_name"
|
||||
name="newsletter_publish_name"
|
||||
checked={formData.newsletter_publish_name}
|
||||
onChange={handleCheckboxChange}
|
||||
className="w-5 h-5 text-[#664fa3] border-2 border-[#ddd8eb] rounded focus:ring-[#664fa3]"
|
||||
/>
|
||||
<Label htmlFor="newsletter_publish_name" className="cursor-pointer text-[#422268]">
|
||||
Publish my name
|
||||
</Label>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="newsletter_publish_photo"
|
||||
name="newsletter_publish_photo"
|
||||
checked={formData.newsletter_publish_photo}
|
||||
onChange={handleCheckboxChange}
|
||||
className="w-5 h-5 text-[#664fa3] border-2 border-[#ddd8eb] rounded focus:ring-[#664fa3]"
|
||||
/>
|
||||
<Label htmlFor="newsletter_publish_photo" className="cursor-pointer text-[#422268]">
|
||||
Publish my photo
|
||||
</Label>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="newsletter_publish_birthday"
|
||||
name="newsletter_publish_birthday"
|
||||
checked={formData.newsletter_publish_birthday}
|
||||
onChange={handleCheckboxChange}
|
||||
className="w-5 h-5 text-[#664fa3] border-2 border-[#ddd8eb] rounded focus:ring-[#664fa3]"
|
||||
/>
|
||||
<Label htmlFor="newsletter_publish_birthday" className="cursor-pointer text-[#422268]">
|
||||
Publish my birthday
|
||||
</Label>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="newsletter_publish_none"
|
||||
name="newsletter_publish_none"
|
||||
checked={formData.newsletter_publish_none}
|
||||
onChange={handleCheckboxChange}
|
||||
className="w-5 h-5 text-[#664fa3] border-2 border-[#ddd8eb] rounded focus:ring-[#664fa3]"
|
||||
/>
|
||||
<Label htmlFor="newsletter_publish_none" className="cursor-pointer text-[#422268]">
|
||||
Do not publish any information
|
||||
</Label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Section 4: Volunteer Interests */}
|
||||
<div className="pt-8 mt-8 border-t border-[#ddd8eb]">
|
||||
<h2 className="text-2xl font-semibold text-[#422268] mb-6 flex items-center gap-2" style={{ fontFamily: "'Inter', sans-serif" }}>
|
||||
<Users className="h-6 w-6 text-[#664fa3]" />
|
||||
Volunteer Interests
|
||||
</h2>
|
||||
<p className="text-[#664fa3] mb-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
|
||||
Select areas where you'd like to volunteer and help our community.
|
||||
</p>
|
||||
<div className="grid md:grid-cols-2 gap-3">
|
||||
{volunteerOptions.map(option => (
|
||||
<div key={option} className="flex items-center gap-3">
|
||||
<input
|
||||
type="checkbox"
|
||||
id={`volunteer_${option.replace(/\s+/g, '_').toLowerCase()}`}
|
||||
checked={formData.volunteer_interests.includes(option)}
|
||||
onChange={() => handleVolunteerToggle(option)}
|
||||
className="w-5 h-5 text-[#664fa3] border-2 border-[#ddd8eb] rounded focus:ring-[#664fa3]"
|
||||
/>
|
||||
<Label
|
||||
htmlFor={`volunteer_${option.replace(/\s+/g, '_').toLowerCase()}`}
|
||||
className="cursor-pointer text-[#422268]"
|
||||
>
|
||||
{option}
|
||||
</Label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Section 5: Member Directory Settings */}
|
||||
<div className="pt-8 mt-8 border-t border-[#ddd8eb]">
|
||||
<h2 className="text-2xl font-semibold text-[#422268] mb-6 flex items-center gap-2" style={{ fontFamily: "'Inter', sans-serif" }}>
|
||||
<BookUser className="h-6 w-6 text-[#ff9e77]" />
|
||||
Member Directory Settings
|
||||
</h2>
|
||||
<p className="text-[#664fa3] mb-6" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
|
||||
Control your visibility and information in the member directory.
|
||||
</p>
|
||||
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center gap-3 p-4 bg-[#f9f5ff] rounded-lg">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="show_in_directory"
|
||||
name="show_in_directory"
|
||||
checked={formData.show_in_directory}
|
||||
onChange={handleCheckboxChange}
|
||||
className="w-5 h-5 text-[#664fa3] border-2 border-[#ddd8eb] rounded focus:ring-[#664fa3]"
|
||||
/>
|
||||
<Label htmlFor="show_in_directory" className="cursor-pointer text-[#422268] font-medium">
|
||||
Include me in the member directory
|
||||
</Label>
|
||||
</div>
|
||||
|
||||
{formData.show_in_directory && (
|
||||
<div className="space-y-6 pl-4 border-l-4 border-[#DDD8EB]">
|
||||
<div>
|
||||
<Label htmlFor="directory_email">Directory Email</Label>
|
||||
<Input
|
||||
id="directory_email"
|
||||
name="directory_email"
|
||||
type="email"
|
||||
value={formData.directory_email}
|
||||
onChange={handleInputChange}
|
||||
className="h-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]"
|
||||
placeholder="Optional - email to show in directory"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="directory_bio">Bio</Label>
|
||||
<Textarea
|
||||
id="directory_bio"
|
||||
name="directory_bio"
|
||||
value={formData.directory_bio}
|
||||
onChange={handleInputChange}
|
||||
className="rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3] min-h-[100px]"
|
||||
placeholder="Tell other members about yourself..."
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="directory_address">Address</Label>
|
||||
<Input
|
||||
id="directory_address"
|
||||
name="directory_address"
|
||||
value={formData.directory_address}
|
||||
onChange={handleInputChange}
|
||||
className="h-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]"
|
||||
placeholder="Optional - address to show in directory"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="directory_phone">Phone</Label>
|
||||
<Input
|
||||
id="directory_phone"
|
||||
name="directory_phone"
|
||||
type="tel"
|
||||
value={formData.directory_phone}
|
||||
onChange={handleInputChange}
|
||||
className="h-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]"
|
||||
placeholder="Optional - phone to show in directory"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="directory_dob">Date of Birth</Label>
|
||||
<Input
|
||||
id="directory_dob"
|
||||
name="directory_dob"
|
||||
type="date"
|
||||
value={formData.directory_dob}
|
||||
onChange={handleInputChange}
|
||||
className="h-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="directory_partner_name">Partner Name</Label>
|
||||
<Input
|
||||
id="directory_partner_name"
|
||||
name="directory_partner_name"
|
||||
value={formData.directory_partner_name}
|
||||
onChange={handleInputChange}
|
||||
className="h-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]"
|
||||
placeholder="Optional - partner name to show in directory"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="pt-8 mt-8 border-t border-[#ddd8eb]">
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
className="bg-[#DDD8EB] text-[#422268] hover:bg-white rounded-full px-8 py-6 text-lg font-medium shadow-lg disabled:opacity-50"
|
||||
data-testid="save-profile-button"
|
||||
>
|
||||
<Save className="h-5 w-5 mr-2" />
|
||||
{loading ? 'Saving...' : 'Save Changes'}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Card>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user