'use client'; import { useState, useEffect } from 'react'; import Image from 'next/image'; import { useParams } from 'next/navigation'; import { useSession } from 'next-auth/react'; // Import shared components import { InfoCard, ExperienceSection, ProfileCard, SpecializationSection, TestimonialsSection, StatusButtons, ContactInfo, } from '@/components/profile'; import { ConnectRequestModal } from '@/components/modals'; import { agentsService, AgentProfile, FieldValueResponse } from '@/services/agents.service'; import { connectionRequestsService, ConnectionStatus } from '@/services/connection-requests.service'; import { uploadService } from '@/services/upload.service'; import { mapFieldValuesToExperience, mapFieldValuesToSpecializationFields, mapFieldValuesToProfileCard, mapFieldValuesToContactInfo, mapFieldValuesToAvailability, ExperienceData, SpecializationFieldsData, ProfileCardData, ContactInfoData, AvailabilityData } from '@/utils/profileDataMapper'; // Mock data for sections not yet available from API const mockData = { preferredWorkEnvironment: 'Preferred work environment includes on-site property visits, in-depth market research, client consultations, property tours, and active involvement in negotiations to ensure the best outcomes', testimonialHighlight: "The most amazing experience I've had as a real estate professional is helping a family secure their dream home and seeing their happiness when they received the keys.", testimonials: [ { id: 1, text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean commodo ligula eget dolor. Sed dignissim, nisl eget tincidunt vulputate, lacus justo bibendum ipsum, vitae tempus risus lorem at nunc. Integer sed arcu vitae risus feugiat vehicula. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.', author: 'Kenedy Kenney', role: 'Chief Operations Officer', rating: 5, }, { id: 2, text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean commodo ligula eget dolor. Sed dignissim, nisl eget tincidunt vulputate, lacus justo bibendum ipsum, vitae tempus risus lorem at nunc. Integer sed arcu vitae risus feugiat vehicula. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.', author: 'Kenedy Kenney', role: 'Chief Operations Officer', rating: 5, }, { id: 3, text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean commodo ligula eget dolor. Sed dignissim, nisl eget tincidunt vulputate, lacus justo bibendum ipsum, vitae tempus risus lorem at nunc. Integer sed arcu vitae risus feugiat vehicula. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.', author: 'Kenedy Kenney', role: 'Chief Operations Officer', rating: 5, }, ], }; // Default experience data when no field values are available const defaultExperience: ExperienceData = { years: '-', contracts: '-', licensingAreas: [], expertiseYears: [], certifications: [], }; // Default specialization fields data when no field values are available const defaultSpecializationFieldsData: SpecializationFieldsData = { fields: [], }; // Default profile card data when no field values are available const defaultProfileCardData: ProfileCardData = { bio: '', expertise: [], serviceAreas: [], city: null, state: null, }; // Default contact info data when no field values are available const defaultContactInfoData: ContactInfoData = { email: null, phone: null, }; // Default availability data when no field values are available const defaultAvailabilityData: AvailabilityData = { type: '', schedule: [], }; export default function AgentProfileView() { const params = useParams(); const id = params.id as string; const { data: session } = useSession(); const [agentProfile, setAgentProfile] = useState(null); const [fieldValues, setFieldValues] = useState([]); const [experienceData, setExperienceData] = useState(defaultExperience); const [specializationFieldsData, setSpecializationFieldsData] = useState(defaultSpecializationFieldsData); const [profileCardData, setProfileCardData] = useState(defaultProfileCardData); const [contactInfoData, setContactInfoData] = useState(defaultContactInfoData); const [availabilityData, setAvailabilityData] = useState(defaultAvailabilityData); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [imageError, setImageError] = useState(false); const [avatarUrl, setAvatarUrl] = useState(null); // Connect modal state const [showConnectModal, setShowConnectModal] = useState(false); const [connectionStatus, setConnectionStatus] = useState(null); const [connectionRequestId, setConnectionRequestId] = useState(null); const defaultImage = '/assets/demo_images/8f017153a7b4a239a4f0691234ef97dd55092282.jpg'; // Helper to check if avatar is an S3 key (not a full URL or local path) const isS3Key = (avatar: string | null | undefined): boolean => { if (!avatar) return false; // S3 keys don't start with http or / return !avatar.startsWith('http') && !avatar.startsWith('/'); }; // Fetch agent profile and field values on mount useEffect(() => { const fetchData = async () => { if (!id) return; try { setLoading(true); setError(null); // Fetch profile and field values in parallel const [profile, fieldValuesResponse] = await Promise.all([ agentsService.getAgentById(id), agentsService.getFieldValuesByAgentId(id), ]); setAgentProfile(profile); setFieldValues(fieldValuesResponse.fieldValues); // Map field values to experience data structure const mappedExperience = mapFieldValuesToExperience(fieldValuesResponse.fieldValues); setExperienceData(mappedExperience); // Map field values to specialization fields data (only fields from "Specialization" section) const mappedSpecializationFields = mapFieldValuesToSpecializationFields(fieldValuesResponse.fieldValues); setSpecializationFieldsData(mappedSpecializationFields); // Map field values to profile card data (bio, expertise, location) const mappedProfileCard = mapFieldValuesToProfileCard(fieldValuesResponse.fieldValues); setProfileCardData(mappedProfileCard); // Map field values to contact info data (email, phone) const mappedContactInfo = mapFieldValuesToContactInfo(fieldValuesResponse.fieldValues); setContactInfoData(mappedContactInfo); // Map field values to availability data const mappedAvailability = mapFieldValuesToAvailability(fieldValuesResponse.fieldValues); setAvailabilityData(mappedAvailability); // If avatar is an S3 key, fetch presigned URL if (profile.avatar && isS3Key(profile.avatar)) { try { const presignedUrl = await uploadService.getPresignedDownloadUrl(profile.avatar); // Add cache-busting parameter to force browser to fetch fresh image // This prevents showing old cached image when avatar is updated const profileWithTimestamp = profile as unknown as { updatedAt?: string }; const cacheBuster = profileWithTimestamp.updatedAt ? new Date(profileWithTimestamp.updatedAt).getTime() : Date.now(); const urlWithCacheBuster = `${presignedUrl}&_t=${cacheBuster}`; setAvatarUrl(urlWithCacheBuster); } catch (avatarErr) { console.error('Failed to get avatar URL:', avatarErr); // Don't fail the whole page, just use default image } } else if (profile.avatar) { // It's already a full URL or local path setAvatarUrl(profile.avatar); } } catch (err) { console.error('Failed to fetch profile:', err); setError('Failed to load profile data'); } finally { setLoading(false); } }; fetchData(); }, [id]); // Fetch connection status when user is logged in useEffect(() => { const fetchConnectionStatus = async () => { if (!id || !session) return; try { const statusResponse = await connectionRequestsService.getConnectionStatus(id); setConnectionStatus(statusResponse?.status || null); setConnectionRequestId(statusResponse?.id || null); } catch (err) { console.error('Failed to fetch connection status:', err); // Non-critical error, don't show to user } }; fetchConnectionStatus(); }, [id, session]); // Handle connection request success const handleConnectionSuccess = () => { setConnectionStatus('PENDING'); }; // Handle unlink/disconnect const handleUnlink = async () => { if (!connectionRequestId) return; try { await connectionRequestsService.cancelRequest(connectionRequestId); setConnectionStatus(null); setConnectionRequestId(null); } catch (err) { console.error('Failed to unlink connection:', err); } }; const getProfileImageUrl = () => { // If image failed to load, return default if (imageError) { return defaultImage; } // If we have a presigned URL from S3, use it if (avatarUrl) { return avatarUrl; } // Check for null, undefined, or empty string if (!agentProfile?.avatar || agentProfile.avatar.trim() === '') { return defaultImage; } // For relative paths (local assets), return as-is if (agentProfile.avatar.startsWith('/')) { return agentProfile.avatar; } // Default fallback 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'; } }; if (loading) { return (

