"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 { MobileBackButton } from "@/components/layout/MobileBackButton"; 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, mapFieldValuesToWorkEnvironment, mapFieldValuesToPersonalTagline, ExperienceData, SpecializationFieldsData, ProfileCardData, ContactInfoData, AvailabilityData, WorkEnvironmentData, PersonalTaglineData, } from "@/utils/profileDataMapper"; // Default work environment data const defaultWorkEnvironmentData: WorkEnvironmentData = { label: "Preferred Work Environment", content: "", }; // Default personal tagline data const defaultPersonalTaglineData: PersonalTaglineData = { label: "Personal Tagline", content: "", }; // Default experience data when no field values are available const defaultExperience: ExperienceData = { years: "-", yearsLabel: "Years in Experience", contracts: "-", contractsLabel: "Number of contracts closed", licensingAreas: [], licensingAreasLabel: "Licensing & Areas", expertiseYears: [], expertiseYearsLabel: "Areas in expertise & Years", certifications: [], certificationsLabel: "Certifications", agencyName: "", agencyDesignation: "", agencyShowDesignation: false, agencySectionLabel: "Real Estate Agency & Designation", }; // 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: [], label: "Availability", }; 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 [workEnvironmentData, setWorkEnvironmentData] = useState(defaultWorkEnvironmentData); const [personalTaglineData, setPersonalTaglineData] = useState(defaultPersonalTaglineData); 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 field values to work environment data const mappedWorkEnv = mapFieldValuesToWorkEnvironment( fieldValuesResponse.fieldValues, ); setWorkEnvironmentData(mappedWorkEnv); // Map field values to personal tagline data const mappedTagline = mapFieldValuesToPersonalTagline( fieldValuesResponse.fieldValues, ); setPersonalTaglineData(mappedTagline); // 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: any) { console.error("Failed to fetch profile:", err); const status = err?.response?.status; if (status === 403) { setError( "This profile is private. You don\u2019t have permission to view it.", ); } else if (status === 404) { setError("Profile not found."); } else { 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...

); } const isPermissionError = error?.includes("permission") || error?.includes("private"); if (error || !agentProfile) { return (
{isPermissionError ? ( ) : ( )}

{isPermissionError ? "Profile Not Available" : "Unable to Load Profile"}

{error || "Profile not found"}

); } const profileImage = getProfileImageUrl(); const profileSchema = { "@context": "https://schema.org", "@type": "Person", name: `${agentProfile.firstName} ${agentProfile.lastName}`.trim(), jobTitle: agentProfile.agentType?.name || "Real Estate Professional", description: profileCardData.bio || agentProfile.bio || "Verified real estate professional on RE-Quest.", image: profileImage ? profileImage.startsWith("http") ? profileImage : `https://re-quest.com${profileImage}` : undefined, email: agentProfile.email || agentProfile.user?.email || undefined, telephone: agentProfile.phone || contactInfoData.phone || undefined, url: `https://re-quest.com/user/profile/${id}`, mainEntityOfPage: { "@type": "WebPage", "@id": `https://re-quest.com/user/profile/${id}`, }, address: { "@type": "PostalAddress", addressLocality: profileCardData.city || undefined, addressRegion: profileCardData.state || undefined, }, memberOf: { "@type": "Organization", name: "RE-Quest", url: "https://re-quest.com", }, sameAs: [`https://re-quest.com/user/profile/${id}`], }; return ( <>