Property listing pages
This commit is contained in:
@@ -356,12 +356,7 @@
|
||||
</div>
|
||||
<p class="text-gray-500 mb-4">
|
||||
This is a beautiful apartment located near the commons
|
||||
</p>
|
||||
<p class="text-gray-500 mb-4">
|
||||
We have a beautiful apartment located near the commons. It is a
|
||||
2 bedroom apartment with a full kitchen and bathroom. It is
|
||||
available for weekly or monthly rentals.
|
||||
</p>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="bg-white p-6 rounded-lg shadow-md mt-6">
|
||||
|
||||
@@ -1,4 +1,107 @@
|
||||
const PropertyPage = () => {
|
||||
return <div>PropertyPage</div>;
|
||||
import PropertyHeaderImage from '@/components/PropertyHeaderImage';
|
||||
import PropertyDetails from '@/components/PropertyDetails';
|
||||
import connectDB from '@/config/database';
|
||||
import Property from '@/models/Property';
|
||||
|
||||
const PropertyPage = async ({ params }) => {
|
||||
await connectDB();
|
||||
const property = await Property.findById(params.id).lean();
|
||||
|
||||
return (
|
||||
<>
|
||||
<PropertyHeaderImage image={property.images[0]} />
|
||||
<section className='bg-blue-50'>
|
||||
<div className='container m-auto py-10 px-6'>
|
||||
<div className='grid grid-cols-1 md:grid-cols-70/30 w-full gap-6'>
|
||||
<PropertyDetails property={property} />
|
||||
|
||||
{/* <!-- Sidebar --> */}
|
||||
<aside className='space-y-4'>
|
||||
<button className='bg-blue-500 hover:bg-blue-600 text-white font-bold w-full py-2 px-4 rounded-full flex items-center justify-center'>
|
||||
<i className='fas fa-bookmark mr-2'></i> Bookmark Property
|
||||
</button>
|
||||
<button className='bg-orange-500 hover:bg-orange-600 text-white font-bold w-full py-2 px-4 rounded-full flex items-center justify-center'>
|
||||
<i className='fas fa-share mr-2'></i> Share Property
|
||||
</button>
|
||||
|
||||
{/* <!-- Contact Form --> */}
|
||||
<div className='bg-white p-6 rounded-lg shadow-md'>
|
||||
<h3 className='text-xl font-bold mb-6'>
|
||||
Contact Property Manager
|
||||
</h3>
|
||||
<form>
|
||||
<div className='mb-4'>
|
||||
<label
|
||||
className='block text-gray-700 text-sm font-bold mb-2'
|
||||
htmlFor='name'
|
||||
>
|
||||
Name:
|
||||
</label>
|
||||
<input
|
||||
className='shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline'
|
||||
id='name'
|
||||
type='text'
|
||||
placeholder='Enter your name'
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className='mb-4'>
|
||||
<label
|
||||
className='block text-gray-700 text-sm font-bold mb-2'
|
||||
htmlFor='email'
|
||||
>
|
||||
Email:
|
||||
</label>
|
||||
<input
|
||||
className='shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline'
|
||||
id='email'
|
||||
type='email'
|
||||
placeholder='Enter your email'
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className='mb-4'>
|
||||
<label
|
||||
className='block text-gray-700 text-sm font-bold mb-2'
|
||||
htmlFor='phone'
|
||||
>
|
||||
Phone:
|
||||
</label>
|
||||
<input
|
||||
className='shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline'
|
||||
id='phone'
|
||||
type='text'
|
||||
placeholder='Enter your phone number'
|
||||
/>
|
||||
</div>
|
||||
<div className='mb-4'>
|
||||
<label
|
||||
className='block text-gray-700 text-sm font-bold mb-2'
|
||||
htmlFor='message'
|
||||
>
|
||||
Message:
|
||||
</label>
|
||||
<textarea
|
||||
className='shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 h-44 focus:outline-none focus:shadow-outline'
|
||||
id='message'
|
||||
placeholder='Enter your message'
|
||||
></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
className='bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded-full w-full focus:outline-none focus:shadow-outline flex items-center justify-center'
|
||||
type='submit'
|
||||
>
|
||||
<i className='fas fa-paper-plane mr-2'></i> Send Message
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
};
|
||||
export default PropertyPage;
|
||||
|
||||
@@ -4,7 +4,7 @@ import connectDB from '@/config/database';
|
||||
|
||||
const PropertiesPage = async () => {
|
||||
await connectDB();
|
||||
const properties = await Property.find({});
|
||||
const properties = await Property.find({}).lean();
|
||||
|
||||
return (
|
||||
<section className='px-4 py-6'>
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
import {
|
||||
FaBed,
|
||||
FaBath,
|
||||
FaRulerCombined,
|
||||
FaTimes,
|
||||
FaCheck,
|
||||
FaMapMarker,
|
||||
} from 'react-icons/fa';
|
||||
|
||||
const PropertyDetails = ({ property }) => {
|
||||
return (
|
||||
<main>
|
||||
<div className='bg-white p-6 rounded-lg shadow-md text-center md:text-left'>
|
||||
<div className='text-gray-500 mb-4'>{property.type}</div>
|
||||
<h1 className='text-3xl font-bold mb-4'>{property.name}</h1>
|
||||
<div className='text-gray-500 mb-4 flex align-middle justify-center md:justify-start'>
|
||||
<FaMapMarker className='text-orange-700 mt-1 mr-1' />
|
||||
<p className='text-orange-700'>
|
||||
{property.location.street}, {property.location.city}{' '}
|
||||
{property.location.state}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<h3 className='text-lg font-bold my-6 bg-gray-800 text-white p-2'>
|
||||
Rates & Options
|
||||
</h3>
|
||||
<div className='flex flex-col md:flex-row justify-around'>
|
||||
<div className='flex items-center justify-center mb-4 border-b border-gray-200 md:border-b-0 pb-4 md:pb-0'>
|
||||
<div className='text-gray-500 mr-2 font-bold'>Nightly</div>
|
||||
<div className='text-2xl font-bold text-blue-500'>
|
||||
{property.rates.nightly ? (
|
||||
`$${property.rates.nightly.toLocaleString()}`
|
||||
) : (
|
||||
<FaTimes className='text-red-700' />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex items-center justify-center mb-4 border-b border-gray-200 md:border-b-0 pb-4 md:pb-0'>
|
||||
<div className='text-gray-500 mr-2 font-bold'>Weekly</div>
|
||||
<div className='text-2xl font-bold text-blue-500'>
|
||||
{property.rates.weekly ? (
|
||||
`$${property.rates.weekly.toLocaleString()}`
|
||||
) : (
|
||||
<FaTimes className='text-red-700' />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex items-center justify-center mb-4 pb-4 md:pb-0'>
|
||||
<div className='text-gray-500 mr-2 font-bold'>Monthly</div>
|
||||
<div className='text-2xl font-bold text-blue-500'>
|
||||
{property.rates.monthly ? (
|
||||
`$${property.rates.monthly.toLocaleString()}`
|
||||
) : (
|
||||
<FaTimes className='text-red-700' />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='bg-white p-6 rounded-lg shadow-md mt-6'>
|
||||
<h3 className='text-lg font-bold mb-6'>Description & Details</h3>
|
||||
<div className='flex justify-center gap-4 text-blue-500 mb-4 text-xl space-x-9'>
|
||||
<p>
|
||||
<FaBed className='inline-block mr-2' /> {property.beds}{' '}
|
||||
<span className='hidden sm:inline'>Beds</span>
|
||||
</p>
|
||||
<p>
|
||||
<FaBath className='inline-block mr-2' /> {property.baths}{' '}
|
||||
<span className='hidden sm:inline'>Baths</span>
|
||||
</p>
|
||||
<p>
|
||||
<FaRulerCombined className='inline-block mr-2' />
|
||||
{property.square_feet}{' '}
|
||||
<span className='hidden sm:inline'>sqft</span>
|
||||
</p>
|
||||
</div>
|
||||
<p className='text-gray-500 mb-4'>{property.description}</p>
|
||||
</div>
|
||||
|
||||
<div className='bg-white p-6 rounded-lg shadow-md mt-6'>
|
||||
<h3 className='text-lg font-bold mb-6'>Amenities</h3>
|
||||
|
||||
<ul className='grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 list-none space-y-2'>
|
||||
{property.amenities.map((amenity, index) => (
|
||||
<li key={index}>
|
||||
<FaCheck className='inline-block text-green-600 mr-2' /> {amenity}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
<div className='bg-white p-6 rounded-lg shadow-md mt-6'>
|
||||
<div id='map'></div>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
};
|
||||
|
||||
export default PropertyDetails;
|
||||
@@ -0,0 +1,22 @@
|
||||
import Image from 'next/image';
|
||||
|
||||
const PropertyHeaderImage = ({ image }) => {
|
||||
return (
|
||||
<section>
|
||||
<div className='container-xl m-auto'>
|
||||
<div className='grid grid-cols-1'>
|
||||
<Image
|
||||
src={`/images/properties/${image}`}
|
||||
alt=''
|
||||
className='object-cover h-[400px] w-full'
|
||||
width={0}
|
||||
height={0}
|
||||
sizes='100vw'
|
||||
priority={true}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
export default PropertyHeaderImage;
|
||||
Generated
+60
-1
@@ -17,8 +17,11 @@
|
||||
"react-spinners": "^0.14.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "20.14.10",
|
||||
"@types/react": "18.3.3",
|
||||
"postcss": "^8",
|
||||
"tailwindcss": "^3.4.1"
|
||||
"tailwindcss": "^3.4.1",
|
||||
"typescript": "5.5.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@alloc/quick-lru": {
|
||||
@@ -329,6 +332,34 @@
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.14.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.10.tgz",
|
||||
"integrity": "sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~5.26.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/prop-types": {
|
||||
"version": "15.7.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz",
|
||||
"integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/react": {
|
||||
"version": "18.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz",
|
||||
"integrity": "sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/prop-types": "*",
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/webidl-conversions": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz",
|
||||
@@ -593,6 +624,13 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/csstype": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.3.5",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz",
|
||||
@@ -2028,6 +2066,27 @@
|
||||
"integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==",
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.5.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz",
|
||||
"integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "5.26.5",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
||||
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
|
||||
+4
-1
@@ -18,7 +18,10 @@
|
||||
"react-spinners": "^0.14.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "20.14.10",
|
||||
"@types/react": "18.3.3",
|
||||
"postcss": "^8",
|
||||
"tailwindcss": "^3.4.1"
|
||||
"tailwindcss": "^3.4.1",
|
||||
"typescript": "5.5.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
[
|
||||
{
|
||||
"_id": "1",
|
||||
"owner": "1",
|
||||
"name": "Boston Commons Retreat",
|
||||
"type": "Apartment",
|
||||
@@ -45,7 +44,6 @@
|
||||
"updatedAt": "2024-01-01T00:00:00.000Z"
|
||||
},
|
||||
{
|
||||
"_id": "2",
|
||||
"owner": "1",
|
||||
"name": "Cozy Downtown Loft",
|
||||
"type": "Apartment",
|
||||
@@ -89,7 +87,6 @@
|
||||
"updatedAt": "2024-01-02T00:00:00.000Z"
|
||||
},
|
||||
{
|
||||
"_id": "3",
|
||||
"owner": "2",
|
||||
"name": "Luxury Condo with a View",
|
||||
"type": "Condo",
|
||||
@@ -135,7 +132,6 @@
|
||||
"updatedAt": "2024-01-03T00:00:00.000Z"
|
||||
},
|
||||
{
|
||||
"_id": "4",
|
||||
"owner": "2",
|
||||
"name": "Charming Cottage Getaway",
|
||||
"type": "Cottage Or Cabin",
|
||||
@@ -169,7 +165,6 @@
|
||||
"updatedAt": "2024-01-04T00:00:00.000Z"
|
||||
},
|
||||
{
|
||||
"_id": "5",
|
||||
"owner": "3",
|
||||
"name": "Modern Downtown Studio",
|
||||
"type": "Studio",
|
||||
@@ -205,7 +200,6 @@
|
||||
"updatedAt": "2024-01-05T00:00:00.000Z"
|
||||
},
|
||||
{
|
||||
"_id": "6",
|
||||
"owner": "3",
|
||||
"name": "Seaside Retreat",
|
||||
"type": "House",
|
||||
@@ -241,7 +235,6 @@
|
||||
"updatedAt": "2024-01-06T00:00:00.000Z"
|
||||
},
|
||||
{
|
||||
"_id": "7",
|
||||
"owner": "4",
|
||||
"name": "Rustic Cabin in the Woods",
|
||||
"type": "Cottage Or Cabin",
|
||||
@@ -276,7 +269,6 @@
|
||||
"updatedAt": "2024-01-07T00:00:00.000Z"
|
||||
},
|
||||
{
|
||||
"_id": "8",
|
||||
"owner": "5",
|
||||
"name": "Ski-In/Ski-Out Chalet",
|
||||
"type": "Chalet",
|
||||
@@ -311,7 +303,6 @@
|
||||
"updatedAt": "2024-01-08T00:00:00.000Z"
|
||||
},
|
||||
{
|
||||
"_id": "9",
|
||||
"owner": "6",
|
||||
"name": "Mountain View Retreat",
|
||||
"type": "House",
|
||||
@@ -347,7 +338,6 @@
|
||||
"updatedAt": "2024-01-09T00:00:00.000Z"
|
||||
},
|
||||
{
|
||||
"_id": "10",
|
||||
"owner": "7",
|
||||
"name": "Historic Downtown Loft",
|
||||
"type": "Apartment",
|
||||
|
||||
Reference in New Issue
Block a user