2026-01-11 22:09:41 +05:30
|
|
|
'use client';
|
|
|
|
|
|
2026-03-04 21:40:16 +05:30
|
|
|
import { useState, useEffect } from 'react';
|
2026-01-11 22:09:41 +05:30
|
|
|
import Link from 'next/link';
|
2026-01-20 01:41:09 +05:30
|
|
|
import Image from 'next/image';
|
2026-03-04 21:40:16 +05:30
|
|
|
import { agentsService, PublicAgentProfile, AgentType } from '@/services/agents.service';
|
|
|
|
|
import { uploadService } from '@/services/upload.service';
|
2026-02-22 21:52:54 +05:30
|
|
|
import type { TopProfessionalsContent } from '@/types/cms';
|
2026-01-11 22:09:41 +05:30
|
|
|
|
2026-01-20 01:41:09 +05:30
|
|
|
interface ProfessionalCardProps {
|
|
|
|
|
name: string;
|
|
|
|
|
subtitle: string;
|
|
|
|
|
location: string;
|
|
|
|
|
experience: string;
|
|
|
|
|
expertise: string[];
|
|
|
|
|
imageUrl: string;
|
2026-03-04 21:40:16 +05:30
|
|
|
isVerified: boolean;
|
|
|
|
|
rating: number | null;
|
2026-01-20 01:41:09 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function ProfessionalCard({
|
|
|
|
|
name,
|
|
|
|
|
subtitle,
|
|
|
|
|
location,
|
|
|
|
|
experience,
|
|
|
|
|
expertise,
|
|
|
|
|
imageUrl,
|
2026-03-04 21:40:16 +05:30
|
|
|
isVerified,
|
|
|
|
|
rating,
|
2026-01-20 01:41:09 +05:30
|
|
|
}: ProfessionalCardProps) {
|
2026-03-04 21:40:16 +05:30
|
|
|
const starCount = rating ? Math.round(rating) : 0;
|
2026-03-14 14:37:19 +05:30
|
|
|
const [imgLoaded, setImgLoaded] = useState(false);
|
|
|
|
|
const placeholder = '/assets/icons/user-placeholder-icon.svg';
|
|
|
|
|
const isPlaceholder = imageUrl === placeholder;
|
2026-03-19 18:18:44 +05:30
|
|
|
const INITIAL_TAG_COUNT = 4;
|
2026-01-20 01:41:09 +05:30
|
|
|
|
2026-03-04 21:40:16 +05:30
|
|
|
return (
|
2026-03-27 06:56:38 +05:30
|
|
|
<div className="bg-white rounded-[15px] overflow-hidden shadow-[0px_4px_20px_rgba(0,0,0,0.08)] h-full flex flex-col">
|
2026-03-04 21:40:16 +05:30
|
|
|
{/* Image */}
|
|
|
|
|
<div className="relative h-[226px] w-full overflow-hidden bg-gray-100">
|
2026-03-14 14:37:19 +05:30
|
|
|
{!imgLoaded && !isPlaceholder && (
|
|
|
|
|
<div className="absolute inset-0 shimmer-loading" />
|
|
|
|
|
)}
|
2026-01-20 01:41:09 +05:30
|
|
|
<Image
|
2026-03-04 21:40:16 +05:30
|
|
|
src={imageUrl}
|
|
|
|
|
alt={name}
|
|
|
|
|
fill
|
2026-03-14 14:37:19 +05:30
|
|
|
className={`object-cover transition-opacity duration-300 ${isPlaceholder || imgLoaded ? 'opacity-100' : 'opacity-0'}`}
|
|
|
|
|
onLoad={() => setImgLoaded(true)}
|
2026-03-18 11:54:40 +05:30
|
|
|
onError={() => setImgLoaded(true)}
|
2026-01-20 01:41:09 +05:30
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
|
2026-03-04 21:40:16 +05:30
|
|
|
{/* Content */}
|
2026-03-19 18:18:44 +05:30
|
|
|
<div className="p-4 flex-1 flex flex-col">
|
2026-03-04 21:40:16 +05:30
|
|
|
{/* Name */}
|
|
|
|
|
<h3 className="font-fractul font-bold text-[14px] leading-normal text-[#00293d]">
|
|
|
|
|
{name}
|
|
|
|
|
</h3>
|
2026-01-20 01:41:09 +05:30
|
|
|
|
2026-03-04 21:40:16 +05:30
|
|
|
{/* Subtitle */}
|
|
|
|
|
<p className="font-serif font-normal text-[10px] leading-normal text-[#00293d] mt-1">
|
|
|
|
|
{subtitle}
|
2026-01-20 01:41:09 +05:30
|
|
|
</p>
|
2026-03-04 21:40:16 +05:30
|
|
|
|
|
|
|
|
{/* Verified Badge */}
|
|
|
|
|
{isVerified && (
|
|
|
|
|
<div className="flex items-center gap-1.5 mt-2">
|
|
|
|
|
<Image
|
|
|
|
|
src="/assets/icons/verified-badge-blue.svg"
|
|
|
|
|
alt="Verified"
|
|
|
|
|
width={14}
|
|
|
|
|
height={14}
|
|
|
|
|
/>
|
|
|
|
|
<span className="font-serif font-medium text-[14px] leading-normal text-[#638559]">
|
|
|
|
|
“Verified local agent”
|
2026-01-20 01:41:09 +05:30
|
|
|
</span>
|
2026-03-04 21:40:16 +05:30
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{/* Location */}
|
|
|
|
|
{location && (
|
|
|
|
|
<div className="mt-3">
|
|
|
|
|
<p className="font-fractul font-bold text-[14px] leading-normal text-[#00293d]">
|
|
|
|
|
Location:
|
|
|
|
|
</p>
|
|
|
|
|
<div className="flex items-center gap-1.5 mt-1">
|
|
|
|
|
<Image
|
|
|
|
|
src="/assets/icons/location-pin-orange.svg"
|
|
|
|
|
alt="Location"
|
|
|
|
|
width={12}
|
|
|
|
|
height={15}
|
|
|
|
|
/>
|
|
|
|
|
<span className="font-serif font-normal text-[10px] leading-normal text-[#00293d]">
|
|
|
|
|
{location}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
2026-01-20 01:41:09 +05:30
|
|
|
|
2026-03-04 21:40:16 +05:30
|
|
|
{/* Expertise */}
|
|
|
|
|
{expertise.length > 0 && (
|
|
|
|
|
<div className="mt-3">
|
|
|
|
|
<p className="font-fractul font-bold text-[14px] leading-normal text-[#00293d]">
|
|
|
|
|
Expertise:
|
|
|
|
|
</p>
|
2026-03-19 18:18:44 +05:30
|
|
|
<div className="flex flex-wrap gap-1.5 mt-2 items-center">
|
2026-03-20 12:47:06 +05:30
|
|
|
{expertise.slice(0, INITIAL_TAG_COUNT).map((tag, index) => (
|
2026-03-04 21:40:16 +05:30
|
|
|
<span
|
|
|
|
|
key={index}
|
|
|
|
|
className="border-[0.7px] border-[#00293d] rounded-[15px] px-[5px] h-[24px] flex items-center justify-center font-serif font-normal text-[13px] leading-[100%] text-[#00293d]"
|
|
|
|
|
>
|
|
|
|
|
{tag}
|
|
|
|
|
</span>
|
|
|
|
|
))}
|
2026-03-19 18:18:44 +05:30
|
|
|
{expertise.length > INITIAL_TAG_COUNT && (
|
2026-03-20 12:47:06 +05:30
|
|
|
<span className="bg-[#e58625]/15 text-[#e58625] rounded-[15px] px-2 h-[24px] flex items-center justify-center font-serif font-semibold text-[12px] leading-[100%]">
|
|
|
|
|
+{expertise.length - INITIAL_TAG_COUNT} more
|
2026-03-19 18:29:18 +05:30
|
|
|
</span>
|
2026-03-19 18:18:44 +05:30
|
|
|
)}
|
2026-03-04 21:40:16 +05:30
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{/* Experience */}
|
|
|
|
|
{experience && (
|
|
|
|
|
<p className="mt-3 text-[14px] leading-normal text-[#00293d]">
|
|
|
|
|
<span className="font-fractul font-bold">Experience:</span>
|
|
|
|
|
<span className="font-serif font-normal"> {experience}</span>
|
|
|
|
|
</p>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{/* Star Rating */}
|
|
|
|
|
{starCount > 0 && (
|
|
|
|
|
<div className="flex gap-1 mt-3">
|
|
|
|
|
{[...Array(5)].map((_, i) => (
|
|
|
|
|
<Image
|
|
|
|
|
key={i}
|
|
|
|
|
src={i < starCount ? '/assets/icons/star-yellow.svg' : '/assets/icons/star-empty-icon.svg'}
|
|
|
|
|
alt="Star"
|
|
|
|
|
width={18}
|
|
|
|
|
height={17}
|
|
|
|
|
/>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
2026-01-20 01:41:09 +05:30
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-04 21:40:16 +05:30
|
|
|
// Helper to extract expertise tags from agent profile
|
|
|
|
|
function getExpertiseTags(profile: PublicAgentProfile): string[] {
|
|
|
|
|
const tags: string[] = [];
|
|
|
|
|
const expertiseFieldSlugs = [
|
|
|
|
|
'about_me_expertise',
|
|
|
|
|
'expertise_areas',
|
|
|
|
|
'specializations',
|
|
|
|
|
'areas_of_expertise',
|
|
|
|
|
'expertise',
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
if (profile.fieldValues && profile.fieldValues.length > 0) {
|
|
|
|
|
profile.fieldValues.forEach((fv) => {
|
|
|
|
|
if (!expertiseFieldSlugs.includes(fv.field.slug)) return;
|
|
|
|
|
const value = fv.jsonValue;
|
|
|
|
|
if (Array.isArray(value)) {
|
|
|
|
|
value.forEach((v) => {
|
|
|
|
|
if (typeof v === 'string' && v.trim()) {
|
|
|
|
|
const displayValue = v
|
|
|
|
|
.split('_')
|
|
|
|
|
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
|
|
|
.join(' ');
|
|
|
|
|
if (!tags.includes(displayValue)) tags.push(displayValue);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
} else if (typeof value === 'string' && value.trim()) {
|
|
|
|
|
if (!tags.includes(value)) tags.push(value);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (profile.specializations && profile.specializations.length > 0) {
|
|
|
|
|
profile.specializations.forEach((spec) => {
|
|
|
|
|
if (!tags.includes(spec)) tags.push(spec);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return tags;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Helper to get location string from agent profile
|
|
|
|
|
function getLocationString(profile: PublicAgentProfile): string {
|
|
|
|
|
if (profile.fieldValues && profile.fieldValues.length > 0) {
|
|
|
|
|
const stateField = profile.fieldValues.find(fv => fv.field.slug === 'state');
|
|
|
|
|
const cityField = profile.fieldValues.find(fv => fv.field.slug === 'city');
|
|
|
|
|
const parts: string[] = [];
|
|
|
|
|
|
|
|
|
|
if (cityField) {
|
|
|
|
|
const val = cityField.jsonValue;
|
|
|
|
|
if (Array.isArray(val) && val.length > 0 && typeof val[0] === 'string') {
|
|
|
|
|
parts.push(val[0].split('_').map(w => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join(' '));
|
|
|
|
|
} else if (typeof val === 'string') {
|
|
|
|
|
parts.push(val);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (stateField) {
|
|
|
|
|
const val = stateField.jsonValue;
|
|
|
|
|
if (Array.isArray(val) && val.length > 0 && typeof val[0] === 'string') {
|
|
|
|
|
parts.push(val[0].split('_').map(w => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join(' '));
|
|
|
|
|
} else if (typeof val === 'string') {
|
|
|
|
|
parts.push(val);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (parts.length > 0) return parts.join(', ');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const legacyParts = [profile.city, profile.state].filter(Boolean);
|
|
|
|
|
if (legacyParts.length > 0) return legacyParts.join(', ');
|
|
|
|
|
|
|
|
|
|
if (profile.serviceAreas && profile.serviceAreas.length > 0) return profile.serviceAreas[0];
|
|
|
|
|
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Helper to resolve avatar URL
|
|
|
|
|
async function resolveAvatarUrl(avatar: string | null): Promise<string> {
|
|
|
|
|
if (!avatar) return '/assets/icons/user-placeholder-icon.svg';
|
|
|
|
|
if (avatar.startsWith('http') || avatar.startsWith('/')) return avatar;
|
|
|
|
|
try {
|
|
|
|
|
return await uploadService.getPresignedDownloadUrl(avatar);
|
|
|
|
|
} catch {
|
|
|
|
|
return '/assets/icons/user-placeholder-icon.svg';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface DisplayProfessional {
|
|
|
|
|
id: string;
|
|
|
|
|
name: string;
|
|
|
|
|
subtitle: string;
|
|
|
|
|
location: string;
|
|
|
|
|
experience: string;
|
|
|
|
|
expertise: string[];
|
|
|
|
|
imageUrl: string;
|
|
|
|
|
isVerified: boolean;
|
|
|
|
|
rating: number | null;
|
|
|
|
|
slug: string;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-22 21:52:54 +05:30
|
|
|
const defaultContent: TopProfessionalsContent = {
|
|
|
|
|
title: '\u201cMeet top real estate professionals\u201d',
|
|
|
|
|
ctaText: 'Discover 5,000+ Top Real Estate Agents in Our Network.',
|
|
|
|
|
ctaButtonText: 'Browse Experts',
|
2026-03-04 21:40:16 +05:30
|
|
|
agents: [],
|
|
|
|
|
lenders: [],
|
2026-02-22 21:52:54 +05:30
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export function TopProfessionals({ content }: { content?: TopProfessionalsContent }) {
|
|
|
|
|
const data = content ?? defaultContent;
|
2026-01-11 22:09:41 +05:30
|
|
|
const [activeTab, setActiveTab] = useState<'agents' | 'lenders'>('agents');
|
2026-03-04 21:40:16 +05:30
|
|
|
const [agents, setAgents] = useState<DisplayProfessional[]>([]);
|
|
|
|
|
const [lenders, setLenders] = useState<DisplayProfessional[]>([]);
|
|
|
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const fetchProfessionals = async () => {
|
|
|
|
|
try {
|
|
|
|
|
// Get agent types to find Professional and Lender type IDs
|
|
|
|
|
const agentTypes = await agentsService.getAgentTypes();
|
|
|
|
|
const professionalType = agentTypes.find(t => t.name === 'Professional');
|
|
|
|
|
const lenderType = agentTypes.find(t => t.name === 'Lender');
|
|
|
|
|
|
|
|
|
|
// Fetch both in parallel - get top 3 of each sorted by rating
|
|
|
|
|
const [agentsResponse, lendersResponse] = await Promise.all([
|
|
|
|
|
professionalType
|
|
|
|
|
? agentsService.searchAgents({
|
|
|
|
|
agentTypeId: professionalType.id,
|
|
|
|
|
limit: 3,
|
|
|
|
|
sortBy: 'averageRating',
|
|
|
|
|
sortOrder: 'desc',
|
|
|
|
|
})
|
|
|
|
|
: Promise.resolve({ data: [], total: 0, page: 1, limit: 3, totalPages: 0 }),
|
|
|
|
|
lenderType
|
|
|
|
|
? agentsService.searchAgents({
|
|
|
|
|
agentTypeId: lenderType.id,
|
|
|
|
|
limit: 3,
|
|
|
|
|
sortBy: 'averageRating',
|
|
|
|
|
sortOrder: 'desc',
|
|
|
|
|
})
|
|
|
|
|
: Promise.resolve({ data: [], total: 0, page: 1, limit: 3, totalPages: 0 }),
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
// Resolve avatars and map to display format
|
|
|
|
|
const mapProfiles = async (profiles: PublicAgentProfile[]): Promise<DisplayProfessional[]> => {
|
|
|
|
|
return Promise.all(
|
|
|
|
|
profiles.map(async (profile) => {
|
|
|
|
|
const imageUrl = await resolveAvatarUrl(profile.avatar);
|
|
|
|
|
const expertise = getExpertiseTags(profile);
|
|
|
|
|
const location = getLocationString(profile);
|
|
|
|
|
const experienceYears = profile.experience
|
|
|
|
|
? `${profile.experience}+ years in real estate.`
|
|
|
|
|
: '';
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
id: profile.id,
|
|
|
|
|
name: `${profile.firstName} ${profile.lastName}`.trim(),
|
|
|
|
|
subtitle: profile.agentType ? `(${profile.agentType.name})` : '',
|
|
|
|
|
location,
|
|
|
|
|
experience: experienceYears,
|
|
|
|
|
expertise,
|
|
|
|
|
imageUrl,
|
|
|
|
|
isVerified: profile.isVerified,
|
|
|
|
|
rating: profile.rating,
|
|
|
|
|
slug: profile.slug,
|
|
|
|
|
};
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const [mappedAgents, mappedLenders] = await Promise.all([
|
|
|
|
|
mapProfiles(agentsResponse.data),
|
|
|
|
|
mapProfiles(lendersResponse.data),
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
setAgents(mappedAgents);
|
|
|
|
|
setLenders(mappedLenders);
|
|
|
|
|
} catch (err) {
|
|
|
|
|
console.error('Failed to fetch top professionals:', err);
|
|
|
|
|
} finally {
|
|
|
|
|
setLoading(false);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
fetchProfessionals();
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
const displayData = activeTab === 'agents' ? agents : lenders;
|
2026-01-11 22:09:41 +05:30
|
|
|
|
|
|
|
|
return (
|
2026-01-20 01:41:09 +05:30
|
|
|
<section className="py-10 md:py-12 bg-white">
|
2026-01-11 22:09:41 +05:30
|
|
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
|
|
|
{/* Section Header */}
|
|
|
|
|
<div className="text-center mb-10">
|
2026-01-20 01:41:09 +05:30
|
|
|
<h2 className="font-fractul font-bold text-[32px] md:text-[40px] leading-[1.2] text-[#00293d]">
|
2026-02-22 21:52:54 +05:30
|
|
|
{data.title}
|
2026-01-11 22:09:41 +05:30
|
|
|
</h2>
|
|
|
|
|
</div>
|
|
|
|
|
|
2026-01-20 01:41:09 +05:30
|
|
|
{/* Tabs Container */}
|
|
|
|
|
<div className="flex justify-center mb-10">
|
2026-01-20 09:44:56 +05:30
|
|
|
<div className="border border-[#00293d]/10 rounded-[5px] p-5 inline-flex items-center">
|
2026-01-20 01:41:09 +05:30
|
|
|
{/* Agents Tab */}
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => setActiveTab('agents')}
|
2026-01-20 09:44:56 +05:30
|
|
|
className={`flex items-center gap-2 px-10 py-3 rounded-[5px] font-serif font-semibold text-[17px] transition-all ${
|
2026-01-20 01:41:09 +05:30
|
|
|
activeTab === 'agents'
|
|
|
|
|
? 'bg-[#00293d] text-[#f0f5fc]'
|
2026-01-20 09:44:56 +05:30
|
|
|
: 'border border-[#00293d]/20 text-[#00293d] hover:bg-gray-50'
|
2026-01-20 01:41:09 +05:30
|
|
|
}`}
|
|
|
|
|
>
|
|
|
|
|
<Image
|
2026-01-20 09:44:56 +05:30
|
|
|
src={activeTab === 'agents' ? '/assets/icons/agents-tab-icon-orange.svg' : '/assets/icons/agents-tab-icon.svg'}
|
2026-01-20 01:41:09 +05:30
|
|
|
alt=""
|
|
|
|
|
width={20}
|
|
|
|
|
height={20}
|
|
|
|
|
/>
|
|
|
|
|
Agents
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
|
|
{/* Divider */}
|
2026-01-20 09:44:56 +05:30
|
|
|
<div className="h-[44px] w-px bg-[#00293d]/20 mx-4" />
|
2026-01-20 01:41:09 +05:30
|
|
|
|
|
|
|
|
{/* Lenders Tab */}
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => setActiveTab('lenders')}
|
2026-01-20 09:44:56 +05:30
|
|
|
className={`flex items-center gap-2 px-10 py-3 rounded-[5px] font-serif font-semibold text-[17px] transition-all ${
|
2026-01-20 01:41:09 +05:30
|
|
|
activeTab === 'lenders'
|
|
|
|
|
? 'bg-[#00293d] text-[#f0f5fc]'
|
2026-01-20 09:44:56 +05:30
|
|
|
: 'border border-[#00293d]/20 text-[#00293d] hover:bg-gray-50'
|
2026-01-20 01:41:09 +05:30
|
|
|
}`}
|
|
|
|
|
>
|
|
|
|
|
<Image
|
2026-01-20 09:44:56 +05:30
|
|
|
src={activeTab === 'lenders' ? '/assets/icons/lenders-tab-icon-orange.svg' : '/assets/icons/lenders-tab-icon.svg'}
|
2026-01-20 01:41:09 +05:30
|
|
|
alt=""
|
|
|
|
|
width={20}
|
|
|
|
|
height={20}
|
|
|
|
|
/>
|
|
|
|
|
Lenders
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
2026-01-11 22:09:41 +05:30
|
|
|
</div>
|
|
|
|
|
|
2026-01-20 01:41:09 +05:30
|
|
|
{/* Cards Grid - 3 cards + CTA sidebar */}
|
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
2026-03-04 21:40:16 +05:30
|
|
|
{loading ? (
|
|
|
|
|
// Loading skeleton cards
|
|
|
|
|
<>
|
|
|
|
|
{[1, 2, 3].map((i) => (
|
|
|
|
|
<div key={i} className="bg-white rounded-[15px] overflow-hidden shadow-[0px_4px_20px_rgba(0,0,0,0.08)] animate-pulse">
|
|
|
|
|
<div className="h-[226px] bg-gray-200" />
|
|
|
|
|
<div className="p-4 space-y-3">
|
|
|
|
|
<div className="h-4 bg-gray-200 rounded w-3/4" />
|
|
|
|
|
<div className="h-3 bg-gray-200 rounded w-1/2" />
|
|
|
|
|
<div className="h-4 bg-gray-200 rounded w-2/3" />
|
|
|
|
|
<div className="flex gap-1.5">
|
|
|
|
|
<div className="h-6 bg-gray-200 rounded-full w-16" />
|
|
|
|
|
<div className="h-6 bg-gray-200 rounded-full w-16" />
|
|
|
|
|
<div className="h-6 bg-gray-200 rounded-full w-16" />
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</>
|
|
|
|
|
) : displayData.length > 0 ? (
|
|
|
|
|
displayData.map((professional) => (
|
|
|
|
|
<ProfessionalCard
|
|
|
|
|
key={professional.id}
|
|
|
|
|
name={professional.name}
|
|
|
|
|
subtitle={professional.subtitle}
|
|
|
|
|
location={professional.location}
|
|
|
|
|
experience={professional.experience}
|
|
|
|
|
expertise={professional.expertise}
|
|
|
|
|
imageUrl={professional.imageUrl}
|
|
|
|
|
isVerified={professional.isVerified}
|
|
|
|
|
rating={professional.rating}
|
|
|
|
|
/>
|
|
|
|
|
))
|
|
|
|
|
) : (
|
|
|
|
|
// No data message
|
|
|
|
|
<div className="col-span-3 flex items-center justify-center py-12">
|
|
|
|
|
<p className="font-serif text-[14px] text-[#00293d]/60">
|
|
|
|
|
No {activeTab === 'agents' ? 'agents' : 'lenders'} available yet.
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
2026-01-11 22:09:41 +05:30
|
|
|
|
2026-01-20 01:41:09 +05:30
|
|
|
{/* CTA Sidebar Card */}
|
2026-01-20 09:44:56 +05:30
|
|
|
<div
|
|
|
|
|
className="rounded-[15px] p-6 flex flex-col items-center justify-center text-center min-h-[400px]"
|
|
|
|
|
style={{
|
|
|
|
|
background: 'linear-gradient(180deg, #C5D6D6 0%, #FFFFFF 100%)',
|
|
|
|
|
}}
|
|
|
|
|
>
|
2026-01-20 01:41:09 +05:30
|
|
|
{/* Building Icon */}
|
|
|
|
|
<div className="mb-4">
|
|
|
|
|
<Image
|
|
|
|
|
src="/assets/icons/building-icon.svg"
|
|
|
|
|
alt="Building"
|
|
|
|
|
width={50}
|
|
|
|
|
height={38}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Text */}
|
|
|
|
|
<p className="font-fractul font-normal text-[14px] leading-[1.4] text-[#00293d] mb-6">
|
2026-02-22 21:52:54 +05:30
|
|
|
{data.ctaText}
|
2026-01-11 22:09:41 +05:30
|
|
|
</p>
|
2026-01-20 01:41:09 +05:30
|
|
|
|
|
|
|
|
{/* Browse Experts Button */}
|
2026-02-02 16:24:39 +05:30
|
|
|
<Link href="/user/profiles">
|
2026-01-20 01:41:09 +05:30
|
|
|
<button className="bg-[#e58625] hover:bg-[#d47820] text-white font-fractul font-bold text-[16px] leading-[19px] px-8 py-3 rounded-[15px] transition-colors">
|
2026-02-22 21:52:54 +05:30
|
|
|
{data.ctaButtonText}
|
2026-01-20 01:41:09 +05:30
|
|
|
</button>
|
2026-01-11 22:09:41 +05:30
|
|
|
</Link>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</section>
|
|
|
|
|
);
|
|
|
|
|
}
|