'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 { testimonialsService, Testimonial } from '@/services/testimonials.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', }; // 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 [imageLoaded, setImageLoaded] = useState(false); const [avatarUrl, setAvatarUrl] = useState(null); const [testimonials, setTestimonials] = useState<{ id: string; text: string; author: string; role: string; rating: number }[]>([]); // Connect modal state const [showConnectModal, setShowConnectModal] = useState(false); const [connectionStatus, setConnectionStatus] = useState(null); const [connectionRequestId, setConnectionRequestId] = useState(null); // 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, field values, and testimonials in parallel const [profile, fieldValuesResponse, testimonialsData] = await Promise.all([ agentsService.getAgentById(id), agentsService.getFieldValuesByAgentId(id), testimonialsService.getAgentTestimonials(id).catch(() => [] as Testimonial[]), ]); 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); // Map testimonials for TestimonialsSection setTestimonials( testimonialsData.map((t) => ({ id: t.id, text: t.text, author: t.authorName, role: t.authorRole, rating: t.rating, })) ); // If avatar is an S3 key, fetch presigned URL if (profile.avatar && isS3Key(profile.avatar)) { try { const presignedUrl = await uploadService.getPresignedDownloadUrl(profile.avatar); setAvatarUrl(presignedUrl); } catch (avatarErr) { console.error('Failed to get avatar URL:', avatarErr); } } else if (profile.avatar) { 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 null if (imageError) { return null; } // 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 null; } // For relative paths (local assets), return as-is if (agentProfile.avatar.startsWith('/')) { return agentProfile.avatar; } // No image available return null; }; // 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 */}
{getProfileImageUrl() ? ( <> {!imageLoaded && (
)} {/* eslint-disable-next-line @next/next/no-img-element */} { if (el?.complete) { if (el.naturalWidth > 0) setImageLoaded(true); else setImageError(true); } }} src={getProfileImageUrl()!} alt="Profile" className={`w-full h-full object-cover transition-opacity duration-300 ${imageLoaded ? 'opacity-100' : 'opacity-0'}`} onLoad={() => setImageLoaded(true)} onError={() => setImageError(true)} /> ) : (
Profile
)} {/* 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}

} /> {testimonials.length > 0 && ( } content={

“{testimonials[0].text.length > 150 ? testimonials[0].text.substring(0, 150) + '...' : testimonials[0].text}”

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