fix: home fetching featured properties and header active styling
This commit is contained in:
parent
8d2a2c83de
commit
d8d7532169
@ -41,6 +41,9 @@
|
||||
.rd-nav-link-custom {
|
||||
@apply text-colorHeader! lg:text-colorHeaderText! lg:hover:text-colorHeaderTextHover!
|
||||
}
|
||||
.rd-nav-link-custom.active {
|
||||
@apply text-colorHeader! lg:text-colorHeaderTextHover! lg:hover:text-colorHeaderTextHover!
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
|
@ -1,27 +1,15 @@
|
||||
"use client";
|
||||
|
||||
import CardProduct from "@/components/CardProduct";
|
||||
import ContactFormSection from "@/components/ContactFormSection";
|
||||
import GoogleReviewBox from "@/components/GoogleReviewBox";
|
||||
import HeroSlider from "@/components/HeroSlider";
|
||||
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 { useRef } from "react";
|
||||
import { Suspense } from "react";
|
||||
|
||||
export default function Home() {
|
||||
const formRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
function scrollToForm() {
|
||||
formRef.current?.scrollIntoView?.({ behavior: "smooth" });
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<HeroSlider onClickBook={scrollToForm} />
|
||||
|
||||
<div className="lg:hidden" ref={formRef}>
|
||||
<ContactFormSection />
|
||||
</div>
|
||||
<HomeTopSection />
|
||||
|
||||
<section className="section section-lg bg-colorSection1">
|
||||
<div className="container">
|
||||
@ -131,29 +119,26 @@ export default function Home() {
|
||||
<div className="layout-4-item">
|
||||
<ul className="list-inline-bordered heading-7">
|
||||
<li>
|
||||
<a href="#">For Rent</a>
|
||||
<a href="/listings-for-rent">For Rent</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">For Sale</a>
|
||||
<a href="/listings-for-sale">For Sale</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div className="my-10!">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5">
|
||||
<CardProduct />
|
||||
<CardProduct />
|
||||
<CardProduct />
|
||||
<CardProduct />
|
||||
<CardProduct />
|
||||
<CardProduct />
|
||||
</div>
|
||||
|
||||
<div className="text-center mt-10!">
|
||||
<a className="button button-primary" href="/">
|
||||
View all properties
|
||||
</a>
|
||||
<Suspense
|
||||
fallback={
|
||||
<>
|
||||
<div className="mt-5">
|
||||
<Loader />
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<ListOfFeaturedProperty />
|
||||
</Suspense>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
5
src/app/(main)/property/[slug]/loading.tsx
Normal file
5
src/app/(main)/property/[slug]/loading.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
import LoaderFixed from "@/components/loaders/LoaderFixed";
|
||||
|
||||
export default function Loading() {
|
||||
return <LoaderFixed />;
|
||||
}
|
@ -179,4 +179,7 @@ export const Properties: CollectionConfig = {
|
||||
group: "Properties",
|
||||
useAsTitle: "name",
|
||||
},
|
||||
access: {
|
||||
read: () => true,
|
||||
},
|
||||
};
|
||||
|
23
src/components/homes/HomeTopSection.tsx
Normal file
23
src/components/homes/HomeTopSection.tsx
Normal file
@ -0,0 +1,23 @@
|
||||
"use client";
|
||||
|
||||
import { useRef } from "react";
|
||||
import HeroSlider from "@/components/HeroSlider";
|
||||
import ContactFormSection from "@/components/ContactFormSection";
|
||||
|
||||
export default function HomeTopSection() {
|
||||
const formRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
function scrollToForm() {
|
||||
formRef.current?.scrollIntoView?.({ behavior: "smooth" });
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<HeroSlider onClickBook={scrollToForm} />
|
||||
|
||||
<div className="lg:hidden" ref={formRef}>
|
||||
<ContactFormSection />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
@ -1,4 +1,16 @@
|
||||
export default function Header() {
|
||||
import { headers } from "next/headers";
|
||||
|
||||
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" : "";
|
||||
};
|
||||
|
||||
return (
|
||||
<header className="section page-header">
|
||||
<div className="rd-navbar-wrap">
|
||||
@ -98,8 +110,8 @@ export default function Header() {
|
||||
</div>
|
||||
<div className="rd-navbar-nav-wrap">
|
||||
<ul className="rd-navbar-nav">
|
||||
<li className="rd-nav-item active">
|
||||
<a className="rd-nav-link" href="/">
|
||||
<li className="rd-nav-item">
|
||||
<a className={`rd-nav-link rd-nav-link-custom ${headerActive("")}`} href="/">
|
||||
HOME
|
||||
</a>
|
||||
</li>
|
||||
@ -113,17 +125,23 @@ export default function Header() {
|
||||
</a>
|
||||
</li>
|
||||
<li className="rd-nav-item">
|
||||
<a className="rd-nav-link rd-nav-link-custom" href="/listings-for-sale">
|
||||
<a
|
||||
className={`rd-nav-link rd-nav-link-custom ${headerActive("listings-for-sale")}`}
|
||||
href="/listings-for-sale"
|
||||
>
|
||||
LISTINGS FOR SALE
|
||||
</a>
|
||||
</li>
|
||||
<li className="rd-nav-item">
|
||||
<a className="rd-nav-link rd-nav-link-custom" href="/listings-for-rent">
|
||||
<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" href="/blog">
|
||||
<a className={`rd-nav-link rd-nav-link-custom ${headerActive("blog")}`} href="/blog">
|
||||
BLOGS
|
||||
</a>
|
||||
</li>
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { CardPropertyData } from "@/schema/property";
|
||||
import { formatCurrency } from "@/utils/general";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
|
||||
type CardPropertyProps = {
|
||||
data: CardPropertyData;
|
||||
@ -37,7 +36,7 @@ export default function CardProperty({ data }: CardPropertyProps) {
|
||||
</div>
|
||||
</div>
|
||||
<h4 className="product-classic-title">
|
||||
<Link href={href}>{data.title}</Link>
|
||||
<a href={href}>{data.title}</a>
|
||||
</h4>
|
||||
<div className="product-classic-divider"></div>
|
||||
<ul className="product-classic-list">
|
||||
|
23
src/components/properties/ListOfFeaturedProperty.tsx
Normal file
23
src/components/properties/ListOfFeaturedProperty.tsx
Normal file
@ -0,0 +1,23 @@
|
||||
import { fetchPropertyLatest } from "@/services/payload/property";
|
||||
import CardProperty from "./CardProperty";
|
||||
|
||||
export default async function ListOfFeaturedProperty() {
|
||||
const latestProperties = await fetchPropertyLatest();
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5">
|
||||
{latestProperties.formattedData.map((pr, idx) => (
|
||||
<CardProperty key={idx} data={pr} />
|
||||
))}
|
||||
</div>
|
||||
{latestProperties.formattedData.length > 0 && (
|
||||
<div className="text-center mt-10!">
|
||||
<a className="button button-primary" href="/listings-for-sale">
|
||||
View all properties
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
24
src/services/hooks/property.ts
Normal file
24
src/services/hooks/property.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { CardPropertyData } from "@/schema/property";
|
||||
import { useState } from "react";
|
||||
import { fetchLatestPropertyREST } from "../rest/property";
|
||||
|
||||
export function useLatestPropertyQuery() {
|
||||
const [data, setData] = useState<CardPropertyData[]>([]);
|
||||
const [isFetching, setFetching] = useState(false);
|
||||
|
||||
async function _fetch() {
|
||||
setFetching(true);
|
||||
const res = await fetchLatestPropertyREST();
|
||||
setFetching(false);
|
||||
|
||||
if (Array.isArray(res?.formattedData)) {
|
||||
setData(res.formattedData);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
_fetch,
|
||||
data,
|
||||
isFetching,
|
||||
};
|
||||
}
|
@ -179,3 +179,33 @@ export async function fetchPropertyDetail({ slug }: FetchPropertyDetailParams) {
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export async function fetchPropertyLatest() {
|
||||
const payload = await getPayload({ config: payloadConfig });
|
||||
const limitPerPage = 6;
|
||||
const dataQuery = await payload.find({
|
||||
collection: "properties",
|
||||
limit: limitPerPage,
|
||||
});
|
||||
|
||||
const formattedData: CardPropertyData[] = dataQuery.docs.map((item) => {
|
||||
return {
|
||||
slug: item.slug ?? "",
|
||||
title: item.name,
|
||||
price: item.base_price,
|
||||
area: item.aboutGroup.area,
|
||||
propertyType: item.property_type,
|
||||
bathrooms_count: item.aboutGroup.bathrooms_count,
|
||||
bedrooms_count: item.aboutGroup.bedrooms_count,
|
||||
images: item.images.map((img) =>
|
||||
typeof img !== "number" ? { url: img?.url ?? "", alt: img.alt } : { url: "", alt: "" }
|
||||
),
|
||||
posted_at: formatDate(item.createdAt),
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
...dataQuery,
|
||||
formattedData,
|
||||
};
|
||||
}
|
||||
|
50
src/services/rest/property.ts
Normal file
50
src/services/rest/property.ts
Normal file
@ -0,0 +1,50 @@
|
||||
import { Property } from "@/payload-types";
|
||||
import { CardPropertyData } from "@/schema/property";
|
||||
import { formatDate } from "@/utils/datetime";
|
||||
import { PaginatedDocs, Where } from "payload";
|
||||
import { stringify } from "qs-esm";
|
||||
|
||||
export async function fetchLatestPropertyREST() {
|
||||
const limitPerPage = 6;
|
||||
|
||||
const queryCondition: Where = {
|
||||
_status: { equals: "published" },
|
||||
};
|
||||
|
||||
const queryParams = stringify(
|
||||
{
|
||||
page: 1,
|
||||
pagination: true,
|
||||
limit: limitPerPage,
|
||||
where: queryCondition,
|
||||
},
|
||||
{ addQueryPrefix: true }
|
||||
);
|
||||
|
||||
const req = await fetch(`/api/properties${queryParams}`);
|
||||
if (req.ok) {
|
||||
const resData = (await req.json()) as PaginatedDocs<Property>;
|
||||
const formattedData: CardPropertyData[] = resData.docs.map((item) => {
|
||||
return {
|
||||
slug: item.slug ?? "",
|
||||
title: item.name,
|
||||
price: item.base_price,
|
||||
area: item.aboutGroup.area,
|
||||
propertyType: item.property_type,
|
||||
bathrooms_count: item.aboutGroup.bathrooms_count,
|
||||
bedrooms_count: item.aboutGroup.bedrooms_count,
|
||||
images: item.images.map((img) =>
|
||||
typeof img !== "number" ? { url: img?.url ?? "", alt: img.alt } : { url: "", alt: "" }
|
||||
),
|
||||
posted_at: formatDate(item.createdAt),
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
...resData,
|
||||
formattedData,
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user