feat: Implement CMS integration for the About page, including new content types and dynamic data loading with default fallbacks.

This commit is contained in:
pradeepkumar
2026-02-24 03:30:22 +05:30
parent b235378815
commit 20857a9d59
2 changed files with 233 additions and 132 deletions

View File

@@ -1,47 +1,107 @@
'use client'; 'use client';
import { useState, useEffect } from 'react';
import Image from 'next/image'; import Image from 'next/image';
import Link from 'next/link'; import Link from 'next/link';
import { CommonHeader } from '@/components/layout/CommonHeader'; import { CommonHeader } from '@/components/layout/CommonHeader';
import { Footer } from '@/components/layout/Footer'; import { Footer } from '@/components/layout/Footer';
import { cmsService } from '@/services/cms.service';
import type {
AboutHeroContent,
AboutStatsContent,
AboutFeaturesContent,
AboutTeamContent,
AboutCtaContent,
CmsContentRecord,
} from '@/types/cms';
const teamMembers = [ // ---------------------------------------------------------------------------
{ // Hardcoded defaults (used when no CMS content is saved)
name: 'Andrew', // ---------------------------------------------------------------------------
role: 'Founder & CEO',
image: '/assets/images/professional-1.jpg',
},
{
name: 'Thomas',
role: 'Co-Founder',
image: '/assets/images/professional-2.jpg',
},
{
name: 'Darren',
role: 'Advisor',
image: '/assets/images/professional-3.jpg',
},
];
const features = [ const defaultHero: AboutHeroContent = {
badge: 'Our Mission',
headline: 'Connecting You with Trusted Agents.',
description:
'We believe finding the right home shouldn\u2019t be complicated. Our platform bridges the gap between buyers, sellers, and verified real estate agents for a smooth and transparent experience.',
ctaButtonText: 'Find Your Agent',
bannerImageUrl: '/assets/images/user_home_top.png',
bannerOverlayText: 'Building the future of property.',
};
const defaultStats: AboutStatsContent = {
stats: [
{ value: '15k+', label: 'Verified Agents' },
{ value: '98%', label: 'Customer Satisfaction' },
],
};
const defaultFeatures: AboutFeaturesContent = {
badge: 'Why Choose Us',
features: [
{ {
icon: '/assets/icons/verified-badge-orange.svg', iconPath: '/assets/icons/verified-badge-orange.svg',
title: 'Verified Agents', title: 'Verified Agents',
description: 'Every agent is vetted and verified for credentials, experience, and customer feedback.', description:
'Every agent is vetted and verified for credentials, experience, and customer feedback.',
}, },
{ {
icon: '/assets/icons/shield-verified-icon.svg', iconPath: '/assets/icons/shield-verified-icon.svg',
title: 'Total Transparency', title: 'Total Transparency',
description: 'Access honest reviews, transaction history, and verified credentials before connecting.', description:
'Access honest reviews, transaction history, and verified credentials before connecting.',
}, },
{ {
icon: '/assets/icons/home-icon.svg', iconPath: '/assets/icons/home-icon.svg',
title: 'Simple Journey', title: 'Simple Journey',
description: 'From search to settlement, we make finding and working with agents effortless.', description:
'From search to settlement, we make finding and working with agents effortless.',
}, },
]; ],
};
const defaultTeam: AboutTeamContent = {
title: 'Meet the minds behind the platform.',
subtitle:
'Our team is passionate about making real estate connections easier and more transparent for everyone.',
members: [
{ name: 'Andrew', role: 'Founder & CEO', imageUrl: '/assets/images/professional-1.jpg' },
{ name: 'Thomas', role: 'Co-Founder', imageUrl: '/assets/images/professional-2.jpg' },
{ name: 'Darren', role: 'Advisor', imageUrl: '/assets/images/professional-3.jpg' },
],
};
const defaultCta: AboutCtaContent = {
title: 'Ready to find an agent?',
description: 'Discover trusted agents and start your property journey with confidence.',
buttonText: 'Start Your Search',
};
// ---------------------------------------------------------------------------
// Page component
// ---------------------------------------------------------------------------
export default function AboutPage() { export default function AboutPage() {
const [hero, setHero] = useState<AboutHeroContent>(defaultHero);
const [stats, setStats] = useState<AboutStatsContent>(defaultStats);
const [features, setFeatures] = useState<AboutFeaturesContent>(defaultFeatures);
const [team, setTeam] = useState<AboutTeamContent>(defaultTeam);
const [cta, setCta] = useState<AboutCtaContent>(defaultCta);
useEffect(() => {
cmsService.getPageContent('about').then((sections) => {
sections.forEach((s: CmsContentRecord) => {
switch (s.sectionKey) {
case 'hero': setHero(s.content as AboutHeroContent); break;
case 'stats': setStats(s.content as AboutStatsContent); break;
case 'features': setFeatures(s.content as AboutFeaturesContent); break;
case 'team': setTeam(s.content as AboutTeamContent); break;
case 'cta': setCta(s.content as AboutCtaContent); break;
}
});
});
}, []);
return ( return (
<div className="min-h-screen bg-white flex flex-col"> <div className="min-h-screen bg-white flex flex-col">
{/* Header */} {/* Header */}
@@ -63,18 +123,18 @@ export default function AboutPage() {
height={19} height={19}
/> />
<span className="font-fractul font-medium text-[14px] text-[#00293d]"> <span className="font-fractul font-medium text-[14px] text-[#00293d]">
Our Mission {hero.badge}
</span> </span>
</div> </div>
{/* Headline */} {/* Headline */}
<h1 className="font-fractul font-bold text-[32px] lg:text-[40px] text-[#00293d] leading-tight mb-6"> <h1 className="font-fractul font-bold text-[32px] lg:text-[40px] text-[#00293d] leading-tight mb-6">
Connecting You with Trusted Agents. {hero.headline}
</h1> </h1>
{/* Description */} {/* Description */}
<p className="font-serif text-[14px] text-[#00293d] leading-relaxed mb-8 max-w-[550px] mx-auto"> <p className="font-serif text-[14px] text-[#00293d] leading-relaxed mb-8 max-w-[550px] mx-auto">
We believe finding the right home shouldn't be complicated. Our platform bridges the gap between buyers, sellers, and verified real estate agents for a smooth and transparent experience. {hero.description}
</p> </p>
{/* CTA Button */} {/* CTA Button */}
@@ -82,7 +142,7 @@ export default function AboutPage() {
href="/agents" href="/agents"
className="inline-flex items-center justify-center w-[160px] h-[46px] border border-[#00293d] rounded-full font-fractul font-medium text-[14px] text-[#00293d] hover:bg-gray-50 transition-colors" className="inline-flex items-center justify-center w-[160px] h-[46px] border border-[#00293d] rounded-full font-fractul font-medium text-[14px] text-[#00293d] hover:bg-gray-50 transition-colors"
> >
Find Your Agent {hero.ctaButtonText}
</Link> </Link>
</div> </div>
</section> </section>
@@ -90,64 +150,59 @@ export default function AboutPage() {
{/* Hero Image with Overlay Text */} {/* Hero Image with Overlay Text */}
<section className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 pb-8"> <section className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 pb-8">
<div className="relative rounded-[20px] overflow-hidden"> <div className="relative rounded-[20px] overflow-hidden">
<Image {/* eslint-disable-next-line @next/next/no-img-element */}
src="/assets/images/user_home_top.png" <img
src={hero.bannerImageUrl || defaultHero.bannerImageUrl}
alt="City buildings" alt="City buildings"
width={1200}
height={500}
className="w-full h-auto object-cover" className="w-full h-auto object-cover"
priority
/> />
{/* Overlay Text */} {/* Overlay Text */}
<div className="absolute bottom-6 left-6 lg:bottom-10 lg:left-10"> <div className="absolute bottom-6 left-6 lg:bottom-10 lg:left-10">
<h2 className="font-fractul font-bold text-[20px] lg:text-[24px] text-[#00293d]"> <h2 className="font-fractul font-bold text-[20px] lg:text-[24px] text-[#00293d]">
Building the future of property. {hero.bannerOverlayText}
</h2> </h2>
</div> </div>
</div> </div>
</section> </section>
{/* Stats Section */} {/* Stats Section */}
{stats.stats.length > 0 && (
<section className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8"> <section className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div className="flex items-center justify-center gap-16 lg:gap-32"> <div className="flex items-center justify-center gap-16 lg:gap-32">
<div className="text-center"> {stats.stats.map((stat, index) => (
<div key={index} className="text-center">
<p className="font-fractul font-bold text-[28px] lg:text-[32px] text-[#e58625]"> <p className="font-fractul font-bold text-[28px] lg:text-[32px] text-[#e58625]">
15k+ {stat.value}
</p> </p>
<p className="font-serif text-[14px] text-[#00293d]"> <p className="font-serif text-[14px] text-[#00293d]">
Verified Agents {stat.label}
</p>
</div>
<div className="text-center">
<p className="font-fractul font-bold text-[28px] lg:text-[32px] text-[#e58625]">
98%
</p>
<p className="font-serif text-[14px] text-[#00293d]">
Customer Satisfaction
</p> </p>
</div> </div>
))}
</div> </div>
</section> </section>
)}
{/* Why Choose Us Section */} {/* Why Choose Us Section */}
{features.features.length > 0 && (
<section className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-16 lg:py-20"> <section className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-16 lg:py-20">
{/* Section Header */} {/* Section Header */}
<div className="text-center mb-12"> <div className="text-center mb-12">
<span className="bg-[#e58625]/10 text-[#e58625] font-fractul font-semibold text-[14px] px-4 py-2 rounded-full"> <span className="bg-[#e58625]/10 text-[#e58625] font-fractul font-semibold text-[14px] px-4 py-2 rounded-full">
Why Choose Us {features.badge}
</span> </span>
</div> </div>
{/* Feature Cards */} {/* Feature Cards */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-6"> <div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{features.map((feature, index) => ( {features.features.map((feature, index) => (
<div <div
key={index} key={index}
className="border border-[#00293d]/10 rounded-[15px] p-6 text-center hover:shadow-lg transition-shadow" className="border border-[#00293d]/10 rounded-[15px] p-6 text-center hover:shadow-lg transition-shadow"
> >
<div className="w-[60px] h-[60px] bg-[#e58625]/10 rounded-full flex items-center justify-center mx-auto mb-4"> <div className="w-[60px] h-[60px] bg-[#e58625]/10 rounded-full flex items-center justify-center mx-auto mb-4">
<Image <Image
src={feature.icon} src={feature.iconPath || '/assets/icons/verified-badge-orange.svg'}
alt="" alt=""
width={28} width={28}
height={28} height={28}
@@ -163,32 +218,30 @@ export default function AboutPage() {
))} ))}
</div> </div>
</section> </section>
)}
{/* Team Section */} {/* Team Section */}
{team.members.length > 0 && (
<section className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 pb-16 lg:pb-20"> <section className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 pb-16 lg:pb-20">
{/* Section Header */} {/* Section Header */}
<div className="text-center mb-12"> <div className="text-center mb-12">
<h2 className="font-fractul font-bold text-[24px] lg:text-[28px] text-[#00293d] mb-3"> <h2 className="font-fractul font-bold text-[24px] lg:text-[28px] text-[#00293d] mb-3">
Meet the minds behind the platform. {team.title}
</h2> </h2>
<p className="font-serif text-[14px] text-[#00293d] max-w-[500px] mx-auto"> <p className="font-serif text-[14px] text-[#00293d] max-w-[500px] mx-auto">
Our team is passionate about making real estate connections easier and more transparent for everyone. {team.subtitle}
</p> </p>
</div> </div>
{/* Team Cards */} {/* Team Cards */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 max-w-4xl mx-auto"> <div className="grid grid-cols-1 md:grid-cols-3 gap-6 max-w-4xl mx-auto">
{teamMembers.map((member, index) => ( {team.members.map((member, index) => (
<div <div key={index} className="text-center">
key={index} <div className="w-[180px] h-[180px] rounded-full overflow-hidden mx-auto mb-4 border-4 border-[#e58625]/20 bg-gray-100">
className="text-center" {/* eslint-disable-next-line @next/next/no-img-element */}
> <img
<div className="w-[180px] h-[180px] rounded-full overflow-hidden mx-auto mb-4 border-4 border-[#e58625]/20"> src={member.imageUrl || '/assets/images/professional-1.jpg'}
<Image
src={member.image}
alt={member.name} alt={member.name}
width={180}
height={180}
className="w-full h-full object-cover" className="w-full h-full object-cover"
/> />
</div> </div>
@@ -202,6 +255,7 @@ export default function AboutPage() {
))} ))}
</div> </div>
</section> </section>
)}
{/* CTA Section */} {/* CTA Section */}
<section className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 pb-16"> <section className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 pb-16">
@@ -209,16 +263,16 @@ export default function AboutPage() {
<div className="flex flex-col lg:flex-row items-center justify-between gap-6"> <div className="flex flex-col lg:flex-row items-center justify-between gap-6">
<div className="lg:max-w-[400px] text-center lg:text-left"> <div className="lg:max-w-[400px] text-center lg:text-left">
<h2 className="font-fractul font-bold text-[24px] lg:text-[28px] text-[#00293d] mb-3"> <h2 className="font-fractul font-bold text-[24px] lg:text-[28px] text-[#00293d] mb-3">
Ready to find an agent? {cta.title}
</h2> </h2>
<p className="font-serif text-[13px] text-[#00293d] mb-5"> <p className="font-serif text-[13px] text-[#00293d] mb-5">
Discover trusted agents and start your property journey with confidence. {cta.description}
</p> </p>
<Link <Link
href="/agents" href="/agents"
className="inline-flex items-center gap-2 bg-[#e58625] text-white px-5 py-2.5 rounded-full font-fractul font-medium text-[13px] hover:bg-[#d47720] transition-colors" className="inline-flex items-center gap-2 bg-[#e58625] text-white px-5 py-2.5 rounded-full font-fractul font-medium text-[13px] hover:bg-[#d47720] transition-colors"
> >
Start Your Search {cta.buttonText}
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="14" height="14" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5 12H19M19 12L12 5M19 12L12 19" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/> <path d="M5 12H19M19 12L12 5M19 12L12 19" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
</svg> </svg>

View File

@@ -56,6 +56,53 @@ export interface TestimonialsContent {
testimonials: TestimonialItem[]; testimonials: TestimonialItem[];
} }
export interface AboutHeroContent {
badge: string;
headline: string;
description: string;
ctaButtonText: string;
bannerImageUrl: string;
bannerOverlayText: string;
}
export interface AboutStatItem {
value: string;
label: string;
}
export interface AboutStatsContent {
stats: AboutStatItem[];
}
export interface AboutFeatureItem {
iconPath: string;
title: string;
description: string;
}
export interface AboutFeaturesContent {
badge: string;
features: AboutFeatureItem[];
}
export interface AboutTeamMember {
name: string;
role: string;
imageUrl: string;
}
export interface AboutTeamContent {
title: string;
subtitle: string;
members: AboutTeamMember[];
}
export interface AboutCtaContent {
title: string;
description: string;
buttonText: string;
}
export interface CmsContentRecord { export interface CmsContentRecord {
id: string; id: string;
pageSlug: string; pageSlug: string;