feat: Integrate CMS to dynamically populate home page sections with dedicated content types and a new service.

This commit is contained in:
pradeepkumar
2026-02-22 21:52:54 +05:30
parent c5dc139633
commit b235378815
9 changed files with 227 additions and 86 deletions

View File

@@ -1,29 +1,34 @@
'use client';
import Image from 'next/image';
import type { FeaturesContent } from '@/types/cms';
const features = [
{
icon: '/assets/icons/hourglass-icon.svg',
title: 'Hire Quickly',
description: 'Connect with experienced local real estate agents who match your needs and preferences. Our simple process helps you get started quickly and move forward without delays or unnecessary complexity.',
},
{
icon: '/assets/icons/verified-badge-icon.svg',
title: 'Verified Agents',
description: 'All agents are carefully identity-verified and background-checked to ensure trust and safety. You can confidently work with professionals who meet quality and reliability standards.',
},
{
icon: '/assets/icons/star-orange-icon.svg',
title: 'Top Rated',
description: 'Work with highly rated agents known for strong expertise and positive client feedback. Their consistent performance helps you make informed and confident property decisions.',
},
{
icon: '/assets/icons/trusted-people-icon.svg',
title: 'Trusted by Thousands',
description: 'Thousands of buyers, sellers, and renters trust our platform to find reliable agents. Our professionals help people navigate property journeys with confidence and ease.',
},
];
const defaultContent: FeaturesContent = {
title: 'Find Trusted Real Estate Professionals On Demand',
subtitle: 'Quickly connect with the right agents exactly when you need them.',
features: [
{
iconPath: '/assets/icons/hourglass-icon.svg',
title: 'Hire Quickly',
description: 'Connect with experienced local real estate agents who match your needs and preferences. Our simple process helps you get started quickly and move forward without delays or unnecessary complexity.',
},
{
iconPath: '/assets/icons/verified-badge-icon.svg',
title: 'Verified Agents',
description: 'All agents are carefully identity-verified and background-checked to ensure trust and safety. You can confidently work with professionals who meet quality and reliability standards.',
},
{
iconPath: '/assets/icons/star-orange-icon.svg',
title: 'Top Rated',
description: 'Work with highly rated agents known for strong expertise and positive client feedback. Their consistent performance helps you make informed and confident property decisions.',
},
{
iconPath: '/assets/icons/trusted-people-icon.svg',
title: 'Trusted by Thousands',
description: 'Thousands of buyers, sellers, and renters trust our platform to find reliable agents. Our professionals help people navigate property journeys with confidence and ease.',
},
],
};
const agents = [
{
@@ -130,7 +135,9 @@ function AgentCard({ agent, className = '' }: { agent: typeof agents[0]; classNa
);
}
export function FeaturesSection() {
export function FeaturesSection({ content }: { content?: FeaturesContent }) {
const data = content ?? defaultContent;
return (
<section className="py-16 md:py-20 bg-white">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
@@ -140,23 +147,23 @@ export function FeaturesSection() {
{/* Section Header */}
<div className="mb-8">
<h2 className="font-fractul font-bold text-[32px] md:text-[40px] leading-[1.2] text-[#00293d] mb-4">
Find Trusted Real Estate Professionals On Demand
{data.title}
</h2>
<p className="font-fractul font-medium text-[18px] md:text-[20px] leading-[1.4] text-[#00293d]">
Quickly connect with the right agents exactly when you need them.
{data.subtitle}
</p>
</div>
{/* Features Grid - 2x2 */}
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
{features.map((feature, index) => (
{data.features.map((feature, index) => (
<div
key={index}
className="border border-[#00293d]/10 rounded-[15px] p-5"
>
<div className="mb-3">
<Image
src={feature.icon}
src={feature.iconPath}
alt={feature.title}
width={35}
height={35}

View File

@@ -4,9 +4,17 @@ import { useState, useEffect } from 'react';
import { useRouter } from 'next/navigation';
import Image from 'next/image';
import { agentsService, AgentType } from '@/services/agents.service';
import type { HeroContent } from '@/types/cms';
const defaultHeroContent: HeroContent = {
headline: '\u201cDiscover verified, top-rated real estate professionals to guide your buying, selling, or investing journey.\u201d',
description: 'Discover verified, top-rated real estate professionals',
ctaButtonText: 'See All Agents',
helperText: 'Connect with trusted local agents to explore homes, compare options, and make smarter decisions.',
};
export function HeroSection() {
export function HeroSection({ content }: { content?: HeroContent }) {
const data = content ?? defaultHeroContent;
const router = useRouter();
const [agentTypes, setAgentTypes] = useState<AgentType[]>([]);
const [searchParams, setSearchParams] = useState({
@@ -106,10 +114,10 @@ export function HeroSection() {
{/* Headline */}
<div className="text-center mb-5">
<h1 className="font-fractul font-bold text-[24px] md:text-[28px] lg:text-[30px] leading-[50px] text-[#00293d] max-w-4xl mx-auto">
&ldquo;Discover verified, top-rated real estate professionals to guide your buying, selling, or investing journey.&rdquo;
{data.headline}
</h1>
<p className="font-fractul font-bold text-[20px] md:text-[24px] leading-[40px] text-[#00293d] mt-2">
Discover verified, top-rated real estate professionals
{data.description}
</p>
</div>
@@ -245,13 +253,13 @@ export function HeroSection() {
onClick={handleSeeAllAgents}
className="px-8 py-3 rounded-[15px] bg-[#e58625] hover:bg-[#d47720] text-[#00293d] font-fractul font-bold text-base transition-colors shadow-md hover:shadow-lg"
>
See All Agents
{data.ctaButtonText}
</button>
</div>
{/* Helper Text */}
<p className="text-center font-serif font-normal text-[14px] leading-[19px] text-[#00293D] mt-3 max-w-[419px] mx-auto">
Connect with trusted local agents to explore homes, compare options, and make smarter decisions.
{data.helperText}
</p>
</div>
</div>

View File

@@ -1,8 +1,9 @@
'use client';
import Image from 'next/image';
import type { TestimonialsContent } from '@/types/cms';
const testimonials = [
const defaultTestimonials = [
{
id: '1',
rating: 5,
@@ -86,69 +87,72 @@ function TestimonialCard({ title, content, author, role, rating }: TestimonialCa
);
}
export function TestimonialsSection() {
const defaultContent: TestimonialsContent = {
title: "Our Clients' Trust Is Our Foundation",
subtitle: 'We help buyers, sellers, and investors close successful real estate deals with confidence in every transaction.',
ratingInfo: 'Clients rate our real estate services 4.9 out of 5 on average, based on recent client reviews.',
stats: [
{
iconPath: '/assets/icons/cities-icon.svg',
boldText: '25+ Cities &',
normalText: 'neighborhoods served',
},
{
iconPath: '/assets/icons/properties-icon.svg',
boldText: '1,000+ Properties',
normalText: 'bought and sold for our clients.',
},
],
testimonials: defaultTestimonials,
};
export function TestimonialsSection({ content }: { content?: TestimonialsContent }) {
const data = content ?? defaultContent;
return (
<section className="py-12 md:py-16 bg-white">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
{/* Section Header */}
<div className="text-center mb-6">
<h2 className="font-fractul font-bold text-[32px] md:text-[40px] leading-[1.2] text-[#00293d] mb-4">
Our Clients&apos; Trust Is Our Foundation
{data.title}
</h2>
<p className="font-fractul font-medium text-[14px] leading-normal text-[#00293d] mb-2">
We help buyers, sellers, and investors close successful real estate deals with confidence in every transaction.
{data.subtitle}
</p>
<p className="font-fractul font-medium text-[14px] leading-normal text-[#00293d]">
Clients rate our real estate services 4.9 out of 5 on average, based on recent client reviews.
{data.ratingInfo}
</p>
</div>
{/* Stats Section */}
<div className="flex justify-center items-center gap-8 md:gap-12 mb-10">
{/* Cities Stat */}
<div className="flex items-center gap-3">
<Image
src="/assets/icons/cities-icon.svg"
alt="Cities"
width={27}
height={27}
/>
<div>
<span className="font-serif font-bold text-[14px] leading-normal text-[#00293d]">
25+ Cities &
</span>
<br />
<span className="font-serif font-normal text-[14px] leading-normal text-[#00293d]">
neighborhoods served
</span>
{data.stats.map((stat, index) => (
<div key={index} className="flex items-center gap-3">
<Image
src={stat.iconPath}
alt=""
width={27}
height={27}
/>
<div>
<span className="font-serif font-bold text-[14px] leading-normal text-[#00293d]">
{stat.boldText}
</span>
<br />
<span className="font-serif font-normal text-[14px] leading-normal text-[#00293d]">
{stat.normalText}
</span>
</div>
</div>
</div>
{/* Properties Stat */}
<div className="flex items-center gap-3">
<Image
src="/assets/icons/properties-icon.svg"
alt="Properties"
width={25}
height={24}
/>
<div>
<span className="font-serif font-bold text-[14px] leading-normal text-[#00293d]">
1,000+ Properties
</span>
<br />
<span className="font-serif font-normal text-[14px] leading-normal text-[#00293d]">
bought and sold for our clients.
</span>
</div>
</div>
))}
</div>
{/* Testimonials Grid */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{testimonials.map((testimonial) => (
{data.testimonials.map((testimonial, index) => (
<TestimonialCard
key={testimonial.id}
key={index}
title={testimonial.title}
content={testimonial.content}
author={testimonial.author}

View File

@@ -3,6 +3,7 @@
import { useState } from 'react';
import Link from 'next/link';
import Image from 'next/image';
import type { TopProfessionalsContent } from '@/types/cms';
// Sample agents data for tabs
const agentsData = [
@@ -177,9 +178,18 @@ function ProfessionalCard({
);
}
export function TopProfessionals() {
const defaultContent: TopProfessionalsContent = {
title: '\u201cMeet top real estate professionals\u201d',
ctaText: 'Discover 5,000+ Top Real Estate Agents in Our Network.',
ctaButtonText: 'Browse Experts',
agents: agentsData,
lenders: lendersData,
};
export function TopProfessionals({ content }: { content?: TopProfessionalsContent }) {
const data = content ?? defaultContent;
const [activeTab, setActiveTab] = useState<'agents' | 'lenders'>('agents');
const displayData = activeTab === 'agents' ? agentsData : lendersData;
const displayData = activeTab === 'agents' ? data.agents : data.lenders;
return (
<section className="py-10 md:py-12 bg-white">
@@ -187,7 +197,7 @@ export function TopProfessionals() {
{/* Section Header */}
<div className="text-center mb-10">
<h2 className="font-fractul font-bold text-[32px] md:text-[40px] leading-[1.2] text-[#00293d]">
&ldquo;Meet top real estate professionals&rdquo;
{data.title}
</h2>
</div>
@@ -238,9 +248,9 @@ export function TopProfessionals() {
{/* Cards Grid - 3 cards + CTA sidebar */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
{/* Professional Cards */}
{displayData.map((professional) => (
{displayData.map((professional, index) => (
<ProfessionalCard
key={professional.id}
key={index}
name={professional.name}
subtitle={professional.subtitle}
location={professional.location}
@@ -269,13 +279,13 @@ export function TopProfessionals() {
{/* Text */}
<p className="font-fractul font-normal text-[14px] leading-[1.4] text-[#00293d] mb-6">
Discover 5,000+ Top Real Estate Agents in Our Network.
{data.ctaText}
</p>
{/* Browse Experts Button */}
<Link href="/user/profiles">
<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">
Browse Experts
{data.ctaButtonText}
</button>
</Link>
</div>