feat: property detail FE integration
This commit is contained in:
parent
6fbbb73e05
commit
e483f8f281
@ -1,48 +1,41 @@
|
|||||||
import CardProperty from "@/components/CardProperty";
|
import CardProperty from "@/components/CardProperty";
|
||||||
import HeroImage from "@/components/HeroImage";
|
import HeroImage from "@/components/HeroImage";
|
||||||
import { CardPropertyData } from "@/schema/property";
|
import { fetchPropertyDetail, fetchPropertySuggestion } from "@/services/payload/property";
|
||||||
import { formatCurrency } from "@/utils/general";
|
import { RichText } from "@payloadcms/richtext-lexical/react";
|
||||||
|
import { headers } from "next/headers";
|
||||||
|
import Image from "next/image";
|
||||||
|
import { notFound } from "next/navigation";
|
||||||
|
|
||||||
const similarPropertiesData: CardPropertyData[] = [
|
export default async function ListingsForRentDetail({ params }: { params: Promise<{ slug: string }> }) {
|
||||||
{
|
const slug = (await params).slug;
|
||||||
title: "401 Biscayne Boulevard, Miami",
|
const propertyDetail = await fetchPropertyDetail({ slug });
|
||||||
slug: "401-biscayne-boulevard",
|
if (!propertyDetail) return notFound();
|
||||||
images: [
|
|
||||||
{ url: "/images/featured-properties-01-480x287.jpg", alt: "biscayne boulevard" },
|
const { data, formattedData } = propertyDetail;
|
||||||
{ url: "/images/featured-properties-01-480x287.jpg", alt: "biscayne boulevard" },
|
const isEmbedMapUrlValid = !!data?.embed_map_url && data.embed_map_url.includes("www.google.com/maps/embed");
|
||||||
{ url: "/images/featured-properties-01-480x287.jpg", alt: "biscayne boulevard" },
|
const headersList = await headers();
|
||||||
],
|
const fullUrl = headersList.get("x-full-url");
|
||||||
price: 5000,
|
const shareUrl = {
|
||||||
propertyType: "rent",
|
facebook: `https://www.facebook.com/sharer/sharer.php?u=${fullUrl}`,
|
||||||
posted_at: "",
|
linkedin: `https://www.linkedin.com/sharing/share-offsite/?url=${fullUrl}`,
|
||||||
area: "480",
|
twitter: `https://twitter.com/intent/tweet?url=${fullUrl}`,
|
||||||
bathrooms_count: "2",
|
};
|
||||||
bedrooms_count: "2",
|
|
||||||
},
|
const similarPropertiesData = await fetchPropertySuggestion();
|
||||||
{
|
|
||||||
title: "402 Biscayne Boulevard, Miami",
|
|
||||||
slug: "402-biscayne-boulevard",
|
|
||||||
images: [{ url: "/images/featured-properties-01-480x287.jpg", alt: "biscayne boulevard" }],
|
|
||||||
price: 5000,
|
|
||||||
propertyType: "rent",
|
|
||||||
posted_at: "",
|
|
||||||
area: "480",
|
|
||||||
bathrooms_count: "2",
|
|
||||||
bedrooms_count: "2",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default function ListingsForRentDetail() {
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<HeroImage title="Lorem Ipsum, Dolor" />
|
<HeroImage title={data.name} />
|
||||||
|
|
||||||
<section className="section section-md bg-gray-12">
|
<section className="section section-md bg-gray-12">
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<div className="row row-50">
|
<div className="row row-50">
|
||||||
<div className="col-lg-7 col-xl-8">
|
<div className="col-lg-7 col-xl-8">
|
||||||
<div className="slick-slider-1">
|
<div className="slick-slider-1">
|
||||||
<div className="slick-slider-price">$5000\mo</div>
|
<div className="slick-slider-price bg-colorPriceTag/90!">
|
||||||
|
{formattedData.price}
|
||||||
|
{data.property_type === "rent" && "/mo"}
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
className="slick-slider carousel-parent"
|
className="slick-slider carousel-parent"
|
||||||
id="parent-carousel"
|
id="parent-carousel"
|
||||||
@ -55,24 +48,13 @@ export default function ListingsForRentDetail() {
|
|||||||
data-child="#child-carousel"
|
data-child="#child-carousel"
|
||||||
data-for="#child-carousel"
|
data-for="#child-carousel"
|
||||||
>
|
>
|
||||||
<div className="item">
|
{formattedData.images.map((img, idx) => (
|
||||||
<img src="/images/single-property-1-763x443.jpg" alt="" width="763" height="443" />
|
<div key={idx} className="item">
|
||||||
|
<div className="bg-colorImgPlaceholder/90 w-full! h-[443px]! relative">
|
||||||
|
<Image src={img.url} alt={img.alt} fill />
|
||||||
</div>
|
</div>
|
||||||
<div className="item">
|
|
||||||
<img src="/images/single-property-2-763x443.jpg" alt="" width="763" height="443" />
|
|
||||||
</div>
|
|
||||||
<div className="item">
|
|
||||||
<img src="/images/single-property-3-763x443.jpg" alt="" width="763" height="443" />
|
|
||||||
</div>
|
|
||||||
<div className="item">
|
|
||||||
<img src="/images/single-property-4-763x443.jpg" alt="" width="763" height="443" />
|
|
||||||
</div>
|
|
||||||
<div className="item">
|
|
||||||
<img src="/images/single-property-5-763x443.jpg" alt="" width="763" height="443" />
|
|
||||||
</div>
|
|
||||||
<div className="item">
|
|
||||||
<img src="/images/single-property-6-763x443.jpg" alt="" width="763" height="443" />
|
|
||||||
</div>
|
</div>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className="slick-slider carousel-child"
|
className="slick-slider carousel-child"
|
||||||
@ -89,62 +71,50 @@ export default function ListingsForRentDetail() {
|
|||||||
data-slide-to-scroll="1"
|
data-slide-to-scroll="1"
|
||||||
data-for="#parent-carousel"
|
data-for="#parent-carousel"
|
||||||
>
|
>
|
||||||
<div>
|
{formattedData.images.map((img, idx) => (
|
||||||
<div className="slick-slide-inner bg-[url(/images/single-property-1-763x443.jpg)]"></div>
|
<div key={idx}>
|
||||||
|
<div className="bg-colorImgPlaceholder/90 w-[135px]! h-[89px]! relative">
|
||||||
|
<Image src={img.url} alt={img.alt} fill />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
|
||||||
<div className="slick-slide-inner bg-[url(/images/single-property-1-763x443.jpg)]"></div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div className="slick-slide-inner bg-[url(/images/single-property-1-763x443.jpg)]"></div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div className="slick-slide-inner bg-[url(/images/single-property-1-763x443.jpg)]"></div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div className="slick-slide-inner bg-[url(/images/single-property-1-763x443.jpg)]"></div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div className="slick-slide-inner bg-[url(/images/single-property-1-763x443.jpg)]"></div>
|
|
||||||
</div>
|
</div>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="features-block">
|
<div className="features-block">
|
||||||
<div className="features-block-inner">
|
<div className="features-block-inner">
|
||||||
<div className="features-block-item">
|
<div className="features-block-item">
|
||||||
<ul className="features-block-list">
|
<ul className="features-block-list">
|
||||||
|
{!!data?.aboutGroup?.bathrooms_count && (
|
||||||
<li>
|
<li>
|
||||||
<span className="icon hotel-icon-10"></span>
|
<span className="icon hotel-icon-10"></span>
|
||||||
<span>2 Bathrooms</span>
|
<span>{data.aboutGroup.bathrooms_count} Bathrooms</span>
|
||||||
</li>
|
</li>
|
||||||
|
)}
|
||||||
|
{!!data?.aboutGroup?.bedrooms_count && (
|
||||||
<li>
|
<li>
|
||||||
<span className="icon hotel-icon-05"></span>
|
<span className="icon hotel-icon-05"></span>
|
||||||
<span>2 Bedrooms</span>
|
<span>{data.aboutGroup.bedrooms_count} Bedrooms</span>
|
||||||
</li>
|
</li>
|
||||||
|
)}
|
||||||
|
{!!data?.aboutGroup?.area && (
|
||||||
<li>
|
<li>
|
||||||
<span className="icon mdi mdi-vector-square"></span>
|
<span className="icon mdi mdi-vector-square"></span>
|
||||||
<span>480 Sq Ft</span>
|
<span>{data.aboutGroup.area} Sq Ft</span>
|
||||||
</li>
|
</li>
|
||||||
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div className="features-block-item">
|
<div className="features-block-item">
|
||||||
<a className="link link-1" href="#">
|
{/* <a className="link link-1" href="#">
|
||||||
<span className="icon mdi mdi-heart-outline"></span>Add to Favorites
|
<span className="icon mdi mdi-heart-outline"></span>Add to Favorites
|
||||||
</a>
|
</a> */}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p>
|
|
||||||
Choose this property if you are looking for a modern house near the ocean shore. With 2 bathrooms and 2
|
<div className="mt-4">
|
||||||
bedrooms as well as a single garage, it is a perfect option for a small family.
|
<RichText data={data.aboutGroup.description} />
|
||||||
</p>
|
</div>
|
||||||
<p>
|
|
||||||
This home has been completely renovated within the past year and features amazing views and sunsets of
|
|
||||||
the local lake, solid wood cabinets (and loads of them), granite counters with colored glass backsplash,
|
|
||||||
sliding glass doors across the entire family room allowing beautiful views of the lake etc. Its
|
|
||||||
affordable price serves as a great bonus for a family looking for an opportunity to save money on Miami
|
|
||||||
MyHome.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className="card-group-custom card-group-corporate"
|
className="card-group-custom card-group-corporate"
|
||||||
@ -179,27 +149,19 @@ export default function ListingsForRentDetail() {
|
|||||||
<div className="layout-1">
|
<div className="layout-1">
|
||||||
<dl className="list-terms-inline">
|
<dl className="list-terms-inline">
|
||||||
<dt>Address:</dt>
|
<dt>Address:</dt>
|
||||||
<dd>Biscayne Blvd</dd>
|
<dd>{data?.addressGroup?.address ?? ""}</dd>
|
||||||
</dl>
|
</dl>
|
||||||
<dl className="list-terms-inline">
|
<dl className="list-terms-inline">
|
||||||
<dt>State/County:</dt>
|
<dt>State/County:</dt>
|
||||||
<dd>Florida</dd>
|
<dd>{data?.addressGroup?.state_code ?? ""}</dd>
|
||||||
</dl>
|
</dl>
|
||||||
<dl className="list-terms-inline">
|
<dl className="list-terms-inline">
|
||||||
<dt>City:</dt>
|
<dt>City:</dt>
|
||||||
<dd>Miami</dd>
|
<dd>{data?.addressGroup?.city_code ?? ""}</dd>
|
||||||
</dl>
|
</dl>
|
||||||
<dl className="list-terms-inline">
|
<dl className="list-terms-inline">
|
||||||
<dt>Zip:</dt>
|
<dt>Zip:</dt>
|
||||||
<dd>8322</dd>
|
<dd>{data?.addressGroup?.zip_code ?? ""}</dd>
|
||||||
</dl>
|
|
||||||
<dl className="list-terms-inline">
|
|
||||||
<dt>Country:</dt>
|
|
||||||
<dd>United States</dd>
|
|
||||||
</dl>
|
|
||||||
<dl className="list-terms-inline">
|
|
||||||
<dt>Area:</dt>
|
|
||||||
<dd>Lake Worth</dd>
|
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -237,21 +199,16 @@ export default function ListingsForRentDetail() {
|
|||||||
>
|
>
|
||||||
<div className="card-body">
|
<div className="card-body">
|
||||||
<ul className="list-marked-2 layout-2">
|
<ul className="list-marked-2 layout-2">
|
||||||
<li>2 Stories</li>
|
{Array.isArray(data.features) &&
|
||||||
<li>Basketball Court</li>
|
data.features.length > 0 &&
|
||||||
<li>Lawn</li>
|
data.features.map((ft, idx) => <li key={idx}>{typeof ft !== "number" && ft.name}</li>)}
|
||||||
<li>Gym</li>
|
|
||||||
<li>Fireplace</li>
|
|
||||||
<li>Sprinklers</li>
|
|
||||||
<li>Private Space</li>
|
|
||||||
<li>Balcony</li>
|
|
||||||
<li>Laundry</li>
|
|
||||||
<li>Ocean View</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{data.property_type === "rent" && (
|
||||||
<div
|
<div
|
||||||
className="card-group-custom card-group-corporate"
|
className="card-group-custom card-group-corporate"
|
||||||
id="accordion0"
|
id="accordion0"
|
||||||
@ -285,38 +242,37 @@ export default function ListingsForRentDetail() {
|
|||||||
<div className="layout-1 columns-1!">
|
<div className="layout-1 columns-1!">
|
||||||
<dl className="list-terms-inline w-full flex justify-between">
|
<dl className="list-terms-inline w-full flex justify-between">
|
||||||
<dt>Base Rent</dt>
|
<dt>Base Rent</dt>
|
||||||
<dd>{formatCurrency(2700)}</dd>
|
<dd>{formattedData.price}</dd>
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
<div className="layout-1 columns-1!">
|
|
||||||
|
{formattedData.additionalPrice.map((p, idx) => (
|
||||||
|
<div className="layout-1 columns-1!" key={idx}>
|
||||||
<dl className="list-terms-inline w-full flex justify-between">
|
<dl className="list-terms-inline w-full flex justify-between">
|
||||||
<dt>Smart Home</dt>
|
<dt>{p.name}</dt>
|
||||||
<dd>{formatCurrency(20)}</dd>
|
<dd>{p.price}</dd>
|
||||||
</dl>
|
|
||||||
</div>
|
|
||||||
<div className="layout-1 columns-1!">
|
|
||||||
<dl className="list-terms-inline w-full flex justify-between">
|
|
||||||
<dt>Utility Service</dt>
|
|
||||||
<dd>{formatCurrency(5)}</dd>
|
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
|
))}
|
||||||
<div className="layout-1 columns-1! mt-2">
|
<div className="layout-1 columns-1! mt-2">
|
||||||
<dl className="list-terms-inline w-full flex justify-between">
|
<dl className="list-terms-inline w-full flex justify-between">
|
||||||
<dd className="font-semibold!">Est. total monthly*</dd>
|
<dd className="font-semibold!">Est. total monthly*</dd>
|
||||||
<dd className="font-semibold!">{formatCurrency(2900)}</dd>
|
<dd className="font-semibold!">{formattedData.totalPrice}</dd>
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isEmbedMapUrlValid && (
|
||||||
<div className="block-group-item">
|
<div className="block-group-item">
|
||||||
<h3>Property Map</h3>
|
<h3>Property Map</h3>
|
||||||
<div className="row row-30">
|
<div className="row row-30">
|
||||||
<div className="col-12">
|
<div className="col-12">
|
||||||
<iframe
|
<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"
|
src={data.embed_map_url ?? ""}
|
||||||
width={"100%"}
|
width={"100%"}
|
||||||
height={450}
|
height={450}
|
||||||
style={{ border: 0 }}
|
style={{ border: 0 }}
|
||||||
@ -326,12 +282,14 @@ export default function ListingsForRentDetail() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="blog-post-solo-footer mt-20">
|
<div className="blog-post-solo-footer mt-20">
|
||||||
<div className="blog-post-solo-footer-left">
|
<div className="blog-post-solo-footer-left">
|
||||||
<ul className="blog-post-solo-footer-list">
|
<ul className="blog-post-solo-footer-list">
|
||||||
<li>
|
<li>
|
||||||
<span className="icon mdi mdi-clock"></span>
|
<span className="icon mdi mdi-clock"></span>
|
||||||
<a href="#">February 10, 2021</a>
|
<a href="#">{formattedData.postedAt}</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@ -343,16 +301,13 @@ export default function ListingsForRentDetail() {
|
|||||||
<li>
|
<li>
|
||||||
<ul className="list-inline-1">
|
<ul className="list-inline-1">
|
||||||
<li>
|
<li>
|
||||||
<a className="icon link-default fa-facebook" href="#"></a>
|
<a target="_blank" className="icon link-default fa-facebook" href={shareUrl.facebook}></a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a className="icon link-default fa-twitter" href="#"></a>
|
<a target="_blank" className="icon link-default fa-twitter" href={shareUrl.twitter}></a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a className="icon link-default fa-google-plus" href="#"></a>
|
<a target="_blank" className="icon link-default fa-linkedin" href={shareUrl.linkedin}></a>
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a className="icon link-default fa-pinterest-p" href="#"></a>
|
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
@ -360,14 +315,16 @@ export default function ListingsForRentDetail() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{similarPropertiesData.formattedData.length > 0 && (
|
||||||
<div className="block-group-item">
|
<div className="block-group-item">
|
||||||
<h3>Similar Properties</h3>
|
<h3>Other Properties</h3>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
{similarPropertiesData.map((p, idx) => (
|
{similarPropertiesData.formattedData.map((p, idx) => (
|
||||||
<CardProperty key={idx} data={p} />
|
<CardProperty key={idx} data={p} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="col-lg-5 col-xl-4">
|
<div className="col-lg-5 col-xl-4">
|
||||||
<div className="row row-50">
|
<div className="row row-50">
|
||||||
|
@ -30,6 +30,7 @@ export default async function ListingsForRent(props: {
|
|||||||
const maxArea = sanitizeNumber(searchParams?.max_area);
|
const maxArea = sanitizeNumber(searchParams?.max_area);
|
||||||
|
|
||||||
const propertiesData = await fetchProperty({
|
const propertiesData = await fetchProperty({
|
||||||
|
property_type: "rent",
|
||||||
page,
|
page,
|
||||||
name: searchParams?.name,
|
name: searchParams?.name,
|
||||||
min_price: minPrice,
|
min_price: minPrice,
|
||||||
|
@ -11,7 +11,7 @@ export const Properties: CollectionConfig = {
|
|||||||
},
|
},
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
name: "propertyType",
|
name: "property_type",
|
||||||
label: "Type",
|
label: "Type",
|
||||||
type: "select",
|
type: "select",
|
||||||
options: [
|
options: [
|
||||||
@ -56,20 +56,18 @@ export const Properties: CollectionConfig = {
|
|||||||
{
|
{
|
||||||
name: "area",
|
name: "area",
|
||||||
label: "Area (Sqft)",
|
label: "Area (Sqft)",
|
||||||
type: "text",
|
type: "number",
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "bathrooms_count",
|
name: "bathrooms_count",
|
||||||
label: "Total Bathrooms",
|
label: "Total Bathrooms",
|
||||||
type: "text",
|
type: "number",
|
||||||
required: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "bedrooms_count",
|
name: "bedrooms_count",
|
||||||
label: "Total Bedrooms",
|
label: "Total Bedrooms",
|
||||||
type: "text",
|
type: "number",
|
||||||
required: true,
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -79,8 +77,8 @@ export const Properties: CollectionConfig = {
|
|||||||
type: "group",
|
type: "group",
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
name: "country_code",
|
name: "state_code",
|
||||||
label: "Country",
|
label: "State",
|
||||||
type: "text",
|
type: "text",
|
||||||
// admin: {
|
// admin: {
|
||||||
// components: {
|
// components: {
|
||||||
@ -90,11 +88,6 @@ export const Properties: CollectionConfig = {
|
|||||||
// },
|
// },
|
||||||
// },
|
// },
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "state_code",
|
|
||||||
label: "State",
|
|
||||||
type: "text",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "city_code",
|
name: "city_code",
|
||||||
label: "City",
|
label: "City",
|
||||||
@ -124,7 +117,7 @@ export const Properties: CollectionConfig = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "base_price",
|
name: "base_price",
|
||||||
label: "Base Price",
|
label: "Price",
|
||||||
type: "number",
|
type: "number",
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
@ -147,7 +140,7 @@ export const Properties: CollectionConfig = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "embed_map_url",
|
name: "embed_map_url",
|
||||||
label: "Embed Map URL",
|
label: "Embed Google Map URL",
|
||||||
type: "text",
|
type: "text",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -8,7 +8,7 @@ type CardPropertyProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default function CardProperty({ data }: CardPropertyProps) {
|
export default function CardProperty({ data }: CardPropertyProps) {
|
||||||
const href = data?.propertyType === "sell" ? `/listings-for-rent/${data.slug}` : `/listings-for-sell/${data.slug}`;
|
const href = data?.propertyType === "rent" ? `/listings-for-rent/${data.slug}` : `/listings-for-sell/${data.slug}`;
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<article className="product-classic">
|
<article className="product-classic">
|
||||||
@ -24,7 +24,7 @@ export default function CardProperty({ data }: CardPropertyProps) {
|
|||||||
>
|
>
|
||||||
{Array.isArray(data.images) &&
|
{Array.isArray(data.images) &&
|
||||||
data.images.map((img, idx) => (
|
data.images.map((img, idx) => (
|
||||||
<div key={idx} className="w-full h-52 bg-colorImgPlaceholder">
|
<div key={idx} className="w-full h-52 bg-colorImgPlaceholder/90">
|
||||||
<Image src={img.url} alt={img.alt ?? ""} fill className="object-cover" />
|
<Image src={img.url} alt={img.alt ?? ""} fill className="object-cover" />
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@ -41,17 +41,19 @@ export default function CardProperty({ data }: CardPropertyProps) {
|
|||||||
</h4>
|
</h4>
|
||||||
<div className="product-classic-divider"></div>
|
<div className="product-classic-divider"></div>
|
||||||
<ul className="product-classic-list">
|
<ul className="product-classic-list">
|
||||||
|
{!!data.area && (
|
||||||
<li>
|
<li>
|
||||||
<span className="icon mdi mdi-vector-square"></span>
|
<span className="icon mdi mdi-vector-square"></span>
|
||||||
<span>{data.area} Sq Ft</span>
|
<span>{data.area} Sq Ft</span>
|
||||||
</li>
|
</li>
|
||||||
{data.bathrooms_count && (
|
)}
|
||||||
|
{!!data.bathrooms_count && (
|
||||||
<li>
|
<li>
|
||||||
<span className="icon hotel-icon-10"></span>
|
<span className="icon hotel-icon-10"></span>
|
||||||
<span>{data.bathrooms_count} Bathrooms</span>
|
<span>{data.bathrooms_count} Bathrooms</span>
|
||||||
</li>
|
</li>
|
||||||
)}
|
)}
|
||||||
{data.bedrooms_count && (
|
{!!data.bedrooms_count && (
|
||||||
<li>
|
<li>
|
||||||
<span className="icon hotel-icon-05"></span>
|
<span className="icon hotel-icon-05"></span>
|
||||||
<span>{data.bedrooms_count} Bedrooms</span>
|
<span>{data.bedrooms_count} Bedrooms</span>
|
||||||
|
@ -239,7 +239,7 @@ export interface PropertyFeature {
|
|||||||
*/
|
*/
|
||||||
export interface Property {
|
export interface Property {
|
||||||
id: number;
|
id: number;
|
||||||
propertyType: 'rent' | 'sell';
|
property_type: 'rent' | 'sell';
|
||||||
name: string;
|
name: string;
|
||||||
slug?: string | null;
|
slug?: string | null;
|
||||||
images: (number | Media)[];
|
images: (number | Media)[];
|
||||||
@ -259,12 +259,11 @@ export interface Property {
|
|||||||
};
|
};
|
||||||
[k: string]: unknown;
|
[k: string]: unknown;
|
||||||
};
|
};
|
||||||
area: string;
|
area: number;
|
||||||
bathrooms_count: string;
|
bathrooms_count?: number | null;
|
||||||
bedrooms_count: string;
|
bedrooms_count?: number | null;
|
||||||
};
|
};
|
||||||
addressGroup: {
|
addressGroup: {
|
||||||
country_code?: string | null;
|
|
||||||
state_code?: string | null;
|
state_code?: string | null;
|
||||||
city_code?: string | null;
|
city_code?: string | null;
|
||||||
zip_code: string;
|
zip_code: string;
|
||||||
@ -457,7 +456,7 @@ export interface PropertyFeaturesSelect<T extends boolean = true> {
|
|||||||
* via the `definition` "properties_select".
|
* via the `definition` "properties_select".
|
||||||
*/
|
*/
|
||||||
export interface PropertiesSelect<T extends boolean = true> {
|
export interface PropertiesSelect<T extends boolean = true> {
|
||||||
propertyType?: T;
|
property_type?: T;
|
||||||
name?: T;
|
name?: T;
|
||||||
slug?: T;
|
slug?: T;
|
||||||
images?: T;
|
images?: T;
|
||||||
@ -472,7 +471,6 @@ export interface PropertiesSelect<T extends boolean = true> {
|
|||||||
addressGroup?:
|
addressGroup?:
|
||||||
| T
|
| T
|
||||||
| {
|
| {
|
||||||
country_code?: T;
|
|
||||||
state_code?: T;
|
state_code?: T;
|
||||||
city_code?: T;
|
city_code?: T;
|
||||||
zip_code?: T;
|
zip_code?: T;
|
||||||
|
@ -6,9 +6,9 @@ export type CardPropertyData = {
|
|||||||
/**
|
/**
|
||||||
* in sqft
|
* in sqft
|
||||||
*/
|
*/
|
||||||
area: string;
|
area?: number | null;
|
||||||
bedrooms_count?: string;
|
bedrooms_count?: number | null;
|
||||||
bathrooms_count?: string;
|
bathrooms_count?: number | null;
|
||||||
posted_at: string;
|
posted_at: string;
|
||||||
propertyType: "rent" | "sell";
|
propertyType: "rent" | "sell";
|
||||||
};
|
};
|
||||||
|
@ -6,4 +6,9 @@ export type FetchPropertyParams = {
|
|||||||
min_price?: number;
|
min_price?: number;
|
||||||
max_price?: number;
|
max_price?: number;
|
||||||
location?: string;
|
location?: string;
|
||||||
|
property_type?: "rent" | "sell";
|
||||||
|
};
|
||||||
|
|
||||||
|
export type FetchPropertyDetailParams = {
|
||||||
|
slug: string;
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import payloadConfig from "@/payload.config";
|
import payloadConfig from "@/payload.config";
|
||||||
import { CardPropertyData } from "@/schema/property";
|
import { CardPropertyData } from "@/schema/property";
|
||||||
import { FetchPropertyParams } from "@/schema/services/property";
|
import { FetchPropertyDetailParams, FetchPropertyParams } from "@/schema/services/property";
|
||||||
import { formatDate } from "@/utils/datetime";
|
import { formatDate } from "@/utils/datetime";
|
||||||
|
import { formatCurrency, getRandomNumber } from "@/utils/general";
|
||||||
import { getPayload, Where } from "payload";
|
import { getPayload, Where } from "payload";
|
||||||
|
|
||||||
export async function fetchProperty({
|
export async function fetchProperty({
|
||||||
@ -12,6 +13,7 @@ export async function fetchProperty({
|
|||||||
max_price,
|
max_price,
|
||||||
min_area,
|
min_area,
|
||||||
max_area,
|
max_area,
|
||||||
|
property_type,
|
||||||
}: FetchPropertyParams = {}) {
|
}: FetchPropertyParams = {}) {
|
||||||
const payload = await getPayload({ config: payloadConfig });
|
const payload = await getPayload({ config: payloadConfig });
|
||||||
|
|
||||||
@ -19,6 +21,11 @@ export async function fetchProperty({
|
|||||||
_status: { equals: "published" },
|
_status: { equals: "published" },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!!property_type) {
|
||||||
|
queryCondition["property_type"] = {
|
||||||
|
equals: property_type,
|
||||||
|
};
|
||||||
|
}
|
||||||
if (!!name) {
|
if (!!name) {
|
||||||
queryCondition["name"] = {
|
queryCondition["name"] = {
|
||||||
contains: name,
|
contains: name,
|
||||||
@ -60,11 +67,11 @@ export async function fetchProperty({
|
|||||||
|
|
||||||
const formattedData: CardPropertyData[] = dataQuery.docs.map((item) => {
|
const formattedData: CardPropertyData[] = dataQuery.docs.map((item) => {
|
||||||
return {
|
return {
|
||||||
slug: "",
|
slug: item.slug ?? "",
|
||||||
title: item.name,
|
title: item.name,
|
||||||
price: item.base_price,
|
price: item.base_price,
|
||||||
area: item.aboutGroup.area,
|
area: item.aboutGroup.area,
|
||||||
propertyType: item.propertyType,
|
propertyType: item.property_type,
|
||||||
bathrooms_count: item.aboutGroup.bathrooms_count,
|
bathrooms_count: item.aboutGroup.bathrooms_count,
|
||||||
bedrooms_count: item.aboutGroup.bedrooms_count,
|
bedrooms_count: item.aboutGroup.bedrooms_count,
|
||||||
images: item.images.map((img) =>
|
images: item.images.map((img) =>
|
||||||
@ -79,3 +86,96 @@ export async function fetchProperty({
|
|||||||
formattedData,
|
formattedData,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function fetchPropertySuggestion() {
|
||||||
|
const payload = await getPayload({ config: payloadConfig });
|
||||||
|
const limitPerPage = 2;
|
||||||
|
const countrQuery = await payload.count({
|
||||||
|
collection: "properties",
|
||||||
|
where: { _status: { equals: "published" } },
|
||||||
|
});
|
||||||
|
|
||||||
|
// randomize page
|
||||||
|
let page = 1;
|
||||||
|
const totalDocs = countrQuery.totalDocs;
|
||||||
|
if (totalDocs > limitPerPage) {
|
||||||
|
const totalPage = Math.ceil(totalDocs / limitPerPage);
|
||||||
|
page = getRandomNumber(totalPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
const dataQuery = await payload.find({
|
||||||
|
collection: "properties",
|
||||||
|
page,
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchPropertyDetail({ slug }: FetchPropertyDetailParams) {
|
||||||
|
const payload = await getPayload({ config: payloadConfig });
|
||||||
|
|
||||||
|
const queryCondition: Where = {
|
||||||
|
_status: { equals: "published" },
|
||||||
|
slug: { equals: slug },
|
||||||
|
};
|
||||||
|
|
||||||
|
const dataQuery = await payload.find({
|
||||||
|
collection: "properties",
|
||||||
|
where: queryCondition,
|
||||||
|
limit: 1,
|
||||||
|
pagination: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!dataQuery?.docs?.[0]) return null;
|
||||||
|
|
||||||
|
const data = dataQuery?.docs?.[0];
|
||||||
|
const postedAt = formatDate(data.createdAt);
|
||||||
|
const images = data.images.map((img) =>
|
||||||
|
typeof img !== "number" ? { url: img?.url ?? "", alt: img.alt } : { url: "", alt: "" }
|
||||||
|
);
|
||||||
|
|
||||||
|
const formattedBasePrice = formatCurrency(data.base_price);
|
||||||
|
let additionalPrice: { name: string; price: string }[] = [];
|
||||||
|
let totalPrice = 0;
|
||||||
|
if (Array.isArray(data.additional_price)) {
|
||||||
|
for (const p of data.additional_price) {
|
||||||
|
additionalPrice.push({
|
||||||
|
name: p.name,
|
||||||
|
price: formatCurrency(p.price),
|
||||||
|
});
|
||||||
|
totalPrice += p.price;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const formattedTotalPrice = formatCurrency(data.base_price + totalPrice);
|
||||||
|
|
||||||
|
return {
|
||||||
|
data,
|
||||||
|
formattedData: {
|
||||||
|
price: formattedBasePrice,
|
||||||
|
additionalPrice,
|
||||||
|
totalPrice: formattedTotalPrice,
|
||||||
|
images,
|
||||||
|
postedAt,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user