From b235378815da8d2ce76b245e8663690d288dfb35 Mon Sep 17 00:00:00 2001 From: pradeepkumar Date: Sun, 22 Feb 2026 21:52:54 +0530 Subject: [PATCH] feat: Integrate CMS to dynamically populate home page sections with dedicated content types and a new service. --- src/app/(user)/user/dashboard/page.tsx | 25 +++++- src/components/home/FeaturesSection.tsx | 61 +++++++------- src/components/home/HeroSection.tsx | 18 +++-- src/components/home/TestimonialsSection.tsx | 90 +++++++++++---------- src/components/home/TopProfessionals.tsx | 24 ++++-- src/services/api.ts | 1 + src/services/cms.service.ts | 24 ++++++ src/services/index.ts | 3 + src/types/cms.ts | 67 +++++++++++++++ 9 files changed, 227 insertions(+), 86 deletions(-) create mode 100644 src/services/cms.service.ts create mode 100644 src/types/cms.ts diff --git a/src/app/(user)/user/dashboard/page.tsx b/src/app/(user)/user/dashboard/page.tsx index 4338db8..e3c47ee 100644 --- a/src/app/(user)/user/dashboard/page.tsx +++ b/src/app/(user)/user/dashboard/page.tsx @@ -1,17 +1,34 @@ 'use client'; +import { useState, useEffect } from 'react'; import { HeroSection } from '@/components/home/HeroSection'; import { FeaturesSection } from '@/components/home/FeaturesSection'; import { TopProfessionals } from '@/components/home/TopProfessionals'; import { TestimonialsSection } from '@/components/home/TestimonialsSection'; +import { cmsService } from '@/services/cms.service'; +import type { HeroContent, FeaturesContent, TopProfessionalsContent, TestimonialsContent, CmsContentRecord } from '@/types/cms'; export default function UserDashboard() { + const [cmsData, setCmsData] = useState>({}); + + useEffect(() => { + const fetchCms = async () => { + const sections = await cmsService.getPageContent('landing'); + const data: Record = {}; + sections.forEach((s: CmsContentRecord) => { + data[s.sectionKey] = s.content; + }); + setCmsData(data); + }; + fetchCms(); + }, []); + return (
- - - - + + + +
); } diff --git a/src/components/home/FeaturesSection.tsx b/src/components/home/FeaturesSection.tsx index 617569a..686f2e8 100644 --- a/src/components/home/FeaturesSection.tsx +++ b/src/components/home/FeaturesSection.tsx @@ -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 (
@@ -140,23 +147,23 @@ export function FeaturesSection() { {/* Section Header */}

- Find Trusted Real Estate Professionals On Demand + {data.title}

- Quickly connect with the right agents exactly when you need them. + {data.subtitle}

{/* Features Grid - 2x2 */}
- {features.map((feature, index) => ( + {data.features.map((feature, index) => (
{feature.title}([]); const [searchParams, setSearchParams] = useState({ @@ -106,10 +114,10 @@ export function HeroSection() { {/* Headline */}

- “Discover verified, top-rated real estate professionals to guide your buying, selling, or investing journey.” + {data.headline}

- Discover verified, top-rated real estate professionals + {data.description}

@@ -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}
{/* Helper Text */}

- Connect with trusted local agents to explore homes, compare options, and make smarter decisions. + {data.helperText}

diff --git a/src/components/home/TestimonialsSection.tsx b/src/components/home/TestimonialsSection.tsx index a44d2ec..db200f6 100644 --- a/src/components/home/TestimonialsSection.tsx +++ b/src/components/home/TestimonialsSection.tsx @@ -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 Header */}

- Our Clients' Trust Is Our Foundation + {data.title}

- We help buyers, sellers, and investors close successful real estate deals with confidence in every transaction. + {data.subtitle}

- Clients rate our real estate services 4.9 out of 5 on average, based on recent client reviews. + {data.ratingInfo}

{/* Stats Section */}
- {/* Cities Stat */} -
- Cities -
- - 25+ Cities & - -
- - neighborhoods served - + {data.stats.map((stat, index) => ( +
+ +
+ + {stat.boldText} + +
+ + {stat.normalText} + +
-
- - {/* Properties Stat */} -
- Properties -
- - 1,000+ Properties - -
- - bought and sold for our clients. - -
-
+ ))}
{/* Testimonials Grid */}
- {testimonials.map((testimonial) => ( + {data.testimonials.map((testimonial, index) => ( ('agents'); - const displayData = activeTab === 'agents' ? agentsData : lendersData; + const displayData = activeTab === 'agents' ? data.agents : data.lenders; return (
@@ -187,7 +197,7 @@ export function TopProfessionals() { {/* Section Header */}

- “Meet top real estate professionals” + {data.title}

@@ -238,9 +248,9 @@ export function TopProfessionals() { {/* Cards Grid - 3 cards + CTA sidebar */}
{/* Professional Cards */} - {displayData.map((professional) => ( + {displayData.map((professional, index) => ( - Discover 5,000+ Top Real Estate Agents in Our Network. + {data.ctaText}

{/* Browse Experts Button */}
diff --git a/src/services/api.ts b/src/services/api.ts index 26240ad..72e4d1c 100644 --- a/src/services/api.ts +++ b/src/services/api.ts @@ -94,6 +94,7 @@ const publicEndpoints = [ '/auth/2fa/verify', '/auth/2fa/verify-backup', '/agent-types', + '/cms', ]; // Check if URL is a public endpoint diff --git a/src/services/cms.service.ts b/src/services/cms.service.ts new file mode 100644 index 0000000..fab9837 --- /dev/null +++ b/src/services/cms.service.ts @@ -0,0 +1,24 @@ +import api, { ApiResponse } from './api'; +import { CmsContentRecord } from '@/types/cms'; + +class CmsService { + async getPageContent(pageSlug: string): Promise { + try { + const response = await api.get>(`/cms/page/${pageSlug}`); + return response.data.data; + } catch { + return []; + } + } + + async getSectionContent(pageSlug: string, sectionKey: string): Promise { + try { + const response = await api.get>(`/cms/page/${pageSlug}/${sectionKey}`); + return response.data.data.content as T; + } catch { + return null; + } + } +} + +export const cmsService = new CmsService(); diff --git a/src/services/index.ts b/src/services/index.ts index 21047a8..ab5eda7 100644 --- a/src/services/index.ts +++ b/src/services/index.ts @@ -51,3 +51,6 @@ export type { // Messaging Services export { socketService } from './socket.service'; export { messagesService } from './messages.service'; + +// CMS Service +export { cmsService } from './cms.service'; diff --git a/src/types/cms.ts b/src/types/cms.ts new file mode 100644 index 0000000..7ad0941 --- /dev/null +++ b/src/types/cms.ts @@ -0,0 +1,67 @@ +export interface HeroContent { + headline: string; + description: string; + ctaButtonText: string; + helperText: string; +} + +export interface FeatureItem { + iconPath: string; + title: string; + description: string; +} + +export interface FeaturesContent { + title: string; + subtitle: string; + features: FeatureItem[]; +} + +export interface ProfessionalItem { + name: string; + subtitle: string; + location: string; + experience: string; + expertise: string[]; + imageUrl: string; +} + +export interface TopProfessionalsContent { + title: string; + ctaText: string; + ctaButtonText: string; + agents: ProfessionalItem[]; + lenders: ProfessionalItem[]; +} + +export interface TestimonialItem { + rating: number; + title: string; + content: string; + author: string; + role: string; +} + +export interface StatItem { + iconPath: string; + boldText: string; + normalText: string; +} + +export interface TestimonialsContent { + title: string; + subtitle: string; + ratingInfo: string; + stats: StatItem[]; + testimonials: TestimonialItem[]; +} + +export interface CmsContentRecord { + id: string; + pageSlug: string; + sectionKey: string; + content: unknown; + isPublished: boolean; + createdAt: string; + updatedAt: string; +}