import React, { useState, useEffect, useMemo, useCallback } from 'react'; import api from '../../utils/api'; import Navbar from '../../components/Navbar'; import MemberFooter from '../../components/MemberFooter'; import { Link } from 'react-router-dom'; import { Card } from '../../components/ui/card'; import { Input } from '../../components/ui/input'; import { Badge } from '../../components/ui/badge'; 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, Calendar } from 'lucide-react'; import { useToast } from '../../hooks/use-toast'; import MemberCard from '../../components/MemberCard'; import MemberBadge from '../../components/MemberBadge'; import useMembers from '../../hooks/use-members'; import useMemberTiers from '../../hooks/use-member-tiers'; const MembersDirectory = () => { const [selectedMember, setSelectedMember] = useState(null); const [profileDialogOpen, setProfileDialogOpen] = useState(false); const { toast } = useToast(); const [currentPage, setCurrentPage] = useState(1); const pageSize = 12; const { tiers } = useMemberTiers(); const allowedRoles = useMemo(() => [], []); const normalizeStatus = useCallback((status) => { if (typeof status === 'string') { return status.toLowerCase(); } return status; }, []); const normalizeMembers = useCallback( (data) => { const list = Array.isArray(data) ? data : data?.members || data?.results || data?.items || data?.data || []; return list.map((member) => ({ ...member, status: normalizeStatus(member.status ?? member.membership_status ?? member.member_status), })); }, [normalizeStatus] ); const searchAccessor = useCallback( (member) => [ `${member.first_name} ${member.last_name}`, member.directory_bio || '' ], [] ); const handleFetchError = useCallback(() => { toast({ title: "Error", description: "Failed to load members directory. Please try again.", variant: "destructive" }); }, [toast]); const { users: members, filteredUsers: filteredMembers, loading, searchQuery, setSearchQuery, filterValue, } = useMembers({ endpoint: '/members/directory', initialFilter: 'all', filterKey: 'status', allowedRoles, searchAccessor, transform: normalizeMembers, fetchErrorMessage: 'Failed to load members directory. Please try again.', onFetchError: handleFetchError }); useEffect(() => { setCurrentPage(1); }, [searchQuery, members]); const totalPages = Math.max(1, Math.ceil(filteredMembers.length / pageSize)); const pageStart = (currentPage - 1) * pageSize; const paginatedMembers = filteredMembers.slice(pageStart, pageStart + pageSize); const totalMembers = useMemo(() => { if (!filterValue || filterValue === 'all') { return members.length; } return members.filter((member) => member.status === filterValue).length; }, [members, filterValue]); const getSocialMediaLink = (url) => { if (!url) return null; // Ensure URL has protocol if (!url.startsWith('http://') && !url.startsWith('https://')) { return `https://${url}`; } 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 Border = ({ yaxis = false }) => { return ( yaxis ?
:
) } return (
{/* Header and Search bar */}
{/* Header */}

LOAF Members

Number of current members in the directory: {totalMembers}

{/* Search Bar */}
setSearchQuery(e.target.value)} className="pl-12 pr-4 py-6 text-3xl font-medium bg-background border-foreground rounded-full focus:border-brand-purple focus:ring-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }} />
{searchQuery && (

Found {filteredMembers.length} {filteredMembers.length === 1 ? 'member' : 'members'}

)}
{/* Border Decoration */} {/* Members Grid */} {loading ? (

Loading members...

) : filteredMembers.length > 0 ? (
{paginatedMembers.map((member) => ( ))}
) : (

{searchQuery ? 'No Members Found' : 'No Members in Directory'}

{searchQuery ? 'Try adjusting your search query.' : 'Members who opt in to the directory will appear here.'}

)} {/* Border Decoration */} {/* todo: use badge to display if member */} {/* Info Card */} {!loading && members.length > 0 && (

Want to appear in the directory?

Update your profile settings to show in the directory and add your photo, bio, and contact information.{' '} Edit your profile →

)}
{/* Profile Detail Dialog */} {selectedMember && ( <> {selectedMember.first_name} {selectedMember.last_name} {selectedMember.directory_partner_name && ( Partner: {selectedMember.directory_partner_name} )}
{/* Bio */} {selectedMember.directory_bio && (

About

{selectedMember.directory_bio}

)} {/* Contact Information */}

Contact Information

{selectedMember.directory_email && ( )} {selectedMember.directory_phone && ( )} {selectedMember.directory_address && (

Address

{selectedMember.directory_address}

)} {selectedMember.directory_dob && (

Birthday

{formatDate(selectedMember.directory_dob)}

)}
{/* Volunteer Interests */} {selectedMember.volunteer_interests && selectedMember.volunteer_interests.length > 0 && (

Volunteer Interests

{selectedMember.volunteer_interests.map((interest, index) => ( {interest} ))}
)} {/* Social Media */} {(selectedMember.social_media_facebook || selectedMember.social_media_instagram || selectedMember.social_media_twitter || selectedMember.social_media_linkedin) && (

Connect on Social Media

{selectedMember.social_media_facebook && ( )} {selectedMember.social_media_instagram && ( )} {selectedMember.social_media_twitter && ( )} {selectedMember.social_media_linkedin && ( )}
)}
)}
{/* Pagination */} {!loading && filteredMembers.length > 0 && (

Showing {pageStart + 1}–{Math.min(pageStart + pageSize, filteredMembers.length)} of {filteredMembers.length}

{Array.from({ length: totalPages }, (_, index) => { const pageNumber = index + 1; const isActive = pageNumber === currentPage; return ( ); })}
)}
); }; export default MembersDirectory;