Merge pull request 'dev' (#12) from dev into main

Reviewed-on: #12
This commit is contained in:
RizqiSyahrendra 2025-04-28 20:55:26 +00:00
commit 4d4033fc7d
34 changed files with 909 additions and 621 deletions

View File

@ -18,6 +18,7 @@
"@payloadcms/plugin-form-builder": "^3.35.1", "@payloadcms/plugin-form-builder": "^3.35.1",
"@payloadcms/richtext-lexical": "^3.35.1", "@payloadcms/richtext-lexical": "^3.35.1",
"@payloadcms/storage-s3": "^3.35.1", "@payloadcms/storage-s3": "^3.35.1",
"clsx": "^2.1.1",
"country-state-city": "^3.2.1", "country-state-city": "^3.2.1",
"dayjs": "^1.11.13", "dayjs": "^1.11.13",
"graphql": "^16.8.1", "graphql": "^16.8.1",
@ -29,7 +30,8 @@
"react-hook-form": "^7.56.1", "react-hook-form": "^7.56.1",
"react-select": "^5.10.1", "react-select": "^5.10.1",
"react-toastify": "^11.0.5", "react-toastify": "^11.0.5",
"swiper": "^11.2.6" "swiper": "^11.2.6",
"tailwind-merge": "^3.2.0"
}, },
"devDependencies": { "devDependencies": {
"@eslint/eslintrc": "^3", "@eslint/eslintrc": "^3",

View File

@ -3744,13 +3744,13 @@ html .page .divider-secondary::after {
} }
.button-primary, .button-primary:focus { .button-primary, .button-primary:focus {
color: #ffffff; color: var(--color-colorBtnPrimaryText);
background-color: #bc986b; background-color: var(--color-colorBtnPrimary);
border-color: #bc986b; border-color: var(--color-colorBtnPrimary);
} }
.button-primary:hover, .button-primary:active { .button-primary:hover, .button-primary:active {
color: #151515; color: var(--color-colorBtnPrimaryText);
opacity: 0.8; opacity: 0.8;
} }
@ -3759,15 +3759,14 @@ html .page .divider-secondary::after {
} }
.button-secondary, .button-secondary:focus { .button-secondary, .button-secondary:focus {
color: #151515; color: var(--color-colorBtnSecondaryText);
background-color: #fdde52; background-color: var(--color-colorBtnSecondary);
border-color: #fdde52; border-color: var(--color-colorBtnSecondary);
} }
.button-secondary:hover, .button-secondary:active { .button-secondary:hover, .button-secondary:active {
color: #ffffff; color: var(--color-colorBtnSecondaryText);
background-color: #bc986b; opacity: 0.8;
border-color: #bc986b;
} }
.button-secondary.button-ujarak::before { .button-secondary.button-ujarak::before {
@ -11354,7 +11353,7 @@ html:not(.lt-ie10) .not-animated {
.ui-to-top:hover { .ui-to-top:hover {
color: #151515; color: #151515;
background: #fdde52; opacity: 0.8;
text-decoration: none; text-decoration: none;
} }
@ -11699,7 +11698,7 @@ html.tablet .ui-to-top {
} }
.rd-navbar-static .rd-menu { .rd-navbar-static .rd-menu {
z-index: 15; z-index: 30;
position: absolute; position: absolute;
display: block; display: block;
visibility: hidden; visibility: hidden;

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 MiB

BIN
public/images/home1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 769 KiB

BIN
public/images/home2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
public/images/home3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 MiB

BIN
public/images/home4.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

BIN
public/images/home5.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

BIN
public/images/home6.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

BIN
public/images/home7.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

BIN
public/images/logo1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -0,0 +1,5 @@
import LoaderFixed from "@/components/loaders/LoaderFixed";
export default function Loading() {
return <LoaderFixed />;
}

View File

@ -0,0 +1,111 @@
import DetailPageBlog from "@/components/blogs/DetailPageBlog";
import DetailPage from "@/components/pages/DetailPage";
import { fetchBlogDetail } from "@/services/payload/blog";
import { fetchPageBySlug } from "@/services/payload/page";
import { getDefaultMetadata } from "@/utils/metadata";
import { Metadata } from "next";
import { headers } from "next/headers";
import { notFound } from "next/navigation";
export async function generateMetadata(props: { params: Promise<{ slug: string }> }): Promise<Metadata> {
const metadata = await getDefaultMetadata();
const params = await props.params;
let title = `Page Not Found - ${metadata.openGraph?.siteName}`;
let description = title;
let publishedAt = "";
let updatedAt = "";
let imgUrl = "";
let createdByName = "";
let canonicalUrl = "";
const blog = await fetchBlogDetail(params.slug);
if (!!blog) {
// check for blog data
title = `${!!blog.data?.meta?.title ? blog.data?.meta?.title : blog.data.title} - ${metadata.openGraph?.siteName}`;
description = `${!!blog.data?.meta?.description ? blog.data?.meta?.description : blog.data.title}`;
imgUrl = blog.img.url;
publishedAt = blog.data.createdAt;
updatedAt = blog.data.updatedAt;
if (!!blog.data?.meta?.canonical_url) {
canonicalUrl = blog.data.meta.canonical_url;
}
if (!!blog?.data?.createdBy && typeof blog.data.createdBy !== "number") {
createdByName = blog.data.createdBy?.name ?? "";
}
} else {
// check for page data when blog is not found
const page = await fetchPageBySlug({ slug: params.slug });
if (!!page) {
title = `${!!page?.data?.meta?.title ? page?.data?.meta?.title : page.data.title} - ${metadata.openGraph?.siteName}`;
description = `${!!page?.data?.meta?.description ? page?.data?.meta?.description : page.data.title}`;
imgUrl = page.heroImg?.url;
publishedAt = page.createdAt;
updatedAt = page.updatedAt;
if (!!page.data?.meta?.canonical_url) {
canonicalUrl = page.data.meta.canonical_url;
}
if (!!page?.data?.createdBy && typeof page?.data?.createdBy !== "number") {
createdByName = page?.data?.createdBy?.name ?? "";
}
}
}
metadata.title = title;
metadata.description = description;
if (!!metadata.openGraph) {
// @ts-ignore
metadata.openGraph.type = "article";
metadata.openGraph.title = title;
metadata.openGraph.description = description;
metadata.openGraph.images = !!imgUrl ? [imgUrl] : undefined;
}
if (!!metadata.alternates && !!canonicalUrl) {
metadata.alternates.canonical = canonicalUrl;
}
metadata.twitter = {
card: "summary_large_image",
title: title,
description: description,
images: !!imgUrl ? [imgUrl] : undefined,
};
metadata.other = {
"article:published_time": publishedAt,
"article:modified_time": updatedAt,
"twitter:label1": "Written by",
"twitter:data1": !!createdByName ? createdByName : "Admin",
"twitter:label2": "Est. reading time",
"twitter:data2": "3 minutes",
};
return metadata;
}
export default async function SinglePage(props: { params: Promise<{ slug: string }> }) {
const params = await props.params;
const headersList = await headers();
const fullUrl = headersList.get("x-full-url");
const shareUrl = {
facebook: `https://www.facebook.com/sharer/sharer.php?u=${fullUrl}`,
linkedin: `https://www.linkedin.com/sharing/share-offsite/?url=${fullUrl}`,
twitter: `https://twitter.com/intent/tweet?url=${fullUrl}`,
};
const blog = await fetchBlogDetail(params.slug);
if (!!blog) {
return (
<>
<DetailPageBlog data={blog} shareUrl={shareUrl} />
</>
);
}
const page = await fetchPageBySlug({ slug: params.slug });
if (!page) return notFound();
return (
<>
<DetailPage data={page} shareUrl={shareUrl} />
</>
);
}

View File

@ -1,19 +1,16 @@
import ListOfRecentBlog from "@/components/blogs/ListOfRecentBlog"; import DetailPageBlog from "@/components/blogs/DetailPageBlog";
import HeroImage from "@/components/HeroImage";
import { fetchBlogDetail } from "@/services/payload/blog"; import { fetchBlogDetail } from "@/services/payload/blog";
import { getDefaultMetadata } from "@/utils/metadata"; import { getDefaultMetadata } from "@/utils/metadata";
import { RichText } from "@payloadcms/richtext-lexical/react";
import { Metadata } from "next"; import { Metadata } from "next";
import { headers } from "next/headers"; import { headers } from "next/headers";
import Image from "next/image";
import { notFound } from "next/navigation"; import { notFound } from "next/navigation";
export async function generateMetadata(props: { params: Promise<{ slug: string }> }): Promise<Metadata> { export async function generateMetadata(props: { params: Promise<{ slug: string }> }): Promise<Metadata> {
const metadata = await getDefaultMetadata(); const metadata = await getDefaultMetadata();
const params = await props.params; const params = await props.params;
let title = "Page"; let title = `Page Not Found - ${metadata.openGraph?.siteName}`;
let description = "Page"; let description = title;
let publishedAt = ""; let publishedAt = "";
let updatedAt = ""; let updatedAt = "";
let imgUrl = ""; let imgUrl = "";
@ -81,135 +78,7 @@ export default async function BlogDetail(props: { params: Promise<{ slug: string
return ( return (
<> <>
<HeroImage title={blog.data.title} /> <DetailPageBlog data={blog} shareUrl={shareUrl} />
<section className="section section-md bg-colorSection1">
<div className="container">
<div className="row justify-content-lg-center">
<div className="col-lg-8">
<article className="blog-post-solo">
<div className="blog-post-solo-part">
<div className="w-full h-56 md:h-80 bg-colorImgPlaceholder/50 relative">
<Image className="object-cover" src={blog.img.url} alt={blog.img.alt} fill />
</div>
<RichText className="mt-4" data={blog.data.content} />
</div>
<div className="blog-post-solo-footer">
<div className="blog-post-solo-footer-left">
<ul className="blog-post-solo-footer-list">
<li>
<span className="icon mdi mdi-clock"></span>
<a href="#">{blog.createdAt}</a>
</li>
</ul>
</div>
<div className="blog-post-solo-footer-right">
<ul className="blog-post-solo-footer-list-1">
<li>
<span>Share this post</span>
</li>
<li>
<a
target="_blank"
className="icon icon-circle icon-rounded icon-5 fa-facebook"
href={shareUrl.facebook}
></a>
</li>
<li>
<a
target="_blank"
className="icon icon-circle icon-rounded icon-6 fa-twitter"
href={shareUrl.twitter}
></a>
</li>
<li>
<a
target="_blank"
className="icon icon-circle icon-rounded icon-4 fa-linkedin"
href={shareUrl.linkedin}
></a>
</li>
</ul>
</div>
</div>
<ListOfRecentBlog currentBlogId={blog?.data?.id} />
</article>
</div>
{/* Sidebar */}
<div className="col-lg-4">
<div className="pdl-xl-40">
<div className="row row-60">
<div className="col-md-6 col-lg-12">
<form action="/blog" className="form-lg rd-search rd-search-classic">
<div className="form-wrap">
<input
className="form-input"
id="rd-search-form-input"
type="text"
name="s"
autoComplete="off"
placeholder="Search blog..."
/>
</div>
<button className="rd-search-submit" type="submit"></button>
</form>
</div>
<div className="col-md-6 col-lg-12">
<div className="block-info-2">
<div className="block-info-2-title">
<h3>Latest Listings</h3>
</div>
<a className="post-minimal-1" href="#">
<div className="post-minimal-1-left">
<Image src="/images/post-agent-01-212x208.jpg" alt="" width="212" height="208" />
</div>
<div className="post-minimal-1-body">
<div className="post-minimal-1-title">
<span>401 Biscayne Blvd</span>
</div>
<div className="post-minimal-1-text">
<span>$5000\mo</span>
</div>
</div>
</a>
<a className="post-minimal-1" href="#">
<div className="post-minimal-1-left">
<Image src="/images/post-agent-02-212x208.jpg" alt="" width="212" height="208" />
</div>
<div className="post-minimal-1-body">
<div className="post-minimal-1-title">
<span>35 Pond St, New York</span>
</div>
<div className="post-minimal-1-text">
<span>$5550\mo</span>
</div>
</div>
</a>
<a className="post-minimal-1" href="#">
<div className="post-minimal-1-left">
<Image src="/images/post-agent-03-212x208.jpg" alt="" width="212" height="208" />
</div>
<div className="post-minimal-1-body">
<div className="post-minimal-1-title">
<span>182 3rd St, Seattle</span>
</div>
<div className="post-minimal-1-text">
<span>$2520\mo</span>
</div>
</div>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</> </>
); );
} }

View File

@ -33,8 +33,15 @@
--color-colorContactForm: var(--color-colorExt50); --color-colorContactForm: var(--color-colorExt50);
--color-colorText1: var(--color-colorExt10); --color-colorText1: var(--color-colorExt10);
--color-colorText2: var(--color-colorExt20); --color-colorText2: var(--color-colorExt20);
--color-colorText3: var(--color-colorExt30);
--color-colorLoaderBackground: var(--color-colorExt20); --color-colorLoaderBackground: var(--color-colorExt20);
--color-colorPriceTag: var(--color-colorExt30); --color-colorPriceTag: var(--color-colorExt30);
--color-colorBtnPrimary: var(--color-colorext40);
--color-colorBtnPrimaryText: var(--color-colorExt20);
--color-colorBtnSecondary: var(--color-colorExt50);
--color-colorBtnSecondaryText: var(--color-colorExt20);
--color-colorLinkText1: var(--color-colorExt30);
--color-colorLinkText2: var(--color-colorext40);
} }
@layer components { @layer components {

View File

@ -1,10 +1,5 @@
import GoogleReviewBox from "@/components/GoogleReviewBox"; import GoogleReviewBox from "@/components/GoogleReviewBox";
import HomeTopSection from "@/components/homes/HomeTopSection"; import HomeTopSection from "@/components/homes/HomeTopSection";
import Loader from "@/components/loaders/Loader";
import ListOfFeaturedProperty from "@/components/properties/ListOfFeaturedProperty";
import Image from "next/image";
import Link from "next/link";
import { Suspense } from "react";
export default function Home() { export default function Home() {
return ( return (
@ -13,95 +8,57 @@ export default function Home() {
<section className="section section-lg bg-colorSection1"> <section className="section section-lg bg-colorSection1">
<div className="container"> <div className="container">
<h2 className="heading-decoration-1">
<span className="heading-inner">Why Dynamic Realty?</span>
</h2>
<div className="row row-30"> <div className="row row-30">
<div className="col-md-6 col-lg-3"> <div className="col-md-12 col-lg-4">
<Link href="/"> <div>
<article className="box-modern"> <article className="box-modern">
<span className="icon box-modern-icon fl-bigmug-line-search74"></span> <div className="flex justify-center">
<span className="icon box-modern-icon fa-home"></span>
</div>
<div className="box-modern-main"> <div className="box-modern-main">
<h4 className="box-modern-title">Buy</h4> <h4 className="box-modern-title">Tailored Management, Trusted Care</h4>
<p> <p className="text-colorText3">
Discover a wide selection of properties available for purchase in prime locations at competitive Committed to providing a personalized structure to each client, with on-site management & a robust
prices. property management
</p> </p>
</div> </div>
</article> </article>
</Link>
</div>
<div className="col-md-6 col-lg-3">
<Link href="/">
<article className="box-modern">
<span className="icon box-modern-icon fl-bigmug-line-label25"></span>
<div className="box-modern-main">
<h4 className="box-modern-title">Sell</h4>
<p>
List your property with ease and reach potential buyers quickly with our expert marketing support.
</p>
</div>
</article>
</Link>
</div>
<div className="col-md-6 col-lg-3">
<Link href="/">
<article className="box-modern">
<span className="icon box-modern-icon fl-bigmug-line-timer35"></span>
<div className="box-modern-main">
<h4 className="box-modern-title">Rent</h4>
<p>Find the perfect rental home or apartment with flexible lease terms and no hidden fees.</p>
</div>
</article>
</Link>
</div>
<div className="col-md-6 col-lg-3">
<Link href="/">
<article className="box-modern">
<span className="icon box-modern-icon fl-bigmug-line-big104"></span>
<div className="box-modern-main">
<h4 className="box-modern-title">Need Help?</h4>
<p>
Have questions? Our support team is here to guide you through every step of your real estate
journey.
</p>
</div>
</article>
</Link>
</div>
</div>
</div>
</section>
<section className="section relative!">
<Image
alt="Experience the Difference in Every Key Turn"
src={"/images/bg-header.webp"}
quality={100}
fill
sizes="100vw"
style={{
objectFit: "cover",
}}
/>
<div className="box-1-cell height-fill context-dark bg-colorHeader/80!">
<div className="cell-inner box-1-outer">
<div className="box-1">
<div className="text-center">
<h2>Experience the Difference in Every Key Turn</h2>
</div> </div>
<div className="mt-4 text-lg"> </div>
<p> <div className="col-md-12 col-lg-4">
Dynamic Realty is a full-service veteran-owned real estate company based in Centerville, GA, <div>
established in 2004. Our services include buying & selling, rentals, & property management. We are <article className="box-modern">
committed to providing a personalized structure to each client, with on-site management & a robust <div className="flex justify-center">
property management division. We strive to provide a service that meets the needs of all our customers <span className="icon box-modern-icon fa-calendar"></span>
& are proud to have been helping people with their real estate needs for the past 20 years. </div>
</p> <div className="box-modern-main">
<p> <h4 className="box-modern-title">20 Years of Real Estate Excellence</h4>
At Dynamic Realty, we offer a comprehensive range of realty services to meet the needs of our <p className="text-colorText3">
customers in Central Georgia. Our services include buying & selling, renting, & property management. We strive to provide a service that meets the needs of all our customers & are proud to have been
We understand the real estate sector inside out & are committed to providing a professional service to helping people with their real estate needs for the past 20 years
our clients. We strive to make the process of reaching your real estate goals as easy & stress-free as </p>
possible. Get in touch with us today to find out more. </div>
</p> </article>
</div>
</div>
<div className="col-md-12 col-lg-4">
<div>
<article className="box-modern">
<div className="flex justify-center">
<span className="icon box-modern-icon fa-map"></span>
</div>
<div className="box-modern-main">
<h4 className="box-modern-title">Expert Guidance, Stress-Free Journeys</h4>
<p className="text-colorText3">
We understand the real estate sector inside out & are committed to providing a professional
service to our clients. We strive to make the process of reaching your real estate goals as easy &
stress-free as possible
</p>
</div>
</article>
</div> </div>
</div> </div>
</div> </div>
@ -109,101 +66,6 @@ export default function Home() {
</section> </section>
<GoogleReviewBox /> <GoogleReviewBox />
<section className="section section-lg bg-colorSection2">
<div className="container">
<div className="layout-4">
<h2 className="heading-decoration-1">
<span className="heading-inner">Featured Properties</span>
</h2>
<div className="layout-4-item">
<ul className="list-inline-bordered heading-7">
<li>
<a href="/listings-for-rent">For Rent</a>
</li>
<li>
<a href="/listings-for-sale">For Sale</a>
</li>
</ul>
</div>
</div>
<div className="my-10!">
<Suspense
fallback={
<>
<div className="mt-5">
<Loader />
</div>
</>
}
>
<ListOfFeaturedProperty />
</Suspense>
</div>
</div>
</section>
<section className="section parallax-container" data-parallax-img="/images/bg-02-1920x916.jpg">
<div className="parallax-content section-lg context-dark text-center">
<div className="container">
<div className="row row-30">
<div className="col-6 col-md-3">
<article className="box-counter">
<div className="box-counter-main">
<span>860</span>
</div>
<p className="box-counter-title">Properties on Map</p>
</article>
</div>
<div className="col-6 col-md-3">
<article className="box-counter">
<div className="box-counter-main">
<div className="counter">29</div>
</div>
<p className="box-counter-title">Professional Agents</p>
</article>
</div>
<div className="col-6 col-md-3">
<article className="box-counter">
<div className="box-counter-main">
<div className="counter">10</div>
<span>k</span>
</div>
<p className="box-counter-title">Happy Clients</p>
</article>
</div>
<div className="col-6 col-md-3">
<article className="box-counter">
<div className="box-counter-main">
<div className="counter">15</div>
</div>
<p className="box-counter-title">New Apartments Daily</p>
</article>
</div>
</div>
</div>
</div>
</section>
<section className="section section-lg bg-colorSection1">
<div className="container">
<h2 className="heading-decoration-1">
<span className="heading-inner">Find Us</span>
</h2>
<div className="row row-30">
<div className="col-12">
<iframe
src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d3360.4823498818855!2d-83.68565822483802!3d32.61997607372962!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x88f3e6ce99781991%3A0xabfd803ad30f6d12!2s100%20N%20Houston%20Lake%20Blvd%2C%20Centerville%2C%20GA%2031028%2C%20USA!5e0!3m2!1sen!2sid!4v1744883077476!5m2!1sen!2sid"
width={"100%"}
height={450}
style={{ border: 0 }}
loading="lazy"
referrerPolicy="no-referrer-when-downgrade"
/>
</div>
</div>
</div>
</section>
</> </>
); );
} }

View File

@ -7,7 +7,7 @@ type HeroImageProps = {
export default function HeroImage({ title = "", imgSrc = "/images/breadcrumbs-bg-05-1922x441.jpg" }: HeroImageProps) { export default function HeroImage({ title = "", imgSrc = "/images/breadcrumbs-bg-05-1922x441.jpg" }: HeroImageProps) {
return ( return (
<section className="breadcrumbs-custom bg-image context-dark"> <section className="breadcrumbs-custom context-dark min-h-[300px]!">
<Image <Image
alt="Blog" alt="Blog"
src={imgSrc} src={imgSrc}
@ -18,7 +18,7 @@ export default function HeroImage({ title = "", imgSrc = "/images/breadcrumbs-bg
objectFit: "cover", objectFit: "cover",
}} }}
/> />
<div className="bg-colorHeroOverlay/50 w-full h-full absolute top-0 left-0" /> <div className="bg-colorHeroOverlay/85 w-full h-full absolute top-0 left-0" />
<div className="container relative"> <div className="container relative">
<h2 className="breadcrumbs-custom-title">{title}</h2> <h2 className="breadcrumbs-custom-title">{title}</h2>
</div> </div>

View File

@ -4,11 +4,6 @@ import Image from "next/image";
import { useState } from "react"; import { useState } from "react";
import { Autoplay } from "swiper/modules"; import { Autoplay } from "swiper/modules";
import { Swiper, SwiperSlide } from "swiper/react"; import { Swiper, SwiperSlide } from "swiper/react";
import ContactFormBox from "./ContactFormBox";
type HeroSliderProps = {
onClickBook?: () => void;
};
type HeroSlideItem = { type HeroSlideItem = {
title: string; title: string;
@ -16,28 +11,30 @@ type HeroSlideItem = {
img: string; img: string;
}; };
export default function HeroSlider({ onClickBook }: HeroSliderProps) { export default function HeroSlider() {
const [slideItems] = useState<HeroSlideItem[]>([ const [slideItems] = useState<HeroSlideItem[]>([
{ {
title: "Real Estate, Redefined", title: "Full-Service Veteran-Owned Real Estate Company",
description: "Where Southern Charm Meets Boutique Real Estate", description:
img: "/images/bg-header2.jpg", "Dynamic Realty is a full-service veteran-owned real estate company based in Centerville, GA, established in 2004. Our services include buying & selling, rentals, & property management. We strive to provide a service that meets the needs of all our customers & are proud to have been helping people with their real estate needs for the past 20 years.",
img: "/images/bg-header4.jpg",
}, },
{ {
title: "Your Vision, Our Expertise", title: "Comprehensive Range of Realty Services",
description: "Discover a Smarter Way to Buy & Sell", description:
img: "/images/bg-header3.jpg", "Our services include buying & selling, renting, & property management. We understand the real estate sector inside out & are committed to providing a professional service to our clients. We strive to make the process of reaching your real estate goals as easy & stress-free as possible.",
img: "/images/bg-header5.jpg",
}, },
]); ]);
return ( return (
<section className="section flex! flex-row! justify-end! relative! min-h-96! lg:min-h-auto!"> <section className="section flex! flex-row! justify-end! relative! min-h-[900px]! lg:min-h-[600px]!">
<Swiper <Swiper
className="absolute w-full h-full" className="absolute w-full h-full"
modules={[Autoplay]} modules={[Autoplay]}
spaceBetween={50} spaceBetween={50}
slidesPerView={1} slidesPerView={1}
autoplay={{ delay: 4000 }} autoplay={{ delay: 10000 }}
loop loop
> >
{slideItems.map((slide, idx) => ( {slideItems.map((slide, idx) => (
@ -52,21 +49,29 @@ export default function HeroSlider({ onClickBook }: HeroSliderProps) {
objectFit: "cover", objectFit: "cover",
}} }}
/> />
<section className="section section-lg text-colorText2! bg-colorHeroOverlay/50 w-full h-full z-20"> <section className="section section-lg text-colorText2! bg-colorHeroOverlay/85 w-full h-full z-20">
<div className="container"> <div className="container">
<div className="row"> <div className="row">
<div className="col-12 col-lg-4 flex justify-center">
<div className="w-[200px] h-[200px] relative mb-4 lg:hidden">
<Image alt="Logo" src={"/images/logo1.png"} quality={100} fill />
</div>
<div className="w-[300px] h-[300px] relative mb-4 hidden lg:block">
<Image alt="Logo" src={"/images/logo1.png"} quality={100} fill />
</div>
</div>
<div className="col-12 col-lg-8 text-wrap space-y-5 md:space-y-8 lg:space-y-12"> <div className="col-12 col-lg-8 text-wrap space-y-5 md:space-y-8 lg:space-y-12">
<div className="text-4xl lg:text-8xl! font-playfairdisplay text-center! lg:text-left!"> <div className="text-4xl lg:text-6xl! font-playfairdisplay text-center! lg:text-left!">
{slide.title} {slide.title}
</div> </div>
<div className="text-lg lg:text-5xl! font-montserrat text-center! lg:text-left!"> <div className="text-lg lg:text-xl! font-montserrat text-center! lg:text-left!">
{slide.description} {slide.description}
</div> </div>
<div className="context-dark flex flex-row justify-center lg:hidden"> {/* <div className="context-dark flex flex-row justify-center lg:hidden">
<button onClick={onClickBook} className="button button-xs button-primary-outline"> <button onClick={onClickBook} className="button button-xs button-primary-outline">
BOOK APPOINTMENT BOOK APPOINTMENT
</button> </button>
</div> </div> */}
<div className="context-dark flex flex-row justify-center lg:hidden"> <div className="context-dark flex flex-row justify-center lg:hidden">
<a href="tel:(478) 225 9061" className="button button-xs button-primary-outline"> <a href="tel:(478) 225 9061" className="button button-xs button-primary-outline">
Call Us Call Us
@ -80,7 +85,7 @@ export default function HeroSlider({ onClickBook }: HeroSliderProps) {
))} ))}
</Swiper> </Swiper>
<ContactFormBox /> {/* <ContactFormBox /> */}
</section> </section>
); );
} }

View File

@ -4,23 +4,9 @@ import { RichText } from "@payloadcms/richtext-lexical/react";
export function ContentBlock(props: any) { export function ContentBlock(props: any) {
return ( return (
<div className="container relative"> <div>
<div className="row"> {/* @ts-ignore */}
{/* Content */} <RichText data={props.content} />
<div className="col-md-10 offset-md-1 col-lg-10 offset-lg-1">
{/* Post */}
<div className="blog-item mb-10">
<div className="blog-item-body">
<div>
{/* @ts-ignore */}
<RichText data={props.content} />
</div>
</div>
</div>
{/* End Post */}
</div>
{/* End Content */}
</div>
</div> </div>
); );
} }

View File

@ -9,7 +9,7 @@ type CardBlogProps = {
}; };
export default function CardBlog({ data, colorPreset = 1, isDescriptionVisible = true }: CardBlogProps) { export default function CardBlog({ data, colorPreset = 1, isDescriptionVisible = true }: CardBlogProps) {
const linkDetail = `/blog/${data.slug}`; const linkDetail = `/${data.slug}`;
if (colorPreset === 2) { if (colorPreset === 2) {
return ( return (

View File

@ -0,0 +1,151 @@
import { fetchBlogDetail } from "@/services/payload/blog";
import Image from "next/image";
import ListOfRecentBlog from "./ListOfRecentBlog";
import HeroImage from "../HeroImage";
import { RichText } from "@payloadcms/richtext-lexical/react";
type shareUrlDestination = "facebook" | "linkedin" | "twitter";
type DetailPageBlogProps = {
data: Awaited<ReturnType<typeof fetchBlogDetail>>;
shareUrl: Record<shareUrlDestination, string>;
};
export default function DetailPageBlog({ data, shareUrl }: DetailPageBlogProps) {
const blog = data;
if (!blog) return <></>;
return (
<>
<HeroImage title={blog.data.title} />
<section className="section section-md bg-colorSection1">
<div className="container">
<div className="row justify-content-lg-center">
<div className="col-lg-8">
<article className="blog-post-solo">
<div className="blog-post-solo-part">
<div className="w-full h-56 md:h-80 bg-colorImgPlaceholder/50 relative">
<Image className="object-cover" src={blog.img.url} alt={blog.img.alt} fill />
</div>
<RichText className="mt-4" data={blog.data.content} />
</div>
<div className="blog-post-solo-footer">
<div className="blog-post-solo-footer-left">
<ul className="blog-post-solo-footer-list">
<li>
<span className="icon mdi mdi-clock"></span>
<a href="#">{blog.createdAt}</a>
</li>
</ul>
</div>
<div className="blog-post-solo-footer-right">
<ul className="blog-post-solo-footer-list-1">
<li>
<span>Share this post</span>
</li>
<li>
<a
target="_blank"
className="icon icon-circle icon-rounded icon-5 fa-facebook"
href={shareUrl.facebook}
></a>
</li>
<li>
<a
target="_blank"
className="icon icon-circle icon-rounded icon-6 fa-twitter"
href={shareUrl.twitter}
></a>
</li>
<li>
<a
target="_blank"
className="icon icon-circle icon-rounded icon-4 fa-linkedin"
href={shareUrl.linkedin}
></a>
</li>
</ul>
</div>
</div>
<ListOfRecentBlog currentBlogId={blog?.data?.id} />
</article>
</div>
{/* Sidebar */}
<div className="col-lg-4">
<div className="pdl-xl-40">
<div className="row row-60">
<div className="col-md-6 col-lg-12">
<form action="/blog" className="form-lg rd-search rd-search-classic">
<div className="form-wrap">
<input
className="form-input"
id="rd-search-form-input"
type="text"
name="s"
autoComplete="off"
placeholder="Search blog..."
/>
</div>
<button className="rd-search-submit" type="submit"></button>
</form>
</div>
<div className="col-md-6 col-lg-12">
<div className="block-info-2">
<div className="block-info-2-title">
<h3>Latest Listings</h3>
</div>
<a className="post-minimal-1" href="#">
<div className="post-minimal-1-left">
<Image src="/images/post-agent-01-212x208.jpg" alt="" width="212" height="208" />
</div>
<div className="post-minimal-1-body">
<div className="post-minimal-1-title">
<span>401 Biscayne Blvd</span>
</div>
<div className="post-minimal-1-text">
<span>$5000\mo</span>
</div>
</div>
</a>
<a className="post-minimal-1" href="#">
<div className="post-minimal-1-left">
<Image src="/images/post-agent-02-212x208.jpg" alt="" width="212" height="208" />
</div>
<div className="post-minimal-1-body">
<div className="post-minimal-1-title">
<span>35 Pond St, New York</span>
</div>
<div className="post-minimal-1-text">
<span>$5550\mo</span>
</div>
</div>
</a>
<a className="post-minimal-1" href="#">
<div className="post-minimal-1-left">
<Image src="/images/post-agent-03-212x208.jpg" alt="" width="212" height="208" />
</div>
<div className="post-minimal-1-body">
<div className="post-minimal-1-title">
<span>182 3rd St, Seattle</span>
</div>
<div className="post-minimal-1-text">
<span>$2520\mo</span>
</div>
</div>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</>
);
}

View File

@ -1,23 +1,21 @@
"use client"; "use client";
import HeroSlider from "@/components/HeroSlider"; import HeroSlider from "@/components/HeroSlider";
import { useRef } from "react";
import ContactFormSection from "../ContactFormSection";
export default function HomeTopSection() { export default function HomeTopSection() {
const formRef = useRef<HTMLDivElement | null>(null); // const formRef = useRef<HTMLDivElement | null>(null);
function scrollToForm() { // function scrollToForm() {
formRef.current?.scrollIntoView?.({ behavior: "smooth" }); // formRef.current?.scrollIntoView?.({ behavior: "smooth" });
} // }
return ( return (
<> <>
<HeroSlider onClickBook={scrollToForm} /> <HeroSlider />
<div className="lg:hidden" ref={formRef}> {/* <div className="lg:hidden" ref={formRef}>
<ContactFormSection /> <ContactFormSection />
</div> </div> */}
</> </>
); );
} }

View File

@ -1,6 +1,16 @@
import Image from "next/image"; import ListOfFeaturedProperiesFooter from "@/components/properties/ListOfFeaturedPropertiesFooter";
import { headers } from "next/headers";
export default async function Footer() {
const headersList = await headers();
const fullUrl = headersList.get("x-full-url");
const shareUrl = {
facebook: `https://www.facebook.com/sharer/sharer.php?u=${fullUrl}`,
linkedin: `https://www.linkedin.com/sharing/share-offsite/?url=${fullUrl}`,
twitter: `https://twitter.com/intent/tweet?url=${fullUrl}`,
};
export default function Footer() {
return ( return (
<> <>
<section className="section section-md bg-colorFooter context-dark"> <section className="section section-md bg-colorFooter context-dark">
@ -10,36 +20,7 @@ export default function Footer() {
<h3 className="heading-square font-weight-sbold" data-item=".heading-square-item"> <h3 className="heading-square font-weight-sbold" data-item=".heading-square-item">
<span className="heading-square-item"></span>Latest Properties <span className="heading-square-item"></span>Latest Properties
</h3> </h3>
<a className="post-minimal" href="single-property.html"> <ListOfFeaturedProperiesFooter />
<div className="post-minimal-image">
<Image src="/images/featured-properties-17-480x287.jpg" alt="" width="161" height="136" />
</div>
<div className="post-minimal-body">
<div className="post-minimal-title">
<span className="text-colorFooterText! hover:text-colorFooterTextHover!">
Retail Store Southwest 186th Street
</span>
</div>
<div className="post-minimal-text">
<span>From $120/month</span>
</div>
</div>
</a>
<a className="post-minimal" href="single-property.html">
<div className="post-minimal-image">
<Image src="/images/featured-properties-17-480x287.jpg" alt="" width="161" height="136" />
</div>
<div className="post-minimal-body">
<div className="post-minimal-title">
<span className="text-colorFooterText! hover:text-colorFooterTextHover!">
Apartment Building with Subunits
</span>
</div>
<div className="post-minimal-text">
<span>From $120/month</span>
</div>
</div>
</a>
</div> </div>
<div className="col-md-6 col-lg-4 col-xl-3 col-bordered"> <div className="col-md-6 col-lg-4 col-xl-3 col-bordered">
<h3 className="heading-square font-weight-sbold" data-item=".heading-square-item"> <h3 className="heading-square font-weight-sbold" data-item=".heading-square-item">
@ -103,16 +84,13 @@ export default function Footer() {
</form> */} </form> */}
<ul className="list-inline-1 space-x-5"> <ul className="list-inline-1 space-x-5">
<li> <li>
<a className="icon fa-facebook text-4xl!" href="#"></a> <a target="_blank" className="icon fa-facebook text-4xl!" href={shareUrl.facebook}></a>
</li> </li>
<li> <li>
<a className="icon fa-twitter text-4xl!" href="#"></a> <a target="_blank" className="icon fa-twitter text-4xl!" href={shareUrl.twitter}></a>
</li> </li>
<li> <li>
<a className="icon fa-google-plus text-4xl!" href="#"></a> <a target="_blank" className="icon fa-linkedin text-4xl!" href={shareUrl.linkedin}></a>
</li>
<li>
<a className="icon fa-pinterest-p text-4xl!" href="#"></a>
</li> </li>
</ul> </ul>
</div> </div>
@ -127,15 +105,15 @@ export default function Footer() {
<span>Dynamic Realty</span> <span>&copy;&nbsp;</span> <span>Dynamic Realty</span> <span>&copy;&nbsp;</span>
<span className="copyright-year"></span> <span className="copyright-year"></span>
<span>&nbsp;</span> <span>&nbsp;</span>
<a href="privacy-policy.html">Privacy Policy</a> <a href="/privacy-policy">Privacy Policy</a>
</p> </p>
</div> </div>
<div className="col-sm-6 text-sm-right"> <div className="col-sm-6 text-sm-right">
<div className="right-1"> {/* <div className="right-1">
<a href="#"> <a href="#">
<span className="icon mdi mdi-plus"></span>Book Appointment <span className="icon mdi mdi-plus"></span>Book Appointment
</a> </a>
</div> </div> */}
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,16 +1,7 @@
import { headers } from "next/headers"; import { HeaderDropdown, HeaderDropdownGroup, HeaderDropdownMenu } from "./HeaderDropdown";
import HeaderFeaturedHomes from "./HeaderFeaturedHomes";
export default async function Header() {
const headerList = await headers();
const fullUrl = headerList.get("x-full-url");
const headerActive = (pathName: string) => {
if (!fullUrl) return "";
const splittedUrl = fullUrl.split("/");
return splittedUrl[3] === pathName ? "active" : "";
};
export default function Header() {
return ( return (
<header className="section page-header"> <header className="section page-header">
<div className="rd-navbar-wrap"> <div className="rd-navbar-wrap">
@ -103,213 +94,248 @@ export default async function Header() {
srcSet="images/logo2.png 2x" srcSet="images/logo2.png 2x"
/> />
</a> </a>
{/* <h3 className="text-colorHeaderText! font-montserrat! font-semibold! hidden lg:inline">
Dynamic Realty
</h3> */}
</div> </div>
</div> </div>
<div className="rd-navbar-nav-wrap"> <div className="rd-navbar-nav-wrap">
<ul className="rd-navbar-nav"> <ul className="rd-navbar-nav">
<li className="rd-nav-item"> <li className="rd-nav-item">
<a className={`rd-nav-link rd-nav-link-custom ${headerActive("")}`} href="/"> <a className="rd-nav-link rd-nav-link-custom" href="#">
HOME Buying / Selling
</a> </a>
<HeaderDropdown>
<HeaderDropdownGroup>
<div className="pr-2">
<form>
<div className="form-wrap">
<input
type="text"
id="search"
className="form-input border-colorHeader!"
placeholder="Search"
/>
</div>
</form>
</div>
<div className="mt-2">
<HeaderFeaturedHomes />
</div>
</HeaderDropdownGroup>
<HeaderDropdownGroup title="Why Dynamic Realty?">
<HeaderDropdownMenu
list={[
{
title:
"Committed to providing a personalized structure to each client, with on-site management & a robust property management",
},
{
title:
"We strive to provide a service that meets the needs of all our customers & are proud to have been helping people with their real estate needs for the past 20 years",
},
{
title:
"We understand the real estate sector inside out & are committed to providing a professional service to our clients. We strive to make the process of reaching your real estate goals as easy & stress-free as possible",
},
]}
/>
</HeaderDropdownGroup>
<HeaderDropdownGroup title="Future Owners">
<HeaderDropdownMenu
list={[
{
title: "Moving In",
child: [
{
title: "What To Be Done Prior",
href: "#",
},
{
title: "Next Steps (keys, utilities, etc)",
href: "#",
},
{
title: "Common Questions",
href: "#",
},
],
},
{
title: "Welcome home",
child: [
{
title: "Payment",
href: "#",
},
{
title: "Utilities",
href: "#",
},
{
title: "Maintenance",
href: "#",
},
],
},
{
title: "Moving Out",
child: [
{
title: "What To Be Done Prior",
href: "#",
},
{
title: "Next Steps (keys, utilities, etc)",
href: "#",
},
{
title: "Common Questions",
href: "#",
},
],
},
]}
/>
</HeaderDropdownGroup>
<HeaderDropdownGroup className="flex! pr-4!">
<div className="flex flex-col self-end!">
<a className="button button-secondary" href="/">
FAQ
</a>
<a className="button button-primary mt-2!" href="/">
LOOKING FOR SELL
</a>
</div>
</HeaderDropdownGroup>
</HeaderDropdown>
</li> </li>
<li className="rd-nav-item"> <li className="rd-nav-item">
<a <a className="rd-nav-link rd-nav-link-custom" href="#">
className="rd-nav-link rd-nav-link-custom" Leasing
href="https://dynamicrealtyinc.managebuilding.com/Resident/public/rentals"
target="_blank"
>
RENTAL PORTAL
</a> </a>
<HeaderDropdown>
<HeaderDropdownGroup>
<div className="pr-2">
<form>
<div className="form-wrap">
<input
type="text"
id="search"
className="form-input border-colorHeader!"
placeholder="Search"
/>
</div>
</form>
</div>
<div className="mt-2">
<HeaderFeaturedHomes />
</div>
</HeaderDropdownGroup>
<HeaderDropdownGroup title="Future Residents">
<HeaderDropdownMenu
list={[
{
title: "Our Rentals",
href: "#",
},
{
title: "Application Process",
child: [
{
title: "Before You Apply",
href: "#",
},
{
title: "Rental Criteria",
href: "#",
},
{
title: "Before You Apply",
href: "#",
},
],
},
]}
/>
</HeaderDropdownGroup>
<HeaderDropdownGroup title="Current Residents">
<HeaderDropdownMenu
list={[
{
title: "Moving In",
child: [
{
title: "What To Be Done Prior",
href: "#",
},
{
title: "Next Steps (keys, utilities, etc)",
href: "#",
},
{
title: "Common Questions",
href: "#",
},
],
},
{
title: "Welcome home",
child: [
{
title: "Payment",
href: "#",
},
{
title: "Utilities",
href: "#",
},
{
title: "Maintenance",
href: "#",
},
],
},
{
title: "Moving Out",
child: [
{
title: "What To Be Done Prior",
href: "#",
},
{
title: "Next Steps (keys, utilities, etc)",
href: "#",
},
{
title: "Common Questions",
href: "#",
},
],
},
]}
/>
</HeaderDropdownGroup>
<HeaderDropdownGroup className="flex! pr-4!">
<div className="flex flex-col self-end!">
<a className="button button-secondary" href="#">
FAQ
</a>
<a className="button button-primary mt-2!" href="#">
APPLY NOW
</a>
</div>
</HeaderDropdownGroup>
</HeaderDropdown>
</li> </li>
<li className="rd-nav-item"> <li className="rd-nav-item">
<a <a className={`rd-nav-link rd-nav-link-custom`} href="/blog">
className={`rd-nav-link rd-nav-link-custom ${headerActive("listings-for-sale")}`} Resources
href="/listings-for-sale"
>
LISTINGS FOR SALE
</a>
</li>
<li className="rd-nav-item">
<a
className={`rd-nav-link rd-nav-link-custom ${headerActive("listings-for-rent")}`}
href="/listings-for-rent"
>
LISTINGS FOR RENT
</a>
</li>
<li className="rd-nav-item">
<a className={`rd-nav-link rd-nav-link-custom ${headerActive("blog")}`} href="/blog">
BLOGS
</a> </a>
</li> </li>
<li className="rd-nav-item block lg:hidden!"> <li className="rd-nav-item block lg:hidden!">
<a className="rd-nav-link rd-nav-link-custom" href="/login"> <a className="rd-nav-link rd-nav-link-custom" href="/login">
LOGIN Login
</a> </a>
</li> </li>
{/* <li className="rd-nav-item">
<a className="rd-nav-link" href="#">
Properties
</a>
<ul className="rd-menu rd-navbar-dropdown">
<li className="rd-dropdown-item">
<a className="rd-dropdown-link" href="properties-grid.html">
Properties Grid
</a>
</li>
<li className="rd-dropdown-item">
<a className="rd-dropdown-link" href="properties-grid-2.html">
Properties Grid 2
</a>
</li>
<li className="rd-dropdown-item">
<a className="rd-dropdown-link" href="properties-list.html">
Properties List
</a>
</li>
<li className="rd-dropdown-item">
<a className="rd-dropdown-link" href="submit-property.html">
Submit property
</a>
</li>
<li className="rd-dropdown-item">
<a className="rd-dropdown-link" href="single-property.html">
Single Property
</a>
</li>
</ul>
</li>
<li className="rd-nav-item">
<a className="rd-nav-link" href="about-us.html">
About Us
</a>
</li>
<li className="rd-nav-item">
<a className="rd-nav-link" href="blog.html">
Blog
</a>
<ul className="rd-menu rd-navbar-dropdown">
<li className="rd-dropdown-item">
<a className="rd-dropdown-link" href="blog-post.html">
Blog post
</a>
</li>
</ul>
</li>
<li className="rd-nav-item">
<a className="rd-nav-link" href="#">
Pages
</a>
<ul className="rd-menu rd-navbar-megamenu">
<li className="rd-megamenu-item">
<h6 className="rd-megamenu-title">Pages 1</h6>
<ul className="rd-megamenu-list">
<li className="rd-megamenu-list-item">
<a className="rd-megamenu-list-link" href="agents.html">
Agents
</a>
</li>
<li className="rd-megamenu-list-item">
<a className="rd-megamenu-list-link" href="agent-single-page.html">
Agent Single Page
</a>
</li>
<li className="rd-megamenu-list-item">
<a className="rd-megamenu-list-link" href="careers.html">
Careers
</a>
</li>
<li className="rd-megamenu-list-item">
<a className="rd-megamenu-list-link" href="gallery.html">
Gallery
</a>
</li>
</ul>
</li>
<li className="rd-megamenu-item">
<h6 className="rd-megamenu-title">Pages 2</h6>
<ul className="rd-megamenu-list">
<li className="rd-megamenu-list-item">
<a className="rd-megamenu-list-link" href="search-results.html">
Search results
</a>
</li>
<li className="rd-megamenu-list-item">
<a className="rd-megamenu-list-link" href="coming-soon.html">
Coming Soon
</a>
</li>
<li className="rd-megamenu-list-item">
<a className="rd-megamenu-list-link" href="404.html">
404
</a>
</li>
<li className="rd-megamenu-list-item">
<a className="rd-megamenu-list-link" href="privacy-policy.html">
Privacy Policy
</a>
</li>
</ul>
</li>
<li className="rd-megamenu-item">
<h6 className="rd-megamenu-title">Elements</h6>
<ul className="rd-megamenu-list">
<li className="rd-megamenu-list-item">
<a className="rd-megamenu-list-link" href="typography.html">
Typography
</a>
</li>
<li className="rd-megamenu-list-item">
<a className="rd-megamenu-list-link" href="buttons.html">
Buttons
</a>
</li>
<li className="rd-megamenu-list-item">
<a className="rd-megamenu-list-link" href="forms.html">
Forms
</a>
</li>
<li className="rd-megamenu-list-item">
<a className="rd-megamenu-list-link" href="tabs-and-accordions.html">
Tabs and accordions
</a>
</li>
<li className="rd-megamenu-list-item">
<a className="rd-megamenu-list-link" href="progress-bars.html">
Progress bars
</a>
</li>
<li className="rd-megamenu-list-item">
<a className="rd-megamenu-list-link" href="tables.html">
Tables
</a>
</li>
<li className="rd-megamenu-list-item">
<a className="rd-megamenu-list-link" href="grid-system.html">
Grid System
</a>
</li>
</ul>
</li>
</ul>
</li>
<li className="rd-nav-item">
<a className="rd-nav-link" href="contact-us.html">
Contact Us
</a>
</li> */}
</ul> </ul>
<div className="rd-navbar-main-item context-dark">
<a className="button button-sm button-primary-outline" href="#">
BOOK APPOINTMENT
</a>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -0,0 +1,81 @@
import { createSlug } from "@/utils/general";
import { cn } from "@/utils/style";
import { FC, Fragment, PropsWithChildren } from "react";
type HeaderDropdownProps = {
className?: string;
};
export const HeaderDropdown: FC<PropsWithChildren<HeaderDropdownProps>> = ({ children, className }) => {
return <ul className={cn("rd-menu rd-navbar-megamenu", className)}>{children}</ul>;
};
type HeaderDropdownGroupProps = {
title?: string;
className?: string;
};
export const HeaderDropdownGroup: FC<PropsWithChildren<HeaderDropdownGroupProps>> = ({
children,
title,
className,
}) => {
return (
<li className={cn("rd-megamenu-item", className)}>
{!!title && <h6 className="rd-megamenu-title">{title}</h6>}
{children}
</li>
);
};
type HeaderDropdownMenuItem = { title: string; href?: string };
type HeaderDropdownMenuItemWithChild = { title: string; child: HeaderDropdownMenuItem[] };
type HeaderDropdownMenuProps = {
list?: (HeaderDropdownMenuItem | HeaderDropdownMenuItemWithChild)[];
};
export const HeaderDropdownMenu: FC<HeaderDropdownMenuProps> = ({ list = [] }) => {
return (
<ul className="rd-megamenu-list">
{list.map((item, idx) => {
const collapseId = createSlug(`collapse-${item.title}-${idx}`);
const hasChild = "child" in item;
const hrefClass = hasChild || !!item?.href ? "text-colorLinkText1! hover:text-colorLinkText2!" : "";
return (
<Fragment key={idx}>
{hasChild && (
<li className="rd-megamenu-list-item">
<a
className={`rd-megamenu-list-link cursor-pointer ${hrefClass}`}
data-toggle="collapse"
data-target={`#${collapseId}`}
aria-controls={collapseId}
aria-expanded="false"
>
{item.title}
</a>
<div className="pl-3 mt-2 space-y-2 collapse visible!" id={collapseId}>
{item.child.map((itemChild, itemChildIdx) => (
<a key={itemChildIdx} className={`rd-megamenu-list-link ${hrefClass}`} href={itemChild.href}>
- {itemChild.title}
</a>
))}
</div>
</li>
)}
{!hasChild && (
<li className="rd-megamenu-list-item">
<a className={`rd-megamenu-list-link ${hrefClass}`} href={item.href}>
{item.title}
</a>
</li>
)}
</Fragment>
);
})}
</ul>
);
};

View File

@ -0,0 +1,34 @@
"use client";
import Image from "next/image";
export default function HeaderFeaturedHomes() {
const images = [
"/images/home1.jpg",
"/images/home2.jpg",
"/images/home3.jpg",
"/images/home4.jpg",
"/images/home5.jpg",
"/images/home6.jpg",
];
return (
<div className="p-2">
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-1">
{images.map((src, index) => (
<div
key={index}
className="overflow-hidden rounded-lg shadow hover:shadow-lg transition-shadow duration-300 relative h-16"
>
<Image
src={src}
alt={`Gallery image ${index + 1}`}
className="object-cover transform transition-transform duration-300 hover:scale-105 cursor-pointer"
fill
/>
</div>
))}
</div>
</div>
);
}

View File

@ -0,0 +1,74 @@
import { fetchPageBySlug } from "@/services/payload/page";
import HeroImage from "../HeroImage";
import { RenderBlocks } from "../blocks/RenderBlocks";
type shareUrlDestination = "facebook" | "linkedin" | "twitter";
type DetailPageProps = {
data: Awaited<ReturnType<typeof fetchPageBySlug>>;
shareUrl: Record<shareUrlDestination, string>;
};
export default function DetailPage({ data, shareUrl }: DetailPageProps) {
const page = data;
if (!page) return <></>;
return (
<>
<HeroImage imgSrc={page.heroImg?.url} title={page.data.title} />
<section className="section section-md bg-colorSection1">
<div className="container">
<div className="row justify-content-lg-center">
<div className="col-12">
<article className="blog-post-solo">
<div className="blog-post-solo-part">
<RenderBlocks blocks={page.data.layout} />
</div>
<div className="blog-post-solo-footer">
<div className="blog-post-solo-footer-left">
<ul className="blog-post-solo-footer-list">
<li>
<span className="icon mdi mdi-clock"></span>
<a href="#">{page.createdAt}</a>
</li>
</ul>
</div>
<div className="blog-post-solo-footer-right">
<ul className="blog-post-solo-footer-list-1">
<li>
<span>Share this post</span>
</li>
<li>
<a
target="_blank"
className="icon icon-circle icon-rounded icon-5 fa-facebook"
href={shareUrl.facebook}
></a>
</li>
<li>
<a
target="_blank"
className="icon icon-circle icon-rounded icon-6 fa-twitter"
href={shareUrl.twitter}
></a>
</li>
<li>
<a
target="_blank"
className="icon icon-circle icon-rounded icon-4 fa-linkedin"
href={shareUrl.linkedin}
></a>
</li>
</ul>
</div>
</div>
</article>
</div>
</div>
</div>
</section>
</>
);
}

View File

@ -0,0 +1,37 @@
"use client";
import { useLatestPropertyQuery } from "@/services/hooks/property";
import { formatCurrency } from "@/utils/general";
import Image from "next/image";
import { useEffect } from "react";
export default function ListOfFeaturedProperiesFooter() {
const { data, _fetch } = useLatestPropertyQuery();
useEffect(() => {
_fetch();
}, []);
const latestProperties = data ? data.slice(0, 2) : [];
return (
<>
{latestProperties.length > 0 &&
latestProperties.map((property, index) => (
<a key={index} className="post-minimal" href={`single-property/${property.slug}`}>
<div className="post-minimal-image">
<Image src={property.images?.[0]?.url ?? ""} alt={property.title} width={161} height={136} />
</div>
<div className="post-minimal-body">
<div className="post-minimal-title">
<span className="text-colorFooterText! hover:text-colorFooterTextHover!">{property.title}</span>
</div>
<div className="post-minimal-text">
<span>From {formatCurrency(property.price)}/month</span>
</div>
</div>
</a>
))}
</>
);
}

View File

@ -1,3 +1,4 @@
"use client";
import { CardPropertyData } from "@/schema/property"; import { CardPropertyData } from "@/schema/property";
import { useState } from "react"; import { useState } from "react";
import { fetchLatestPropertyREST } from "../rest/property"; import { fetchLatestPropertyREST } from "../rest/property";

View File

@ -0,0 +1,40 @@
import payloadConfig from "@/payload.config";
import { formatDate } from "@/utils/datetime";
import { getPayload } from "payload";
export const fetchPageBySlug = async ({ slug }: { slug: string | undefined }) => {
const payload = await getPayload({ config: payloadConfig });
const result = await payload.find({
collection: "pages",
// draft,
limit: 1,
pagination: false,
// overrideAccess: draft,
where: {
_status: { equals: "published" },
slug: {
equals: slug,
},
},
});
if (!result.docs?.[0]) {
return null;
}
const data = result.docs[0];
const heroImgUrl = typeof data.hero_img !== "number" ? (data?.hero_img?.url ?? "") : "";
const heroImgAlt = typeof data.hero_img !== "number" ? (data?.hero_img?.alt ?? "") : "";
return {
data: data,
createdAt: formatDate(data.createdAt),
updatedAt: formatDate(data.updatedAt),
heroImg: {
url: heroImgUrl,
alt: heroImgAlt,
},
};
};

View File

@ -24,3 +24,10 @@ export function formatCurrency(num: number): string {
//maximumFractionDigits: 0, // Causes 2500.99 to be printed as $2,501 //maximumFractionDigits: 0, // Causes 2500.99 to be printed as $2,501
}).format(num); }).format(num);
} }
export function createSlug(val: string) {
return val
.replace(/ /g, "-")
.replace(/[^\w-/]+/g, "")
.toLowerCase();
}

6
src/utils/style.ts Normal file
View File

@ -0,0 +1,6 @@
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}

View File

@ -5044,6 +5044,7 @@ __metadata:
"@types/node": "npm:^20" "@types/node": "npm:^20"
"@types/react": "npm:^19" "@types/react": "npm:^19"
"@types/react-dom": "npm:^19" "@types/react-dom": "npm:^19"
clsx: "npm:^2.1.1"
country-state-city: "npm:^3.2.1" country-state-city: "npm:^3.2.1"
dayjs: "npm:^1.11.13" dayjs: "npm:^1.11.13"
eslint: "npm:^9" eslint: "npm:^9"
@ -5061,6 +5062,7 @@ __metadata:
react-select: "npm:^5.10.1" react-select: "npm:^5.10.1"
react-toastify: "npm:^11.0.5" react-toastify: "npm:^11.0.5"
swiper: "npm:^11.2.6" swiper: "npm:^11.2.6"
tailwind-merge: "npm:^3.2.0"
tailwindcss: "npm:^4" tailwindcss: "npm:^4"
typescript: "npm:^5" typescript: "npm:^5"
languageName: unknown languageName: unknown
@ -9759,6 +9761,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"tailwind-merge@npm:^3.2.0":
version: 3.2.0
resolution: "tailwind-merge@npm:3.2.0"
checksum: 10c0/294f6c2db0df74405bff126107107426c3126a70a1717d78e8d6811db65546c9bb3d61282bdb8d9fbded23f6bc8ec3e8e61031a4f53265f90b7f3dba558f88f4
languageName: node
linkType: hard
"tailwindcss@npm:4.1.4, tailwindcss@npm:^4": "tailwindcss@npm:4.1.4, tailwindcss@npm:^4":
version: 4.1.4 version: 4.1.4
resolution: "tailwindcss@npm:4.1.4" resolution: "tailwindcss@npm:4.1.4"