'use client'; import { useState, useEffect, useCallback, Suspense } from 'react'; import { useSearchParams } from 'next/navigation'; import Image from 'next/image'; import Link from 'next/link'; import { FilterModal, FilterState, FilterField } from '@/components/profiles/FilterModal'; import { agentsService, AgentType, PublicAgentProfile, SearchAgentsParams } from '@/services/agents.service'; import { profileSectionsService, FilterableField } from '@/services/profile-sections.service'; import { uploadService } from '@/services/upload.service'; interface FilterSectionProps { title: string; options: { label: string; value: string }[]; selectedOptions: string[]; onToggle: (optionValue: string) => void; showMore?: boolean; } function FilterSection({ title, options, selectedOptions, onToggle, showMore }: FilterSectionProps) { const [expanded, setExpanded] = useState(false); const displayOptions = expanded ? options : options.slice(0, 6); return (

{title}

{displayOptions.map((option) => ( ))}
{showMore && options.length > 6 && ( )}
); } interface ProfileCardProps { profile: PublicAgentProfile; } function ProfileCard({ profile }: ProfileCardProps) { const [showFullBio, setShowFullBio] = useState(false); const [avatarUrl, setAvatarUrl] = useState(null); const defaultImage = '/assets/demo_images/8f017153a7b4a239a4f0691234ef97dd55092282.jpg'; const truncatedBio = profile.bio && profile.bio.length > 150 ? profile.bio.slice(0, 150) + '...' : profile.bio || ''; // Fetch presigned URL for avatar if it's an S3 key useEffect(() => { const fetchAvatarUrl = async () => { if (profile.avatar && !profile.avatar.startsWith('http') && !profile.avatar.startsWith('/')) { try { const presignedUrl = await uploadService.getPresignedDownloadUrl(profile.avatar); setAvatarUrl(presignedUrl); } catch (error) { console.error('Failed to get avatar URL:', error); } } else if (profile.avatar) { setAvatarUrl(profile.avatar); } }; fetchAvatarUrl(); }, [profile.avatar]); const getProfileImageUrl = () => { if (avatarUrl) return avatarUrl; if (profile.avatar?.startsWith('/')) return profile.avatar; return defaultImage; }; // Format member since date const formatMemberSince = (dateString?: string) => { if (!dateString) return 'Member'; try { const date = new Date(dateString); return date.toLocaleDateString('en-US', { month: 'long', year: 'numeric' }); } catch { return 'Member'; } }; return (
{/* Profile Image */}
{/* eslint-disable-next-line @next/next/no-img-element */} {`${profile.firstName} { const target = e.target as HTMLImageElement; target.src = defaultImage; }} />
{/* Profile Info */}
{/* Header */}

{profile.firstName} {profile.lastName}

{profile.isVerified && ( Verified (Verified Expert) )}

{profile.agentType?.name || 'Real Estate Professional'}

{profile.rating && (
{profile.rating.toFixed(1)} Rating
)}
{/* Location & Member Since */}
{profile.serviceAreas && profile.serviceAreas.length > 0 && (
Location {profile.serviceAreas[0]}
)}
Member Since Member Since {formatMemberSince(profile.createdAt)}
{/* Bio */} {profile.bio && (

{showFullBio ? profile.bio : truncatedBio} {profile.bio.length > 150 && ( )}

)} {/* Specializations */} {profile.specializations && profile.specializations.length > 0 && (

Expertise:

{profile.specializations.map((tag, index) => ( {tag} ))}
)} {/* Languages */} {profile.languages && profile.languages.length > 0 && (
{profile.languages.map((lang, index) => ( {lang} ))}
)}
); } function ProfilesPageContent() { const searchParams = useSearchParams(); // State for API data const [agents, setAgents] = useState([]); const [agentTypes, setAgentTypes] = useState([]); const [filterFields, setFilterFields] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [totalPages, setTotalPages] = useState(1); const [currentPage, setCurrentPage] = useState(1); const [totalResults, setTotalResults] = useState(0); // State for search and filters - initialize from URL params const [searchQuery, setSearchQuery] = useState(() => searchParams.get('name') || ''); const [activeTypeId, setActiveTypeId] = useState(() => searchParams.get('type')); const [isInitialized, setIsInitialized] = useState(false); const [listingType, setListingType] = useState<'agents' | 'lenders'>('agents'); const [isFilterModalOpen, setIsFilterModalOpen] = useState(false); const [filters, setFilters] = useState({}); // Mark as initialized after first render and sync with URL param changes useEffect(() => { setIsInitialized(true); }, []); // Sync state when URL params change (e.g., browser back/forward) useEffect(() => { if (isInitialized) { const type = searchParams.get('type'); const name = searchParams.get('name'); setActiveTypeId(type); setSearchQuery(name || ''); } }, [searchParams, isInitialized]); // Fetch agent types and filter fields on mount useEffect(() => { const fetchInitialData = async () => { try { const [types, fields] = await Promise.all([ agentsService.getAgentTypes(), profileSectionsService.getFilterableFields(), ]); setAgentTypes(types); // Convert FilterableField to FilterField format const filterFieldsData: FilterField[] = fields.map((f: FilterableField) => ({ slug: f.slug, name: f.name, fieldType: f.fieldType, options: f.options, })); setFilterFields(filterFieldsData); } catch (error) { console.error('Failed to fetch initial data:', error); } }; fetchInitialData(); }, []); // Fetch agents based on filters const fetchAgents = useCallback(async () => { try { setLoading(true); setError(null); const params: SearchAgentsParams = { page: currentPage, limit: 10, sortBy: 'createdAt', sortOrder: 'desc', }; // Add search query if (searchQuery) { params.search = searchQuery; } // Add agent type filter if (activeTypeId) { params.agentTypeId = activeTypeId; } // Add location from URL params const location = searchParams.get('location'); if (location) { // Try to set as city first params.city = location; } const response = await agentsService.searchAgents(params); setAgents(response.data); setTotalPages(response.totalPages); setTotalResults(response.total); } catch (err) { console.error('Failed to fetch agents:', err); setError('Failed to load agents. Please try again.'); } finally { setLoading(false); } }, [currentPage, searchQuery, activeTypeId, searchParams]); // Fetch agents when filters change (only after initialization) useEffect(() => { if (isInitialized) { fetchAgents(); } }, [fetchAgents, isInitialized]); const toggleFilter = (fieldSlug: string, optionValue: string) => { setFilters(prev => { const currentValues = prev[fieldSlug] || []; const newValues = currentValues.includes(optionValue) ? currentValues.filter(v => v !== optionValue) : [...currentValues, optionValue]; return { ...prev, [fieldSlug]: newValues }; }); }; const clearAllFilters = () => { setFilters({}); }; const applyFilters = () => { setIsFilterModalOpen(false); setCurrentPage(1); // Reset to first page when applying filters // Filters are applied via the fetchAgents effect }; const handleSearch = () => { setCurrentPage(1); fetchAgents(); }; const handleTypeChange = (typeId: string | null) => { setActiveTypeId(typeId); setCurrentPage(1); }; // Get filter field by slug for sidebar const getFilterFieldBySlug = (slug: string): FilterField | undefined => { return filterFields.find(f => f.slug === slug); }; // Define which fields to show in sidebar (main filter categories) const sidebarFilterSlugs = ['client_specialization', 'loan_type', 'property_type', 'price_point']; return (
{/* Left Sidebar */}
{/* Listing Type */}

Listing Type

{/* Filters */}

Filters

{/* Dynamic Filter Sections */} {sidebarFilterSlugs.map(slug => { const field = getFilterFieldBySlug(slug); if (!field) return null; return ( toggleFilter(field.slug, optionValue)} showMore={field.options.length > 6} /> ); })} {filterFields.length === 0 && (

Loading filters...

)}
{/* Main Content */}
{/* Search Bar */}
setSearchQuery(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && handleSearch()} className="w-full h-[44px] px-4 pr-12 rounded-[10px] border border-[#00293d]/20 bg-white font-serif text-[14px] text-[#00293d] placeholder:text-[#00293d]/50 focus:outline-none focus:border-[#e58625]" />
{/* Agent Type Tabs */}
{agentTypes.map((type) => ( ))}
{/* Results Count */} {!loading && (

{totalResults} {totalResults === 1 ? 'agent' : 'agents'} found

)} {/* Loading State */} {loading && (

Loading agents...

)} {/* Error State */} {error && !loading && (

{error}

)} {/* Empty State */} {!loading && !error && agents.length === 0 && (

No agents found

Try adjusting your search criteria or filters

)} {/* Profile Cards */} {!loading && !error && agents.length > 0 && (
{agents.map((profile) => ( ))}
)} {/* Pagination */} {!loading && !error && totalPages > 1 && (
Page {currentPage} of {totalPages}
)}
{/* Filter Modal */} setIsFilterModalOpen(false)} filters={filters} filterFields={filterFields} onToggleFilter={toggleFilter} onClearAll={clearAllFilters} onApply={applyFilters} />
); } export default function ProfilesPage() { return (

Loading...

}>
); }