'use client' import { Avatar, AvatarFallback, AvatarImage } from '@thedaviddias/design-system/avatar' import { Button } from '@thedaviddias/design-system/button' import { Calendar, Github, Search } from 'lucide-react' import Link from 'next/link' import { useEffect, useMemo, useState } from 'react' import { useAnalyticsEvents } from '@/components/analytics-tracker' import { Card, CardContent } from '@/components/ui/card' interface Member { id: string firstName?: string | null lastName?: string | null username?: string | null imageUrl?: string | null createdAt: string publicMetadata?: { github_username?: string | null migrated_from?: string | null } } interface MembersListProps { members: Member[] currentPage: number totalPages: number totalUsers: number hasNextPage: boolean hasPrevPage: boolean } /** * Generates a URL slug from a member's username or ID */ function generateSlugFromUser(user: Member): string { if (!user) return '' const username = user.username || user.publicMetadata?.github_username if (!username) return user.id.slice(-8) return username } /** * Renders a paginated and searchable list of community members */ export function MembersList({ members, currentPage, totalPages, totalUsers, hasNextPage, hasPrevPage }: MembersListProps) { const [searchQuery, setSearchQuery] = useState('') const { trackSearch } = useAnalyticsEvents() const filteredMembers = useMemo(() => { if (!searchQuery.trim()) { return members } const query = searchQuery.toLowerCase() return members.filter(user => { const displayName = user.firstName && user.lastName ? `${user.firstName} ${user.lastName}` : user.firstName || user.lastName || user.username || 'Member' const username = user.username || user.publicMetadata?.github_username return displayName.toLowerCase().includes(query) || username?.toLowerCase().includes(query) }) }, [members, searchQuery]) const filteredCount = filteredMembers.length // Track member searches with debouncing useEffect(() => { if (searchQuery.trim()) { const debounceTimer = setTimeout(() => { trackSearch(searchQuery, filteredCount, 'members-list-search') }, 500) return () => clearTimeout(debounceTimer) } }, [searchQuery, filteredCount, trackSearch]) return ( <> {/* Search */}
setSearchQuery(e.target.value)} className="w-full rounded-lg border border-input bg-background px-4 py-2 pl-10 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2" />
{/* Results count */}
Showing {searchQuery ? filteredCount : members.length} of {totalUsers} members {searchQuery && Filtered by "{searchQuery}"}
{/* Members Grid */} {filteredMembers.length > 0 ? ( <>
{filteredMembers.map(user => { const userSlug = generateSlugFromUser(user) const displayName = user.firstName && user.lastName ? `${user.firstName} ${user.lastName}` : user.firstName || user.lastName || user.username || 'Member' const username = user.username || user.publicMetadata?.github_username const joinDate = new Date(user.createdAt).toLocaleDateString('en-US', { month: 'short', year: 'numeric' }) return ( {user.imageUrl ? ( ) : ( {displayName.charAt(0).toUpperCase()} )}

{displayName}

{username && (

@{username}

)}
{joinDate}
{username && (
)}
) })}
{/* Pagination - only show if not searching */} {!searchQuery && totalPages > 1 && ( )} ) : (

{searchQuery ? 'No members found' : 'No members found'}

{searchQuery ? `No members match "${searchQuery}". Try a different search term.` : 'There are no members on this page.'}

{searchQuery && ( )}
)} ) }