forked from andika/membership-fe
Update:- Membership Plan- Donation- Member detail for Member Directory
This commit is contained in:
@@ -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>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user