├── server
├── .env
├── config
│ ├── prismaConfig.js
│ └── auth0Config.js
├── vercel.json
├── routes
│ ├── residencyRoute.js
│ └── userRoute.js
├── package.json
├── index.js
├── .gitignore
├── prisma
│ └── schema.prisma
├── controllers
│ ├── resdCntrl.js
│ └── userCntrl.js
├── data
│ └── Residency.json
└── yarn.lock
├── client
├── vercel.json
├── public
│ ├── logo.png
│ ├── r1.png
│ ├── r2.png
│ ├── r3.png
│ ├── logo2.png
│ ├── realty.png
│ ├── tower.png
│ ├── value.png
│ ├── contact.jpg
│ ├── equinix.png
│ ├── prologis.png
│ └── hero-image.png
├── src
│ ├── context
│ │ └── UserDetailContext.js
│ ├── components
│ │ ├── Companies
│ │ │ ├── Companies.css
│ │ │ └── Companies.jsx
│ │ ├── UploadImage
│ │ │ ├── UploadImage.css
│ │ │ └── UploadImage.jsx
│ │ ├── Footer
│ │ │ ├── Footer.css
│ │ │ └── Footer.jsx
│ │ ├── GetStarted
│ │ │ ├── GetStarted.css
│ │ │ └── GetStarted.jsx
│ │ ├── SearchBar
│ │ │ └── SearchBar.jsx
│ │ ├── Map
│ │ │ └── Map.jsx
│ │ ├── Residencies
│ │ │ ├── Residencies.css
│ │ │ └── Residencies.jsx
│ │ ├── Header
│ │ │ ├── Header.css
│ │ │ └── Header.jsx
│ │ ├── PropertyCard
│ │ │ ├── PropertyCard.jsx
│ │ │ └── PropertyCard.css
│ │ ├── ProfileMenu
│ │ │ └── ProfileMenu.jsx
│ │ ├── Value
│ │ │ ├── Value.css
│ │ │ └── Value.jsx
│ │ ├── GeoCoderMarker
│ │ │ └── GeoCoderMarker.jsx
│ │ ├── Contact
│ │ │ ├── Contact.css
│ │ │ └── Contact.jsx
│ │ ├── Heart
│ │ │ └── Heart.jsx
│ │ ├── Layout
│ │ │ └── Layout.jsx
│ │ ├── BookingModal
│ │ │ └── BookingModal.jsx
│ │ ├── Hero
│ │ │ ├── Hero.css
│ │ │ └── Hero.jsx
│ │ ├── BasicDetails
│ │ │ └── BasicDetails.jsx
│ │ ├── AddLocation
│ │ │ └── AddLocation.jsx
│ │ ├── AddPropertyModal
│ │ │ └── AddPropertyModal.jsx
│ │ └── Facilities
│ │ │ └── Facilities.jsx
│ ├── App.css
│ ├── hooks
│ │ ├── useCountries.jsx
│ │ ├── useProperties.jsx
│ │ ├── useAuthCheck.jsx
│ │ ├── useHeaderColor.jsx
│ │ ├── useFavourites.jsx
│ │ └── useBookings.jsx
│ ├── pages
│ │ ├── Properties
│ │ │ ├── Properties.css
│ │ │ └── Properties.jsx
│ │ ├── Website.jsx
│ │ ├── Property
│ │ │ ├── Property.css
│ │ │ └── Property.jsx
│ │ ├── Favourites
│ │ │ └── Favourites.jsx
│ │ └── Bookings
│ │ │ └── Bookings.jsx
│ ├── main.jsx
│ ├── utils
│ │ ├── common.js
│ │ ├── accordion.jsx
│ │ ├── slider.json
│ │ └── api.js
│ ├── App.jsx
│ └── index.css
├── vite.config.js
├── .gitignore
├── index.html
└── package.json
├── Project snip
├── Like.JPG
├── DashBoard.JPG
└── Adding property.JPG
└── README.md
/server/.env:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/client/vercel.json:
--------------------------------------------------------------------------------
1 | { "rewrites": [{ "source": "/(.*)", "destination": "/" }] }
--------------------------------------------------------------------------------
/Project snip/Like.JPG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prazivi/Real_State_FullStack/HEAD/Project snip/Like.JPG
--------------------------------------------------------------------------------
/client/public/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prazivi/Real_State_FullStack/HEAD/client/public/logo.png
--------------------------------------------------------------------------------
/client/public/r1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prazivi/Real_State_FullStack/HEAD/client/public/r1.png
--------------------------------------------------------------------------------
/client/public/r2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prazivi/Real_State_FullStack/HEAD/client/public/r2.png
--------------------------------------------------------------------------------
/client/public/r3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prazivi/Real_State_FullStack/HEAD/client/public/r3.png
--------------------------------------------------------------------------------
/client/public/logo2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prazivi/Real_State_FullStack/HEAD/client/public/logo2.png
--------------------------------------------------------------------------------
/client/public/realty.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prazivi/Real_State_FullStack/HEAD/client/public/realty.png
--------------------------------------------------------------------------------
/client/public/tower.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prazivi/Real_State_FullStack/HEAD/client/public/tower.png
--------------------------------------------------------------------------------
/client/public/value.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prazivi/Real_State_FullStack/HEAD/client/public/value.png
--------------------------------------------------------------------------------
/Project snip/DashBoard.JPG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prazivi/Real_State_FullStack/HEAD/Project snip/DashBoard.JPG
--------------------------------------------------------------------------------
/client/public/contact.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prazivi/Real_State_FullStack/HEAD/client/public/contact.jpg
--------------------------------------------------------------------------------
/client/public/equinix.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prazivi/Real_State_FullStack/HEAD/client/public/equinix.png
--------------------------------------------------------------------------------
/client/public/prologis.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prazivi/Real_State_FullStack/HEAD/client/public/prologis.png
--------------------------------------------------------------------------------
/client/public/hero-image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prazivi/Real_State_FullStack/HEAD/client/public/hero-image.png
--------------------------------------------------------------------------------
/Project snip/Adding property.JPG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prazivi/Real_State_FullStack/HEAD/Project snip/Adding property.JPG
--------------------------------------------------------------------------------
/server/config/prismaConfig.js:
--------------------------------------------------------------------------------
1 | import { PrismaClient } from '@prisma/client'
2 | const prisma = new PrismaClient()
3 |
4 | export {prisma}
--------------------------------------------------------------------------------
/client/src/context/UserDetailContext.js:
--------------------------------------------------------------------------------
1 | import {createContext} from 'react'
2 |
3 | const UserDetailContext = createContext()
4 |
5 | export default UserDetailContext
--------------------------------------------------------------------------------
/client/src/components/Companies/Companies.css:
--------------------------------------------------------------------------------
1 | .c-container{
2 | justify-content: space-around;
3 | gap: 1rem;
4 | }
5 | .c-container>img{
6 | width: 9rem;
7 | }
--------------------------------------------------------------------------------
/client/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | })
8 |
--------------------------------------------------------------------------------
/server/vercel.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 2,
3 | "name": "node-js",
4 | "builds": [
5 | { "src": "index.js", "use": "@vercel/node" }
6 | ],
7 | "routes": [
8 | { "src": "/(.*)", "dest": "/" }
9 | ]
10 | }
--------------------------------------------------------------------------------
/server/config/auth0Config.js:
--------------------------------------------------------------------------------
1 | import {auth} from 'express-oauth2-jwt-bearer'
2 |
3 | const jwtCheck = auth({
4 | audience: "http://localhost:8000",
5 | issuerBaseURL: "https://dev-03ifqltxbr6nn0hn.us.auth0.com",
6 | tokenSigningAlg: "RS256"
7 | })
8 |
9 | export default jwtCheck
--------------------------------------------------------------------------------
/client/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/client/src/App.css:
--------------------------------------------------------------------------------
1 | .App{
2 | position: relative;
3 | overflow-x: clip;
4 | background-color: white;
5 | }
6 | .App>:nth-child(1)
7 | {
8 | background: var(--black);
9 | position: relative;
10 | }
11 | .white-gradient{
12 | position: absolute;
13 | width: 20rem;
14 | height: 20rem;
15 | background: rgba(255, 255, 255, 0.522);
16 | filter: blur(100px);
17 | border-radius: 100px;
18 | }
--------------------------------------------------------------------------------
/server/routes/residencyRoute.js:
--------------------------------------------------------------------------------
1 | import express from "express";
2 | import { createResidency, getAllResidencies, getResidency } from "../controllers/resdCntrl.js";
3 | import jwtCheck from "../config/auth0Config.js";
4 | const router = express.Router();
5 |
6 | router.post("/create", jwtCheck, createResidency)
7 | router.get("/allresd", getAllResidencies)
8 | router.get("/:id", getResidency)
9 | export {router as residencyRoute}
--------------------------------------------------------------------------------
/client/src/hooks/useCountries.jsx:
--------------------------------------------------------------------------------
1 | import countries from "world-countries";
2 |
3 | const formattedCountries = countries.map((country)=> ({
4 | value: country.name.common,
5 | label: `${country.name.common} ${country.flag}`,
6 | latlng: country.latlng,
7 | region: country.region
8 | }))
9 |
10 | const useCountries = ()=> {
11 | const getAll = ()=> formattedCountries;
12 | return {getAll}
13 | }
14 |
15 | export default useCountries
--------------------------------------------------------------------------------
/client/src/components/UploadImage/UploadImage.css:
--------------------------------------------------------------------------------
1 | .uploadWrapper{
2 | margin-top: 3rem;
3 | gap: 2rem;
4 | }
5 |
6 | .uploadZone{
7 | width: 80%;
8 | height: 25rem;
9 | border: 2px dashed grey;
10 | cursor: pointer;
11 | }
12 | .uploadedImage{
13 | width: 80%;
14 | height: 25rem;
15 | border-radius: 10px;
16 | cursor: pointer;
17 | overflow: hidden;
18 | }
19 | .uploadedImage>img{
20 | width: 100%;
21 | height: 100%;
22 | object-fit: cover;
23 | }
--------------------------------------------------------------------------------
/client/src/pages/Properties/Properties.css:
--------------------------------------------------------------------------------
1 | .properties-container{
2 | gap: 2rem;
3 | }
4 | .properties-container .search-bar{
5 | width: clamp(12rem, 100%, 28rem);
6 | border-radius: 30px;
7 | border: 1px solid rgba(120, 120, 120, 0.374);
8 | flex-wrap: nowrap;
9 | }
10 |
11 | .properties-container .search-bar .button{
12 | border-radius: 30px;
13 | font-size: 12px;
14 | padding: 10px 15px;
15 | }
16 |
17 | .properties-container .search-bar input{
18 | width: 70%;
19 | }
--------------------------------------------------------------------------------
/client/src/hooks/useProperties.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { useQuery } from "react-query";
3 | import { getAllProperties } from "../utils/api";
4 |
5 | const useProperties = () => {
6 | const { data, isLoading, isError, refetch } = useQuery(
7 | "allProperties",
8 | getAllProperties,
9 | { refetchOnWindowFocus: false }
10 | );
11 |
12 | return {
13 | data,
14 | isError,
15 | isLoading,
16 | refetch,
17 | };
18 | };
19 |
20 | export default useProperties;
21 |
--------------------------------------------------------------------------------
/client/src/components/Companies/Companies.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import './Companies.css'
3 | const Companies = () => {
4 | return (
5 |
13 | );
14 | };
15 |
16 | export default Companies;
17 |
--------------------------------------------------------------------------------
/client/src/hooks/useAuthCheck.jsx:
--------------------------------------------------------------------------------
1 | import {useAuth0} from '@auth0/auth0-react'
2 | import { toast } from "react-toastify";
3 |
4 |
5 | const useAuthCheck = () => {
6 |
7 | const {isAuthenticated} = useAuth0()
8 | const validateLogin = () => {
9 | if(!isAuthenticated)
10 | {
11 | toast.error("you must be logged in", {position: "bottom-right"})
12 | return false
13 | } else return true
14 | }
15 | return (
16 | {
17 | validateLogin
18 | }
19 | )
20 | }
21 |
22 | export default useAuthCheck
--------------------------------------------------------------------------------
/client/src/components/Footer/Footer.css:
--------------------------------------------------------------------------------
1 | .f-container{
2 | justify-content: space-between;
3 | border-top: .5px solid rgba(0,0,0,0.148)
4 | }
5 | .f-left{
6 | gap: 1rem;
7 | }
8 | .f-menu{
9 | gap: 1.5rem;
10 | margin-top: 1.5rem;
11 | font-weight: 500;
12 | }
13 | @media (max-width: 768px) {
14 | .f-container{
15 | justify-content: center;
16 | }
17 | .f-container>div{
18 | align-items: center!important;
19 | text-align: center;
20 | }
21 | .f-right{
22 | justify-content: center;
23 | }
24 | }
--------------------------------------------------------------------------------
/client/src/components/GetStarted/GetStarted.css:
--------------------------------------------------------------------------------
1 | .inner-container{
2 | gap: 1.5rem;
3 | background: #4161df;
4 | padding: 2rem;
5 | border-radius: 10px;
6 | border: 6px solid #5d77d6;
7 | text-align: center;
8 | }
9 |
10 | .inner-container .primaryText{
11 | color: white;
12 | font-weight: 600;
13 | }
14 | .inner-container .secondaryText{
15 | color: rgba(255, 255, 255, 0.587);
16 | }
17 | .inner-container .button {
18 | background: #5a73d7;
19 | border: 2px solid white;
20 | box-shadow: var(--shadow);
21 | border-radius: 10px;
22 | }
--------------------------------------------------------------------------------
/server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "server",
3 | "version": "1.0.0",
4 | "main": "index.js",
5 | "license": "MIT",
6 | "type": "module",
7 |
8 | "scripts": {
9 | "start": "nodemon index.js",
10 | "postinstall": "prisma generate"
11 | },
12 | "dependencies": {
13 | "@prisma/client": "^4.16.2",
14 | "cookie-parser": "^1.4.6",
15 | "cors": "^2.8.5",
16 | "dotenv": "^16.3.1",
17 | "express": "^4.18.2",
18 | "express-async-handler": "^1.2.0",
19 | "express-oauth2-jwt-bearer": "^1.5.0",
20 | "nodemon": "^2.0.22",
21 | "prisma": "^4.16.2"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/client/src/components/SearchBar/SearchBar.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { HiLocationMarker } from "react-icons/hi";
3 |
4 | const SearchBar = ({ filter, setFilter }) => {
5 | return (
6 |
7 |
8 | setFilter(e.target.value)}
13 | />
14 | Search
15 |
16 | );
17 | };
18 |
19 | export default SearchBar;
20 |
--------------------------------------------------------------------------------
/server/index.js:
--------------------------------------------------------------------------------
1 | import express from 'express';
2 | import dotenv from 'dotenv';
3 | import cookieParser from 'cookie-parser';
4 | import cors from 'cors';
5 | import { userRoute } from './routes/userRoute.js';
6 | import { residencyRoute } from './routes/residencyRoute.js';
7 | dotenv.config()
8 |
9 | const app = express();
10 |
11 | const PORT = process.env.PORT || 3000;
12 |
13 | app.use(express.json())
14 | app.use(cookieParser())
15 | app.use(cors())
16 |
17 | app.listen(PORT, ()=> {
18 | console.log(`Server is running on port ${PORT}`);
19 | });
20 |
21 | app.use('/api/user', userRoute)
22 | app.use("/api/residency", residencyRoute)
--------------------------------------------------------------------------------
/client/src/hooks/useHeaderColor.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from "react";
2 |
3 | const useHeaderColor = () => {
4 | const [headerColor, setHeaderColor] = useState(false)
5 | //to handle shadow of header
6 | useEffect(() => {
7 | function handleScroll() {
8 | if (window.scrollY > 8) {
9 | setHeaderColor("#302e2e")
10 | } else {
11 | setHeaderColor("none");
12 | }
13 | }
14 | window.addEventListener("scroll", handleScroll);
15 | return () => {
16 | window.removeEventListener("scroll", handleScroll);
17 | };
18 | }, []);
19 |
20 | return headerColor
21 | };
22 |
23 | export default useHeaderColor;
24 |
--------------------------------------------------------------------------------
/server/routes/userRoute.js:
--------------------------------------------------------------------------------
1 | import express from "express";
2 | import {
3 | bookVisit,
4 | cancelBooking,
5 | createUser,
6 | getAllBookings,
7 | getAllFavorites,
8 | toFav,
9 | } from "../controllers/userCntrl.js";
10 | import jwtCheck from "../config/auth0Config.js";
11 | const router = express.Router();
12 |
13 | router.post("/register", jwtCheck, createUser);
14 | router.post("/bookVisit/:id", jwtCheck, bookVisit);
15 | router.post("/allBookings", getAllBookings);
16 | router.post("/removeBooking/:id", jwtCheck, cancelBooking);
17 | router.post("/toFav/:rid", jwtCheck, toFav);
18 | router.post("/allFav/", jwtCheck, getAllFavorites);
19 | export { router as userRoute };
20 |
--------------------------------------------------------------------------------
/client/src/main.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import "./index.css";
4 | import App from "./App";
5 | import { Auth0Provider } from "@auth0/auth0-react";
6 |
7 | ReactDOM.createRoot(document.getElementById("root")).render(
8 |
9 |
18 |
19 |
20 |
21 | );
22 |
--------------------------------------------------------------------------------
/client/src/components/Map/Map.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {MapContainer, TileLayer} from 'react-leaflet'
3 | import GeoCoderMarker from '../GeoCoderMarker/GeoCoderMarker'
4 | const Map = ({address, city, country}) => {
5 | return (
6 |
18 |
19 |
20 |
21 | )
22 | }
23 |
24 | export default Map
--------------------------------------------------------------------------------
/server/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 |
6 | # Runtime data
7 | pids
8 | *.pid
9 | *.seed
10 |
11 | # Directory for instrumented libs generated by jscoverage/JSCover
12 | lib-cov
13 |
14 | # Coverage directory used by tools like istanbul
15 | coverage
16 |
17 | # nyc test coverage
18 | .nyc_output
19 |
20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
21 | .grunt
22 |
23 | # node-waf configuration
24 | .lock-wscript
25 |
26 | # Compiled binary addons (http://nodejs.org/api/addons.html)
27 | build/Release
28 |
29 | # Dependency directories
30 | node_modules
31 | jspm_packages
32 |
33 | # Optional npm cache directory
34 | .npm
35 |
36 | # Optional REPL history
37 | .node_repl_history
38 | .next
--------------------------------------------------------------------------------
/client/src/components/GetStarted/GetStarted.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import "./GetStarted.css";
3 | const GetStarted = () => {
4 | return (
5 |
6 |
7 |
8 |
Get started with Homyz
9 |
10 | Subscribe and find super attractive price quotes from us.
11 |
12 | Find your residence soon
13 |
14 |
15 | Get Started
16 |
17 |
18 |
19 |
20 | );
21 | };
22 |
23 | export default GetStarted;
24 |
--------------------------------------------------------------------------------
/client/src/pages/Website.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Companies from "../components/Companies/Companies";
3 | import Contact from "../components/Contact/Contact";
4 | import Footer from "../components/Footer/Footer";
5 | import GetStarted from "../components/GetStarted/GetStarted";
6 | import Header from "../components/Header/Header";
7 | import Hero from "../components/Hero/Hero";
8 | import Residencies from "../components/Residencies/Residencies";
9 | import Value from "../components/Value/Value";
10 |
11 |
12 | const Website = () => {
13 | return (
14 |
15 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | )
26 | }
27 |
28 | export default Website
--------------------------------------------------------------------------------
/client/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
13 | Real Estate
14 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/client/src/components/Residencies/Residencies.css:
--------------------------------------------------------------------------------
1 | .r-container {
2 | gap: 2rem;
3 | position: relative;
4 | overflow: hidden;
5 | }
6 |
7 | .r-head{
8 | margin-bottom: 2rem;
9 | }
10 |
11 | .swiper-horizontal{
12 | overflow: visible;
13 | }
14 | .r-buttons{
15 | position: absolute;
16 | gap: 1rem;
17 | top: -4rem;
18 | right: 0;
19 | }
20 | .r-buttons button{
21 | font-size: 1.2rem;
22 | padding: .2rem .8rem;
23 | color: blue;
24 | border: none;
25 | border-radius: 5px;
26 | background-color: white;
27 | cursor: pointer;
28 | }
29 | .r-buttons>:nth-child(1)
30 | {
31 | background-color: #EEEEFF;
32 | }
33 | .r-buttons>:nth-child(2)
34 | {
35 | box-shadow: 0px 0px 5px 1px rgba(0, 0, 0, 0.25);
36 | }
37 |
38 |
39 | @media (max-width: 640px) {
40 | .r-head{
41 | align-items: center;
42 | }
43 | .r-buttons{
44 | position: initial;
45 | }
46 | }
--------------------------------------------------------------------------------
/client/src/components/Header/Header.css:
--------------------------------------------------------------------------------
1 | .h-wrapper {
2 | z-index: 99;
3 | position: sticky;
4 | top: 0;
5 | }
6 | .h-container {
7 | padding-top: 1rem;
8 | padding-bottom: 1rem;
9 | color: var(--secondary);
10 | justify-content: space-between;
11 | }
12 |
13 | .h-menu {
14 | gap: 2rem;
15 | }
16 | .h-menu > *:hover {
17 | cursor: pointer;
18 | }
19 | .menu-icon {
20 | display: none;
21 | }
22 |
23 | @media (max-width: 768px) {
24 | .menu-icon {
25 | display: block;
26 | }
27 | .h-menu {
28 | color: var(--black);
29 | position: absolute;
30 | gap: 2rem;
31 | font-weight: 500;
32 | flex-direction: column;
33 | right: 4rem;
34 | top: 3rem;
35 | background: white;
36 | display: flex;
37 | border-radius: 10px;
38 | transition: all 200ms ease;
39 | align-items: flex-start;
40 | padding: 3rem;
41 | box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.05);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/client/src/components/PropertyCard/PropertyCard.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import './PropertyCard.css'
3 | import {AiFillHeart} from 'react-icons/ai'
4 | import {truncate} from 'lodash'
5 | import { useNavigate } from "react-router-dom";
6 | import Heart from "../Heart/Heart";
7 | const PropertyCard = ({card}) => {
8 |
9 | const navigate = useNavigate();
10 | return (
11 | navigate(`../properties/${card.id}`)}
13 | >
14 |
15 |
16 |
17 | $
18 | {card.price}
19 |
20 | {truncate(card.title, {length: 15})}
21 | {truncate(card.description, {length: 80})}
22 |
23 | );
24 | };
25 |
26 | export default PropertyCard;
27 |
--------------------------------------------------------------------------------
/client/src/components/ProfileMenu/ProfileMenu.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {Avatar, Menu} from '@mantine/core'
3 | import { useNavigate } from 'react-router-dom'
4 | const ProfileMenu = ({user, logout}) => {
5 | const navigate = useNavigate()
6 | return (
7 |
8 |
9 |
10 |
11 |
12 | navigate("./favourites", {replace: true})}>
13 | Favourites
14 |
15 |
16 | navigate("./bookings", {replace: true})}>
17 | Bookings
18 |
19 |
20 | {
21 | localStorage.clear();
22 | logout()
23 | }}>
24 | Logout
25 |
26 |
27 |
28 | )
29 | }
30 |
31 | export default ProfileMenu
--------------------------------------------------------------------------------
/client/src/components/PropertyCard/PropertyCard.css:
--------------------------------------------------------------------------------
1 | .r-card {
2 | gap: 0.6rem;
3 | padding: 1rem;
4 | border-radius: 10px;
5 | max-width: max-content;
6 | margin: auto;
7 | transition: all 300ms ease-in;
8 | position: relative;
9 | z-index: 0;
10 | }
11 | .r-card>svg{
12 | position: absolute;
13 | top: 25px;
14 | right: 30px;
15 | z-index: 1;
16 | }
17 |
18 | .r-card > img {
19 | width: 15rem;
20 | height: 10rem;
21 | border-radius: 10px;
22 | }
23 | .r-card > :nth-child(3) {
24 | font-size: 1.2rem;
25 | font-weight: 600;
26 | }
27 | .r-card > :nth-child(4) {
28 | font-size: 1.5rem;
29 | }
30 | .r-card > :nth-child(5) {
31 | font-size: 0.7rem;
32 | width: 15rem;
33 | }
34 |
35 | .r-card:hover {
36 | scale: 1.025;
37 | cursor: pointer;
38 | background: linear-gradient(
39 | 180deg,
40 | rgba(255, 255, 255, 0) 0%,
41 | rgba(136, 160, 255, 0.46) 217.91%
42 | );
43 | box-shadow: 0px 72px 49px -51px rgba(136, 160, 255, 0.21);
44 | }
--------------------------------------------------------------------------------
/client/src/utils/common.js:
--------------------------------------------------------------------------------
1 | export const getMenuStyles = (menuOpened) => {
2 | if (document.documentElement.clientWidth <= 800) {
3 | return { right: !menuOpened && "-100%" };
4 | }
5 | };
6 |
7 | export const sliderSettings = {
8 | slidesPerView: 1,
9 | spaceBetween: 50,
10 | breakpoints: {
11 | 480: {
12 | slidesPerView: 1,
13 | },
14 | 600: {
15 | slidesPerView: 2,
16 | },
17 | 750: {
18 | slidesPerView: 3,
19 | },
20 | 1100: {
21 | slidesPerView: 4,
22 | },
23 | },
24 | };
25 |
26 | export const updateFavourites = (id, favourites) => {
27 | if (favourites.includes(id)) {
28 | return favourites.filter((resId) => resId !== id);
29 | } else {
30 | return [...favourites, id];
31 | }
32 | };
33 |
34 | export const checkFavourites = (id, favourites) => {
35 | return favourites?.includes(id) ? "#fa3e5f" : "white";
36 | };
37 |
38 | export const validateString = (value) => {
39 | return value?.length < 3 || value === null
40 | ? "Must have atleast 3 characters"
41 | : null;
42 | };
43 |
--------------------------------------------------------------------------------
/client/src/hooks/useFavourites.jsx:
--------------------------------------------------------------------------------
1 | import React, { useContext, useEffect, useRef } from "react";
2 | import UserDetailContext from "../context/UserDetailContext";
3 | import { useQuery } from "react-query";
4 | import { useAuth0 } from "@auth0/auth0-react";
5 | import { getAllFav } from "../utils/api";
6 |
7 | const useFavourites = () => {
8 | const { userDetails, setUserDetails } = useContext(UserDetailContext);
9 | const queryRef = useRef();
10 | const { user } = useAuth0();
11 |
12 | const { data, isLoading, isError, refetch } = useQuery({
13 | queryKey: "allFavourites",
14 | queryFn: () => getAllFav(user?.email, userDetails?.token),
15 | onSuccess: (data) =>
16 | setUserDetails((prev) => ({ ...prev, favourites: data })),
17 | enabled: user !== undefined,
18 | staleTime: 30000,
19 | });
20 |
21 | queryRef.current = refetch;
22 |
23 | useEffect(() => {
24 | queryRef.current && queryRef.current();
25 | }, [userDetails?.token]);
26 |
27 | return { data, isError, isLoading, refetch };
28 | };
29 |
30 | export default useFavourites;
31 |
--------------------------------------------------------------------------------
/client/src/utils/accordion.jsx:
--------------------------------------------------------------------------------
1 | import { HiShieldCheck } from "react-icons/hi";
2 | import { MdCancel, MdAnalytics } from "react-icons/md";
3 | const data = [
4 | {
5 | icon: ,
6 | heading: "Best interest rates on the market",
7 | detail:
8 | "Exercitation in fugiat est ut ad ea cupidatat ut in cupidatat occaecat ut occaecat consequat est minim minim esse tempor laborum consequat esse adipisicing eu reprehenderit enim.",
9 | },
10 | {
11 | icon: ,
12 | heading: "Prevent unstable prices",
13 | detail:
14 | "Exercitation in fugiat est ut ad ea cupidatat ut in cupidatat occaecat ut occaecat consequat est minim minim esse tempor laborum consequat esse adipisicing eu reprehenderit enim.",
15 | },
16 | {
17 | icon: ,
18 | heading: "Best price on the market",
19 | detail:
20 | "Exercitation in fugiat est ut ad ea cupidatat ut in cupidatat occaecat ut occaecat consequat est minim minim esse tempor laborum consequat esse adipisicing eu reprehenderit enim.",
21 | },
22 | ];
23 | export default data;
--------------------------------------------------------------------------------
/client/src/hooks/useBookings.jsx:
--------------------------------------------------------------------------------
1 | import React, { useContext, useEffect, useRef } from "react";
2 | import UserDetailContext from "../context/UserDetailContext";
3 | import { useQuery } from "react-query";
4 | import { useAuth0 } from "@auth0/auth0-react";
5 | import { getAllBookings, getAllFav } from "../utils/api";
6 |
7 | const useBookings = () => {
8 | const { userDetails, setUserDetails } = useContext(UserDetailContext);
9 | const queryRef = useRef();
10 | const { user } = useAuth0();
11 |
12 | const { data, isLoading, isError, refetch } = useQuery({
13 | queryKey: "allBookings",
14 | queryFn: () => getAllBookings(user?.email, userDetails?.token),
15 | onSuccess: (data) =>
16 | setUserDetails((prev) => ({ ...prev, bookings: data })),
17 | enabled: user !== undefined,
18 | staleTime: 30000,
19 | });
20 |
21 | queryRef.current = refetch;
22 |
23 | useEffect(() => {
24 | queryRef.current && queryRef.current();
25 | }, [userDetails?.token]);
26 |
27 | return { data, isError, isLoading, refetch };
28 | };
29 |
30 | export default useBookings;
31 |
--------------------------------------------------------------------------------
/client/src/components/Footer/Footer.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import "./Footer.css";
3 | const Footer = () => {
4 | return (
5 |
6 |
7 | {/* left side */}
8 |
9 |
10 |
11 | Our vision is to make all people
12 | the best place to live for them.
13 |
14 |
15 |
16 |
17 |
Information
18 |
145 New York, FL 5467, USA
19 |
20 | Property
21 | Services
22 | Product
23 | About Us
24 |
25 |
26 |
27 |
28 | );
29 | };
30 |
31 | export default Footer;
32 |
--------------------------------------------------------------------------------
/server/prisma/schema.prisma:
--------------------------------------------------------------------------------
1 | generator client {
2 | provider = "prisma-client-js"
3 | }
4 |
5 | datasource db {
6 | provider = "mongodb"
7 | url = env("DATABASE_URL")
8 | }
9 |
10 | model User {
11 | id String @id @default(auto()) @map("_id") @db.ObjectId
12 | name String?
13 | email String @unique
14 | image String?
15 | bookedVisits Json[]
16 | favResidenciesID String[] @db.ObjectId
17 | ownedResidencies Residency[] @relation("Owner")
18 | }
19 |
20 | model Residency {
21 | id String @id @default(auto()) @map("_id") @db.ObjectId
22 | title String
23 | description String
24 | price Int
25 | address String
26 | city String
27 | country String
28 | image String
29 | facilities Json
30 | userEmail String
31 | owner User @relation("Owner", fields: [userEmail], references: [email])
32 | createdAt DateTime @default(now())
33 | updatedAt DateTime @updatedAt
34 |
35 | @@unique(fields: [address, userEmail])
36 | }
37 |
--------------------------------------------------------------------------------
/client/src/components/Value/Value.css:
--------------------------------------------------------------------------------
1 | .v-container .image-container {
2 | border: 8px solid rgb(232 232 232 / 93%);
3 | }
4 | .v-container > div {
5 | flex: 1;
6 | }
7 | .v-right {
8 | gap: 0.5rem;
9 | }
10 | .accordion {
11 | margin-top: 2rem;
12 | border: none;
13 | }
14 | .accordionItem {
15 | background: white;
16 | border: 0.8px solid rgba(128, 128, 128, 0.143);
17 | border-radius: 8px;
18 | overflow: hidden;
19 | margin-bottom: 20px;
20 | }
21 |
22 | .accordionItem.expanded {
23 | box-shadow: var(--shadow);
24 | border-radius: 6px;
25 | }
26 | .accordionButton {
27 | background: white;
28 | padding: 1rem;
29 | width: 100%;
30 | justify-content: space-between;
31 | cursor: pointer;
32 | }
33 |
34 | .icon {
35 | padding: 10px;
36 | background: #eeeeff;
37 | border-radius: 5px;
38 | }
39 | .icon svg {
40 | fill: var(--blue);
41 | }
42 |
43 | .accordionButton .primaryText{
44 | font-size: 1.1rem;
45 | }
46 |
47 | @media (max-width: 1024px) {
48 | .v-container{
49 | flex-direction: column;
50 | }
51 | }
52 |
53 | @media (max-width: 768px) {
54 | .accordionButton .primaryText{
55 | font-size: .8rem;
56 | }
57 | }
--------------------------------------------------------------------------------
/client/src/components/GeoCoderMarker/GeoCoderMarker.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from 'react'
2 | import { Marker, Popup, useMap } from 'react-leaflet'
3 | import L from 'leaflet'
4 | import "leaflet/dist/leaflet.css"
5 | import icon from "leaflet/dist/images/marker-icon.png";
6 | import iconShadow from "leaflet/dist/images/marker-shadow.png";
7 | import * as ELG from 'esri-leaflet-geocoder'
8 |
9 | let DefaulIcon = L.icon ({
10 | iconUrl : icon,
11 | shadowUrl: iconShadow
12 | })
13 | L.Marker.prototype.options.icon = DefaulIcon
14 |
15 |
16 | const GeoCoderMarker = ({address}) => {
17 |
18 | const map = useMap()
19 | const [position, setPosition] = useState([60, 19])
20 |
21 | useEffect(()=> {
22 | ELG.geocode().text(address).run((err, results, response)=> {
23 | if(results?.results?.length > 0){
24 | const {lat, lng} = results?.results[0].latlng
25 | setPosition([lat, lng])
26 | map.flyTo([lat, lng], 6)
27 | }
28 | })
29 | }, [address])
30 |
31 | return (
32 |
33 |
34 |
35 | )
36 | }
37 |
38 | export default GeoCoderMarker
--------------------------------------------------------------------------------
/client/src/utils/slider.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "name": "Aliva Priva Jardin",
4 | "price": "47,043",
5 | "detail": "Jakarta Garden City Street, Cakung. Pulo Gadung, Jakarta Timur, DKI Jakarta",
6 | "image": "./r1.png"
7 | },
8 | {
9 | "name": "Asatti Garden City",
10 | "price": "66,353",
11 | "detail": "Pahlawan Street XVII No.215, Cinangka, Sawangan, Depok, Jawa Barat",
12 | "image": "./r2.png"
13 | },
14 | {
15 | "name": "Citralan Puri Serang",
16 | "price": "35,853",
17 | "detail": "Ruko Puri Indah Residence Block A7, Lingkar Street, Ciracas, Serang, Banten",
18 | "image": "./r3.png"
19 | },
20 | {
21 | "name": "Aliva Priva Jardin",
22 | "price": "47,043",
23 | "detail": "Jakarta Garden City Street, Cakung. Pulo Gadung, Jakarta Timur, DKI Jakarta",
24 | "image": "./r1.png"
25 | },
26 | {
27 | "name": "Asatti Garden City",
28 | "price": "66,353",
29 | "detail": "Pahlawan Street XVII No.215, Cinangka, Sawangan, Depok, Jawa Barat",
30 | "image": "./r2.png"
31 | },
32 | {
33 | "name": "Citralan Puri Serang",
34 | "price": "35,853",
35 | "detail": "Ruko Puri Indah Residence Block A7, Lingkar Street, Ciracas, Serang, Banten",
36 | "image": "./r3.png"
37 | }
38 | ]
39 |
--------------------------------------------------------------------------------
/client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "realestate",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "preview": "vite preview"
10 | },
11 | "dependencies": {
12 | "@auth0/auth0-react": "2.0.1",
13 | "@emotion/react": "^11.11.1",
14 | "@mantine/core": "^6.0.16",
15 | "@mantine/dates": "^6.0.16",
16 | "@mantine/form": "^6.0.16",
17 | "@mantine/hooks": "^6.0.16",
18 | "axios": "^1.4.0",
19 | "dayjs": "^1.11.9",
20 | "esri-leaflet-geocoder": "2.3.3",
21 | "framer-motion": "^10.8.5",
22 | "leaflet": "^1.9.4",
23 | "lodash": "^4.17.21",
24 | "react": "^18.2.0",
25 | "react-accessible-accordion": "^5.0.0",
26 | "react-countup": "^6.4.2",
27 | "react-dom": "^18.2.0",
28 | "react-icons": "^4.8.0",
29 | "react-leaflet": "4.2.1",
30 | "react-outside-click-handler": "^1.3.0",
31 | "react-query": "^3.39.3",
32 | "react-router-dom": "6.10.0",
33 | "react-spinners": "^0.13.8",
34 | "react-toastify": "^9.1.3",
35 | "swiper": "^9.1.1",
36 | "world-countries": "^4.0.0"
37 | },
38 | "devDependencies": {
39 | "@types/react": "^18.0.28",
40 | "@types/react-dom": "^18.0.11",
41 | "@vitejs/plugin-react": "^3.1.0",
42 | "vite": "^4.2.0"
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/client/src/pages/Property/Property.css:
--------------------------------------------------------------------------------
1 | .property-container{
2 | gap: 2rem;
3 | position: relative;
4 | }
5 |
6 | .property-container .like{
7 | position: absolute;
8 | top: 3rem;right: 3rem;
9 | cursor: pointer;
10 | }
11 |
12 |
13 | .property-container>img{
14 | align-self: center;
15 | max-height: 35rem;
16 | width: 100%;
17 | border-radius: 1rem;
18 | object-fit: cover;
19 | }
20 |
21 | .property-details{
22 | width: 100%;
23 | gap: 2rem;
24 | justify-content: space-between;
25 | }
26 |
27 | .property-details>div{
28 | flex: 1;
29 | gap: 1.5rem;
30 | }
31 |
32 | .property-details .left .head{
33 | justify-content: space-between;
34 | width: 100%;
35 | }
36 |
37 | .facilities{
38 | gap: 1rem;
39 | font-size: 0.9rem;
40 | padding: 1rem;
41 | }
42 |
43 | .facility{
44 | gap: 0.5rem;
45 | }
46 |
47 | .property-details .button{
48 | width: 100%;
49 | margin-top: 1rem;
50 | }
51 |
52 | .property-details .button:hover{
53 | transform: scale(1.05);
54 | }
55 |
56 |
57 | @media (max-width: 700px) {
58 | .property-container>div{
59 | flex-direction: column;
60 | }
61 | .head{
62 | flex-direction: column;
63 | }
64 | .facilities{
65 | flex-direction: column;
66 | align-items: flex-start;
67 | }
68 | .property-details>div{
69 | width: 100%;
70 | }
71 | }
--------------------------------------------------------------------------------
/client/src/components/Contact/Contact.css:
--------------------------------------------------------------------------------
1 | .c-container {
2 | justify-content: space-between;
3 | }
4 | .c-container > div {
5 | flex: 1;
6 | }
7 | .c-right {
8 | width: 100%;
9 | }
10 | .c-left {
11 | gap: 0.5rem;
12 | }
13 | .contactModes {
14 | margin-top: 2rem;
15 | gap: 1rem;
16 | }
17 | .contactModes .row {
18 | gap: 1.5rem;
19 | }
20 | .mode {
21 | width: 16rem;
22 | padding: 1rem;
23 | border: 0.8px solid rgba(128, 128, 128, 0.143);
24 | border-radius: 5px;
25 | gap: 1rem;
26 | transition: all 300ms ease-in;
27 | }
28 | .mode .button {
29 | width: 100%;
30 | background: var(--lightBlue);
31 | color: var(--blue);
32 | font-size: 0.9rem;
33 | font-weight: 600;
34 | }
35 | .mode > :nth-child(1) {
36 | width: 100%;
37 | gap: 1.6rem;
38 | }
39 | .mode .detail .primaryText {
40 | font-size: 1.1rem;
41 | font-weight: 600;
42 | }
43 |
44 | .mode:hover {
45 | scale: 1.1;
46 | box-shadow: var(--shadow);
47 | }
48 |
49 | .mode .button:hover {
50 | background: var(--blue-gradient);
51 | color: white;
52 | scale: 0.8;
53 | }
54 |
55 | @media (max-width: 1024px) {
56 | .c-container {
57 | flex-direction: column;
58 | }
59 | .c-right {
60 | justify-content: center;
61 | }
62 | }
63 | @media (max-width: 1024px)
64 | {
65 | .contactModes{
66 | width: 100%;
67 | }
68 | .row{
69 | flex-direction: column;
70 | width: 100%;
71 | }
72 | .mode{
73 | width: 100%;
74 | }
75 | }
--------------------------------------------------------------------------------
/client/src/components/Heart/Heart.jsx:
--------------------------------------------------------------------------------
1 | import { useContext, useEffect, useState } from "react"
2 | import { AiFillHeart } from "react-icons/ai"
3 | import useAuthCheck from "../../hooks/useAuthCheck"
4 | import { useMutation } from "react-query"
5 | import { useAuth0 } from "@auth0/auth0-react"
6 | import UserDetailContext from "../../context/UserDetailContext"
7 | import { checkFavourites, updateFavourites } from "../../utils/common"
8 | import { toFav } from "../../utils/api"
9 |
10 | const Heart = ({id}) => {
11 |
12 | const [heartColor, setHeartColor] = useState("white")
13 | const {validateLogin} = useAuthCheck()
14 | const {user} = useAuth0()
15 |
16 | const {
17 | userDetails: { favourites, token },
18 | setUserDetails,
19 | } = useContext(UserDetailContext);
20 |
21 | useEffect(()=> {
22 | setHeartColor(()=> checkFavourites(id, favourites))
23 | },[favourites])
24 |
25 |
26 | const {mutate} = useMutation({
27 | mutationFn: () => toFav(id, user?.email, token),
28 | onSuccess: ()=> {
29 | setUserDetails((prev)=> (
30 | {
31 | ...prev,
32 | favourites: updateFavourites(id, prev.favourites)
33 | }
34 | ))
35 | }
36 | })
37 |
38 | const handleLike = () => {
39 | if(validateLogin())
40 | {
41 | mutate()
42 | setHeartColor((prev)=> prev === "#fa3e5f" ? "white": "#fa3e5f")
43 | }
44 | }
45 |
46 | return (
47 | {
48 | e.stopPropagation()
49 | handleLike()
50 | }}/>
51 | )
52 | }
53 |
54 | export default Heart
--------------------------------------------------------------------------------
/client/src/components/Layout/Layout.jsx:
--------------------------------------------------------------------------------
1 | import React, { useContext, useEffect } from "react";
2 | import Header from "../Header/Header";
3 | import Footer from "../Footer/Footer";
4 | import { Outlet } from "react-router-dom";
5 | import { useAuth0 } from "@auth0/auth0-react";
6 | import UserDetailContext from "../../context/UserDetailContext";
7 | import { useMutation } from "react-query";
8 | import { createUser } from "../../utils/api";
9 | import useFavourites from "../../hooks/useFavourites";
10 | import useBookings from "../../hooks/useBookings";
11 |
12 | const Layout = () => {
13 |
14 | useFavourites()
15 | useBookings()
16 |
17 | const { isAuthenticated, user, getAccessTokenWithPopup } = useAuth0();
18 | const { setUserDetails } = useContext(UserDetailContext);
19 |
20 | const { mutate } = useMutation({
21 | mutationKey: [user?.email],
22 | mutationFn: (token) => createUser(user?.email, token),
23 | });
24 |
25 | useEffect(() => {
26 | const getTokenAndRegsiter = async () => {
27 |
28 | const res = await getAccessTokenWithPopup({
29 | authorizationParams: {
30 | audience: "http://localhost:8000",
31 | scope: "openid profile email",
32 | },
33 | });
34 | localStorage.setItem("access_token", res);
35 | setUserDetails((prev) => ({ ...prev, token: res }));
36 | mutate(res)
37 | };
38 |
39 |
40 | isAuthenticated && getTokenAndRegsiter();
41 | }, [isAuthenticated]);
42 |
43 | return (
44 | <>
45 |
46 |
47 |
48 |
49 |
50 | >
51 | );
52 | };
53 |
54 | export default Layout;
55 |
--------------------------------------------------------------------------------
/server/controllers/resdCntrl.js:
--------------------------------------------------------------------------------
1 | import asyncHandler from "express-async-handler";
2 |
3 | import { prisma } from "../config/prismaConfig.js";
4 |
5 | export const createResidency = asyncHandler(async (req, res) => {
6 | const {
7 | title,
8 | description,
9 | price,
10 | address,
11 | country,
12 | city,
13 | facilities,
14 | image,
15 | userEmail,
16 | } = req.body.data;
17 |
18 | console.log(req.body.data);
19 | try {
20 | const residency = await prisma.residency.create({
21 | data: {
22 | title,
23 | description,
24 | price,
25 | address,
26 | country,
27 | city,
28 | facilities,
29 | image,
30 | owner: { connect: { email: userEmail } },
31 | },
32 | });
33 |
34 | res.send({ message: "Residency created successfully", residency });
35 | } catch (err) {
36 | if (err.code === "P2002") {
37 | throw new Error("A residency with address already there");
38 | }
39 | throw new Error(err.message);
40 | }
41 | });
42 |
43 | // function to get all the documents/residencies
44 | export const getAllResidencies = asyncHandler(async (req, res) => {
45 | const residencies = await prisma.residency.findMany({
46 | orderBy: {
47 | createdAt: "desc",
48 | },
49 | });
50 | res.send(residencies);
51 | });
52 |
53 | // function to get a specific document/residency
54 | export const getResidency = asyncHandler(async (req, res) => {
55 | const { id } = req.params;
56 |
57 | try {
58 | const residency = await prisma.residency.findUnique({
59 | where: { id },
60 | });
61 | res.send(residency);
62 | } catch (err) {
63 | throw new Error(err.message);
64 | }
65 | });
66 |
--------------------------------------------------------------------------------
/client/src/components/BookingModal/BookingModal.jsx:
--------------------------------------------------------------------------------
1 | import React, { useContext, useState } from "react";
2 | import { Modal, Button } from "@mantine/core";
3 | import { DatePicker } from "@mantine/dates";
4 | import { useMutation } from "react-query";
5 | import UserDetailContext from "../../context/UserDetailContext.js";
6 | import { bookVisit } from "../../utils/api.js";
7 | import { toast } from "react-toastify";
8 | import dayjs from "dayjs";
9 | const BookingModal = ({ opened, setOpened, email, propertyId }) => {
10 | const [value, setValue] = useState(null);
11 | const {
12 | userDetails: { token },
13 | setUserDetails,
14 | } = useContext(UserDetailContext);
15 |
16 | const handleBookingSuccess = () => {
17 | toast.success("You have booked your visit", {
18 | position: "bottom-right",
19 | });
20 | setUserDetails((prev) => ({
21 | ...prev,
22 | bookings: [
23 | ...prev.bookings,
24 | {
25 | id: propertyId,
26 | date: dayjs(value).format("DD/MM/YYYY"),
27 | },
28 | ],
29 | }));
30 | };
31 |
32 | const { mutate, isLoading } = useMutation({
33 | mutationFn: () => bookVisit(value, propertyId, email, token),
34 | onSuccess: () => handleBookingSuccess(),
35 | onError: ({ response }) => toast.error(response.data.message),
36 | onSettled: () => setOpened(false),
37 | });
38 |
39 | return (
40 | setOpened(false)}
43 | title="Select your date of visit"
44 | centered
45 | >
46 |
47 |
48 | mutate()}>
49 | Book visit
50 |
51 |
52 |
53 | );
54 | };
55 |
56 | export default BookingModal;
57 |
--------------------------------------------------------------------------------
/client/src/pages/Properties/Properties.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import SearchBar from "../../components/SearchBar/SearchBar";
3 | import "./Properties.css";
4 | import useProperties from "../../hooks/useProperties";
5 | import { PuffLoader } from "react-spinners";
6 | import PropertyCard from "../../components/PropertyCard/PropertyCard";
7 | const Properties = () => {
8 | const { data, isError, isLoading } = useProperties();
9 | const [filter, setFilter] = useState("");
10 | if (isError) {
11 | return (
12 |
13 | Error while fetching data
14 |
15 | );
16 | }
17 |
18 | if (isLoading) {
19 | return (
20 |
29 | );
30 | }
31 | return (
32 |
33 |
34 |
35 |
36 |
37 | {
38 | // data.map((card, i)=> (
))
39 |
40 | data
41 | .filter(
42 | (property) =>
43 | property.title.toLowerCase().includes(filter.toLowerCase()) ||
44 | property.city.toLowerCase().includes(filter.toLowerCase()) ||
45 | property.country.toLowerCase().includes(filter.toLowerCase())
46 | )
47 | .map((card, i) => (
48 |
49 | ))
50 | }
51 |
52 |
53 |
54 | );
55 | };
56 |
57 | export default Properties;
58 |
--------------------------------------------------------------------------------
/client/src/components/UploadImage/UploadImage.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useRef, useState } from "react";
2 | import { AiOutlineCloudUpload } from "react-icons/ai";
3 | import "./UploadImage.css";
4 | import { Button, Group } from "@mantine/core";
5 | const UploadImage = ({
6 | propertyDetails,
7 | setPropertyDetails,
8 | nextStep,
9 | prevStep,
10 | }) => {
11 | const [imageURL, setImageURL] = useState(propertyDetails.image);
12 | const cloudinaryRef = useRef();
13 | const widgetRef = useRef();
14 | const handleNext = () => {
15 | setPropertyDetails((prev) => ({ ...prev, image: imageURL }));
16 | nextStep();
17 | };
18 | useEffect(() => {
19 | cloudinaryRef.current = window.cloudinary;
20 | widgetRef.current = cloudinaryRef.current.createUploadWidget(
21 | {
22 | cloudName: "dcdhklrjc",
23 | uploadPreset: "vx0dyjgc",
24 | maxFiles: 1,
25 | },
26 | (err, result) => {
27 | if (result.event === "success") {
28 | setImageURL(result.info.secure_url);
29 | }
30 | }
31 | );
32 | }, []);
33 | return (
34 |
35 | {!imageURL ? (
36 |
widgetRef.current?.open()}
39 | >
40 |
41 |
Upload Image
42 |
43 | ) : (
44 |
widgetRef.current?.open()}
47 | >
48 |
49 |
50 | )}
51 |
52 |
53 |
54 | Back
55 |
56 |
57 | Next
58 |
59 |
60 |
61 | );
62 | };
63 |
64 | export default UploadImage;
65 |
--------------------------------------------------------------------------------
/client/src/App.jsx:
--------------------------------------------------------------------------------
1 | import { Suspense, useState } from "react";
2 | import "./App.css";
3 | import Layout from "./components/Layout/Layout";
4 |
5 | import Website from "./pages/Website";
6 | import { BrowserRouter, Routes, Route } from "react-router-dom";
7 | import Properties from "./pages/Properties/Properties";
8 | import { QueryClient, QueryClientProvider } from "react-query";
9 | import { ReactQueryDevtools } from "react-query/devtools";
10 | import { ToastContainer } from "react-toastify";
11 | import "react-toastify/dist/ReactToastify.css";
12 | import Property from "./pages/Property/Property";
13 | import UserDetailContext from "./context/UserDetailContext";
14 | import Bookings from "./pages/Bookings/Bookings";
15 | import Favourites from "./pages/Favourites/Favourites";
16 |
17 | function App() {
18 | const queryClient = new QueryClient();
19 |
20 | const [userDetails, setUserDetails] = useState({
21 | favourites: [],
22 | bookings: [],
23 | token: null,
24 | });
25 |
26 | return (
27 |
28 |
29 |
30 | Loading...}>
31 |
32 | }>
33 | } />
34 |
35 | } />
36 | } />
37 |
38 | } />
39 | } />
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | );
49 | }
50 |
51 | export default App;
52 |
--------------------------------------------------------------------------------
/client/src/components/Hero/Hero.css:
--------------------------------------------------------------------------------
1 | .hero-wrapper {
2 | color: white;
3 | position: relative;
4 | z-index: 1;
5 | padding-bottom: 2rem;
6 | }
7 |
8 |
9 | .hero-container {
10 | justify-content: space-around;
11 | align-items: flex-end;
12 | }
13 |
14 |
15 | /* left side */
16 | .hero-left {
17 | gap: 3rem;
18 | }
19 | .hero-title {
20 | position: relative;
21 | z-index: 1;
22 | }
23 | .hero-title > h1 {
24 | font-weight: 600;
25 | font-size: 3.8rem;
26 | line-height: 4rem;
27 | }
28 | .orange-circle {
29 | height: 4rem;
30 | width: 4rem;
31 | background: var(--orange-gradient);
32 | border-radius: 999px;
33 | position: absolute;
34 | right: 28%;
35 | top: -10%;
36 | z-index: -1;
37 | }
38 | .search-bar {
39 | background-color: white;
40 | border-radius: 5px;
41 | border: 3px solid rgba(120, 120, 120, 0.374);
42 | padding: 0.5rem 1rem;
43 | width: 100%;
44 | justify-content: space-between;
45 | }
46 | .search-bar > input {
47 | border: none;
48 | outline: none;
49 | }
50 | .stats{
51 | width: 100%;
52 | justify-content: space-between;
53 | }
54 | .stat>:nth-child(1)
55 | {
56 | font-size: 2rem;
57 | }
58 | .stat>:nth-child(1)>:last-child{
59 | color: orange;
60 | }
61 | /* right side */
62 |
63 | .image-container {
64 | width: 30rem;
65 | height: 35rem;
66 | overflow: hidden;
67 | border-radius: 15rem 15rem 0 0;
68 | border: 8px solid rgba(255, 255, 255, 0.12);
69 | }
70 | .image-container>img{
71 | width: 100%;
72 | height: 100%;
73 | }
74 |
75 | /* mobile screens */
76 | @media (max-width: 640px){
77 | .hero-container{
78 | margin-top: 2rem;
79 | }
80 | .hero-title>h1{
81 | font-size: 2.5rem;
82 | line-height: 3rem;
83 | }
84 | .image-container{
85 | width: 95%;
86 | height: 25rem;
87 | }
88 | .stats{
89 | justify-content: center;
90 | gap: 1.5rem;
91 | font-weight: wrap;
92 | }
93 | .stat>:nth-child(1){
94 | font-size: 1.5rem;
95 | }
96 | .stat>:nth-child(2)
97 | {
98 | font-size: 0.8rem;
99 | }
100 | }
--------------------------------------------------------------------------------
/client/src/components/Residencies/Residencies.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Swiper, SwiperSlide, useSwiper } from "swiper/react";
3 | // Import Swiper styles
4 | import "swiper/css";
5 | import "./Residencies.css";
6 | import { sliderSettings } from "../../utils/common";
7 | import PropertyCard from "../PropertyCard/PropertyCard";
8 | import useProperties from "../../hooks/useProperties";
9 | import {PuffLoader} from 'react-spinners'
10 |
11 | const Residencies = () => {
12 |
13 | const {data, isError, isLoading} = useProperties()
14 |
15 | if(isError){
16 | return(
17 |
18 | Error while fetching data
19 |
20 | )
21 | }
22 |
23 | if(isLoading){
24 | return(
25 |
34 | )
35 | }
36 |
37 |
38 | return (
39 |
40 |
41 |
42 | Best Choices
43 | Popular Residencies
44 |
45 |
46 |
47 | {/* slider */}
48 | {data.slice(0, 8).map((card, i) => (
49 |
50 |
51 |
52 | ))}
53 |
54 |
55 |
56 | );
57 | };
58 |
59 | export default Residencies;
60 |
61 | const SlideNextButton = () => {
62 | const swiper = useSwiper();
63 | return (
64 |
65 | swiper.slidePrev()} className="r-prevButton">
66 | <
67 |
68 | swiper.slideNext()} className="r-nextButton">
69 | >
70 |
71 |
72 | );
73 | };
74 |
--------------------------------------------------------------------------------
/client/src/components/BasicDetails/BasicDetails.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { TextInput, Box, Textarea, Group, Button, NumberInput } from "@mantine/core";
3 | import { useForm } from "@mantine/form";
4 | import { validateString } from "../../utils/common";
5 |
6 | const BasicDetails = ({ prevStep, nextStep, propertyDetails, setPropertyDetails }) => {
7 | const form = useForm({
8 | initialValues: {
9 | title: propertyDetails.title,
10 | description: propertyDetails.description,
11 | price: propertyDetails.price,
12 | },
13 | validate: {
14 | title: (value) => validateString(value),
15 | description: (value) => validateString(value),
16 | price: (value) =>
17 | value < 1000 ? "Must be greater than 999 dollars" : null,
18 | },
19 | });
20 |
21 | const {title, description, price} = form.values
22 |
23 |
24 | const handleSubmit = ()=> {
25 | const {hasErrors} = form.validate()
26 | if(!hasErrors) {
27 | setPropertyDetails((prev)=> ({...prev, title, description, price}))
28 | nextStep()
29 | }
30 | }
31 | return (
32 |
33 |
65 |
66 | );
67 | };
68 |
69 | export default BasicDetails;
70 |
--------------------------------------------------------------------------------
/client/src/pages/Favourites/Favourites.jsx:
--------------------------------------------------------------------------------
1 | import React, { useContext, useState } from "react";
2 | import SearchBar from "../../components/SearchBar/SearchBar";
3 | import useProperties from "../../hooks/useProperties";
4 | import { PuffLoader } from "react-spinners";
5 | import PropertyCard from "../../components/PropertyCard/PropertyCard";
6 | import "../Properties/Properties.css";
7 | import UserDetailContext from "../../context/UserDetailContext";
8 |
9 | const Favourites = () => {
10 | const { data, isError, isLoading } = useProperties();
11 | const [filter, setFilter] = useState("");
12 | const {
13 | userDetails: { favourites },
14 | } = useContext(UserDetailContext);
15 |
16 | if (isError) {
17 | return (
18 |
19 | Error while fetching data
20 |
21 | );
22 | }
23 |
24 | if (isLoading) {
25 | return (
26 |
35 | );
36 | }
37 | return (
38 |
39 |
40 |
41 |
42 |
43 | {
44 | // data.map((card, i)=> (
))
45 |
46 | data
47 | .filter((property) => favourites.includes(property.id))
48 |
49 | .filter(
50 | (property) =>
51 | property.title.toLowerCase().includes(filter.toLowerCase()) ||
52 | property.city.toLowerCase().includes(filter.toLowerCase()) ||
53 | property.country.toLowerCase().includes(filter.toLowerCase())
54 | )
55 | .map((card, i) => (
56 |
57 | ))
58 | }
59 |
60 |
61 |
62 | );
63 | };
64 |
65 | export default Favourites;
66 |
--------------------------------------------------------------------------------
/client/src/pages/Bookings/Bookings.jsx:
--------------------------------------------------------------------------------
1 | import React, { useContext, useState } from "react";
2 | import SearchBar from "../../components/SearchBar/SearchBar";
3 | import useProperties from "../../hooks/useProperties";
4 | import { PuffLoader } from "react-spinners";
5 | import PropertyCard from "../../components/PropertyCard/PropertyCard";
6 | import "../Properties/Properties.css";
7 | import UserDetailContext from "../../context/UserDetailContext";
8 |
9 | const Bookings = () => {
10 | const { data, isError, isLoading } = useProperties();
11 | const [filter, setFilter] = useState("");
12 | const {
13 | userDetails: { bookings },
14 | } = useContext(UserDetailContext);
15 |
16 | if (isError) {
17 | return (
18 |
19 | Error while fetching data
20 |
21 | );
22 | }
23 |
24 | if (isLoading) {
25 | return (
26 |
35 | );
36 | }
37 | return (
38 |
39 |
40 |
41 |
42 |
43 | {
44 | // data.map((card, i)=> (
))
45 |
46 | data
47 | .filter((property) =>
48 | bookings.map((booking) => booking.id).includes(property.id)
49 | )
50 |
51 | .filter(
52 | (property) =>
53 | property.title.toLowerCase().includes(filter.toLowerCase()) ||
54 | property.city.toLowerCase().includes(filter.toLowerCase()) ||
55 | property.country.toLowerCase().includes(filter.toLowerCase())
56 | )
57 | .map((card, i) => (
58 |
59 | ))
60 | }
61 |
62 |
63 |
64 | );
65 | };
66 |
67 | export default Bookings;
68 |
--------------------------------------------------------------------------------
/client/src/components/Hero/Hero.jsx:
--------------------------------------------------------------------------------
1 | import "./Hero.css";
2 | import CountUp from "react-countup";
3 | import { motion } from "framer-motion";
4 | import SearchBar from "../SearchBar/SearchBar";
5 | const Hero = () => {
6 | return (
7 |
8 |
9 | {/* left side */}
10 |
11 |
12 |
13 |
21 | Discover
22 | Most Suitable
23 | Property
24 |
25 |
26 |
27 | Find a variety of properties that suit you very easilty
28 | Forget all difficulties in finding a residence for you
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | +
37 |
38 | Premium Product
39 |
40 |
41 |
42 |
43 | +
44 |
45 | Happy Customer
46 |
47 |
48 |
49 |
50 | +
51 |
52 | Awards Winning
53 |
54 |
55 |
56 |
57 | {/* right side */}
58 |
59 |
68 |
69 |
70 |
71 |
72 |
73 | );
74 | };
75 |
76 | export default Hero;
77 |
--------------------------------------------------------------------------------
/client/src/components/Header/Header.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import "./Header.css";
3 | import { BiMenuAltRight } from "react-icons/bi";
4 | import { getMenuStyles } from "../../utils/common";
5 | import useHeaderColor from "../../hooks/useHeaderColor";
6 | import OutsideClickHandler from "react-outside-click-handler";
7 | import { Link, NavLink } from "react-router-dom";
8 | import { useAuth0 } from "@auth0/auth0-react";
9 | import ProfileMenu from "../ProfileMenu/ProfileMenu";
10 | import AddPropertyModal from "../AddPropertyModal/AddPropertyModal";
11 | import useAuthCheck from "../../hooks/useAuthCheck.jsx";
12 |
13 | const Header = () => {
14 | const [menuOpened, setMenuOpened] = useState(false);
15 | const headerColor = useHeaderColor();
16 | const [modalOpened, setModalOpened] = useState(false);
17 | const { loginWithRedirect, isAuthenticated, user, logout } = useAuth0();
18 | const { validateLogin } = useAuthCheck();
19 |
20 |
21 | const handleAddPropertyClick = () => {
22 | if (validateLogin()) {
23 | setModalOpened(true);
24 | }
25 | };
26 | return (
27 |
28 |
29 | {/* logo */}
30 |
31 |
32 |
33 |
34 | {/* menu */}
35 |
{
37 | setMenuOpened(false);
38 | }}
39 | >
40 |
45 |
Properties
46 |
47 |
Contact
48 |
49 | {/* add property */}
50 |
Add Property
51 |
52 | {/* login button */}
53 | {!isAuthenticated ? (
54 |
55 | Login
56 |
57 | ) : (
58 |
59 | )}
60 |
61 |
62 |
63 | {/* for medium and small screens */}
64 |
setMenuOpened((prev) => !prev)}
67 | >
68 |
69 |
70 |
71 |
72 | );
73 | };
74 |
75 | export default Header;
76 |
--------------------------------------------------------------------------------
/client/src/components/AddLocation/AddLocation.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { useForm } from "@mantine/form";
3 | import { validateString } from "../../utils/common";
4 | import { Button, Group, Select, TextInput } from "@mantine/core";
5 | import useCountries from "../../hooks/useCountries";
6 | import Map from "../Map/Map";
7 |
8 | const AddLocation = ({ propertyDetails, setPropertyDetails, nextStep }) => {
9 | const { getAll } = useCountries();
10 | const form = useForm({
11 | initialValues: {
12 | country: propertyDetails?.country,
13 | city: propertyDetails?.city,
14 | address: propertyDetails?.address,
15 | },
16 |
17 | validate: {
18 | country: (value) => validateString(value),
19 | city: (value) => validateString(value),
20 | address: (value) => validateString(value),
21 | },
22 | });
23 |
24 |
25 | const { country, city, address } = form.values;
26 |
27 |
28 | const handleSubmit = ()=> {
29 | const {hasErrors} = form.validate();
30 | if(!hasErrors) {
31 | setPropertyDetails((prev)=> ({...prev, city, address, country}))
32 | nextStep()
33 | }
34 | }
35 | return (
36 | {
38 | e.preventDefault();
39 | handleSubmit()
40 | }}
41 | >
42 |
51 | {/* left side */}
52 | {/* inputs */}
53 |
54 |
55 |
64 |
65 |
71 |
72 |
78 |
79 |
80 | {/* right side */}
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 | Next Step
89 |
90 |
91 | );
92 | };
93 |
94 | export default AddLocation;
95 |
--------------------------------------------------------------------------------
/client/src/index.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --primary: #1f3e72;
3 | --secondary: rgba(255, 255, 255, 0.78);
4 | --black: #131110;
5 | --blue-gradient: linear-gradient(97.05deg, #4066ff 3.76%, #2949c6 100%);
6 | --orange-gradient: linear-gradient(270deg, #ffb978 0%, #ff922d 100%);
7 | --blue: #4066ff;
8 | --lightBlue: #eeeeff;
9 | --shadow: 0px 23px 21px -8px rgba(136, 160, 255, 0.25);
10 | }
11 |
12 | * {
13 | margin: 0;
14 | padding: 0;
15 | box-sizing: border-box;
16 | font-family: "Poppins", sans-serif;
17 | scroll-behavior: smooth;
18 | }
19 | .wrapper{
20 | background-color: white;
21 | }
22 | a {
23 | color: inherit;
24 | text-decoration: none;
25 | }
26 | .paddings {
27 | padding: 1.5rem;
28 | }
29 |
30 | .innerWidth {
31 | width: 100%;
32 | }
33 |
34 | .flexCenter {
35 | display: flex;
36 | row-gap: 2rem;
37 | justify-content: center;
38 | align-items: center;
39 | flex-wrap: wrap;
40 | }
41 | .flexStart {
42 | display: flex;
43 | justify-content: flex-start;
44 | align-items: center;
45 | }
46 |
47 | .flexEnd {
48 | display: flex;
49 | justify-content: flex-end;
50 | align-items: center;
51 | }
52 |
53 | .flexColCenter {
54 | display: flex;
55 | justify-content: center;
56 | align-items: center;
57 | flex-direction: column;
58 | }
59 | .flexColStart {
60 | display: flex;
61 | flex-direction: column;
62 | justify-content: center;
63 | align-items: flex-start;
64 | }
65 | .flexColEnd {
66 | display: flex;
67 | flex-direction: column;
68 | justify-content: center;
69 | align-items: flex-end;
70 | }
71 |
72 | .primaryText {
73 | color: #1f3e72;
74 | font-weight: bold;
75 | font-size: 2rem;
76 | }
77 |
78 | .secondaryText {
79 | color: rgb(140 139 139);
80 | font-size: 0.9rem;
81 | }
82 | .orangeText {
83 | color: orange;
84 | font-size: 1.5rem;
85 | font-weight: 600;
86 | }
87 |
88 | /* media queries */
89 | @media (min-width: 1536px) {
90 | .innerWidth {
91 | max-width: 1280px;
92 | margin: auto;
93 | }
94 | }
95 |
96 | @media (min-width: 640px) {
97 | .paddings {
98 | padding: 4rem;
99 | }
100 | }
101 |
102 | @media (min-width: 1280px), (min-width: 768px) {
103 | .paddings {
104 | padding: 2rem;
105 | }
106 | }
107 |
108 | @media (max-width: 640px) {
109 | .primaryText {
110 | font-size: 1.5rem;
111 | }
112 | .orangeText {
113 | font-size: 1.2rem;
114 | }
115 | }
116 |
117 | .button {
118 | font-weight: 500;
119 | padding: 0.6rem 1.4rem;
120 | color: white;
121 | background: var(--blue-gradient);
122 | border: none;
123 | border-radius: 4px;
124 | transition: all 300ms ease-in;
125 | }
126 | .button:hover {
127 | cursor: pointer;
128 | transform: scale(1.1);
129 | }
130 |
--------------------------------------------------------------------------------
/client/src/components/AddPropertyModal/AddPropertyModal.jsx:
--------------------------------------------------------------------------------
1 | import { Container, Modal, Stepper } from "@mantine/core";
2 | import React, { useState } from "react";
3 | import AddLocation from "../AddLocation/AddLocation";
4 | import { useAuth0 } from "@auth0/auth0-react";
5 | import UploadImage from "../UploadImage/UploadImage";
6 | import BasicDetails from "../BasicDetails/BasicDetails";
7 | import Facilities from "../Facilities/Facilities";
8 |
9 | const AddPropertyModal = ({ opened, setOpened }) => {
10 | const [active, setActive] = useState(0);
11 | const { user } = useAuth0();
12 |
13 | const [propertyDetails, setPropertyDetails] = useState({
14 | title: "",
15 | description: "",
16 | price: 0,
17 | country: "",
18 | city: "",
19 | address: "",
20 | image: null,
21 | facilities: {
22 | bedrooms: 0,
23 | parkings: 0,
24 | bathrooms: 0,
25 | },
26 | userEmail: user?.email,
27 | });
28 |
29 | const nextStep = () => {
30 | setActive((current) => (current < 4 ? current + 1 : current));
31 | };
32 |
33 | const prevStep = () => {
34 | setActive((current) => (current > 0 ? current - 1 : current));
35 | };
36 |
37 | return (
38 | setOpened(false)}
41 | closeOnClickOutside
42 | size={"90rem"}
43 | >
44 |
45 |
51 |
52 |
57 |
58 |
59 |
65 |
66 |
67 |
73 |
74 |
75 |
76 |
83 |
84 |
85 | Completed, click back button to get to previous step
86 |
87 |
88 |
89 |
90 | );
91 | };
92 |
93 | export default AddPropertyModal;
94 |
--------------------------------------------------------------------------------
/client/src/components/Value/Value.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import {
3 | Accordion,
4 | AccordionItem,
5 | AccordionItemHeading,
6 | AccordionItemButton,
7 | AccordionItemPanel,
8 | AccordionItemState,
9 | } from "react-accessible-accordion";
10 | import "react-accessible-accordion/dist/fancy-example.css";
11 | import {
12 | MdOutlineArrowDropDown,
13 | MdOutlineArrowDropDownCircle,
14 | } from "react-icons/md";
15 | import data from "../../utils/accordion.jsx";
16 | import "./Value.css";
17 | // Demo styles, see 'Styles' section below for some notes on use.
18 |
19 | const Value = () => {
20 | return (
21 |
22 |
23 | {/* left side */}
24 |
25 |
26 |
27 |
28 |
29 |
30 | {/* right */}
31 |
32 |
Our Value
33 |
34 |
Value We Give to You
35 |
36 |
37 | We always ready to help by providijng the best services for you.
38 |
39 | We beleive a good blace to live can make your life better
40 |
41 |
42 |
47 | {data.map((item, i) => {
48 | const [className, setClassName] = useState(null);
49 | return (
50 |
51 |
52 |
53 | {/* just for getting state of item */}
54 |
55 | {({ expanded }) =>
56 | expanded
57 | ? setClassName("expanded")
58 | : setClassName("collapsed")
59 | }
60 |
61 | {item.icon}
62 |
65 | {item.heading}
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 | {item.detail}
74 |
75 |
76 | );
77 | })}
78 |
79 |
80 |
81 |
82 | );
83 | };
84 |
85 | export default Value;
86 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | @author:- Pranav Gupta
2 | # Homzy - A Real Estate Website
3 |
4 | 
5 |
6 | ## Overview
7 |
8 | Homzy is a real estate website that simplifies the process of buying and selling properties. It provides a user-friendly interface for property owners to list their properties and for potential buyers to search and view available properties. The website allows users to search for properties in any location and view surrounding areas to get a better understanding of the neighborhood. Additionally, users can like and add properties to their favorites list for future reference. To enhance security and provide a seamless authentication experience, Auth0 is used for both client-side and server-side authentication. The UI components are developed with Mantine to ensure a polished and responsive user interface.
9 |
10 | ## Key Features
11 |
12 | - Property Search: Users can search for properties in any location using the search functionality. The website also provides the ability to view areas around a property on a map.
13 |
14 | - Favorites List: Logged-in users can like properties and add them to their favorites list for easy access and comparison.
15 |
16 | - User Authentication: Auth0 is integrated into the website to handle user authentication securely.
17 |
18 | - Login and Signup: Users can create an account and log in to access additional features and save their preferences.
19 |
20 | ## Tech Stack
21 |
22 | The project is built using the following technologies:
23 |
24 | - React: A popular JavaScript library for building user interfaces.
25 | - Node.js: A server-side JavaScript runtime environment.
26 | - Express: A web application framework for Node.js, simplifying server-side development.
27 | - MongoDB: A NoSQL database used for storing property and user data.
28 | - Auth0: A third-party authentication service that handles user authentication for both the client-side and server-side.
29 | - Mantine: A React component library providing UI components for a polished user interface.
30 |
31 | ## How to Run
32 |
33 | To run Homzy on your computer, follow these steps:
34 |
35 | 1. Clone this repository to your local machine:
36 | git clone https://github.com/prazivi/Homzy-Real-Estate.git
37 |
38 |
39 | 2. Navigate to the project directory:
40 | cd Homzy-Real-Estate
41 |
42 |
43 | 3. Install the required dependencies for both the client and server sides:
44 | cd client
45 | npm install
46 | cd ../server
47 | npm install
48 |
49 |
50 | 4. Set up environment variables:
51 | - For the server, create a `.env` file in the `server` directory and set the required variables for MongoDB connection and Auth0 credentials.
52 |
53 | 5. Start the development server for both the client and server sides:
54 | cd client
55 | npm start
56 | cd ../server
57 | npm start
58 |
59 |
60 | 6. Open your web browser and visit `http://localhost:3000` to access the Homzy website.
61 |
62 | ## Contributing
63 |
64 | Contributions to this project are welcome. If you wish to contribute, please follow the standard GitHub workflow by creating pull requests and discussing any proposed changes via issues.
65 |
66 | ## Reporting Issues
67 |
68 | If you encounter any issues while using the Homzy website or have any suggestions for improvement, please open an issue on the GitHub repository. Your feedback is valuable to us, and we will address any reported issues promptly.
69 |
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/client/src/components/Facilities/Facilities.jsx:
--------------------------------------------------------------------------------
1 | import { useAuth0 } from "@auth0/auth0-react";
2 | import { Box, Button, Group, NumberInput } from "@mantine/core";
3 | import { useForm } from "@mantine/form";
4 | import React, { useContext } from "react";
5 | import UserDetailContext from "../../context/UserDetailContext";
6 | import useProperties from "../../hooks/useProperties.jsx";
7 | import { useMutation } from "react-query";
8 | import { toast } from "react-toastify";
9 | import { createResidency } from "../../utils/api";
10 | const Facilities = ({
11 | prevStep,
12 | propertyDetails,
13 | setPropertyDetails,
14 | setOpened,
15 | setActiveStep,
16 | }) => {
17 | const form = useForm({
18 | initialValues: {
19 | bedrooms: propertyDetails.facilities.bedrooms,
20 | parkings: propertyDetails.facilities.parkings,
21 | bathrooms: propertyDetails.facilities.bathrooms,
22 | },
23 | validate: {
24 | bedrooms: (value) => (value < 1 ? "Must have atleast one room" : null),
25 | bathrooms: (value) =>
26 | value < 1 ? "Must have atleast one bathroom" : null,
27 | },
28 | });
29 |
30 | const { bedrooms, parkings, bathrooms } = form.values;
31 |
32 | const handleSubmit = () => {
33 | const { hasErrors } = form.validate();
34 | if (!hasErrors) {
35 | setPropertyDetails((prev) => ({
36 | ...prev,
37 | facilities: { bedrooms, parkings, bathrooms },
38 | }));
39 | mutate();
40 | }
41 | };
42 |
43 | // ==================== upload logic
44 | const { user } = useAuth0();
45 | const {
46 | userDetails: { token },
47 | } = useContext(UserDetailContext);
48 | const { refetch: refetchProperties } = useProperties();
49 |
50 | const {mutate, isLoading} = useMutation({
51 | mutationFn: ()=> createResidency({
52 | ...propertyDetails, facilities: {bedrooms, parkings , bathrooms},
53 | }, token),
54 | onError: ({ response }) => toast.error(response.data.message, {position: "bottom-right"}),
55 | onSettled: ()=> {
56 | toast.success("Added Successfully", {position: "bottom-right"});
57 | setPropertyDetails({
58 | title: "",
59 | description: "",
60 | price: 0,
61 | country: "",
62 | city: "",
63 | address: "",
64 | image: null,
65 | facilities: {
66 | bedrooms: 0,
67 | parkings: 0,
68 | bathrooms: 0,
69 | },
70 | userEmail: user?.email,
71 | })
72 | setOpened(false)
73 | setActiveStep(0)
74 | refetchProperties()
75 | }
76 |
77 | })
78 |
79 | return (
80 |
81 | {
83 | e.preventDefault();
84 | handleSubmit();
85 | }}
86 | >
87 |
93 |
98 |
104 |
105 |
106 | Back
107 |
108 |
109 | {isLoading ? "Submitting" : "Add Property"}
110 |
111 |
112 |
113 |
114 | );
115 | };
116 |
117 | export default Facilities;
118 |
--------------------------------------------------------------------------------
/client/src/components/Contact/Contact.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import "./Contact.css";
3 | import { MdCall } from "react-icons/md";
4 | import { BsFillChatDotsFill } from "react-icons/bs";
5 | import {HiChatBubbleBottomCenter} from 'react-icons/hi2'
6 | const Contact = () => {
7 | return (
8 |
88 | );
89 | };
90 |
91 | export default Contact;
92 |
--------------------------------------------------------------------------------
/client/src/utils/api.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | import dayjs from "dayjs";
3 | import { toast } from "react-toastify";
4 |
5 | export const api = axios.create({
6 | baseURL: "https://full-stack-real-estate-youtube.vercel.app/api",
7 | });
8 |
9 | export const getAllProperties = async () => {
10 | try {
11 | const response = await api.get("/residency/allresd", {
12 | timeout: 10 * 1000,
13 | });
14 |
15 | if (response.status === 400 || response.status === 500) {
16 | throw response.data;
17 | }
18 | return response.data;
19 | } catch (error) {
20 | toast.error("Something went wrong");
21 | throw error;
22 | }
23 | };
24 |
25 | export const getProperty = async (id) => {
26 | try {
27 | const response = await api.get(`/residency/${id}`, {
28 | timeout: 10 * 1000,
29 | });
30 |
31 | if (response.status === 400 || response.status === 500) {
32 | throw response.data;
33 | }
34 | return response.data;
35 | } catch (error) {
36 | toast.error("Something went wrong");
37 | throw error;
38 | }
39 | };
40 |
41 | export const createUser = async (email, token) => {
42 | try {
43 | await api.post(
44 | `/user/register`,
45 | { email },
46 | {
47 | headers: {
48 | Authorization: `Bearer ${token}`,
49 | },
50 | }
51 | );
52 | } catch (error) {
53 | toast.error("Something went wrong, Please try again");
54 | throw error;
55 | }
56 | };
57 |
58 | export const bookVisit = async (date, propertyId, email, token) => {
59 | try {
60 | await api.post(
61 | `/user/bookVisit/${propertyId}`,
62 | {
63 | email,
64 | id: propertyId,
65 | date: dayjs(date).format("DD/MM/YYYY"),
66 | },
67 | {
68 | headers: {
69 | Authorization: `Bearer ${token}`,
70 | },
71 | }
72 | );
73 | } catch (error) {
74 | toast.error("Something went wrong, Please try again");
75 | throw error;
76 | }
77 | };
78 |
79 | export const removeBooking = async (id, email, token) => {
80 | try {
81 | await api.post(
82 | `/user/removeBooking/${id}`,
83 | {
84 | email,
85 | },
86 | {
87 | headers: {
88 | Authorization: `Bearer ${token}`,
89 | },
90 | }
91 | );
92 | } catch (error) {
93 | toast.error("Something went wrong, Please try again");
94 |
95 | throw error;
96 | }
97 | };
98 |
99 | export const toFav = async (id, email, token) => {
100 | try {
101 | await api.post(
102 | `/user/toFav/${id}`,
103 | {
104 | email,
105 | },
106 | {
107 | headers: {
108 | Authorization: `Bearer ${token}`,
109 | },
110 | }
111 | );
112 | } catch (e) {
113 | throw e;
114 | }
115 | };
116 |
117 |
118 | export const getAllFav = async (email, token) => {
119 | if(!token) return
120 | try{
121 |
122 | const res = await api.post(
123 | `/user/allFav`,
124 | {
125 | email,
126 | },
127 | {
128 | headers: {
129 | Authorization: `Bearer ${token}`,
130 | },
131 | }
132 | );
133 |
134 | return res.data["favResidenciesID"]
135 |
136 | }catch(e)
137 | {
138 | toast.error("Something went wrong while fetching favs");
139 | throw e
140 | }
141 | }
142 |
143 |
144 | export const getAllBookings = async (email, token) => {
145 |
146 | if(!token) return
147 | try {
148 | const res = await api.post(
149 | `/user/allBookings`,
150 | {
151 | email,
152 | },
153 | {
154 | headers: {
155 | Authorization: `Bearer ${token}`,
156 | },
157 | }
158 | );
159 | return res.data["bookedVisits"];
160 |
161 |
162 | } catch (error) {
163 | toast.error("Something went wrong while fetching bookings");
164 | throw error
165 | }
166 | }
167 |
168 |
169 | export const createResidency = async (data, token) => {
170 | console.log(data)
171 | try{
172 | const res = await api.post(
173 | `/residency/create`,
174 | {
175 | data
176 | },
177 | {
178 | headers: {
179 | Authorization: `Bearer ${token}`,
180 | },
181 | }
182 | )
183 | }catch(error)
184 | {
185 | throw error
186 | }
187 | }
--------------------------------------------------------------------------------
/server/controllers/userCntrl.js:
--------------------------------------------------------------------------------
1 | import asyncHandler from "express-async-handler";
2 |
3 | import { prisma } from "../config/prismaConfig.js";
4 |
5 | export const createUser = asyncHandler(async (req, res) => {
6 | console.log("creating a user");
7 |
8 | let { email } = req.body;
9 | const userExists = await prisma.user.findUnique({ where: { email: email } });
10 | if (!userExists) {
11 | const user = await prisma.user.create({ data: req.body });
12 | res.send({
13 | message: "User registered successfully",
14 | user: user,
15 | });
16 | } else res.status(201).send({ message: "User already registered" });
17 | });
18 |
19 | // function to book a visit to resd
20 | export const bookVisit = asyncHandler(async (req, res) => {
21 | const { email, date } = req.body;
22 | const { id } = req.params;
23 |
24 | try {
25 | const alreadyBooked = await prisma.user.findUnique({
26 | where: { email },
27 | select: { bookedVisits: true },
28 | });
29 |
30 | if (alreadyBooked.bookedVisits.some((visit) => visit.id === id)) {
31 | res
32 | .status(400)
33 | .json({ message: "This residency is already booked by you" });
34 | } else {
35 | await prisma.user.update({
36 | where: { email: email },
37 | data: {
38 | bookedVisits: { push: { id, date } },
39 | },
40 | });
41 | res.send("your visit is booked successfully");
42 | }
43 | } catch (err) {
44 | throw new Error(err.message);
45 | }
46 | });
47 |
48 | // funtion to get all bookings of a user
49 | export const getAllBookings = asyncHandler(async (req, res) => {
50 | const { email } = req.body;
51 | try {
52 | const bookings = await prisma.user.findUnique({
53 | where: { email },
54 | select: { bookedVisits: true },
55 | });
56 | res.status(200).send(bookings);
57 | } catch (err) {
58 | throw new Error(err.message);
59 | }
60 | });
61 |
62 | // function to cancel the booking
63 | export const cancelBooking = asyncHandler(async (req, res) => {
64 | const { email } = req.body;
65 | const { id } = req.params;
66 | try {
67 | const user = await prisma.user.findUnique({
68 | where: { email: email },
69 | select: { bookedVisits: true },
70 | });
71 |
72 | const index = user.bookedVisits.findIndex((visit) => visit.id === id);
73 |
74 | if (index === -1) {
75 | res.status(404).json({ message: "Booking not found" });
76 | } else {
77 | user.bookedVisits.splice(index, 1);
78 | await prisma.user.update({
79 | where: { email },
80 | data: {
81 | bookedVisits: user.bookedVisits,
82 | },
83 | });
84 |
85 | res.send("Booking cancelled successfully");
86 | }
87 | } catch (err) {
88 | throw new Error(err.message);
89 | }
90 | });
91 |
92 | // function to add a resd in favourite list of a user
93 | export const toFav = asyncHandler(async (req, res) => {
94 | const { email } = req.body;
95 | const { rid } = req.params;
96 |
97 | try {
98 | const user = await prisma.user.findUnique({
99 | where: { email },
100 | });
101 |
102 | if (user.favResidenciesID.includes(rid)) {
103 | const updateUser = await prisma.user.update({
104 | where: { email },
105 | data: {
106 | favResidenciesID: {
107 | set: user.favResidenciesID.filter((id) => id !== rid),
108 | },
109 | },
110 | });
111 |
112 | res.send({ message: "Removed from favorites", user: updateUser });
113 | } else {
114 | const updateUser = await prisma.user.update({
115 | where: { email },
116 | data: {
117 | favResidenciesID: {
118 | push: rid,
119 | },
120 | },
121 | });
122 | res.send({ message: "Updated favorites", user: updateUser });
123 | }
124 | } catch (err) {
125 | throw new Error(err.message);
126 | }
127 | });
128 |
129 | // function to get all favorites
130 | export const getAllFavorites = asyncHandler(async (req, res) => {
131 | const { email } = req.body;
132 | try {
133 | const favResd = await prisma.user.findUnique({
134 | where: { email },
135 | select: { favResidenciesID: true },
136 | });
137 | res.status(200).send(favResd);
138 | } catch (err) {
139 | throw new Error(err.message);
140 | }
141 | });
142 |
--------------------------------------------------------------------------------
/client/src/pages/Property/Property.jsx:
--------------------------------------------------------------------------------
1 | import React, { useContext, useState } from "react";
2 | import { useMutation, useQuery } from "react-query";
3 | import { useLocation } from "react-router-dom";
4 | import { getProperty, removeBooking } from "../../utils/api";
5 | import { PuffLoader } from "react-spinners";
6 | import { AiFillHeart } from "react-icons/ai";
7 | import "./Property.css";
8 |
9 | import { FaShower } from "react-icons/fa";
10 | import { AiTwotoneCar } from "react-icons/ai";
11 | import { MdLocationPin, MdMeetingRoom } from "react-icons/md";
12 | import Map from "../../components/Map/Map";
13 | import useAuthCheck from "../../hooks/useAuthCheck";
14 | import { useAuth0 } from "@auth0/auth0-react";
15 | import BookingModal from "../../components/BookingModal/BookingModal";
16 | import UserDetailContext from "../../context/UserDetailContext.js";
17 | import { Button } from "@mantine/core";
18 | import { toast } from "react-toastify";
19 | import Heart from "../../components/Heart/Heart";
20 | const Property = () => {
21 | const { pathname } = useLocation();
22 | const id = pathname.split("/").slice(-1)[0];
23 | const { data, isLoading, isError } = useQuery(["resd", id], () =>
24 | getProperty(id)
25 | );
26 |
27 | const [modalOpened, setModalOpened] = useState(false);
28 | const { validateLogin } = useAuthCheck();
29 | const { user } = useAuth0();
30 |
31 | const {
32 | userDetails: { token, bookings },
33 | setUserDetails,
34 | } = useContext(UserDetailContext);
35 |
36 | const { mutate: cancelBooking, isLoading: cancelling } = useMutation({
37 | mutationFn: () => removeBooking(id, user?.email, token),
38 | onSuccess: () => {
39 | setUserDetails((prev) => ({
40 | ...prev,
41 | bookings: prev.bookings.filter((booking) => booking?.id !== id),
42 | }));
43 |
44 | toast.success("Booking cancelled", { position: "bottom-right" });
45 | },
46 | });
47 |
48 | if (isLoading) {
49 | return (
50 |
55 | );
56 | }
57 |
58 | if (isError) {
59 | return (
60 |
61 |
62 | Error while fetching the property details
63 |
64 |
65 | );
66 | }
67 |
68 | return (
69 |
70 |
71 | {/* like button */}
72 |
73 |
74 |
75 |
76 | {/* image */}
77 |
78 |
79 |
80 | {/* left */}
81 |
82 | {/* head */}
83 |
84 | {data?.title}
85 |
86 | $ {data?.price}
87 |
88 |
89 |
90 | {/* facilities */}
91 |
92 | {/* bathrooms */}
93 |
94 |
95 | {data?.facilities?.bathrooms} Bathrooms
96 |
97 |
98 | {/* parkings */}
99 |
100 |
101 |
{data?.facilities.parkings} Parking
102 |
103 |
104 | {/* rooms */}
105 |
106 |
107 | {data?.facilities.bedrooms} Room/s
108 |
109 |
110 |
111 | {/* description */}
112 |
113 |
114 | {data?.description}
115 |
116 |
117 | {/* address */}
118 |
119 |
120 |
121 |
122 | {data?.address}{" "}
123 | {data?.city}{" "}
124 | {data?.country}
125 |
126 |
127 |
128 | {/* booking button */}
129 | {bookings?.map((booking) => booking.id).includes(id) ? (
130 | <>
131 |
cancelBooking()}
136 | disabled={cancelling}
137 | >
138 | Cancel booking
139 |
140 |
141 | Your visit already booked for date{" "}
142 | {bookings?.filter((booking) => booking?.id === id)[0].date}
143 |
144 | >
145 | ) : (
146 |
{
149 | validateLogin() && setModalOpened(true);
150 | }}
151 | >
152 | Book your visit
153 |
154 | )}
155 |
156 |
162 |
163 |
164 | {/* right side */}
165 |
166 |
171 |
172 |
173 |
174 |
175 | );
176 | };
177 |
178 | export default Property;
179 |
--------------------------------------------------------------------------------
/server/data/Residency.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "_id": { "$oid": "64392d39dfd90fb29e464fb7" },
4 | "title": "Sunny Meadow Cottage",
5 | "description": "Massive opportunity to build your dream home at the base of Mummy Mountain in the 3 C's school district. Home is currently updated and very livable if your plans are to build at a later date.* Bonus * to live hillside without hillside restrictions in the town of PV. Run don't walk to capture this needle in a hay stack.",
6 | "price": 6000,
7 | "address": "Street 1",
8 | "city": "Chicago",
9 | "country": "US",
10 | "image": "https://images.pexels.com/photos/7031406/pexels-photo-7031406.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
11 | "userEmail": "mukhtarhamza294@gmail.com",
12 | "createdAt": { "$date": "2023-04-14T10:38:48.762Z" },
13 | "updatedAt": { "$date": "2023-04-14T10:38:48.762Z" },
14 | "facilities": { "bathrooms": "2", "parking": "1", "bedrooms": "5" }
15 | },
16 | {
17 | "_id": { "$oid": "64392e0bdfd90fb29e464fbc" },
18 | "title": "Coastal Breeze Villa",
19 | "description": "Massive opportunity to build your dream home at the base of Mummy Mountain in the 3 C's school district. Home is currently updated and very livable if your plans are to build at a later date.* Bonus * to live hillside without hillside restrictions in the town of PV. Run don't walk to capture this needle in a hay stack.",
20 | "price": 8000,
21 | "address": "Street 2",
22 | "city": "Multan",
23 | "country": "Pakistan",
24 | "image": "https://3.bp.blogspot.com/-84l-BoUL090/VTDHcQzSTNI/AAAAAAAAuHI/Khftta_CF5E/s1920/wow-home-design.jpg",
25 | "facilities": { "bathrooms": "5", "parking": "1", "bedrooms": "4" },
26 | "userEmail": "mukhtarhamza294@gmail.com",
27 | "createdAt": { "$date": "2023-04-14T10:42:19.231Z" },
28 | "updatedAt": { "$date": "2023-04-14T10:42:19.231Z" }
29 | },
30 | {
31 | "_id": { "$oid": "6439001c74446601d6fce96d" },
32 | "title": "Citralan Puri Serang",
33 | "description": "Massive opportunity to build your dream home at the base of Mummy Mountain in the 3 C's school district. Home is currently updated and very livable if your plans are to build at a later date.* Bonus * to live hillside without hillside restrictions in the town of PV. Run don't walk to capture this needle in a hay stack.",
34 | "price": 3000,
35 | "address": "Street 3",
36 | "city": "California",
37 | "country": "US",
38 | "image": "https://images.pexels.com/photos/323780/pexels-photo-323780.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
39 | "userEmail": "mukhtarhamza294@gmail.com",
40 | "createdAt": { "$date": "2023-04-14T07:26:20.051Z" },
41 | "updatedAt": { "$date": "2023-04-14T07:26:20.051Z" },
42 | "facilities": { "bathrooms": "3", "parking": "1", "bedrooms": "2" }
43 | },
44 | {
45 | "_id": { "$oid": "64392e1edfd90fb29e464fbd" },
46 | "title": "Autumn Mist Cottage",
47 | "description": "Massive opportunity to build your dream home at the base of Mummy Mountain in the 3 C's school district. Home is currently updated and very livable if your plans are to build at a later date.* Bonus * to live hillside without hillside restrictions in the town of PV. Run don't walk to capture this needle in a hay stack.",
48 | "price": 3800,
49 | "address": "Street 4",
50 | "city": "Karachi",
51 | "country": "Pakistan",
52 | "image": "https://e0.pxfuel.com/wallpapers/12/377/desktop-wallpaper-beautiful-houses-beautiful-mansion.jpg",
53 | "facilities": { "bathrooms": "2", "parking": "1", "bedrooms": "3" },
54 | "userEmail": "mukhtarhamza294@gmail.com",
55 | "createdAt": { "$date": "2023-04-14T10:42:37.946Z" },
56 | "updatedAt": { "$date": "2023-04-14T10:42:37.946Z" }
57 | },
58 | {
59 | "_id": { "$oid": "64392d67dfd90fb29e464fb8" },
60 | "title": "Rustic Pine Lodge",
61 | "description": "Massive opportunity to build your dream home at the base of Mummy Mountain in the 3 C's school district. Home is currently updated and very livable if your plans are to build at a later date.* Bonus * to live hillside without hillside restrictions in the town of PV. Run don't walk to capture this needle in a hay stack.",
62 | "price": 7050,
63 | "address": "Street 5",
64 | "city": "San Diego",
65 | "country": "US",
66 | "image": "https://images.pexels.com/photos/208736/pexels-photo-208736.jpeg",
67 | "userEmail": "mukhtarhamza294@gmail.com",
68 | "createdAt": { "$date": "2023-04-14T10:39:35.044Z" },
69 | "updatedAt": { "$date": "2023-04-14T10:39:35.044Z" },
70 | "facilities": { "bathrooms": "5", "parking": "1", "bedrooms": "4" }
71 | },
72 | {
73 | "_id": { "$oid": "64392df2dfd90fb29e464fbb" },
74 | "title": "Rolling Hills Retreat",
75 | "description": "Massive opportunity to build your dream home at the base of Mummy Mountain inthe 3 C's school district. Home is currently updated and very livable if your plans are to build at a later date.* Bonus * to live hillside without hillside restrictions in the town of PV. Run don't walk to capture this needle in a hay stack.",
76 | "price": 2000,
77 | "address": "Street 6",
78 | "city": "Phoenix",
79 | "country": "US",
80 | "image": "https://www.hollywoodreporter.com/wp-content/uploads/2023/03/image_h_01-H-2023.jpg",
81 | "facilities": { "bathrooms": "7", "parking": "2", "bedrooms": "10" },
82 | "userEmail": "mukhtarhamza294@gmail.com",
83 | "createdAt": { "$date": "2023-04-14T10:41:54.156Z" },
84 | "updatedAt": { "$date": "2023-04-14T10:41:54.156Z" }
85 | },
86 | {
87 | "_id": { "$oid": "64392e32dfd90fb29e464fbe" },
88 | "title": "Riverstone Manor",
89 | "description": "Massive opportunity to build your dream home at the base of Mummy Mountain in the 3 C's school district. Home is currently updated and very livable if your plans are to build at a later date.* Bonus * to live hillside without hillside restrictions in the town of PV. Run don't walk to capture this needle in a hay stack.",
90 | "price": 2000,
91 | "address": "Street 7",
92 | "city": "Tampa",
93 | "country": "US",
94 | "image": "https://st.hzcdn.com/simgs/pictures/exteriors/builders-in-kochi-creo-homes-pvt-ltd-img~a751d25e0d2ef808_4-0254-1-cb5c87b.jpg",
95 | "facilities": { "bathrooms": "5", "parking": "1", "bedrooms": "4" },
96 | "userEmail": "mukhtarhamza294@gmail.com",
97 | "createdAt": { "$date": "2023-04-14T10:42:58.826Z" },
98 | "updatedAt": { "$date": "2023-04-14T10:42:58.826Z" }
99 | },
100 | {
101 | "_id": { "$oid": "64392e46dfd90fb29e464fbf" },
102 | "title": "Summerhill Estate",
103 | "description": "Massive opportunity to build your dream home at the base of Mummy Mountain in the 3 C's school district. Home is currently updated and very livable if your plans are to build at a later date.* Bonus * to live hillside without hillside restrictions in the town of PV. Run don't walk to capture this needle in a hay stack.",
104 | "price": 2000,
105 | "address": "Street 8",
106 | "city": "Denver",
107 | "country": "US",
108 | "image": "https://i.pinimg.com/originals/89/56/5c/89565c305737e1da2a1a5b62600fbcd4.jpg",
109 | "facilities": { "bathrooms": "2", "parking": "1", "bedrooms": "2" },
110 | "userEmail": "mukhtarhamza294@gmail.com",
111 | "createdAt": { "$date": "2023-04-14T10:43:18.186Z" },
112 | "updatedAt": { "$date": "2023-04-14T10:43:18.186Z" }
113 | },
114 | {
115 | "_id": { "$oid": "64392e57dfd90fb29e464fc0" },
116 | "title": "Crimson Peak Chalet",
117 | "description": "Massive opportunity to build your dream home at the base of Mummy Mountain in the 3 C's school district. Home is currently updated and very livable if your plans are to build at a later date.* Bonus * to live hillside without hillside restrictions in the town of PV. Run don't walk to capture this needle in a hay stack.",
118 | "price": 2000,
119 | "address": "Street 9",
120 | "city": "Chicago",
121 | "country": "US",
122 | "image": "https://cutewallpaper.org/24/house-images/1819611997.jpg",
123 | "facilities": { "bathrooms": "3", "parking": "1", "bedrooms": "4" },
124 | "userEmail": "mukhtarhamza294@gmail.com",
125 | "createdAt": { "$date": "2023-04-14T10:43:35.761Z" },
126 | "updatedAt": { "$date": "2023-04-14T10:43:35.761Z" }
127 | },
128 | {
129 | "_id": { "$oid": "64392e69dfd90fb29e464fc1" },
130 | "title": "Cedar Ridge Ranch",
131 | "description": "Massive opportunity to build your dream home at the base of Mummy Mountain in the 3 C's school district. Home is currently updated and very livable if your plans are to build at a later date.* Bonus * to live hillside without hillside restrictions in the town of PV. Run don't walk to capture this needle in a hay stack.",
132 | "price": 2000,
133 | "address": "Street 10",
134 | "city": " New York",
135 | "country": "US",
136 | "image": "https://media.istockphoto.com/id/1184625380/photo/large-modern-expensive-home.jpg?s=612x612&w=0&k=20&c=TuX7dnAQvrxt02_BMnlgtVMMdvBLmh3gcXL1bPnobP8=",
137 | "facilities": { "bathrooms": "5", "parking": "1", "bedrooms": "4" },
138 | "userEmail": "mukhtarhamza294@gmail.com",
139 | "createdAt": { "$date": "2023-04-14T10:43:53.665Z" },
140 | "updatedAt": { "$date": "2023-04-14T10:43:53.665Z" }
141 | },
142 | {
143 | "_id": { "$oid": "643a30b60c4348510952923a" },
144 | "title": "Modern Marvel Mansion",
145 | "description": "Massive opportunity to build your dream home at the base of Mummy Mountain in the 3 C's school district. Home is currently updated and very livable if your plans are to build at a later date.* Bonus * to live hillside without hillside restrictions in the town of PV. Run don't walk to capture this needle in a hay stack.",
146 | "price": 2000,
147 | "address": "Street 11",
148 | "city": "Multan",
149 | "country": "Pakistan",
150 | "image": "https://www.homedecorbuzz.com/wp-content/uploads/2017/07/Beautiful-pink-kitchen-design-image.jpg",
151 | "facilities": { "bathrooms": "5", "parking": "1", "bedrooms": "4" },
152 | "userEmail": "mukhtarhamza294@gmail.com",
153 | "createdAt": { "$date": "2023-04-15T05:05:58.189Z" },
154 | "updatedAt": { "$date": "2023-04-15T05:05:58.189Z" }
155 | },
156 | {
157 | "_id": { "$oid": "643900fa74446601d6fce96e" },
158 | "title": "Whispering Willow Estate",
159 | "description": "Massive opportunity to build your dream home at the base of Mummy Mountain in the 3 C's school district. Home is currently updated and very livable if your plans are to build at a later date.* Bonus * to live hillside without hillside restrictions in the town of PV. Run don't walk to capture this needle in a hay stack.",
160 | "price": 4000,
161 | "address": "Street 12",
162 | "city": "Tokyo",
163 | "country": "Japan",
164 | "image": "https://images.pexels.com/photos/1127119/pexels-photo-1127119.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
165 | "userEmail": "mukhtarhamza294@gmail.com",
166 | "createdAt": { "$date": "2023-04-14T07:29:58.262Z" },
167 | "updatedAt": { "$date": "2023-04-14T07:29:58.262Z" },
168 | "facilities": { "bathrooms": "4", "parking": "1", "bedrooms": "4" }
169 | },
170 | {
171 | "_id": { "$oid": "6439011d74446601d6fce96f" },
172 | "title": "Blue Horizon Manor",
173 | "description": "Massive opportunity to build your dream home at the base of Mummy Mountain in the 3 C's school district. Home is currently updated and very livable if your plans are to build at a later date.* Bonus * to live hillside without hillside restrictions in the town of PV. Run don't walk to capture this needle in a hay stack.",
174 | "price": 2500,
175 | "address": "Street 13",
176 | "city": "Delhi",
177 | "country": "India",
178 | "image": "https://images.pexels.com/photos/1396132/pexels-photo-1396132.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
179 | "userEmail": "mukhtarhamza294@gmail.com",
180 | "createdAt": { "$date": "2023-04-14T07:30:36.761Z" },
181 | "updatedAt": { "$date": "2023-04-14T07:30:36.761Z" },
182 | "facilities": { "bathrooms": "2", "parking": "1", "bedrooms": "3" }
183 | },
184 | {
185 | "_id": { "$oid": "64392ddddfd90fb29e464fba" },
186 | "title": "Golden Fields Ranch",
187 | "description": "Massive opportunity to build your dream home at the base of Mummy Mountain in the 3 C's school district. Home is currently updated and very livable if your plans are to build at a later date.* Bonus * to live hillside without hillside restrictions in the town of PV. Run don't walk to capture this needle in a hay stack.",
188 | "price": 4000,
189 | "address": "Street 14",
190 | "city": "Los Angeles",
191 | "country": "US",
192 | "image": "https://lovehomedesigns.com/wp-content/uploads/2022/01/cute-house-012522.jpg",
193 | "facilities": { "bathrooms": "5", "parking": "1", "bedrooms": "4" },
194 | "userEmail": "mukhtarhamza294@gmail.com",
195 | "createdAt": { "$date": "2023-04-14T10:41:32.998Z" },
196 | "updatedAt": { "$date": "2023-04-14T10:41:32.998Z" }
197 | },
198 | {
199 | "_id": { "$oid": "6438ffdd74446601d6fce96c" },
200 | "title": "Asatti Garden City",
201 | "description": "Massive opportunity to build your dream home at the base of Mummy Mountain in the 3 C's school district. Home is currently updated and very livable if your plans are to build at a later date.* Bonus * to live hillside without hillside restrictions in the town of PV. Run don't walk to capture this needle in a hay stack.",
202 | "price": 2000,
203 | "address": "Street 15",
204 | "city": " New York",
205 | "country": "US",
206 | "image": "https://images.pexels.com/photos/106399/pexels-photo-106399.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1%201x,%20https://images.pexels.com/photos/106399/pexels-photo-106399.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2%202x",
207 | "userEmail": "mukhtarhamza294@gmail.com",
208 | "createdAt": { "$date": "2023-04-14T07:25:13.156Z" },
209 | "updatedAt": { "$date": "2023-04-14T07:25:13.156Z" },
210 | "facilities": { "bathrooms": "2", "parking": "2", "bedrooms": "10" }
211 | },
212 | {
213 | "_id": { "$oid": "645cd0d8616ffb7ce699bb8e" },
214 | "title": "Jansheen villan",
215 | "description": "Villa is a marvelous piece of luxury and Art. Decorated with two helipads, wide compartment, long sea view and astonishing paints make it more bold.",
216 | "price": 8000,
217 | "address": "Street 16",
218 | "city": "Lahore",
219 | "country": "Pakistan",
220 | "image": "https://res.cloudinary.com/dkg6ajfen/image/upload/v1683804284/jlap7xq7owdsfb4tlsyt.png",
221 | "facilities": { "bedrooms": 12, "parkings": 2, "bathrooms": 3 },
222 | "userEmail": "ahmadrao925@gmail.com",
223 | "createdAt": { "$date": "2023-05-11T11:26:15.44Z" },
224 | "updatedAt": { "$date": "2023-05-11T11:26:15.44Z" }
225 | },
226 | {
227 | "_id": { "$oid": "645e27938b90ba42adbed599" },
228 | "title": "The Johnson Palace",
229 | "description": "This is an amazing place to live with all facilities around you",
230 | "price": 5000,
231 | "address": "Street 17",
232 | "city": "New York",
233 | "country": "United States",
234 | "image": "https://res.cloudinary.com/dkg6ajfen/image/upload/v1683892030/eb99xugad9w3pswx7h3f.jpg",
235 | "facilities": { "bedrooms": 2, "parkings": 3, "bathrooms": 2 },
236 | "userEmail": "zain@gaintime.io",
237 | "createdAt": { "$date": "2023-05-12T11:48:35.028Z" },
238 | "updatedAt": { "$date": "2023-05-12T11:48:35.028Z" }
239 | },
240 | {
241 | "_id": { "$oid": "6460802e05fa5b3fd0527f65" },
242 | "title": "ZKC Villa ",
243 | "description": "Its an amazing house to live",
244 | "price": 5000,
245 | "address": "Street 18",
246 | "city": "Delhi",
247 | "country": "India",
248 | "image": "https://res.cloudinary.com/dkg6ajfen/image/upload/v1684045778/zdc3pb1d0kgfwxuk8hch.jpg",
249 | "facilities": { "bedrooms": 5, "parkings": 2, "bathrooms": 3 },
250 | "userEmail": "ahmadrao925@gmail.com",
251 | "createdAt": { "$date": "2023-05-14T06:31:09.883Z" },
252 | "updatedAt": { "$date": "2023-05-14T06:31:09.883Z" }
253 | },
254 | {
255 | "_id": { "$oid": "648a1ed5d52203e45f037608" },
256 | "title": "Beautiful House",
257 | "description": "This is a good home to live",
258 | "price": 10000,
259 | "address": "Street 19",
260 | "city": "Mumbai",
261 | "country": "India",
262 | "image": "https://res.cloudinary.com/dkg6ajfen/image/upload/v1686773421/ihdmmu9mjsors2rvlu4j.png",
263 | "facilities": { "bedrooms": 5, "parkings": 5, "bathrooms": 8 },
264 | "userEmail": "zain@gaintime.io",
265 | "createdAt": { "$date": "2023-06-14T20:11:01.081Z" },
266 | "updatedAt": { "$date": "2023-06-14T20:11:01.081Z" }
267 | }
268 | ]
269 |
--------------------------------------------------------------------------------
/server/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 | "@prisma/client@^4.16.2":
6 | version "4.16.2"
7 | resolved "https://registry.yarnpkg.com/@prisma/client/-/client-4.16.2.tgz#3bb9ebd49b35c8236b3d468d0215192267016e2b"
8 | integrity sha512-qCoEyxv1ZrQ4bKy39GnylE8Zq31IRmm8bNhNbZx7bF2cU5aiCCnSa93J2imF88MBjn7J9eUQneNxUQVJdl/rPQ==
9 | dependencies:
10 | "@prisma/engines-version" "4.16.1-1.4bc8b6e1b66cb932731fb1bdbbc550d1e010de81"
11 |
12 | "@prisma/engines-version@4.16.1-1.4bc8b6e1b66cb932731fb1bdbbc550d1e010de81":
13 | version "4.16.1-1.4bc8b6e1b66cb932731fb1bdbbc550d1e010de81"
14 | resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-4.16.1-1.4bc8b6e1b66cb932731fb1bdbbc550d1e010de81.tgz#d3b5dcf95b6d220e258cbf6ae19b06d30a7e9f14"
15 | integrity sha512-q617EUWfRIDTriWADZ4YiWRZXCa/WuhNgLTVd+HqWLffjMSPzyM5uOWoauX91wvQClSKZU4pzI4JJLQ9Kl62Qg==
16 |
17 | "@prisma/engines@4.16.2":
18 | version "4.16.2"
19 | resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-4.16.2.tgz#5ec8dd672c2173d597e469194916ad4826ce2e5f"
20 | integrity sha512-vx1nxVvN4QeT/cepQce68deh/Turxy5Mr+4L4zClFuK1GlxN3+ivxfuv+ej/gvidWn1cE1uAhW7ALLNlYbRUAw==
21 |
22 | abbrev@1:
23 | version "1.1.1"
24 | resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
25 | integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
26 |
27 | accepts@~1.3.8:
28 | version "1.3.8"
29 | resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e"
30 | integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==
31 | dependencies:
32 | mime-types "~2.1.34"
33 | negotiator "0.6.3"
34 |
35 | anymatch@~3.1.2:
36 | version "3.1.3"
37 | resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e"
38 | integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==
39 | dependencies:
40 | normalize-path "^3.0.0"
41 | picomatch "^2.0.4"
42 |
43 | array-flatten@1.1.1:
44 | version "1.1.1"
45 | resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
46 | integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==
47 |
48 | balanced-match@^1.0.0:
49 | version "1.0.2"
50 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
51 | integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
52 |
53 | binary-extensions@^2.0.0:
54 | version "2.2.0"
55 | resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
56 | integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
57 |
58 | body-parser@1.20.1:
59 | version "1.20.1"
60 | resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668"
61 | integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==
62 | dependencies:
63 | bytes "3.1.2"
64 | content-type "~1.0.4"
65 | debug "2.6.9"
66 | depd "2.0.0"
67 | destroy "1.2.0"
68 | http-errors "2.0.0"
69 | iconv-lite "0.4.24"
70 | on-finished "2.4.1"
71 | qs "6.11.0"
72 | raw-body "2.5.1"
73 | type-is "~1.6.18"
74 | unpipe "1.0.0"
75 |
76 | brace-expansion@^1.1.7:
77 | version "1.1.11"
78 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
79 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
80 | dependencies:
81 | balanced-match "^1.0.0"
82 | concat-map "0.0.1"
83 |
84 | braces@~3.0.2:
85 | version "3.0.2"
86 | resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
87 | integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
88 | dependencies:
89 | fill-range "^7.0.1"
90 |
91 | bytes@3.1.2:
92 | version "3.1.2"
93 | resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5"
94 | integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==
95 |
96 | call-bind@^1.0.0:
97 | version "1.0.2"
98 | resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c"
99 | integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==
100 | dependencies:
101 | function-bind "^1.1.1"
102 | get-intrinsic "^1.0.2"
103 |
104 | chokidar@^3.5.2:
105 | version "3.5.3"
106 | resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
107 | integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
108 | dependencies:
109 | anymatch "~3.1.2"
110 | braces "~3.0.2"
111 | glob-parent "~5.1.2"
112 | is-binary-path "~2.1.0"
113 | is-glob "~4.0.1"
114 | normalize-path "~3.0.0"
115 | readdirp "~3.6.0"
116 | optionalDependencies:
117 | fsevents "~2.3.2"
118 |
119 | concat-map@0.0.1:
120 | version "0.0.1"
121 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
122 | integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
123 |
124 | content-disposition@0.5.4:
125 | version "0.5.4"
126 | resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe"
127 | integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==
128 | dependencies:
129 | safe-buffer "5.2.1"
130 |
131 | content-type@~1.0.4:
132 | version "1.0.5"
133 | resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918"
134 | integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==
135 |
136 | cookie-parser@^1.4.6:
137 | version "1.4.6"
138 | resolved "https://registry.yarnpkg.com/cookie-parser/-/cookie-parser-1.4.6.tgz#3ac3a7d35a7a03bbc7e365073a26074824214594"
139 | integrity sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==
140 | dependencies:
141 | cookie "0.4.1"
142 | cookie-signature "1.0.6"
143 |
144 | cookie-signature@1.0.6:
145 | version "1.0.6"
146 | resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
147 | integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==
148 |
149 | cookie@0.4.1:
150 | version "0.4.1"
151 | resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1"
152 | integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==
153 |
154 | cookie@0.5.0:
155 | version "0.5.0"
156 | resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b"
157 | integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==
158 |
159 | cors@^2.8.5:
160 | version "2.8.5"
161 | resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29"
162 | integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==
163 | dependencies:
164 | object-assign "^4"
165 | vary "^1"
166 |
167 | debug@2.6.9:
168 | version "2.6.9"
169 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
170 | integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
171 | dependencies:
172 | ms "2.0.0"
173 |
174 | debug@^3.2.7:
175 | version "3.2.7"
176 | resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
177 | integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==
178 | dependencies:
179 | ms "^2.1.1"
180 |
181 | depd@2.0.0:
182 | version "2.0.0"
183 | resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
184 | integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
185 |
186 | destroy@1.2.0:
187 | version "1.2.0"
188 | resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015"
189 | integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==
190 |
191 | dotenv@^16.3.1:
192 | version "16.3.1"
193 | resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e"
194 | integrity sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==
195 |
196 | ee-first@1.1.1:
197 | version "1.1.1"
198 | resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
199 | integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
200 |
201 | encodeurl@~1.0.2:
202 | version "1.0.2"
203 | resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
204 | integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==
205 |
206 | escape-html@~1.0.3:
207 | version "1.0.3"
208 | resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
209 | integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==
210 |
211 | etag@~1.8.1:
212 | version "1.8.1"
213 | resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
214 | integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==
215 |
216 | express-async-handler@^1.2.0:
217 | version "1.2.0"
218 | resolved "https://registry.yarnpkg.com/express-async-handler/-/express-async-handler-1.2.0.tgz#ffc9896061d90f8d2e71a2d2b8668db5b0934391"
219 | integrity sha512-rCSVtPXRmQSW8rmik/AIb2P0op6l7r1fMW538yyvTMltCO4xQEWMmobfrIxN2V1/mVrgxB8Az3reYF6yUZw37w==
220 |
221 | express-oauth2-jwt-bearer@^1.5.0:
222 | version "1.5.0"
223 | resolved "https://registry.yarnpkg.com/express-oauth2-jwt-bearer/-/express-oauth2-jwt-bearer-1.5.0.tgz#80ab2255308d25a36b09031a05c2a6b41f90e50a"
224 | integrity sha512-C8avk1VfopX3zjqfTLg9EuYFjNRdmXdYncBoZGmjxQzGx7cQRiupeWV5r3G2SYGzx0gDw1uyu1cdJrmILOvd3g==
225 | dependencies:
226 | jose "^4.13.1"
227 |
228 | express@^4.18.2:
229 | version "4.18.2"
230 | resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59"
231 | integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==
232 | dependencies:
233 | accepts "~1.3.8"
234 | array-flatten "1.1.1"
235 | body-parser "1.20.1"
236 | content-disposition "0.5.4"
237 | content-type "~1.0.4"
238 | cookie "0.5.0"
239 | cookie-signature "1.0.6"
240 | debug "2.6.9"
241 | depd "2.0.0"
242 | encodeurl "~1.0.2"
243 | escape-html "~1.0.3"
244 | etag "~1.8.1"
245 | finalhandler "1.2.0"
246 | fresh "0.5.2"
247 | http-errors "2.0.0"
248 | merge-descriptors "1.0.1"
249 | methods "~1.1.2"
250 | on-finished "2.4.1"
251 | parseurl "~1.3.3"
252 | path-to-regexp "0.1.7"
253 | proxy-addr "~2.0.7"
254 | qs "6.11.0"
255 | range-parser "~1.2.1"
256 | safe-buffer "5.2.1"
257 | send "0.18.0"
258 | serve-static "1.15.0"
259 | setprototypeof "1.2.0"
260 | statuses "2.0.1"
261 | type-is "~1.6.18"
262 | utils-merge "1.0.1"
263 | vary "~1.1.2"
264 |
265 | fill-range@^7.0.1:
266 | version "7.0.1"
267 | resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
268 | integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
269 | dependencies:
270 | to-regex-range "^5.0.1"
271 |
272 | finalhandler@1.2.0:
273 | version "1.2.0"
274 | resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32"
275 | integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==
276 | dependencies:
277 | debug "2.6.9"
278 | encodeurl "~1.0.2"
279 | escape-html "~1.0.3"
280 | on-finished "2.4.1"
281 | parseurl "~1.3.3"
282 | statuses "2.0.1"
283 | unpipe "~1.0.0"
284 |
285 | forwarded@0.2.0:
286 | version "0.2.0"
287 | resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
288 | integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==
289 |
290 | fresh@0.5.2:
291 | version "0.5.2"
292 | resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
293 | integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==
294 |
295 | fsevents@~2.3.2:
296 | version "2.3.2"
297 | resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
298 | integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
299 |
300 | function-bind@^1.1.1:
301 | version "1.1.1"
302 | resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
303 | integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
304 |
305 | get-intrinsic@^1.0.2:
306 | version "1.2.1"
307 | resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.1.tgz#d295644fed4505fc9cde952c37ee12b477a83d82"
308 | integrity sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==
309 | dependencies:
310 | function-bind "^1.1.1"
311 | has "^1.0.3"
312 | has-proto "^1.0.1"
313 | has-symbols "^1.0.3"
314 |
315 | glob-parent@~5.1.2:
316 | version "5.1.2"
317 | resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
318 | integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
319 | dependencies:
320 | is-glob "^4.0.1"
321 |
322 | has-flag@^3.0.0:
323 | version "3.0.0"
324 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
325 | integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==
326 |
327 | has-proto@^1.0.1:
328 | version "1.0.1"
329 | resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0"
330 | integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==
331 |
332 | has-symbols@^1.0.3:
333 | version "1.0.3"
334 | resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8"
335 | integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==
336 |
337 | has@^1.0.3:
338 | version "1.0.3"
339 | resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
340 | integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
341 | dependencies:
342 | function-bind "^1.1.1"
343 |
344 | http-errors@2.0.0:
345 | version "2.0.0"
346 | resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3"
347 | integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==
348 | dependencies:
349 | depd "2.0.0"
350 | inherits "2.0.4"
351 | setprototypeof "1.2.0"
352 | statuses "2.0.1"
353 | toidentifier "1.0.1"
354 |
355 | iconv-lite@0.4.24:
356 | version "0.4.24"
357 | resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
358 | integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
359 | dependencies:
360 | safer-buffer ">= 2.1.2 < 3"
361 |
362 | ignore-by-default@^1.0.1:
363 | version "1.0.1"
364 | resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09"
365 | integrity sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==
366 |
367 | inherits@2.0.4:
368 | version "2.0.4"
369 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
370 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
371 |
372 | ipaddr.js@1.9.1:
373 | version "1.9.1"
374 | resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3"
375 | integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==
376 |
377 | is-binary-path@~2.1.0:
378 | version "2.1.0"
379 | resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
380 | integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
381 | dependencies:
382 | binary-extensions "^2.0.0"
383 |
384 | is-extglob@^2.1.1:
385 | version "2.1.1"
386 | resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
387 | integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
388 |
389 | is-glob@^4.0.1, is-glob@~4.0.1:
390 | version "4.0.3"
391 | resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
392 | integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
393 | dependencies:
394 | is-extglob "^2.1.1"
395 |
396 | is-number@^7.0.0:
397 | version "7.0.0"
398 | resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
399 | integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
400 |
401 | jose@^4.13.1:
402 | version "4.14.4"
403 | resolved "https://registry.yarnpkg.com/jose/-/jose-4.14.4.tgz#59e09204e2670c3164ee24cbfe7115c6f8bff9ca"
404 | integrity sha512-j8GhLiKmUAh+dsFXlX1aJCbt5KMibuKb+d7j1JaOJG6s2UjX1PQlW+OKB/sD4a/5ZYF4RcmYmLSndOoU3Lt/3g==
405 |
406 | media-typer@0.3.0:
407 | version "0.3.0"
408 | resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
409 | integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==
410 |
411 | merge-descriptors@1.0.1:
412 | version "1.0.1"
413 | resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
414 | integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==
415 |
416 | methods@~1.1.2:
417 | version "1.1.2"
418 | resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
419 | integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==
420 |
421 | mime-db@1.52.0:
422 | version "1.52.0"
423 | resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
424 | integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
425 |
426 | mime-types@~2.1.24, mime-types@~2.1.34:
427 | version "2.1.35"
428 | resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
429 | integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
430 | dependencies:
431 | mime-db "1.52.0"
432 |
433 | mime@1.6.0:
434 | version "1.6.0"
435 | resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
436 | integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
437 |
438 | minimatch@^3.1.2:
439 | version "3.1.2"
440 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
441 | integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
442 | dependencies:
443 | brace-expansion "^1.1.7"
444 |
445 | ms@2.0.0:
446 | version "2.0.0"
447 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
448 | integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==
449 |
450 | ms@2.1.3, ms@^2.1.1:
451 | version "2.1.3"
452 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
453 | integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
454 |
455 | negotiator@0.6.3:
456 | version "0.6.3"
457 | resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd"
458 | integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==
459 |
460 | nodemon@^2.0.22:
461 | version "2.0.22"
462 | resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.22.tgz#182c45c3a78da486f673d6c1702e00728daf5258"
463 | integrity sha512-B8YqaKMmyuCO7BowF1Z1/mkPqLk6cs/l63Ojtd6otKjMx47Dq1utxfRxcavH1I7VSaL8n5BUaoutadnsX3AAVQ==
464 | dependencies:
465 | chokidar "^3.5.2"
466 | debug "^3.2.7"
467 | ignore-by-default "^1.0.1"
468 | minimatch "^3.1.2"
469 | pstree.remy "^1.1.8"
470 | semver "^5.7.1"
471 | simple-update-notifier "^1.0.7"
472 | supports-color "^5.5.0"
473 | touch "^3.1.0"
474 | undefsafe "^2.0.5"
475 |
476 | nopt@~1.0.10:
477 | version "1.0.10"
478 | resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee"
479 | integrity sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==
480 | dependencies:
481 | abbrev "1"
482 |
483 | normalize-path@^3.0.0, normalize-path@~3.0.0:
484 | version "3.0.0"
485 | resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
486 | integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
487 |
488 | object-assign@^4:
489 | version "4.1.1"
490 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
491 | integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
492 |
493 | object-inspect@^1.9.0:
494 | version "1.12.3"
495 | resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9"
496 | integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==
497 |
498 | on-finished@2.4.1:
499 | version "2.4.1"
500 | resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f"
501 | integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==
502 | dependencies:
503 | ee-first "1.1.1"
504 |
505 | parseurl@~1.3.3:
506 | version "1.3.3"
507 | resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
508 | integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
509 |
510 | path-to-regexp@0.1.7:
511 | version "0.1.7"
512 | resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
513 | integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==
514 |
515 | picomatch@^2.0.4, picomatch@^2.2.1:
516 | version "2.3.1"
517 | resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
518 | integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
519 |
520 | prisma@^4.16.2:
521 | version "4.16.2"
522 | resolved "https://registry.yarnpkg.com/prisma/-/prisma-4.16.2.tgz#469e0a0991c6ae5bcde289401726bb012253339e"
523 | integrity sha512-SYCsBvDf0/7XSJyf2cHTLjLeTLVXYfqp7pG5eEVafFLeT0u/hLFz/9W196nDRGUOo1JfPatAEb+uEnTQImQC1g==
524 | dependencies:
525 | "@prisma/engines" "4.16.2"
526 |
527 | proxy-addr@~2.0.7:
528 | version "2.0.7"
529 | resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025"
530 | integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==
531 | dependencies:
532 | forwarded "0.2.0"
533 | ipaddr.js "1.9.1"
534 |
535 | pstree.remy@^1.1.8:
536 | version "1.1.8"
537 | resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.8.tgz#c242224f4a67c21f686839bbdb4ac282b8373d3a"
538 | integrity sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==
539 |
540 | qs@6.11.0:
541 | version "6.11.0"
542 | resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a"
543 | integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==
544 | dependencies:
545 | side-channel "^1.0.4"
546 |
547 | range-parser@~1.2.1:
548 | version "1.2.1"
549 | resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
550 | integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
551 |
552 | raw-body@2.5.1:
553 | version "2.5.1"
554 | resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857"
555 | integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==
556 | dependencies:
557 | bytes "3.1.2"
558 | http-errors "2.0.0"
559 | iconv-lite "0.4.24"
560 | unpipe "1.0.0"
561 |
562 | readdirp@~3.6.0:
563 | version "3.6.0"
564 | resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
565 | integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==
566 | dependencies:
567 | picomatch "^2.2.1"
568 |
569 | safe-buffer@5.2.1:
570 | version "5.2.1"
571 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
572 | integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
573 |
574 | "safer-buffer@>= 2.1.2 < 3":
575 | version "2.1.2"
576 | resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
577 | integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
578 |
579 | semver@^5.7.1:
580 | version "5.7.1"
581 | resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
582 | integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
583 |
584 | semver@~7.0.0:
585 | version "7.0.0"
586 | resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e"
587 | integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==
588 |
589 | send@0.18.0:
590 | version "0.18.0"
591 | resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be"
592 | integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==
593 | dependencies:
594 | debug "2.6.9"
595 | depd "2.0.0"
596 | destroy "1.2.0"
597 | encodeurl "~1.0.2"
598 | escape-html "~1.0.3"
599 | etag "~1.8.1"
600 | fresh "0.5.2"
601 | http-errors "2.0.0"
602 | mime "1.6.0"
603 | ms "2.1.3"
604 | on-finished "2.4.1"
605 | range-parser "~1.2.1"
606 | statuses "2.0.1"
607 |
608 | serve-static@1.15.0:
609 | version "1.15.0"
610 | resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540"
611 | integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==
612 | dependencies:
613 | encodeurl "~1.0.2"
614 | escape-html "~1.0.3"
615 | parseurl "~1.3.3"
616 | send "0.18.0"
617 |
618 | setprototypeof@1.2.0:
619 | version "1.2.0"
620 | resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
621 | integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
622 |
623 | side-channel@^1.0.4:
624 | version "1.0.4"
625 | resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf"
626 | integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==
627 | dependencies:
628 | call-bind "^1.0.0"
629 | get-intrinsic "^1.0.2"
630 | object-inspect "^1.9.0"
631 |
632 | simple-update-notifier@^1.0.7:
633 | version "1.1.0"
634 | resolved "https://registry.yarnpkg.com/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz#67694c121de354af592b347cdba798463ed49c82"
635 | integrity sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==
636 | dependencies:
637 | semver "~7.0.0"
638 |
639 | statuses@2.0.1:
640 | version "2.0.1"
641 | resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"
642 | integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==
643 |
644 | supports-color@^5.5.0:
645 | version "5.5.0"
646 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
647 | integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
648 | dependencies:
649 | has-flag "^3.0.0"
650 |
651 | to-regex-range@^5.0.1:
652 | version "5.0.1"
653 | resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
654 | integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
655 | dependencies:
656 | is-number "^7.0.0"
657 |
658 | toidentifier@1.0.1:
659 | version "1.0.1"
660 | resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
661 | integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==
662 |
663 | touch@^3.1.0:
664 | version "3.1.0"
665 | resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.0.tgz#fe365f5f75ec9ed4e56825e0bb76d24ab74af83b"
666 | integrity sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==
667 | dependencies:
668 | nopt "~1.0.10"
669 |
670 | type-is@~1.6.18:
671 | version "1.6.18"
672 | resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
673 | integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==
674 | dependencies:
675 | media-typer "0.3.0"
676 | mime-types "~2.1.24"
677 |
678 | undefsafe@^2.0.5:
679 | version "2.0.5"
680 | resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c"
681 | integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==
682 |
683 | unpipe@1.0.0, unpipe@~1.0.0:
684 | version "1.0.0"
685 | resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
686 | integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==
687 |
688 | utils-merge@1.0.1:
689 | version "1.0.1"
690 | resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
691 | integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==
692 |
693 | vary@^1, vary@~1.1.2:
694 | version "1.1.2"
695 | resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
696 | integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==
697 |
--------------------------------------------------------------------------------