Loading profile...

); } if (error || !agentProfile) { return (

Unable to Load Profile

{error || 'Profile not found'}

); } return (
{/* Main Layout - Responsive: Column on mobile, Row on desktop */}
{/* Left Sidebar - Status & Contact */}
{/* Profile Image */}
{/* eslint-disable-next-line @next/next/no-img-element */} Profile { const target = e.target as HTMLImageElement; target.src = defaultImage; setImageError(true); }} /> {/* Gradient Overlay */}
{/* Status Buttons - Public view shows availability status with Connect/Pending/Unlink button */} setShowConnectModal(true)} onUnlinkClick={handleUnlink} /> {/* Contact Info */}
{/* Right Content - Profile Info + Experience + All Sections */}
{/* Profile Card - No edit button for user view */} 0 ? profileCardData.expertise : (agentProfile.specializations || [])} showEditButton={false} messageHref="/user/message" connectionStatus={connectionStatus} onConnectClick={() => setShowConnectModal(true)} onUnlinkClick={handleUnlink} /> {/* Experience Section - Dynamic data from profile fields */} {/* Info Cards Section */}
} content={
{availabilityData.type && (

{availabilityData.type}

)}
{availabilityData.schedule.length > 0 ? ( availabilityData.schedule.map((item, index) => (

{item}

)) ) : (

Not specified

)}
} /> } content={

{mockData.preferredWorkEnvironment}

} /> } content={

“{mockData.testimonialHighlight}”

} />
{/* Specialization Section */} {/* Testimonials Section */}
{/* Connect Request Modal */} setShowConnectModal(false)} agentProfileId={id} agentName={`${agentProfile.firstName || ''} ${agentProfile.lastName || ''}`.trim() || 'Agent'} existingStatus={connectionStatus} onSuccess={handleConnectionSuccess} />
); }