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,7 +5,15 @@ import MemberFooter from '../../components/MemberFooter';
import { Card } from '../../components/ui/card';
import { Input } from '../../components/ui/input';
import { Badge } from '../../components/ui/badge';
import { User, Search, Mail, MapPin, Phone, Heart, Facebook, Instagram, Twitter, Linkedin } from 'lucide-react';
import { Button } from '../../components/ui/button';
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from '../../components/ui/dialog';
import { User, Search, Mail, MapPin, Phone, Heart, Facebook, Instagram, Twitter, Linkedin, UserCircle } from 'lucide-react';
import { useToast } from '../../hooks/use-toast';
const MembersDirectory = () => {
@@ -13,6 +21,8 @@ const MembersDirectory = () => {
const [filteredMembers, setFilteredMembers] = useState([]);
const [searchQuery, setSearchQuery] = useState('');
const [loading, setLoading] = useState(true);
const [selectedMember, setSelectedMember] = useState(null);
const [profileDialogOpen, setProfileDialogOpen] = useState(false);
const { toast } = useToast();
useEffect(() => {
@@ -69,6 +79,25 @@ const MembersDirectory = () => {
return url;
};
const handleViewProfile = async (memberId) => {
try {
const response = await api.get(`/members/directory/${memberId}`);
setSelectedMember(response.data);
setProfileDialogOpen(true);
} catch (error) {
toast({
title: "Error",
description: "Failed to load member profile. Please try again.",
variant: "destructive"
});
}
};
const formatDate = (dateString) => {
if (!dateString) return null;
return new Date(dateString).toLocaleDateString('en-US', { month: 'long', day: 'numeric' });
};
const MemberCard = ({ member }) => (
<Card className="p-6 bg-white rounded-2xl border border-[#ddd8eb] hover:shadow-lg transition-all h-full">
{/* Profile Photo */}
@@ -202,6 +231,17 @@ const MembersDirectory = () => {
</div>
</div>
)}
{/* View Profile Button */}
<div className="pt-4 mt-4 border-t border-[#ddd8eb]">
<Button
onClick={() => handleViewProfile(member.id)}
className="w-full bg-[#DDD8EB] text-[#422268] hover:bg-[#664fa3] hover:text-white rounded-full py-5"
>
<UserCircle className="h-4 w-4 mr-2" />
View Full Profile
</Button>
</div>
</Card>
);
@@ -287,6 +327,192 @@ const MembersDirectory = () => {
</Card>
)}
</div>
{/* Profile Detail Dialog */}
<Dialog open={profileDialogOpen} onOpenChange={setProfileDialogOpen}>
<DialogContent className="sm:max-w-[600px] bg-white rounded-2xl max-h-[90vh] overflow-y-auto">
{selectedMember && (
<>
<DialogHeader>
<DialogTitle className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>
{selectedMember.first_name} {selectedMember.last_name}
</DialogTitle>
{selectedMember.directory_partner_name && (
<DialogDescription className="flex items-center gap-2 text-lg" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<Heart className="h-5 w-5 text-[#ff9e77]" />
<span className="text-[#664fa3]">Partner: {selectedMember.directory_partner_name}</span>
</DialogDescription>
)}
</DialogHeader>
<div className="space-y-6 py-4">
{/* Bio */}
{selectedMember.directory_bio && (
<div>
<h3 className="text-lg font-semibold text-[#422268] mb-2" style={{ fontFamily: "'Inter', sans-serif" }}>
About
</h3>
<p className="text-[#664fa3] leading-relaxed" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{selectedMember.directory_bio}
</p>
</div>
)}
{/* Contact Information */}
<div>
<h3 className="text-lg font-semibold text-[#422268] mb-3" style={{ fontFamily: "'Inter', sans-serif" }}>
Contact Information
</h3>
<div className="space-y-3">
{selectedMember.directory_email && (
<div className="flex items-center gap-3">
<div className="p-2 rounded-lg bg-[#F8F7FB]">
<Mail className="h-5 w-5 text-[#664fa3]" />
</div>
<div>
<p className="text-xs text-[#664fa3] mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Email</p>
<a
href={`mailto:${selectedMember.directory_email}`}
className="text-[#422268] hover:text-[#664fa3] font-medium"
style={{ fontFamily: "'Nunito Sans', sans-serif" }}
>
{selectedMember.directory_email}
</a>
</div>
</div>
)}
{selectedMember.directory_phone && (
<div className="flex items-center gap-3">
<div className="p-2 rounded-lg bg-[#F8F7FB]">
<Phone className="h-5 w-5 text-[#664fa3]" />
</div>
<div>
<p className="text-xs text-[#664fa3] mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Phone</p>
<a
href={`tel:${selectedMember.directory_phone}`}
className="text-[#422268] hover:text-[#664fa3] font-medium"
style={{ fontFamily: "'Nunito Sans', sans-serif" }}
>
{selectedMember.directory_phone}
</a>
</div>
</div>
)}
{selectedMember.directory_address && (
<div className="flex items-start gap-3">
<div className="p-2 rounded-lg bg-[#F8F7FB]">
<MapPin className="h-5 w-5 text-[#664fa3]" />
</div>
<div>
<p className="text-xs text-[#664fa3] mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Address</p>
<p className="text-[#422268] font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{selectedMember.directory_address}
</p>
</div>
</div>
)}
{selectedMember.directory_dob && (
<div className="flex items-center gap-3">
<div className="p-2 rounded-lg bg-[#F8F7FB]">
<Heart className="h-5 w-5 text-[#ff9e77]" />
</div>
<div>
<p className="text-xs text-[#664fa3] mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Birthday</p>
<p className="text-[#422268] font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{formatDate(selectedMember.directory_dob)}
</p>
</div>
</div>
)}
</div>
</div>
{/* Volunteer Interests */}
{selectedMember.volunteer_interests && selectedMember.volunteer_interests.length > 0 && (
<div>
<h3 className="text-lg font-semibold text-[#422268] mb-3" style={{ fontFamily: "'Inter', sans-serif" }}>
Volunteer Interests
</h3>
<div className="flex flex-wrap gap-2">
{selectedMember.volunteer_interests.map((interest, index) => (
<Badge
key={index}
className="bg-[#DDD8EB] text-[#422268] hover:bg-[#664fa3] hover:text-white"
>
{interest}
</Badge>
))}
</div>
</div>
)}
{/* Social Media */}
{(selectedMember.social_media_facebook || selectedMember.social_media_instagram ||
selectedMember.social_media_twitter || selectedMember.social_media_linkedin) && (
<div>
<h3 className="text-lg font-semibold text-[#422268] mb-3" style={{ fontFamily: "'Inter', sans-serif" }}>
Connect on Social Media
</h3>
<div className="flex gap-3">
{selectedMember.social_media_facebook && (
<a
href={getSocialMediaLink(selectedMember.social_media_facebook)}
target="_blank"
rel="noopener noreferrer"
className="p-3 rounded-lg bg-[#F8F7FB] hover:bg-[#DDD8EB] transition-colors"
title="Facebook"
>
<Facebook className="h-6 w-6 text-[#1877F2]" />
</a>
)}
{selectedMember.social_media_instagram && (
<a
href={getSocialMediaLink(selectedMember.social_media_instagram)}
target="_blank"
rel="noopener noreferrer"
className="p-3 rounded-lg bg-[#F8F7FB] hover:bg-[#DDD8EB] transition-colors"
title="Instagram"
>
<Instagram className="h-6 w-6 text-[#E4405F]" />
</a>
)}
{selectedMember.social_media_twitter && (
<a
href={getSocialMediaLink(selectedMember.social_media_twitter)}
target="_blank"
rel="noopener noreferrer"
className="p-3 rounded-lg bg-[#F8F7FB] hover:bg-[#DDD8EB] transition-colors"
title="Twitter/X"
>
<Twitter className="h-6 w-6 text-[#1DA1F2]" />
</a>
)}
{selectedMember.social_media_linkedin && (
<a
href={getSocialMediaLink(selectedMember.social_media_linkedin)}
target="_blank"
rel="noopener noreferrer"
className="p-3 rounded-lg bg-[#F8F7FB] hover:bg-[#DDD8EB] transition-colors"
title="LinkedIn"
>
<Linkedin className="h-6 w-6 text-[#0A66C2]" />
</a>
)}
</div>
</div>
)}
</div>
</>
)}
</DialogContent>
</Dialog>
<MemberFooter />
</div>
);