Update:- Membership Plan- Donation- Member detail for Member Directory

This commit is contained in:
Koncept Kit
2025-12-11 19:29:00 +07:00
parent da211b6e38
commit 59f50f3fac
21 changed files with 1851 additions and 197 deletions

View File

@@ -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>