added show more functionality && added calendar for tour requests
This commit is contained in:
parent
4b0a86b0dc
commit
af0fb622b8
8 changed files with 176 additions and 20 deletions
85
real_estate/client/package-lock.json
generated
85
real_estate/client/package-lock.json
generated
|
@ -11,6 +11,7 @@
|
||||||
"@reduxjs/toolkit": "^2.2.1",
|
"@reduxjs/toolkit": "^2.2.1",
|
||||||
"firebase": "^10.8.1",
|
"firebase": "^10.8.1",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
|
"react-calendar": "^4.8.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-icons": "^5.0.1",
|
"react-icons": "^5.0.1",
|
||||||
"react-redux": "^9.1.0",
|
"react-redux": "^9.1.0",
|
||||||
|
@ -1639,6 +1640,19 @@
|
||||||
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
|
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/lodash": {
|
||||||
|
"version": "4.17.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.0.tgz",
|
||||||
|
"integrity": "sha512-t7dhREVv6dbNj0q17X12j7yDG4bD/DHYX7o5/DbDxobP0HnGPgpRz2Ej77aL7TZT3DSw13fqUTj8J4mMnqa7WA=="
|
||||||
|
},
|
||||||
|
"node_modules/@types/lodash.memoize": {
|
||||||
|
"version": "4.1.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/lodash.memoize/-/lodash.memoize-4.1.9.tgz",
|
||||||
|
"integrity": "sha512-glY1nQuoqX4Ft8Uk+KfJudOD7DQbbEDF6k9XpGncaohW3RW4eSWBlx6AA0fZCrh40tZcQNH4jS/Oc59J6Eq+aw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/lodash": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "20.11.25",
|
"version": "20.11.25",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.25.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.25.tgz",
|
||||||
|
@ -1702,6 +1716,14 @@
|
||||||
"vite": "^4 || ^5"
|
"vite": "^4 || ^5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@wojtekmaj/date-utils": {
|
||||||
|
"version": "1.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@wojtekmaj/date-utils/-/date-utils-1.5.1.tgz",
|
||||||
|
"integrity": "sha512-+i7+JmNiE/3c9FKxzWFi2IjRJ+KzZl1QPu6QNrsgaa2MuBgXvUy4gA1TVzf/JMdIIloB76xSKikTWuyYAIVLww==",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/wojtekmaj/date-utils?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/acorn": {
|
"node_modules/acorn": {
|
||||||
"version": "8.11.3",
|
"version": "8.11.3",
|
||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
|
||||||
|
@ -2215,6 +2237,14 @@
|
||||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/clsx": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/color-convert": {
|
"node_modules/color-convert": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
|
@ -3081,6 +3111,18 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/get-user-locale": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/get-user-locale/-/get-user-locale-2.3.1.tgz",
|
||||||
|
"integrity": "sha512-VEvcsqKYx7zhZYC1CjecrDC5ziPSpl1gSm0qFFJhHSGDrSC+x4+p1KojWC/83QX//j476gFhkVXP/kNUc9q+bQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/lodash.memoize": "^4.1.7",
|
||||||
|
"lodash.memoize": "^4.1.1"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/wojtekmaj/get-user-locale?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/glob": {
|
"node_modules/glob": {
|
||||||
"version": "7.2.3",
|
"version": "7.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||||
|
@ -3835,6 +3877,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
|
||||||
"integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="
|
"integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="
|
||||||
},
|
},
|
||||||
|
"node_modules/lodash.memoize": {
|
||||||
|
"version": "4.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
|
||||||
|
"integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag=="
|
||||||
|
},
|
||||||
"node_modules/lodash.merge": {
|
"node_modules/lodash.merge": {
|
||||||
"version": "4.6.2",
|
"version": "4.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
||||||
|
@ -3978,7 +4025,6 @@
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
|
@ -4430,7 +4476,6 @@
|
||||||
"version": "15.8.1",
|
"version": "15.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||||
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"loose-envify": "^1.4.0",
|
"loose-envify": "^1.4.0",
|
||||||
"object-assign": "^4.1.1",
|
"object-assign": "^4.1.1",
|
||||||
|
@ -4500,6 +4545,31 @@
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-calendar": {
|
||||||
|
"version": "4.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-calendar/-/react-calendar-4.8.0.tgz",
|
||||||
|
"integrity": "sha512-qFgwo+p58sgv1QYMI1oGNaop90eJVKuHTZ3ZgBfrrpUb+9cAexxsKat0sAszgsizPMVo7vOXedV7Lqa0GQGMvA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@wojtekmaj/date-utils": "^1.1.3",
|
||||||
|
"clsx": "^2.0.0",
|
||||||
|
"get-user-locale": "^2.2.1",
|
||||||
|
"prop-types": "^15.6.0",
|
||||||
|
"warning": "^4.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/wojtekmaj/react-calendar?sponsor=1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
||||||
|
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-dom": {
|
"node_modules/react-dom": {
|
||||||
"version": "18.2.0",
|
"version": "18.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
|
||||||
|
@ -4523,8 +4593,7 @@
|
||||||
"node_modules/react-is": {
|
"node_modules/react-is": {
|
||||||
"version": "16.13.1",
|
"version": "16.13.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
|
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/react-redux": {
|
"node_modules/react-redux": {
|
||||||
"version": "9.1.0",
|
"version": "9.1.0",
|
||||||
|
@ -5561,6 +5630,14 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/warning": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
|
||||||
|
"integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
|
||||||
|
"dependencies": {
|
||||||
|
"loose-envify": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/websocket-driver": {
|
"node_modules/websocket-driver": {
|
||||||
"version": "0.7.4",
|
"version": "0.7.4",
|
||||||
"resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz",
|
"resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz",
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
"@reduxjs/toolkit": "^2.2.1",
|
"@reduxjs/toolkit": "^2.2.1",
|
||||||
"firebase": "^10.8.1",
|
"firebase": "^10.8.1",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
|
"react-calendar": "^4.8.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-icons": "^5.0.1",
|
"react-icons": "^5.0.1",
|
||||||
"react-redux": "^9.1.0",
|
"react-redux": "^9.1.0",
|
||||||
|
|
34
real_estate/client/src/components/Contact.jsx
Normal file
34
real_estate/client/src/components/Contact.jsx
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import { useEffect, useState } from "react"
|
||||||
|
|
||||||
|
|
||||||
|
const Contact = ({listing}) => {
|
||||||
|
const [agent, setAgent] = useState(null);
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchAgent = async () => {
|
||||||
|
try {
|
||||||
|
const res = await fetch(`/server/user/${listing.userRef}`);
|
||||||
|
const data = await res.json();
|
||||||
|
setAgent(data);
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
fetchAgent();
|
||||||
|
}, [listing.userRef]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{
|
||||||
|
agent && (
|
||||||
|
<div className="">
|
||||||
|
<p>Contact<span>{agent.username}</span>{' '} for {' '} <span>{listing.name.toLowerCase()}</span></p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Contact
|
|
@ -4,7 +4,7 @@ const ListingCard = ({listing}) => {
|
||||||
return (
|
return (
|
||||||
<div className='bg-white shadow-md hover:shadow-lg transition-shadow overflow-hidden rounded-md w-full sm:w-[230px]'>
|
<div className='bg-white shadow-md hover:shadow-lg transition-shadow overflow-hidden rounded-md w-full sm:w-[230px]'>
|
||||||
<Link to={`/listing/${listing._id}`}>
|
<Link to={`/listing/${listing._id}`}>
|
||||||
<img src={listing.imageUrls[0]} alt="" className='h-[300px] sm:h-[150px] w-full object-cover hover:scale-105 transition-scale duration-300'/>
|
<img src={listing.imageUrls[0]} alt="" className='h-[150px] sm:h-[150px] w-full object-cover hover:scale-105 transition-scale duration-300'/>
|
||||||
<div className="p-2">
|
<div className="p-2">
|
||||||
<p className='font-semibold text-gray-800 text-xl truncate mt-2 mb-1'>
|
<p className='font-semibold text-gray-800 text-xl truncate mt-2 mb-1'>
|
||||||
${listing.offer ? listing.discountPrice.toLocaleString('en-Us') : listing.currentPrice.toLocaleString('en-Us')}
|
${listing.offer ? listing.discountPrice.toLocaleString('en-Us') : listing.currentPrice.toLocaleString('en-Us')}
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
//import { IoIosSearch } from "react-icons/io";
|
|
||||||
import { IoIosLock } from "react-icons/io";
|
import { IoIosLock } from "react-icons/io";
|
||||||
import {Link} from 'react-router-dom';
|
import {Link} from 'react-router-dom';
|
||||||
import {useSelector} from 'react-redux'
|
import {useSelector} from 'react-redux'
|
||||||
|
|
||||||
|
|
||||||
const Navbar = () => {
|
const Navbar = () => {
|
||||||
const {currentUser} = useSelector((state) => state.user)
|
const {currentUser} = useSelector((state) => state.user)
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header className="bg-blue-950">
|
<header className="bg-blue-950">
|
||||||
|
@ -15,14 +16,10 @@ const Navbar = () => {
|
||||||
<h3 className=" text-gray-600 text-xs mt-1 uppercase tracking-widest">Lifestyle properties</h3>
|
<h3 className=" text-gray-600 text-xs mt-1 uppercase tracking-widest">Lifestyle properties</h3>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
{/*<form className="bg-white rounded-lg flex items-center">
|
|
||||||
<input type="text" placeholder="Search your dream property" className="bg-transparent px-4 py-2 focus:outline-none text-sm w-60 sm:w-64"/>
|
|
||||||
<IoIosSearch className="text-slate-600 text-lg"/>
|
|
||||||
</form>*/}
|
|
||||||
<ul className="flex gap-4 sm:px-10 px-5">
|
<ul className="flex gap-4 sm:px-10 px-5">
|
||||||
<Link to="/"><li className="text-white tracking-wide text-center uppercase text-sm hidden md:inline">Home</li></Link>
|
<Link to="/"><li className="text-white tracking-wide text-center uppercase text-sm hidden md:inline">Home</li></Link>
|
||||||
<Link to="/"><li className="text-white tracking-wide text-center uppercase text-sm hidden md:inline">Buying</li></Link>
|
<Link to="/"><li className="text-white tracking-wide text-center uppercase text-sm hidden md:inline">Buying</li></Link>
|
||||||
<Link to="/"><li className="text-white tracking-wide text-center uppercase text-sm hidden md:inline">Selling</li></Link>
|
<Link to="/"><li className="text-white tracking-wide text-center uppercase text-sm hidden md:inline">Renting</li></Link>
|
||||||
<Link to="/contact"><li className="text-white tracking-wide text-center uppercase text-sm hidden md:inline">Contact</li></Link>
|
<Link to="/contact"><li className="text-white tracking-wide text-center uppercase text-sm hidden md:inline">Contact</li></Link>
|
||||||
<Link to="/profile" className="flex items-center gap-1">
|
<Link to="/profile" className="flex items-center gap-1">
|
||||||
{currentUser ? (
|
{currentUser ? (
|
||||||
|
|
|
@ -20,8 +20,8 @@ const Search = () => {
|
||||||
}
|
}
|
||||||
}, [location.search])
|
}, [location.search])
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit} className="text-black mt-5 bg-white rounded-lg flex items-center">
|
<form onSubmit={handleSubmit} className="text-black mt-5 bg-white flex items-center border border-blue-800 rounded-md">
|
||||||
<input type="text" placeholder="Search your dream property" value={searchTerm} onChange={(e) => setSearchTerm(e.target.value)} className="px-4 py-3 focus:outline-none bg-transparent text-sm w-60 sm:w-80"/>
|
<input type="text" placeholder="Search your dream property" value={searchTerm} onChange={(e) => setSearchTerm(e.target.value)} className="px-4 py-2 focus:outline-none bg-transparent text-sm w-60 sm:w-80"/>
|
||||||
<button>
|
<button>
|
||||||
<IoIosSearch className="text-slate-600 text-lg m-2"/>
|
<IoIosSearch className="text-slate-600 text-lg m-2"/>
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -8,8 +8,15 @@ import 'swiper/css/thumbs';
|
||||||
import { FreeMode, Navigation, Thumbs } from 'swiper/modules';
|
import { FreeMode, Navigation, Thumbs } from 'swiper/modules';
|
||||||
import { BiSolidMap } from "react-icons/bi";
|
import { BiSolidMap } from "react-icons/bi";
|
||||||
|
|
||||||
|
import Calendar from "react-calendar";
|
||||||
|
import "react-calendar/dist/Calendar.css";
|
||||||
|
|
||||||
|
|
||||||
const Listing = () => {
|
const Listing = () => {
|
||||||
|
const [date, changeDate] = useState(new Date());
|
||||||
|
function changeValue(val) {
|
||||||
|
changeDate(val);
|
||||||
|
}
|
||||||
const [thumbsSwiper, setThumbsSwiper] = useState(null);
|
const [thumbsSwiper, setThumbsSwiper] = useState(null);
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
@ -45,9 +52,10 @@ const Listing = () => {
|
||||||
{error && <p className="text-center my-10 text-xl text-red-700">Something went wrong</p>}
|
{error && <p className="text-center my-10 text-xl text-red-700">Something went wrong</p>}
|
||||||
{listing && !loading && !error &&
|
{listing && !loading && !error &&
|
||||||
<>
|
<>
|
||||||
|
|
||||||
<Swiper style={{'--swiper-navigation-color': 'transparent', '--swiper-pagination-color': 'transparent',}} loop={true} spaceBetween={10} navigation={true} thumbs={{ swiper: thumbsSwiper }} modules={[FreeMode, Navigation, Thumbs]} className="mySwiper2" >
|
<Swiper style={{'--swiper-navigation-color': 'transparent', '--swiper-pagination-color': 'transparent',}} loop={true} spaceBetween={10} navigation={true} thumbs={{ swiper: thumbsSwiper }} modules={[FreeMode, Navigation, Thumbs]} className="mySwiper2" >
|
||||||
{listing.imageUrls.map(url =>
|
{listing.imageUrls.map(url =>
|
||||||
<SwiperSlide key={url}><img src={url} alt="" className="rounded-md my-1"/></SwiperSlide>
|
<SwiperSlide key={url}><img src={url} alt="" className="rounded-sm md:my-1"/></SwiperSlide>
|
||||||
)}
|
)}
|
||||||
</Swiper>
|
</Swiper>
|
||||||
<Swiper onSwiper={setThumbsSwiper} loop={true} spaceBetween={10} slidesPerView={5} freeMode={true} watchSlidesProgress={true} modules={[FreeMode, Navigation, Thumbs]} className="mySwiper">
|
<Swiper onSwiper={setThumbsSwiper} loop={true} spaceBetween={10} slidesPerView={5} freeMode={true} watchSlidesProgress={true} modules={[FreeMode, Navigation, Thumbs]} className="mySwiper">
|
||||||
|
@ -55,7 +63,8 @@ const Listing = () => {
|
||||||
<SwiperSlide key={url}><img src={url} alt="" /></SwiperSlide>
|
<SwiperSlide key={url}><img src={url} alt="" /></SwiperSlide>
|
||||||
)}
|
)}
|
||||||
</Swiper>
|
</Swiper>
|
||||||
<div className="flex flex-col max-w-4xl mx-auto p-4 gap-4">
|
<div className="flex max-w-4xl mx-auto flex-col md:flex-row">
|
||||||
|
<div className="flex flex-col p-4 gap-4 max-w-4xl md:max-w-2xl">
|
||||||
<p className="text-2xl font-semibold mt-5 capitalize">
|
<p className="text-2xl font-semibold mt-5 capitalize">
|
||||||
{listing.name} - $ {''}
|
{listing.name} - $ {''}
|
||||||
{listing.offer ? listing.discountPrice.toLocaleString('en-US') : listing.currentPrice.toLocaleString('en-US')}
|
{listing.offer ? listing.discountPrice.toLocaleString('en-US') : listing.currentPrice.toLocaleString('en-US')}
|
||||||
|
@ -90,7 +99,19 @@ const Listing = () => {
|
||||||
<span className='font-semibold text-black mt-4 mb-2 text-xl capitalize'>About this property</span>
|
<span className='font-semibold text-black mt-4 mb-2 text-xl capitalize'>About this property</span>
|
||||||
<span>{listing.description}</span>
|
<span>{listing.description}</span>
|
||||||
</p>
|
</p>
|
||||||
<button onClick={() => window.location = 'mailto:chicagoland@example.com'} className='bg-blue-950 text-white rounded-md uppercase hover:opacity-95 p-2 mb-10 text-sm'>Contact Agent</button>
|
<button onClick={() => window.location = 'mailto:chicagoland@example.com'} className='bg-blue-950 text-white rounded-md uppercase hover:opacity-95 p-2 mb-10 text-sm font-semibold'>Contact Agent</button>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col p-2 gap-2 mt-3 max-w-4xl md:max-w-2xl">
|
||||||
|
<div className="border border-gray-300 p-4 rounded-md">
|
||||||
|
<h2 className="text-xl text-blue-950 font-semibold capitalize md:text-start text-center">Schedule tour</h2>
|
||||||
|
<h3 className="md:text-start text-center">What is your preferred tour date?</h3>
|
||||||
|
<div className="mt-2 flex flex-col justify-center items-center">
|
||||||
|
<Calendar onChange = {changeValue} value = {date} />
|
||||||
|
<p className="mt-2 mb-6 text-center text-blue-700">The selected date is - {date.toLocaleDateString()}</p>
|
||||||
|
</div>
|
||||||
|
<button className="w-full bg-gray-300 rounded-md uppercase hover:opacity-95 p-2 text-sm font-semibold">Request tour</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import ListingCard from "../components/ListingCard";
|
||||||
const SearchProperties = () => {
|
const SearchProperties = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [showMore, setShowMore] = useState(false);
|
||||||
const [listings, setListings] = useState([]);
|
const [listings, setListings] = useState([]);
|
||||||
const [sidebarSearch, setSidebarSearch] = useState({
|
const [sidebarSearch, setSidebarSearch] = useState({
|
||||||
searchTerm: '',
|
searchTerm: '',
|
||||||
|
@ -38,9 +39,15 @@ const SearchProperties = () => {
|
||||||
}
|
}
|
||||||
const fetchListings = async () => {
|
const fetchListings = async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
setShowMore(false)
|
||||||
const searchQuery = urlParams.toString();
|
const searchQuery = urlParams.toString();
|
||||||
const res = await fetch(`/server/listing/get?${searchQuery}`);
|
const res = await fetch(`/server/listing/get?${searchQuery}`);
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
|
if(data.length > 11){
|
||||||
|
showMore(true);
|
||||||
|
} else{
|
||||||
|
setShowMore(false);
|
||||||
|
}
|
||||||
setListings(data);
|
setListings(data);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
};
|
};
|
||||||
|
@ -81,10 +88,25 @@ const SearchProperties = () => {
|
||||||
navigate(`/search?${searchQuery}`);
|
navigate(`/search?${searchQuery}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//show more functionality
|
||||||
|
const onShowMore = async () => {
|
||||||
|
const numberOfListings = listings.length;
|
||||||
|
const startIndex = numberOfListings;
|
||||||
|
const urlParams = new URLSearchParams(location.search);
|
||||||
|
urlParams.set('startIndex', startIndex);
|
||||||
|
const searchQuery = urlParams.toString();
|
||||||
|
const res = await fetch(`/server/listing/get?${searchQuery}`);
|
||||||
|
const data = await res.json();
|
||||||
|
if (data.length < 12) {
|
||||||
|
setShowMore(false);
|
||||||
|
}
|
||||||
|
setListings([...listings, ...data]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col md:flex-row">
|
<div className="flex flex-col lg:flex-row">
|
||||||
<div className="p-10 border-b-2 md:border-r-2 md:min-h-screen">
|
<div className="p-8 border-b-2 md:border-r-2 lg:min-h-screen">
|
||||||
<form onSubmit={handleSubmit} className="flex flex-col gap-5">
|
<form onSubmit={handleSubmit} className="flex flex-col gap-5">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<label className="whitespace-nowrap font-semibold text-blue-900">Search: </label>
|
<label className="whitespace-nowrap font-semibold text-blue-900">Search: </label>
|
||||||
|
@ -135,8 +157,8 @@ const SearchProperties = () => {
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<h1 className="text-2xl font-semibold border-b p-10 text-blue-900 capitalize">Listing results</h1>
|
<h1 className="text-2xl font-semibold border-b p-8 text-blue-900 capitalize">Listing results</h1>
|
||||||
<div className="flex flex-wrap p-10 gap-4">
|
<div className="flex flex-wrap p-8 gap-4">
|
||||||
{!loading && listings.length === 0 &&(
|
{!loading && listings.length === 0 &&(
|
||||||
<p className="text-red-700 text-md font-semibold tracking-wider capitalize ">No listing found</p>
|
<p className="text-red-700 text-md font-semibold tracking-wider capitalize ">No listing found</p>
|
||||||
)}
|
)}
|
||||||
|
@ -144,6 +166,10 @@ const SearchProperties = () => {
|
||||||
<p className="text-blue-900 text-md font-semibold tracking-wider capitalize text-center w-full">loading...</p>
|
<p className="text-blue-900 text-md font-semibold tracking-wider capitalize text-center w-full">loading...</p>
|
||||||
)}
|
)}
|
||||||
{!loading && listings && listings.map((listing) => (<ListingCard key={listing._id} listing={listing}/>))}
|
{!loading && listings && listings.map((listing) => (<ListingCard key={listing._id} listing={listing}/>))}
|
||||||
|
|
||||||
|
{showMore &&(
|
||||||
|
<button onClick={onShowMore} className="w-full text-center text-blue-900 hover:underline p-10">Show more</button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Add table
Reference in a new issue