fix: property list and detail UI slicing
This commit is contained in:
parent
12e7941f6c
commit
f574fdc66d
@ -588,9 +588,9 @@ a:hover {
|
||||
color: #967244;
|
||||
}
|
||||
|
||||
a[href*='tel'], a[href*='mailto'] {
|
||||
/* a[href*='tel'], a[href*='mailto'] {
|
||||
white-space: nowrap;
|
||||
}
|
||||
} */
|
||||
|
||||
.link-default, .link-default:active, .link-default:focus {
|
||||
color: #424445;
|
||||
@ -1177,7 +1177,7 @@ a.privacy-link {
|
||||
padding: 12px 11px;
|
||||
color: #9cc1ff;
|
||||
letter-spacing: 0;
|
||||
background-color: #31323c;
|
||||
background-color: var(--color-colorContactForm);
|
||||
}
|
||||
|
||||
.block-callboard a, .block-callboard a:focus, .block-callboard a:active {
|
||||
|
@ -34,6 +34,7 @@
|
||||
--color-colorText1: var(--color-colorExt10);
|
||||
--color-colorText2: var(--color-colorExt20);
|
||||
--color-colorLoaderBackground: var(--color-colorExt20);
|
||||
--color-colorPriceTag: var(--color-colorExt30);
|
||||
}
|
||||
|
||||
@layer components {
|
||||
|
594
src/app/(main)/listings-for-rent/[slug]/page.tsx
Normal file
594
src/app/(main)/listings-for-rent/[slug]/page.tsx
Normal file
@ -0,0 +1,594 @@
|
||||
import CardProperty from "@/components/CardProperty";
|
||||
import HeroImage from "@/components/HeroImage";
|
||||
import { CardPropertyData } from "@/schema/property";
|
||||
import { formatCurrency } from "@/utils/general";
|
||||
|
||||
const similarPropertiesData: CardPropertyData[] = [
|
||||
{
|
||||
id: 1,
|
||||
title: "401 Biscayne Boulevard, Miami",
|
||||
slug: "401-biscayne-boulevard",
|
||||
images: [
|
||||
{ url: "/images/featured-properties-01-480x287.jpg", alt: "biscayne boulevard" },
|
||||
{ url: "/images/featured-properties-01-480x287.jpg", alt: "biscayne boulevard" },
|
||||
{ url: "/images/featured-properties-01-480x287.jpg", alt: "biscayne boulevard" },
|
||||
],
|
||||
price: 5000,
|
||||
propertyType: "rent",
|
||||
posted_at: "",
|
||||
area: 480,
|
||||
bathrooms_count: 2,
|
||||
bedrooms_count: 2,
|
||||
is_available: true,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
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,
|
||||
is_available: true,
|
||||
},
|
||||
];
|
||||
|
||||
export default function ListingsForRentDetail() {
|
||||
return (
|
||||
<>
|
||||
<HeroImage title="Lorem Ipsum, Dolor" />
|
||||
|
||||
<section className="section section-md bg-gray-12">
|
||||
<div className="container">
|
||||
<div className="row row-50">
|
||||
<div className="col-lg-7 col-xl-8">
|
||||
<div className="slick-slider-1">
|
||||
<div className="slick-slider-price">$5000\mo</div>
|
||||
<div
|
||||
className="slick-slider carousel-parent"
|
||||
id="parent-carousel"
|
||||
data-arrows="true"
|
||||
data-loop="true"
|
||||
data-dots="false"
|
||||
data-swipe="true"
|
||||
data-fade="true"
|
||||
data-items="1"
|
||||
data-child="#child-carousel"
|
||||
data-for="#child-carousel"
|
||||
>
|
||||
<div className="item">
|
||||
<img src="/images/single-property-1-763x443.jpg" alt="" width="763" height="443" />
|
||||
</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
|
||||
className="slick-slider carousel-child"
|
||||
id="child-carousel"
|
||||
data-arrows="true"
|
||||
data-loop="true"
|
||||
data-dots="false"
|
||||
data-swipe="true"
|
||||
data-items="1"
|
||||
data-sm-items="3"
|
||||
data-md-items="4"
|
||||
data-lg-items="4"
|
||||
data-xl-items="5"
|
||||
data-slide-to-scroll="1"
|
||||
data-for="#parent-carousel"
|
||||
>
|
||||
<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 className="slick-slide-inner bg-[url(/images/single-property-1-763x443.jpg)]"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="features-block">
|
||||
<div className="features-block-inner">
|
||||
<div className="features-block-item">
|
||||
<ul className="features-block-list">
|
||||
<li>
|
||||
<span className="icon hotel-icon-10"></span>
|
||||
<span>2 Bathrooms</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className="icon hotel-icon-05"></span>
|
||||
<span>2 Bedrooms</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className="icon mdi mdi-vector-square"></span>
|
||||
<span>480 Sq Ft</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="features-block-item">
|
||||
<a className="link link-1" href="#">
|
||||
<span className="icon mdi mdi-heart-outline"></span>Add to Favorites
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
||||
Choose this property if you are looking for a modern house near the ocean shore. With 2 bathrooms and 2
|
||||
bedrooms as well as a single garage, it is a perfect option for a small family.
|
||||
</p>
|
||||
<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
|
||||
className="card-group-custom card-group-corporate"
|
||||
id="accordion1"
|
||||
role="tablist"
|
||||
aria-multiselectable="false"
|
||||
>
|
||||
<article className="card card-custom card-corporate">
|
||||
<div className="card-header" id="accordion1-heading-1" role="tab">
|
||||
<div className="card-title">
|
||||
<a
|
||||
className="card-link"
|
||||
role="button"
|
||||
data-toggle="collapse"
|
||||
href="#accordion1-collapse-1"
|
||||
aria-controls="accordion1-collapse-1"
|
||||
aria-expanded="true"
|
||||
>
|
||||
<span>Address</span>
|
||||
<div className="card-arrow"></div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="show visible!"
|
||||
id="accordion1-collapse-1"
|
||||
role="tabpanel"
|
||||
aria-labelledby="accordion1-heading-1"
|
||||
data-parent="#accordion1"
|
||||
>
|
||||
<div className="card-body">
|
||||
<div className="layout-1">
|
||||
<dl className="list-terms-inline">
|
||||
<dt>Address:</dt>
|
||||
<dd>Biscayne Blvd</dd>
|
||||
</dl>
|
||||
<dl className="list-terms-inline">
|
||||
<dt>State/County:</dt>
|
||||
<dd>Florida</dd>
|
||||
</dl>
|
||||
<dl className="list-terms-inline">
|
||||
<dt>City:</dt>
|
||||
<dd>Miami</dd>
|
||||
</dl>
|
||||
<dl className="list-terms-inline">
|
||||
<dt>Zip:</dt>
|
||||
<dd>8322</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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
<div
|
||||
className="card-group-custom card-group-corporate"
|
||||
id="accordion2"
|
||||
role="tablist"
|
||||
aria-multiselectable="false"
|
||||
>
|
||||
<article className="card card-custom card-corporate">
|
||||
<div className="card-header" id="accordion2-heading-1" role="tab">
|
||||
<div className="card-title">
|
||||
<a
|
||||
className="card-link"
|
||||
role="button"
|
||||
data-toggle="collapse"
|
||||
href="#accordion2-collapse-1"
|
||||
aria-controls="accordion2-collapse-1"
|
||||
aria-expanded="true"
|
||||
>
|
||||
<span>Features</span>
|
||||
<div className="card-arrow"></div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="show visible!"
|
||||
id="accordion2-collapse-1"
|
||||
role="tabpanel"
|
||||
aria-labelledby="accordion2-heading-1"
|
||||
data-parent="#accordion2"
|
||||
>
|
||||
<div className="card-body">
|
||||
<ul className="list-marked-2 layout-2">
|
||||
<li>2 Stories</li>
|
||||
<li>Basketball Court</li>
|
||||
<li>Lawn</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>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
<div
|
||||
className="card-group-custom card-group-corporate"
|
||||
id="accordion0"
|
||||
role="tablist"
|
||||
aria-multiselectable="false"
|
||||
>
|
||||
<article className="card card-custom card-corporate">
|
||||
<div className="card-header" id="accordion0-heading-0" role="tab">
|
||||
<div className="card-title">
|
||||
<a
|
||||
className="card-link"
|
||||
role="button"
|
||||
data-toggle="collapse"
|
||||
href="#accordion0-collapse-0"
|
||||
aria-controls="accordion0-collapse-0"
|
||||
aria-expanded="true"
|
||||
>
|
||||
<span>Pricing Detail</span>
|
||||
<div className="card-arrow"></div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="show visible!"
|
||||
id="accordion0-collapse-0"
|
||||
role="tabpanel"
|
||||
aria-labelledby="accordion0-heading-0"
|
||||
data-parent="#accordion0"
|
||||
>
|
||||
<div className="card-body">
|
||||
<div className="layout-1 columns-1!">
|
||||
<dl className="list-terms-inline w-full flex justify-between">
|
||||
<dt>Base Rent</dt>
|
||||
<dd>{formatCurrency(2700)}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div className="layout-1 columns-1!">
|
||||
<dl className="list-terms-inline w-full flex justify-between">
|
||||
<dt>Smart Home</dt>
|
||||
<dd>{formatCurrency(20)}</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>
|
||||
</div>
|
||||
<div className="layout-1 columns-1! mt-2">
|
||||
<dl className="list-terms-inline w-full flex justify-between">
|
||||
<dd className="font-semibold!">Est. total monthly*</dd>
|
||||
<dd className="font-semibold!">{formatCurrency(2900)}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div className="block-group-item">
|
||||
<h3>Property Map</h3>
|
||||
<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>
|
||||
<div className="blog-post-solo-footer mt-20">
|
||||
<div className="blog-post-solo-footer-left">
|
||||
<ul className="blog-post-solo-footer-list">
|
||||
<li>
|
||||
<span className="icon mdi mdi-clock"></span>
|
||||
<a href="#">February 10, 2021</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>
|
||||
<ul className="list-inline-1">
|
||||
<li>
|
||||
<a className="icon link-default fa-facebook" href="#"></a>
|
||||
</li>
|
||||
<li>
|
||||
<a className="icon link-default fa-twitter" href="#"></a>
|
||||
</li>
|
||||
<li>
|
||||
<a className="icon link-default fa-google-plus" href="#"></a>
|
||||
</li>
|
||||
<li>
|
||||
<a className="icon link-default fa-pinterest-p" href="#"></a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="block-group-item">
|
||||
<h3>Similar Properties</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{similarPropertiesData.map((p) => (
|
||||
<CardProperty key={p.id} data={p} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-lg-5 col-xl-4">
|
||||
<div className="row row-50">
|
||||
<div className="col-md-6 col-lg-12">
|
||||
<div className="block-info">
|
||||
<h3>Find Your Property</h3>
|
||||
<form
|
||||
className="rd-mailform form-select"
|
||||
data-form-output="form-output-global"
|
||||
data-form-type="contact"
|
||||
method="post"
|
||||
action="bat/rd-mailform.php"
|
||||
>
|
||||
<div className="form-wrap form-wrap-validation">
|
||||
<select
|
||||
className="form-input select-filter"
|
||||
data-style="modern"
|
||||
data-placeholder="Choose Location"
|
||||
data-minimum-results-for-search="Infinity"
|
||||
data-constraints="@Required"
|
||||
>
|
||||
<option label="placeholder"></option>
|
||||
<option value="2">Alaska</option>
|
||||
<option value="3">Arizona</option>
|
||||
<option value="4">Arkansas</option>
|
||||
<option value="5">California</option>
|
||||
<option value="6">Colorado</option>
|
||||
<option value="7">Connecticut</option>
|
||||
<option value="8">Delaware</option>
|
||||
<option value="9">Florida</option>
|
||||
</select>
|
||||
<span className="select-arrow"></span>
|
||||
</div>
|
||||
<div className="form-wrap form-wrap-validation">
|
||||
<select
|
||||
className="form-input select-filter"
|
||||
data-style="modern"
|
||||
data-placeholder="Property Status"
|
||||
data-minimum-results-for-search="Infinity"
|
||||
data-constraints="@Required"
|
||||
>
|
||||
<option label="placeholder"></option>
|
||||
<option value="2">Low</option>
|
||||
<option value="3">Middle</option>
|
||||
<option value="4">Primary</option>
|
||||
</select>
|
||||
<span className="select-arrow"></span>
|
||||
</div>
|
||||
<div className="form-wrap form-wrap-validation">
|
||||
<select
|
||||
className="form-input select-filter"
|
||||
data-style="modern"
|
||||
data-placeholder="Property Type"
|
||||
data-minimum-results-for-search="Infinity"
|
||||
data-constraints="@Required"
|
||||
>
|
||||
<option label="placeholder"></option>
|
||||
<option value="2">Low</option>
|
||||
<option value="3">Middle</option>
|
||||
<option value="4">Primary</option>
|
||||
</select>
|
||||
<span className="select-arrow"></span>
|
||||
</div>
|
||||
<div className="form-wrap-group">
|
||||
<div className="form-wrap form-wrap-validation">
|
||||
<select
|
||||
className="form-input select-filter"
|
||||
data-style="modern"
|
||||
data-placeholder="Min Price"
|
||||
data-minimum-results-for-search="Infinity"
|
||||
data-constraints="@Required"
|
||||
>
|
||||
<option label="placeholder"></option>
|
||||
<option value="2">100 $</option>
|
||||
<option value="3">200 $</option>
|
||||
<option value="4">300 $</option>
|
||||
</select>
|
||||
<span className="select-arrow"></span>
|
||||
</div>
|
||||
<div className="form-wrap form-wrap-validation">
|
||||
<select
|
||||
className="form-input select-filter"
|
||||
data-style="modern"
|
||||
data-placeholder="Max Price"
|
||||
data-minimum-results-for-search="Infinity"
|
||||
data-constraints="@Required"
|
||||
>
|
||||
<option label="placeholder"></option>
|
||||
<option value="2">1000 $</option>
|
||||
<option value="3">2000 $</option>
|
||||
<option value="4">3000 $</option>
|
||||
</select>
|
||||
<span className="select-arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-wrap-group">
|
||||
<div className="form-wrap form-wrap-validation">
|
||||
<select
|
||||
className="form-input select-filter"
|
||||
data-style="modern"
|
||||
data-placeholder="Min Area"
|
||||
data-minimum-results-for-search="Infinity"
|
||||
data-constraints="@Required"
|
||||
>
|
||||
<option label="placeholder"></option>
|
||||
<option value="2">100 Sq Ft</option>
|
||||
<option value="3">200 Sq Ft</option>
|
||||
<option value="4">300 Sq Ft</option>
|
||||
</select>
|
||||
<span className="select-arrow"></span>
|
||||
</div>
|
||||
<div className="form-wrap form-wrap-validation">
|
||||
<select
|
||||
className="form-input select-filter"
|
||||
data-style="modern"
|
||||
data-placeholder="Max Area"
|
||||
data-minimum-results-for-search="Infinity"
|
||||
data-constraints="@Required"
|
||||
>
|
||||
<option label="placeholder"></option>
|
||||
<option value="2">1000 Sq Ft</option>
|
||||
<option value="3">2000 Sq Ft</option>
|
||||
<option value="4">3000 Sq Ft</option>
|
||||
</select>
|
||||
<span className="select-arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-button">
|
||||
<button className="button button-block button-primary" type="submit">
|
||||
Search
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-md-6 col-lg-12">
|
||||
<article className="block-callboard">
|
||||
<div className="block-callboard-body">
|
||||
<h3 className="block-callboard-title">Request a Showing</h3>
|
||||
<form
|
||||
className="rd-form rd-mailform"
|
||||
data-form-output="form-output-global"
|
||||
data-form-type="contact"
|
||||
method="post"
|
||||
action="bat/rd-mailform.php"
|
||||
>
|
||||
<div className="row row-20">
|
||||
<div className="col-12">
|
||||
<div className="form-wrap">
|
||||
<input
|
||||
className="form-input"
|
||||
id="contact-name"
|
||||
type="text"
|
||||
name="name"
|
||||
data-constraints="@Required"
|
||||
/>
|
||||
<label className="form-label" htmlFor="contact-name">
|
||||
Your Name
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12">
|
||||
<div className="form-wrap">
|
||||
<input
|
||||
className="form-input"
|
||||
id="contact-email"
|
||||
type="email"
|
||||
name="email"
|
||||
data-constraints="@Email @Required"
|
||||
/>
|
||||
<label className="form-label" htmlFor="contact-email">
|
||||
E-mail
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12">
|
||||
<div className="form-wrap">
|
||||
<input
|
||||
className="form-input"
|
||||
id="contact-phone"
|
||||
type="text"
|
||||
name="phone"
|
||||
data-constraints="@PhoneNumber"
|
||||
/>
|
||||
<label className="form-label" htmlFor="contact-phone">
|
||||
Phone
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12">
|
||||
<div className="form-wrap">
|
||||
<label className="form-label" htmlFor="contact-message">
|
||||
Message
|
||||
</label>
|
||||
<textarea
|
||||
className="form-input"
|
||||
id="contact-message"
|
||||
name="message"
|
||||
data-constraints="@Required"
|
||||
></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12">
|
||||
<button className="button button-block button-secondary" type="submit">
|
||||
Send message
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
}
|
292
src/app/(main)/listings-for-rent/page.tsx
Normal file
292
src/app/(main)/listings-for-rent/page.tsx
Normal file
@ -0,0 +1,292 @@
|
||||
import CardProperty from "@/components/CardProperty";
|
||||
import HeroImage from "@/components/HeroImage";
|
||||
import Pagination from "@/components/Pagination";
|
||||
import { CardPropertyData } from "@/schema/property";
|
||||
import { getDefaultMetadata } from "@/utils/metadata";
|
||||
import { Metadata } from "next";
|
||||
|
||||
const metaDesc = "Explore the latest properties on the Dynamic Realty.";
|
||||
|
||||
export async function generateMetadata(): Promise<Metadata> {
|
||||
const metadata = await getDefaultMetadata();
|
||||
metadata.title = `Listings For Rent - ${metadata.openGraph?.siteName}`;
|
||||
metadata.description = metaDesc;
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
const propertiesData: CardPropertyData[] = [
|
||||
{
|
||||
id: 1,
|
||||
title: "401 Biscayne Boulevard, Miami",
|
||||
slug: "401-biscayne-boulevard",
|
||||
images: [
|
||||
{ url: "/images/featured-properties-01-480x287.jpg", alt: "biscayne boulevard" },
|
||||
{ url: "/images/featured-properties-01-480x287.jpg", alt: "biscayne boulevard" },
|
||||
{ url: "/images/featured-properties-01-480x287.jpg", alt: "biscayne boulevard" },
|
||||
],
|
||||
price: 5000,
|
||||
propertyType: "rent",
|
||||
posted_at: "",
|
||||
area: 480,
|
||||
bathrooms_count: 2,
|
||||
bedrooms_count: 2,
|
||||
is_available: true,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
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,
|
||||
is_available: true,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: "403 Biscayne Boulevard, Miami",
|
||||
slug: "403-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,
|
||||
is_available: true,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: "404 Biscayne Boulevard, Miami",
|
||||
slug: "404-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,
|
||||
is_available: true,
|
||||
},
|
||||
];
|
||||
|
||||
export default function ListingsForRent() {
|
||||
return (
|
||||
<>
|
||||
<HeroImage title="Listings For Rent" />
|
||||
|
||||
<section className="section section-md bg-gray-12">
|
||||
<div className="container">
|
||||
<div className="row row-50">
|
||||
<div className="col-lg-7 col-xl-8">
|
||||
<div className="row row-30">
|
||||
<div className="col-12">
|
||||
<ul className="block-info-1">
|
||||
<li>
|
||||
<div className="form-wrap-group-1">
|
||||
<div className="form-wrap">
|
||||
<select
|
||||
className="form-input select-filter"
|
||||
data-style="modern"
|
||||
data-placeholder="Publication Date"
|
||||
data-minimum-results-for-search="Infinity"
|
||||
data-constraints="@Required"
|
||||
>
|
||||
<option label="placeholder"></option>
|
||||
<option value="2">Monday</option>
|
||||
<option value="3">Tuesday</option>
|
||||
<option value="4">Wednesday</option>
|
||||
<option value="5">Thursday</option>
|
||||
<option value="6">Friday</option>
|
||||
<option value="7">Saturday</option>
|
||||
<option value="8">Sunday</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="form-wrap">
|
||||
<select
|
||||
className="form-input select-filter"
|
||||
data-style="modern"
|
||||
data-placeholder="Price Low to High"
|
||||
data-minimum-results-for-search="Infinity"
|
||||
data-constraints="@Required"
|
||||
>
|
||||
<option label="placeholder"></option>
|
||||
<option value="1">Price Low to High</option>
|
||||
<option value="2">Price High to Low</option>
|
||||
<option value="3">Most Popular</option>
|
||||
<option value="4">Top Rated</option>
|
||||
<option value="5">Best Sellers</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="col-12">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{propertiesData.map((p) => (
|
||||
<CardProperty key={p.id} data={p} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12">
|
||||
{/* <ul className="pagination-custom">
|
||||
<li>
|
||||
<a className="active" href="#">
|
||||
1
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">2</a>
|
||||
</li>
|
||||
</ul> */}
|
||||
<Pagination hasNextPage={true} hasPreviousPage={true} totalPages={10} page={3} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-lg-5 col-xl-4">
|
||||
<div className="row row-50">
|
||||
<div className="col-md-6 col-lg-12">
|
||||
<div className="block-info">
|
||||
<h3>Find Your Property</h3>
|
||||
<form
|
||||
className="rd-mailform form-select"
|
||||
data-form-output="form-output-global"
|
||||
data-form-type="contact"
|
||||
method="post"
|
||||
action="bat/rd-mailform.php"
|
||||
>
|
||||
<div className="form-wrap form-wrap-validation">
|
||||
<select
|
||||
className="form-input select-filter"
|
||||
data-style="modern"
|
||||
data-placeholder="Choose Location"
|
||||
data-minimum-results-for-search="Infinity"
|
||||
data-constraints="@Required"
|
||||
>
|
||||
<option label="placeholder"></option>
|
||||
<option value="2">Alaska</option>
|
||||
<option value="3">Arizona</option>
|
||||
<option value="4">Arkansas</option>
|
||||
<option value="5">California</option>
|
||||
<option value="6">Colorado</option>
|
||||
<option value="7">Connecticut</option>
|
||||
<option value="8">Delaware</option>
|
||||
<option value="9">Florida</option>
|
||||
</select>
|
||||
<span className="select-arrow"></span>
|
||||
</div>
|
||||
<div className="form-wrap form-wrap-validation">
|
||||
<select
|
||||
className="form-input select-filter"
|
||||
data-style="modern"
|
||||
data-placeholder="Property Status"
|
||||
data-minimum-results-for-search="Infinity"
|
||||
data-constraints="@Required"
|
||||
>
|
||||
<option label="placeholder"></option>
|
||||
<option value="2">Low</option>
|
||||
<option value="3">Middle</option>
|
||||
<option value="4">Primary</option>
|
||||
</select>
|
||||
<span className="select-arrow"></span>
|
||||
</div>
|
||||
<div className="form-wrap form-wrap-validation">
|
||||
<select
|
||||
className="form-input select-filter"
|
||||
data-style="modern"
|
||||
data-placeholder="Property Type"
|
||||
data-minimum-results-for-search="Infinity"
|
||||
data-constraints="@Required"
|
||||
>
|
||||
<option label="placeholder"></option>
|
||||
<option value="2">Low</option>
|
||||
<option value="3">Middle</option>
|
||||
<option value="4">Primary</option>
|
||||
</select>
|
||||
<span className="select-arrow"></span>
|
||||
</div>
|
||||
<div className="form-wrap-group">
|
||||
<div className="form-wrap form-wrap-validation">
|
||||
<select
|
||||
className="form-input select-filter"
|
||||
data-style="modern"
|
||||
data-placeholder="Min Price"
|
||||
data-minimum-results-for-search="Infinity"
|
||||
data-constraints="@Required"
|
||||
>
|
||||
<option label="placeholder"></option>
|
||||
<option value="2">100 $</option>
|
||||
<option value="3">200 $</option>
|
||||
<option value="4">300 $</option>
|
||||
</select>
|
||||
<span className="select-arrow"></span>
|
||||
</div>
|
||||
<div className="form-wrap form-wrap-validation">
|
||||
<select
|
||||
className="form-input select-filter"
|
||||
data-style="modern"
|
||||
data-placeholder="Max Price"
|
||||
data-minimum-results-for-search="Infinity"
|
||||
data-constraints="@Required"
|
||||
>
|
||||
<option label="placeholder"></option>
|
||||
<option value="2">1000 $</option>
|
||||
<option value="3">2000 $</option>
|
||||
<option value="4">3000 $</option>
|
||||
</select>
|
||||
<span className="select-arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-wrap-group">
|
||||
<div className="form-wrap form-wrap-validation">
|
||||
<select
|
||||
className="form-input select-filter"
|
||||
data-style="modern"
|
||||
data-placeholder="Min Area"
|
||||
data-minimum-results-for-search="Infinity"
|
||||
data-constraints="@Required"
|
||||
>
|
||||
<option label="placeholder"></option>
|
||||
<option value="2">100 Sq Ft</option>
|
||||
<option value="3">200 Sq Ft</option>
|
||||
<option value="4">300 Sq Ft</option>
|
||||
</select>
|
||||
<span className="select-arrow"></span>
|
||||
</div>
|
||||
<div className="form-wrap form-wrap-validation">
|
||||
<select
|
||||
className="form-input select-filter"
|
||||
data-style="modern"
|
||||
data-placeholder="Max Area"
|
||||
data-minimum-results-for-search="Infinity"
|
||||
data-constraints="@Required"
|
||||
>
|
||||
<option label="placeholder"></option>
|
||||
<option value="2">1000 Sq Ft</option>
|
||||
<option value="3">2000 Sq Ft</option>
|
||||
<option value="4">3000 Sq Ft</option>
|
||||
</select>
|
||||
<span className="select-arrow"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-button">
|
||||
<button className="button button-block button-primary" type="submit">
|
||||
Search
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
}
|
63
src/components/CardProperty.tsx
Normal file
63
src/components/CardProperty.tsx
Normal file
@ -0,0 +1,63 @@
|
||||
import { CardPropertyData } from "@/schema/property";
|
||||
import { formatCurrency } from "@/utils/general";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
|
||||
type CardPropertyProps = {
|
||||
data: CardPropertyData;
|
||||
};
|
||||
|
||||
export default function CardProperty({ data }: CardPropertyProps) {
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<article className="product-classic">
|
||||
<div className="product-classic-media">
|
||||
<div
|
||||
className="owl-carousel"
|
||||
data-items="1"
|
||||
data-nav="true"
|
||||
data-stage-padding="0"
|
||||
data-loop="false"
|
||||
data-margin="0"
|
||||
data-mouse-drag="false"
|
||||
>
|
||||
{Array.isArray(data.images) &&
|
||||
data.images.map((img, idx) => (
|
||||
<Image key={idx} src={img.url} alt={img.alt ?? ""} width="480" height="287" />
|
||||
))}
|
||||
</div>
|
||||
<div className="product-classic-price bg-colorPriceTag/80!">
|
||||
<span>
|
||||
{formatCurrency(data.price)}
|
||||
{data.propertyType === "rent" && `/mo`}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<h4 className="product-classic-title">
|
||||
<Link href="/listings-for-rent/slug">{data.title}</Link>
|
||||
</h4>
|
||||
<div className="product-classic-divider"></div>
|
||||
<ul className="product-classic-list">
|
||||
<li>
|
||||
<span className="icon mdi mdi-vector-square"></span>
|
||||
<span>{data.area} Sq Ft</span>
|
||||
</li>
|
||||
{data.bathrooms_count && (
|
||||
<li>
|
||||
<span className="icon hotel-icon-10"></span>
|
||||
<span>{data.bathrooms_count} Bathrooms</span>
|
||||
</li>
|
||||
)}
|
||||
{data.bedrooms_count && (
|
||||
<li>
|
||||
<span className="icon hotel-icon-05"></span>
|
||||
<span>{data.bedrooms_count} Bedrooms</span>
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
</article>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
120
src/components/Pagination.tsx
Normal file
120
src/components/Pagination.tsx
Normal file
@ -0,0 +1,120 @@
|
||||
"use client";
|
||||
|
||||
import { usePathname } from "next/navigation";
|
||||
|
||||
interface PaginationProps {
|
||||
page: number;
|
||||
hasPreviousPage: boolean;
|
||||
hasNextPage: boolean;
|
||||
totalPages: number;
|
||||
usePathParams?: boolean;
|
||||
}
|
||||
|
||||
export default function Pagination({
|
||||
page,
|
||||
hasPreviousPage,
|
||||
hasNextPage,
|
||||
totalPages,
|
||||
usePathParams = false,
|
||||
}: PaginationProps) {
|
||||
const activePage = page;
|
||||
const pathName = usePathname();
|
||||
|
||||
// Function to handle page change
|
||||
const handlePageChange = (page: string | number) => {
|
||||
if (typeof page === "string") return;
|
||||
if (typeof window === "undefined") return;
|
||||
const url = new URL(window.location.href);
|
||||
const searchParams = new URLSearchParams(url.search);
|
||||
|
||||
if (usePathParams) {
|
||||
let updatedPath = "";
|
||||
if (pathName.includes("/page")) {
|
||||
updatedPath = pathName.replace(/\/page\/\d+/, `/page/${page}`);
|
||||
} else {
|
||||
updatedPath = `${pathName}/page/${page}`;
|
||||
}
|
||||
window.location.href = `${updatedPath}?${searchParams}`;
|
||||
} else {
|
||||
searchParams.set("page", `${page}`);
|
||||
window.location.href = `${pathName}/?${searchParams}`;
|
||||
}
|
||||
};
|
||||
|
||||
const getPageNumbers = () => {
|
||||
const pages = [];
|
||||
const showEllipsisStart = activePage > 4;
|
||||
const showEllipsisEnd = activePage < totalPages - 3;
|
||||
|
||||
if (totalPages <= 7) {
|
||||
// Show all pages if total is 7 or less
|
||||
for (let i = 1; i <= totalPages; i++) {
|
||||
pages.push(i);
|
||||
}
|
||||
} else {
|
||||
// Always show first page
|
||||
pages.push(1);
|
||||
|
||||
if (showEllipsisStart) {
|
||||
pages.push("...");
|
||||
}
|
||||
|
||||
// Show pages around current page
|
||||
const start = showEllipsisStart ? Math.max(2, activePage - 1) : 2;
|
||||
const end = showEllipsisEnd ? Math.min(totalPages - 1, activePage + 1) : totalPages - 1;
|
||||
|
||||
for (let i = start; i <= end; i++) {
|
||||
pages.push(i);
|
||||
}
|
||||
|
||||
if (showEllipsisEnd) {
|
||||
pages.push("...");
|
||||
}
|
||||
|
||||
// Always show last page
|
||||
pages.push(totalPages);
|
||||
}
|
||||
|
||||
return pages;
|
||||
};
|
||||
|
||||
return (
|
||||
<ul className={"pagination-custom"}>
|
||||
{/* Previous Page Button */}
|
||||
{hasPreviousPage && (
|
||||
<>
|
||||
<li className="cursor-pointer">
|
||||
<a
|
||||
onClick={() => activePage > 1 && handlePageChange(activePage - 1)}
|
||||
className={activePage === 1 ? "disabled" : ""}
|
||||
>
|
||||
{"<"}
|
||||
</a>
|
||||
</li>
|
||||
</>
|
||||
)}
|
||||
|
||||
{getPageNumbers().map((page, key) => (
|
||||
<li key={key} className="cursor-pointer">
|
||||
<a onClick={() => handlePageChange(page)} className={activePage === page ? "active" : ""}>
|
||||
{page}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
|
||||
{/* Next Page Button */}
|
||||
{hasNextPage && (
|
||||
<>
|
||||
<li className="cursor-pointer">
|
||||
<a
|
||||
onClick={() => activePage < totalPages && handlePageChange(activePage + 1)}
|
||||
className={activePage === totalPages ? "disabled" : ""}
|
||||
>
|
||||
{">"}
|
||||
</a>
|
||||
</li>
|
||||
</>
|
||||
)}
|
||||
</ul>
|
||||
);
|
||||
}
|
@ -118,7 +118,7 @@ export default function Header() {
|
||||
</a>
|
||||
</li>
|
||||
<li className="rd-nav-item">
|
||||
<a className="rd-nav-link rd-nav-link-custom" href="/">
|
||||
<a className="rd-nav-link rd-nav-link-custom" href="/listings-for-rent">
|
||||
LISTINGS FOR RENT
|
||||
</a>
|
||||
</li>
|
||||
|
16
src/schema/property.ts
Normal file
16
src/schema/property.ts
Normal file
@ -0,0 +1,16 @@
|
||||
export type CardPropertyData = {
|
||||
id: number;
|
||||
slug?: string | null;
|
||||
title: string;
|
||||
price: number;
|
||||
images?: { url: string; alt?: string }[];
|
||||
/**
|
||||
* in sqft
|
||||
*/
|
||||
area: number;
|
||||
bedrooms_count?: number;
|
||||
bathrooms_count?: number;
|
||||
posted_at: string;
|
||||
propertyType: "rent" | "sell";
|
||||
is_available: boolean;
|
||||
};
|
@ -5,3 +5,22 @@ export function limitString(text: string) {
|
||||
export function getRandomNumber(range: number): number {
|
||||
return Math.floor(Math.random() * range) + 1;
|
||||
}
|
||||
|
||||
export function formatCurrency(num: number): string {
|
||||
return Intl.NumberFormat("en-US", {
|
||||
style: "currency",
|
||||
currency: "USD",
|
||||
|
||||
// These options can be used to round to whole numbers.
|
||||
trailingZeroDisplay: "stripIfInteger", // This is probably what most people
|
||||
// want. It will only stop printing
|
||||
// the fraction when the input
|
||||
// amount is a round number (int)
|
||||
// already. If that's not what you
|
||||
// need, have a look at the options
|
||||
// below.
|
||||
//minimumFractionDigits: 0, // This suffices for whole numbers, but will
|
||||
// print 2500.10 as $2,500.1
|
||||
//maximumFractionDigits: 0, // Causes 2500.99 to be printed as $2,501
|
||||
}).format(num);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user