2026-01-11 21:30:57 +05:30
'use client' ;
2026-01-24 22:19:30 +05:30
import { useState , useEffect } from 'react' ;
2026-01-11 21:30:57 +05:30
import { useSession } from 'next-auth/react' ;
2026-01-11 22:09:41 +05:30
import Image from 'next/image' ;
2026-03-21 08:56:10 +05:30
import Link from 'next/link' ;
2026-01-12 12:34:58 +05:30
2026-01-20 12:17:47 +05:30
// Import shared components
2026-01-12 12:34:58 +05:30
import {
InfoCard ,
2026-01-18 18:16:55 +05:30
ExperienceSection ,
2026-01-18 19:39:46 +05:30
ProfileCard ,
SpecializationSection ,
TestimonialsSection ,
2026-01-18 19:44:24 +05:30
StatusButtons ,
ContactInfo ,
2026-01-24 22:19:30 +05:30
PhotoUploadModal ,
2026-01-20 12:17:47 +05:30
} from '@/components/profile' ;
2026-01-24 23:10:02 +05:30
import { agentsService , AgentProfile , FieldValueResponse } from '@/services/agents.service' ;
2026-03-04 20:24:41 +05:30
import { connectionRequestsService } from '@/services/connection-requests.service' ;
2026-01-24 22:19:30 +05:30
import { uploadService } from '@/services/upload.service' ;
2026-03-31 22:39:06 +05:30
import { usersService } from '@/services/users.service' ;
2026-03-05 05:36:10 +05:30
import { testimonialsService , Testimonial } from '@/services/testimonials.service' ;
2026-02-02 10:19:55 +05:30
import { mapFieldValuesToExperience , mapFieldValuesToProfileCard , mapFieldValuesToAvailability , mapFieldValuesToSpecializationFields , mapFieldValuesToContactInfo , ExperienceData , ProfileCardData , AvailabilityData , SpecializationFieldsData , ContactInfoData } from '@/utils/profileDataMapper' ;
2026-01-11 22:09:41 +05:30
2026-01-24 22:19:30 +05:30
// Mock data for sections not yet available from API
const mockData = {
2026-01-11 22:09:41 +05:30
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' ,
} ;
2026-01-24 23:10:02 +05:30
// Default experience data when no field values are available
const defaultExperience : ExperienceData = {
years : '-' ,
contracts : '-' ,
licensingAreas : [ ] ,
expertiseYears : [ ] ,
certifications : [ ] ,
} ;
2026-01-24 23:31:32 +05:30
// Default profile card data when no field values are available
const defaultProfileCardData : ProfileCardData = {
bio : '' ,
expertise : [ ] ,
serviceAreas : [ ] ,
2026-02-01 23:25:27 +05:30
city : null ,
state : null ,
2026-01-24 23:31:32 +05:30
} ;
// Default availability data when no field values are available
const defaultAvailabilityData : AvailabilityData = {
type : '' ,
schedule : [ ] ,
} ;
2026-01-25 01:15:02 +05:30
// Default specialization fields data when no field values are available
const defaultSpecializationFieldsData : SpecializationFieldsData = {
fields : [ ] ,
} ;
2026-02-02 10:19:55 +05:30
// Default contact info data when no field values are available
const defaultContactInfoData : ContactInfoData = {
email : null ,
phone : null ,
} ;
2026-01-11 21:30:57 +05:30
export default function AgentDashboard() {
2026-03-19 10:22:21 +05:30
const { data : session , status : sessionStatus } = useSession ( ) ;
2026-01-24 22:19:30 +05:30
const [ agentProfile , setAgentProfile ] = useState < AgentProfile | null > ( null ) ;
2026-01-24 23:10:02 +05:30
const [ fieldValues , setFieldValues ] = useState < FieldValueResponse [ ] > ( [ ] ) ;
const [ experienceData , setExperienceData ] = useState < ExperienceData > ( defaultExperience ) ;
2026-01-24 23:31:32 +05:30
const [ profileCardData , setProfileCardData ] = useState < ProfileCardData > ( defaultProfileCardData ) ;
const [ availabilityData , setAvailabilityData ] = useState < AvailabilityData > ( defaultAvailabilityData ) ;
2026-01-25 01:15:02 +05:30
const [ specializationFieldsData , setSpecializationFieldsData ] = useState < SpecializationFieldsData > ( defaultSpecializationFieldsData ) ;
2026-02-02 10:19:55 +05:30
const [ contactInfoData , setContactInfoData ] = useState < ContactInfoData > ( defaultContactInfoData ) ;
2026-01-24 22:19:30 +05:30
const [ loading , setLoading ] = useState ( true ) ;
const [ error , setError ] = useState < string | null > ( null ) ;
const [ isPhotoModalOpen , setIsPhotoModalOpen ] = useState ( false ) ;
const [ imageError , setImageError ] = useState ( false ) ;
2026-03-28 17:01:37 +05:30
const [ imageLoaded , setImageLoaded ] = useState ( false ) ;
2026-01-24 23:10:02 +05:30
const [ avatarUrl , setAvatarUrl ] = useState < string | null > ( null ) ;
2026-02-02 00:34:31 +05:30
const [ isAvailable , setIsAvailable ] = useState ( true ) ;
const [ isTogglingAvailability , setIsTogglingAvailability ] = useState ( false ) ;
2026-03-04 20:24:41 +05:30
const [ pendingRequestCount , setPendingRequestCount ] = useState ( 0 ) ;
2026-03-05 05:36:10 +05:30
const [ testimonials , setTestimonials ] = useState < { id : string ; text : string ; author : string ; role : string ; rating : number } [ ] > ( [ ] ) ;
2026-03-31 22:39:06 +05:30
const [ isSubmittingVerification , setIsSubmittingVerification ] = useState ( false ) ;
2026-01-24 22:19:30 +05:30
const defaultImage = '/assets/demo_images/8f017153a7b4a239a4f0691234ef97dd55092282.jpg' ;
2026-01-24 23:10:02 +05:30
// 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 ( '/' ) ;
} ;
2026-03-19 10:22:21 +05:30
// Fetch agent profile and field values when session is ready
2026-01-24 22:19:30 +05:30
useEffect ( ( ) = > {
2026-03-19 10:22:21 +05:30
// Wait until session is authenticated and tokens are available
if ( sessionStatus !== 'authenticated' ) return ;
const token = typeof window !== 'undefined' ? localStorage . getItem ( 'accessToken' ) : null ;
if ( ! token ) return ;
2026-01-24 23:10:02 +05:30
const fetchData = async ( ) = > {
2026-01-24 22:19:30 +05:30
try {
setLoading ( true ) ;
setError ( null ) ;
2026-01-24 23:10:02 +05:30
2026-03-05 05:36:10 +05:30
// Fetch profile, field values, pending request count, and testimonials in parallel
const [ profile , fieldValuesResponse , requestCounts , testimonialsData ] = await Promise . all ( [
2026-01-24 23:10:02 +05:30
agentsService . getMyProfile ( ) ,
agentsService . getFieldValues ( ) ,
2026-03-04 20:24:41 +05:30
connectionRequestsService . getRequestCounts ( ) . catch ( ( ) = > null ) ,
2026-03-05 05:36:10 +05:30
testimonialsService . getMyTestimonials ( 'latest' ) . catch ( ( ) = > [ ] as Testimonial [ ] ) ,
2026-01-24 23:10:02 +05:30
] ) ;
2026-01-24 22:19:30 +05:30
setAgentProfile ( profile ) ;
2026-01-24 23:10:02 +05:30
setFieldValues ( fieldValuesResponse . fieldValues ) ;
2026-02-02 00:34:31 +05:30
setIsAvailable ( profile . isAvailable ? ? true ) ;
2026-03-04 20:24:41 +05:30
if ( requestCounts ) {
setPendingRequestCount ( requestCounts . pending ) ;
}
2026-01-24 23:10:02 +05:30
2026-03-05 05:36:10 +05:30
// Map testimonials for TestimonialsSection (latest 3)
setTestimonials (
testimonialsData . slice ( 0 , 3 ) . map ( ( t ) = > ( {
id : t.id ,
text : t.text ,
author : t.authorName ,
role : t.authorRole ,
rating : t.rating ,
} ) )
) ;
2026-01-24 23:10:02 +05:30
// Map field values to experience data structure
const mappedExperience = mapFieldValuesToExperience ( fieldValuesResponse . fieldValues ) ;
setExperienceData ( mappedExperience ) ;
2026-01-24 23:31:32 +05:30
// Map field values to profile card data (bio, expertise)
const mappedProfileCard = mapFieldValuesToProfileCard ( fieldValuesResponse . fieldValues ) ;
setProfileCardData ( mappedProfileCard ) ;
// Map field values to availability data
const mappedAvailability = mapFieldValuesToAvailability ( fieldValuesResponse . fieldValues ) ;
setAvailabilityData ( mappedAvailability ) ;
2026-01-25 01:15:02 +05:30
// Map field values to specialization fields data (only fields from "Specialization" section)
const mappedSpecializationFields = mapFieldValuesToSpecializationFields ( fieldValuesResponse . fieldValues ) ;
setSpecializationFieldsData ( mappedSpecializationFields ) ;
2026-02-02 10:19:55 +05:30
// Map field values to contact info data (email, phone)
const mappedContactInfo = mapFieldValuesToContactInfo ( fieldValuesResponse . fieldValues ) ;
setContactInfoData ( mappedContactInfo ) ;
2026-01-24 23:10:02 +05:30
// 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 ) ;
// 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 ) ;
}
2026-01-24 22:19:30 +05:30
} catch ( err ) {
console . error ( 'Failed to fetch profile:' , err ) ;
setError ( 'Failed to load profile data' ) ;
} finally {
setLoading ( false ) ;
}
} ;
2026-01-24 23:10:02 +05:30
fetchData ( ) ;
2026-03-19 10:22:21 +05:30
} , [ sessionStatus ] ) ;
2026-01-24 22:19:30 +05:30
2026-02-02 00:34:31 +05:30
// Handle availability toggle
const handleToggleAvailability = async ( ) = > {
if ( isTogglingAvailability ) return ;
try {
setIsTogglingAvailability ( true ) ;
const newAvailability = ! isAvailable ;
// Update on server
const updatedProfile = await agentsService . updateProfile ( { isAvailable : newAvailability } ) ;
// Update local state
setIsAvailable ( newAvailability ) ;
setAgentProfile ( updatedProfile ) ;
} catch ( err ) {
console . error ( 'Failed to update availability:' , err ) ;
// Revert on error - state stays the same
} finally {
setIsTogglingAvailability ( false ) ;
}
} ;
2026-01-24 22:19:30 +05:30
const handlePhotoUpload = async ( file : File ) = > {
// Upload avatar to S3 (uses agent ID as filename for auto-replacement)
const uploadedFile = await uploadService . uploadAvatar ( file ) ;
2026-01-24 23:10:02 +05:30
// Update agent profile with the S3 key
2026-01-24 22:19:30 +05:30
const updatedProfile = await agentsService . updateProfile ( { avatar : uploadedFile.url } ) ;
setImageError ( false ) ; // Reset error state for new image
setAgentProfile ( updatedProfile ) ;
2026-01-24 23:10:02 +05:30
// Fetch new presigned URL for the uploaded avatar
if ( uploadedFile . url && isS3Key ( uploadedFile . url ) ) {
try {
const presignedUrl = await uploadService . getPresignedDownloadUrl ( uploadedFile . url ) ;
setAvatarUrl ( presignedUrl ) ;
} catch ( err ) {
console . error ( 'Failed to get presigned URL for new avatar:' , err ) ;
}
}
2026-03-15 17:07:53 +05:30
// Notify header and other components to refresh avatar
window . dispatchEvent ( new Event ( 'profile-updated' ) ) ;
2026-01-24 22:19:30 +05:30
} ;
const getProfileImageUrl = ( ) = > {
// If image failed to load, return default
if ( imageError ) {
return defaultImage ;
}
2026-01-24 23:10:02 +05:30
// If we have a presigned URL from S3, use it
if ( avatarUrl ) {
return avatarUrl ;
}
2026-01-24 22:19:30 +05:30
// Check for null, undefined, or empty string
if ( ! agentProfile ? . avatar || agentProfile . avatar . trim ( ) === '' ) {
return defaultImage ;
}
2026-01-24 23:10:02 +05:30
// For relative paths (local assets), return as-is
if ( agentProfile . avatar . startsWith ( '/' ) ) {
2026-01-24 22:19:30 +05:30
return agentProfile . avatar ;
}
2026-01-24 23:10:02 +05:30
// Default fallback
return defaultImage ;
2026-01-24 22:19:30 +05:30
} ;
if ( loading ) {
return (
< div className = "flex items-center justify-center h-[calc(100vh-180px)]" >
< div className = "flex flex-col items-center gap-4" >
< div className = "animate-spin rounded-full h-12 w-12 border-b-2 border-[#E58625]" > < / div >
< p className = "text-[14px] font-serif text-[#00293D]/70" > Loading profile . . . < / p >
< / div >
< / div >
) ;
}
if ( error || ! agentProfile ) {
return (
< div className = "flex items-center justify-center h-[calc(100vh-180px)]" >
< div className = "bg-white rounded-[20px] p-8 shadow-[0px_10px_20px_rgba(217,217,217,0.5)] max-w-md text-center" >
< div className = "w-16 h-16 bg-red-100 rounded-full flex items-center justify-center mx-auto mb-4" >
< svg className = "w-8 h-8 text-red-500" fill = "none" viewBox = "0 0 24 24" stroke = "currentColor" >
< path strokeLinecap = "round" strokeLinejoin = "round" strokeWidth = { 2 } d = "M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" / >
< / svg >
< / div >
< h2 className = "text-[18px] font-bold font-serif text-[#00293D] mb-2" > Unable to Load Profile < / h2 >
< p className = "text-[14px] font-serif text-[#00293D]/70 mb-4" > { error || 'Profile not found' } < / p >
< button
onClick = { ( ) = > window . location . reload ( ) }
className = "px-6 py-2 bg-[#E58625] rounded-full text-[14px] font-semibold font-serif text-white hover:bg-[#E58625]/90 transition-colors"
>
Try Again
< / button >
< / div >
< / div >
) ;
}
// 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' ;
}
} ;
2026-01-11 21:30:57 +05:30
2026-03-31 22:39:06 +05:30
// Check if agent has uploaded documents
const hasDocuments = fieldValues . some (
( fv ) = > fv . sectionSlug === 'upload-documents' && fv . value && ( Array . isArray ( fv . value ) ? fv . value . length > 0 : true )
) ;
const handleSubmitVerification = async ( ) = > {
setIsSubmittingVerification ( true ) ;
try {
await usersService . submitForVerification ( ) ;
// Update local state
if ( agentProfile ) {
setAgentProfile ( { . . . agentProfile , verificationStatus : 'PENDING_REVIEW' , verificationNote : null } as AgentProfile ) ;
}
} catch ( err : any ) {
const data = err ? . response ? . data ;
if ( data ? . missingFields && Array . isArray ( data . missingFields ) ) {
const fieldList = data . missingFields . map ( ( f : any ) = > ` • ${ f . sectionName } → ${ f . fieldName } ` ) . join ( '\n' ) ;
alert ( ` Please fill in all required fields: \ n \ n ${ fieldList } ` ) ;
} else {
alert ( data ? . message || 'Failed to submit for verification' ) ;
}
} finally {
setIsSubmittingVerification ( false ) ;
}
} ;
2026-01-11 21:30:57 +05:30
return (
< div className = "space-y-6" >
2026-03-21 08:56:10 +05:30
{ /* Verification Status Banner */ }
{ agentProfile ? . verificationStatus === 'REJECTED' && (
< div className = "bg-red-50 border border-red-200 rounded-[15px] p-4" >
< div className = "flex items-start gap-3" >
< Image src = "/assets/icons/caution-icon.svg" alt = "Warning" width = { 20 } height = { 20 } className = "mt-0.5 flex-shrink-0" / >
< div className = "flex-1" >
< p className = "font-fractul font-bold text-[14px] text-red-800" > Profile Verification Rejected < / p >
{ agentProfile . verificationNote && (
< p className = "font-serif text-[13px] text-red-700 mt-1" > Reason : { agentProfile . verificationNote } < / p >
) }
< p className = "font-serif text-[13px] text-red-600 mt-2" > Please update your profile and documents , then resubmit for verification . < / p >
2026-03-31 22:39:06 +05:30
< div className = "flex gap-2 mt-2" >
< Link href = "/agent/edit" className = "inline-block px-4 py-1.5 border border-[#e58625] text-[#e58625] rounded-[10px] font-serif text-[13px] hover:bg-[#e58625]/5 transition-colors" >
Edit Profile
< / Link >
< button
onClick = { handleSubmitVerification }
disabled = { isSubmittingVerification }
className = "px-4 py-1.5 bg-[#e58625] text-white rounded-[10px] font-serif text-[13px] hover:bg-[#d47920] transition-colors disabled:opacity-50"
>
{ isSubmittingVerification ? 'Submitting...' : 'Submit for Review' }
< / button >
< / div >
2026-03-21 08:56:10 +05:30
< / div >
< / div >
< / div >
) }
{ agentProfile ? . verificationStatus === 'PENDING_REVIEW' && (
< div className = "bg-yellow-50 border border-yellow-200 rounded-[15px] p-4" >
< div className = "flex items-center gap-3" >
< div className = "w-5 h-5 rounded-full bg-yellow-400 flex-shrink-0" / >
< p className = "font-fractul font-bold text-[14px] text-yellow-800" > Profile verification is under review . You will be notified once approved . < / p >
< / div >
< / div >
) }
{ agentProfile ? . verificationStatus === 'NONE' && (
< div className = "bg-blue-50 border border-blue-200 rounded-[15px] p-4" >
< div className = "flex items-center gap-3" >
< Image src = "/assets/icons/caution-icon.svg" alt = "Info" width = { 20 } height = { 20 } className = "flex-shrink-0" / >
< div className = "flex-1" >
< p className = "font-fractul font-bold text-[14px] text-blue-800" > Complete your profile and upload verification documents to get verified . < / p >
2026-03-31 22:39:06 +05:30
< div className = "flex gap-2 mt-2" >
< Link href = "/agent/edit" className = "inline-block px-4 py-1.5 border border-[#e58625] text-[#e58625] rounded-[10px] font-serif text-[13px] hover:bg-[#e58625]/5 transition-colors" >
Complete Profile
< / Link >
< button
onClick = { handleSubmitVerification }
disabled = { isSubmittingVerification }
className = "px-4 py-1.5 bg-[#e58625] text-white rounded-[10px] font-serif text-[13px] hover:bg-[#d47920] transition-colors disabled:opacity-50"
>
{ isSubmittingVerification ? 'Submitting...' : 'Submit for Review' }
< / button >
< / div >
2026-03-21 08:56:10 +05:30
< / div >
< / div >
< / div >
) }
2026-01-18 19:27:13 +05:30
{ /* Main Layout - Responsive: Column on mobile, Row on desktop */ }
< div className = "flex flex-col lg:flex-row gap-6 lg:items-stretch" >
2026-01-11 22:09:41 +05:30
{ /* Left Sidebar - Status & Contact */ }
2026-01-18 19:27:13 +05:30
< div className = "w-full lg:w-[280px] flex-shrink-0 space-y-4 flex flex-col items-center lg:items-start" >
2026-01-12 14:13:37 +05:30
{ /* Profile Image */ }
2026-01-18 19:27:13 +05:30
< div className = "relative w-[200px] lg:w-[260px]" >
2026-03-27 15:47:29 +05:30
< div className = "w-[200px] h-[200px] lg:w-[260px] lg:h-[260px] rounded-[15px] overflow-hidden relative" >
2026-03-28 17:01:37 +05:30
{ /* Shimmer while loading, initials only on error */ }
{ imageError ? (
< div className = "absolute inset-0 bg-[#c4d9d4] flex items-center justify-center rounded-[15px]" >
< span className = "font-bold text-[#00293d] text-[80px]" > { agentProfile ? . firstName ? . [ 0 ] ? . toUpperCase ( ) || '?' } < / span >
< / div >
) : ! imageLoaded ? (
< div className = "absolute inset-0 shimmer-loading rounded-[15px]" / >
) : null }
2026-01-24 22:19:30 +05:30
{ /* eslint-disable-next-line @next/next/no-img-element */ }
< img
src = { getProfileImageUrl ( ) }
2026-01-12 14:13:37 +05:30
alt = "Profile"
2026-03-28 17:01:37 +05:30
className = { ` absolute inset-0 w-full h-full object-cover transition-opacity duration-300 ${ imageLoaded ? 'opacity-100' : 'opacity-0' } ` }
onLoad = { ( ) = > setImageLoaded ( true ) }
onError = { ( ) = > setImageError ( true ) }
2026-01-12 14:13:37 +05:30
/ >
2026-01-18 08:44:53 +05:30
{ /* Gradient Overlay */ }
< div className = "absolute inset-0 bg-gradient-to-t from-black/50 via-black/20 to-transparent pointer-events-none" / >
2026-01-12 14:13:37 +05:30
< / div >
2026-01-18 08:44:53 +05:30
{ /* Edit Icon - Top Right Outside */ }
2026-01-24 22:19:30 +05:30
< button
onClick = { ( ) = > setIsPhotoModalOpen ( true ) }
className = "absolute -top-3 -right-3 w-9 h-9 bg-white rounded-[10px] shadow-md flex items-center justify-center hover:bg-gray-50 transition-colors"
>
2026-01-12 14:13:37 +05:30
< Image
src = "/assets/icons/edit-icon.svg"
2026-01-18 21:29:52 +05:30
alt = "Edit profile image"
2026-01-12 14:13:37 +05:30
width = { 20 }
height = { 20 }
/ >
2026-01-18 21:29:52 +05:30
< / button >
2026-01-12 14:13:37 +05:30
< / div >
2026-02-02 00:34:31 +05:30
{ /* Status Buttons - Toggle for agent to set availability */ }
< StatusButtons
isAvailable = { isAvailable }
showToggle = { true }
onToggleAvailability = { handleToggleAvailability }
/ >
2026-01-12 14:13:37 +05:30
{ /* Contact Info */ }
2026-01-24 22:19:30 +05:30
< ContactInfo
2026-02-02 10:19:55 +05:30
email = { agentProfile . email || agentProfile . user ? . email || contactInfoData . email || '' }
phone = { agentProfile . phone || contactInfoData . phone || '' }
2026-01-24 22:19:30 +05:30
/ >
2026-01-12 14:13:37 +05:30
< / div >
2026-01-11 21:30:57 +05:30
2026-01-18 19:27:13 +05:30
{ /* Right Content - Profile Info + Experience + All Sections */ }
2026-01-18 20:18:30 +05:30
< div className = "flex-1 space-y-4" >
2026-01-18 18:16:55 +05:30
{ /* Profile Card */ }
2026-01-18 19:39:46 +05:30
< ProfileCard
2026-01-24 22:19:30 +05:30
firstName = { agentProfile . firstName }
lastName = { agentProfile . lastName }
isVerified = { agentProfile . isVerified }
title = { agentProfile . agentType ? . name || 'Real Estate Agent' }
2026-02-01 23:25:27 +05:30
location = {
profileCardData . city && profileCardData . state
? ` ${ profileCardData . city } , ${ profileCardData . state } `
: profileCardData . city || profileCardData . state || profileCardData . serviceAreas ? . [ 0 ] || 'United States'
}
2026-01-24 22:19:30 +05:30
memberSince = { formatMemberSince ( ( agentProfile as unknown as { createdAt? : string } ) . createdAt ) }
2026-01-24 23:31:32 +05:30
bio = { profileCardData . bio || agentProfile . bio || '' }
expertise = { profileCardData . expertise }
2026-01-20 12:17:47 +05:30
showEditButton = { true }
editHref = "/agent/edit"
messageHref = "/agent/message"
requestsHref = "/agent/network"
2026-03-04 20:24:41 +05:30
pendingRequestCount = { pendingRequestCount }
2026-01-18 19:39:46 +05:30
/ >
2026-01-11 21:30:57 +05:30
2026-01-24 23:10:02 +05:30
{ /* Experience Section - Dynamic data from profile fields */ }
< ExperienceSection experience = { experienceData } / >
2026-01-18 18:16:55 +05:30
2026-01-18 19:27:13 +05:30
{ /* Info Cards Section */ }
< div className = "flex flex-col lg:grid lg:grid-cols-3 gap-6" >
2026-01-18 18:16:55 +05:30
< InfoCard
title = "Availability"
icon = {
< Image
2026-01-18 20:28:09 +05:30
src = "/assets/icons/availability-clock-icon.svg"
2026-01-18 18:16:55 +05:30
alt = "Availability"
2026-01-18 20:28:09 +05:30
width = { 28 }
height = { 31 }
2026-01-18 18:16:55 +05:30
/ >
}
content = {
< div >
2026-01-24 23:31:32 +05:30
{ availabilityData . type && (
< p className = "font-semibold font-serif text-[14px] leading-[19px] text-[#00293D] mb-2" > { availabilityData . type } < / p >
) }
2026-01-18 18:16:55 +05:30
< div className = "space-y-1" >
2026-01-24 23:31:32 +05:30
{ availabilityData . schedule . length > 0 ? (
availabilityData . schedule . map ( ( item , index ) = > (
< p key = { index } className = "font-normal font-serif text-[14px] leading-[19px] text-[#00293D]" > { item } < / p >
) )
) : (
< p className = "font-normal font-serif text-[14px] leading-[19px] text-[#00293D]/60" > Not specified < / p >
) }
2026-01-18 18:16:55 +05:30
< / div >
< / div >
}
/ >
< InfoCard
2026-01-18 20:28:09 +05:30
title = ""
2026-01-18 18:16:55 +05:30
icon = {
< Image
2026-01-18 20:28:09 +05:30
src = "/assets/icons/work-environment-icon.svg"
2026-01-18 18:16:55 +05:30
alt = "Work environment"
2026-01-18 20:28:09 +05:30
width = { 28 }
height = { 28 }
2026-01-18 18:16:55 +05:30
/ >
}
2026-01-24 22:19:30 +05:30
content = { < p className = "font-serif font-semibold text-[14px] leading-[19px] text-[#00293D]" > { mockData . preferredWorkEnvironment } < / p > }
2026-01-18 18:16:55 +05:30
/ >
2026-03-05 05:36:10 +05:30
{ testimonials . length > 0 && (
< InfoCard
title = ""
icon = {
< Image
src = "/assets/icons/testimonial-star-icon.svg"
alt = "Testimonial"
width = { 28 }
height = { 28 }
/ >
}
content = { < p className = "text-[14px] font-semibold font-serif leading-[19px] text-center text-[#00293D]" > & ldquo ; { testimonials [ 0 ] . text . length > 150 ? testimonials [ 0 ] . text . substring ( 0 , 150 ) + '...' : testimonials [ 0 ] . text } & rdquo ; < / p > }
/ >
) }
2026-01-12 14:13:37 +05:30
< / div >
2026-01-18 19:27:13 +05:30
{ /* Specialization Section */ }
2026-01-25 01:15:02 +05:30
< SpecializationSection fieldsData = { specializationFieldsData } / >
2026-01-18 18:16:55 +05:30
2026-01-18 19:27:13 +05:30
{ /* Testimonials Section */ }
2026-03-05 05:36:10 +05:30
< TestimonialsSection testimonials = { testimonials } / >
2026-01-12 14:13:37 +05:30
< / div >
< / div >
2026-01-24 22:19:30 +05:30
{ /* Photo Upload Modal */ }
< PhotoUploadModal
isOpen = { isPhotoModalOpen }
onClose = { ( ) = > setIsPhotoModalOpen ( false ) }
onUpload = { handlePhotoUpload }
currentPhoto = { agentProfile . avatar }
2026-01-24 23:10:02 +05:30
currentPhotoUrl = { avatarUrl }
2026-01-24 22:19:30 +05:30
/ >
2026-01-11 21:30:57 +05:30
< / div >
) ;
}