Open In App

Real Estate Management using MERN

Last Updated : 05 Jan, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

In this article, we will guide you through the process of building a Real Estate Management Application using the MERN stack. MERN stands for MongoDB, Express, React, and Node. MongoDB will serve as our database, Express will handle the backend, React will create the frontend, and Node.js will be the runtime for our server.

Preview of final output: Let us have a look at how the final application will look like.

Screenshot-2567-01-01-at-102608-(1)

Final Preview

Prerequisites:

Approach to create Real Estate Management Website using MERN:

  • Import Statements:
    • Import necessary dependencies and components.
    • React is imported for defining React components.
    • useState and useEffect are imported from React for managing state and handling side effects respectively.
    • PropertyList and AddProperty are imported, assumed to be custom components.
    • Axios is imported for making HTTP requests.
  • Functional Component:
    • Define a functional component named App.
  • State Management:
    • Use the useState hook to manage the state of properties, which is initialized as an empty array.
    • The properties state will hold the list of properties fetched from the server.
  • Data Fetching:
    • Use the useEffect hook to make an HTTP GET request to ‘http://localhost:5000/api/properties‘ when the component mounts.
    • Update the properties state with the data received from the server.
  • Event Handlers:
    • Define event handlers:
      • handleAddProperty: Takes a new property as an argument and adds it to the properties state using the setProperties function.
      • handleContactOwner: Takes contact information as an argument and displays an alert with the contact information.
      • handleDeleteProperty: Takes a property ID as an argument, makes an HTTP DELETE request to delete the property, and then updates the state by filtering out the deleted property.
  • Component Rendering:
    • Render a div containing:
      • Two heading elements (h1) with class and inline styles.
      • The AddProperty component, passing the handleAddProperty function as a prop.
      • The PropertyList component, passing the handleDeleteProperty and handleContactOwner functions, as well as the properties state.

Steps to Create the Backend:

Step 1: Create a directory for project

npm init backend

Step 2: Open Restaurant-Recommendation using the command

cd backend

Step 3: Installing the required packages

npm install express mongoose cors body-parser

This will install Express.js for the backend, Mongoose for MongoDB integration, CORS for handling cross-origin requests, and Body Parser for parsing incoming JSON requests.

Project Structure:

Screenshot-2566-12-31-at-150800

Server folder structure

The updated dependencies in package.json file for backend will look like:

"dependencies": {
"body-parser": "^1.20.2",
"cors": "^2.8.5",
"express": "^4.18.2",
"mongoose": "^8.0.0",
}

Example: Create a file named `server.js` in server folder. This file will serve as our backend server.

Javascript




const express = require("express");
const mongoose = require("mongoose");
const cors = require("cors");
const bodyParser = require("body-parser");
const app = express();
const PORT = process.env.PORT || 5000;
 
app.use(cors());
app.use(express.json());
app.use(bodyParser.json());
 
// Connect to MongoDB
mongoose
        useNewUrlParser: true,
        useUnifiedTopology: true,
    })
    .then(() => {
        console.log("Connected to MongoDB");
    });
 
// Define the Property schema
const propertySchema = new mongoose.Schema({
    title: String,
    description: String,
    image: String,
    contact: String,
    reviews: [
        {
            user: String,
            rating: Number,
            comment: String,
        },
    ],
});
 
const Property = mongoose.model("Property", propertySchema);
 
// API endpoints
// Route to add a new property
app.post("/api/properties", async (req, res) => {
    try {
        const { title, description, image, contact } = req.body;
 
        // Validate request data
        if (!title || !description || !image || !contact) {
            return res
                .status(400)
                .json({ message: "Incomplete property data" });
        }
 
        // Create a new property
        const newProperty = new Property({
            title,
            description,
            image,
            contact,
            reviews: [],
        });
 
        // Save the new property to the database
        const savedProperty = await newProperty.save();
 
        // Respond with the newly added property
        res.status(201).json(savedProperty);
    } catch (error) {
        console.error("Error adding property:", error);
        res.status(500).json({ message: "Internal Server Error" });
    }
});
 
// Route to get all properties
app.get("/api/properties", async (req, res) => {
    try {
        const properties = await Property.find();
        res.json(properties);
    } catch (error) {
        res.status(500).json({ message: error.message });
    }
});
 
// Route to add a review for a property
app.post("/api/properties/:id/review", async (req, res) => {
    const { user, rating, comment } = req.body;
 
    try {
        const property = await Property.findById(req.params.id);
        property.reviews.push({ user, rating, comment });
        await property.save();
        res.status(201).json(property);
    } catch (error) {
        res.status(400).json({ message: error.message });
    }
});
 
// Delete a property by ID
app.delete("/api/properties/:id", async (req, res) => {
    const propertyId = req.params.id;
 
    try {
        // Find the property by ID and delete it from the database
        const deletedProperty = await Property.findByIdAndDelete(propertyId);
 
        if (!deletedProperty) {
            return res.status(404).json({ message: "Property not found" });
        }
 
        res.json({ message: "Property deleted", deletedProperty });
    } catch (error) {
        console.error("Error deleting property:", error);
        res.status(500).json({ message: "Internal Server Error" });
    }
});
 
app.listen(PORT, () => {
    console.log(`Server is running on port ${PORT}`);
});


Step 3: Defining the Property Schema and API Endpoints

Continue the `server.js` file by defining the Property schema and implementing API endpoints for adding properties, getting all properties, adding reviews, and deleting properties.

This is already implemented in Step 2.

 // Define the Property schema
const propertySchema = new mongoose.Schema({
title: String,
description: String,
image: String,
contact: String,
reviews: [
{
user: String,
rating: Number,
comment: String,
},
],
});

const Property = mongoose.model('Property', propertySchema);

Steps to Create the Frontend:

Step 1: Set up React frontend using the command.

npx create-react-app client

Step 2: Navigate to the project folder using the command.

cd client

Step 3: Install axios

npm i axios

Project Structure:

Screenshot-2567-01-04-at-005029

Frontend project structure

The updated dependencies in package.json for frontend will look like:

"dependencies": {
"axios": "^1.5.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
}

Example: Below is the client code.

Javascript




// src/App.js
import React, { useState, useEffect } from "react";
import PropertyList from "./components/PropertyList";
import AddProperty from "./components/AddProperty";
import axios from "axios";
import "./App.css";
 
const App = () => {
    const [properties, setProperties] = useState([]);
 
    useEffect(() => {
        axios
            .get("http://localhost:5000/api/properties")
            .then((response) => setProperties(response.data))
            .catch((error) => console.error(error));
    }, []); // Empty dependency array to fetch properties once on mount
 
    const handleAddProperty = (newProperty) => {
        setProperties((prevProperties) => [...prevProperties, newProperty]);
    };
 
    const handleContactOwner = (contact) => {
        alert(`Contacting the owner of property is ${contact}`);
    };
 
    const handleDeleteProperty = (propertyId) => {
        axios
            .delete(`http://localhost:5000/api/properties/${propertyId}`)
            .then((response) => {
                // Filter out the deleted property from the state
                setProperties((prevProperties) =>
                    prevProperties.filter(
                        (property) => property._id !== propertyId
                    )
                );
            })
            .catch((error) => console.error(error));
    };
 
    return (
        <div style={{}}>
            <h1 className="gfg" style={{ margin: "10px 10px" }}>
                GFG
            </h1>
            <h1 style={{ marginTop: "10px" }}>Real Estate Management</h1>
            <div>
                <AddProperty onAddProperty={handleAddProperty} />
                <PropertyList
                    onDeleteProperty={handleDeleteProperty}
                    properties={properties}
                    onContactOwner={handleContactOwner}
                />
            </div>
        </div>
    );
};
 
export default App;


Javascript




//components/ProductList.js
 
import React, { useContext, useEffect, useState } from "react";
import ProductItem from "./ProductItem";
import { itemContext } from "../context/itemContext";
import "../App.css";
 
const ProductList = () => {
    const { products } = useContext(itemContext);
    const [sortedProducts, setSortedProducts] = useState([...products]);
    // Keep a local state for sorted products
    const [minPrice, setMinPrice] = useState(0);
    const [maxPrice, setMaxPrice] = useState(3000);
    const [selectedType, setSelectedType] = useState("all");
    // 'all' represents no type filter
 
    useEffect(() => {
        setSortedProducts([...products]);
    }, [products]);
 
    const handleSortByPrice = () => {
        const sorted = [...sortedProducts].sort((a, b) => a.price - b.price);
        setSortedProducts(sorted);
    };
 
    const handleFilterByPriceRange = () => {
        const filtered = products.filter(
            (product) => product.price >= minPrice && product.price <= maxPrice
        );
        setSortedProducts(filtered);
    };
 
    const handleFilterByType = () => {
        if (selectedType === "all") {
            // Reset the type filter
            setSortedProducts([...products]);
        } else {
            const filtered = products.filter(
                (product) => product.type === selectedType
            );
            setSortedProducts(filtered);
        }
    };
 
    return (
        <div className="prdt-list">
            <h2>Product List</h2>
            <div className="filter-btn">
                <button onClick={handleSortByPrice}>Sort by Price</button>
                <label>
                    Min Price:
                    <input
                        type="number"
                        value={minPrice}
                        onChange={(e) => setMinPrice(Number(e.target.value))}
                    />
                </label>
                <label>
                    Max Price:
                    <input
                        type="number"
                        value={maxPrice}
                        onChange={(e) => setMaxPrice(Number(e.target.value))}
                    />
                </label>
                <button onClick={() => handleFilterByPriceRange()}>
                    Filter by Price Range
                </button>
                <label>
                    Filter by Type:
                    <select
                        value={selectedType}
                        onChange={(e) => setSelectedType(e.target.value)}
                    >
                        <option value="all">All</option>
                        <option value="Fruit">Fruit</option>
                        <option value="Vegetable">Vegetable</option>
                        {/* Add more options as needed */}
                    </select>
                </label>
                <button onClick={handleFilterByType}>Filter by Type</button>
            </div>
            <ul className="item-card">
                {sortedProducts.map((product) => (
                    <ProductItem key={product._id} product={product} />
                ))}
            </ul>
            <div className="buy-now-btn">Buy Now</div>
        </div>
    );
};
 
export default ProductList;


Javascript




// src/components/AddProperty.js
import React, { useState } from 'react';
import axios from 'axios';
import '../App.css'
 
const AddProperty = ({ onAddProperty }) => {
    const [newProperty, setNewProperty] = useState({
        title: '',
        description: '',
        image: '',
        contact: '',
    });
 
    const handleAddProperty = () => {
        // Submit a new property
        axios.post('http://localhost:5000/api/properties', newProperty)
            .then(response => {
                // Notify the parent component about the new property
                onAddProperty(response.data);
 
                // Clear the newProperty state for the next entry
                setNewProperty({
                    title: '',
                    description: '',
                    image: '',
                    contact: '',
                });
            })
            .catch(error => console.error(error));
    };
 
    return (
        <div>
            <h2 style={{ "color": "#007BFF" }}>
                Add a New Property
            </h2>
            <form onSubmit={(e) => {
                e.preventDefault();
                handleAddProperty();
            }}>
                <div className="form-row">
                    <label>Title:
                        <input type="text"
                            value={newProperty.title}
                            onChange={(e) =>
                                setNewProperty(
                                    {
                                        ...newProperty,
                                        title: e.target.value
                                    })}
                            required />
                    </label>
                    <label>Description:
                        <input type="text"
                            value={newProperty.description}
                            onChange={
                                (e) =>
                                    setNewProperty(
                                        {
                                            ...newProperty,
                                            description: e.target.value
                                        })} required />
                    </label>
                </div>
                <div className="form-row">
                    <label>Image URL:
                        <input type="text"
                            value={newProperty.image}
                            onChange={
                                (e) =>
                                    setNewProperty(
                                        {
                                            ...newProperty,
                                            image: e.target.value
                                        })} required />
                    </label>
                    <label>Contact: <input type="text"
                        value={newProperty.contact}
                        onChange={
                            (e) => setNewProperty(
                                {
                                    ...newProperty,
                                    contact: e.target.value
                                })} required />
                    </label>
                </div>
                <button type="submit"
                    style={{ backgroundColor: "blue" }}>
                    Add Property
                </button>
            </form>
        </div>
    );
};
 
export default AddProperty;


CSS




input {
    border: 2px solid black;
}
 
.property-list {
    display: flex;
    flex-wrap: wrap;
    gap: 20px;
}
 
.property-card {
    border: 1px solid #ddd;
    border-radius: 18px;
    padding: 15px;
    width: -moz-fit-content;
    width: fit-content;
    box-shadow: 0 14px 18px rgba(0, 0, 0, 0.1);
    background-color: #fff;
    margin-bottom: 3rem;
}
 
.property-card h3 {
    margin-bottom: 10px;
}
 
.property-card button {
    background-color: #007BFF;
    color: #fff;
    border: none;
    padding: 8px;
    cursor: pointer;
    border-radius: 4px;
}
 
button {
    background-color: #007BFF;
}
 
.property-card button:hover {
    background-color: #0056b3;
}
 
img {
    height: 200px;
    width: 300px;
    border-radius: 10px;
    margin-bottom: 10px;
}
 
.list-container {
    display: flex;
    flex-direction: row;
    justify-content: center;
    gap: 15px;
 
}
 
h1,
h2 {
    text-align: center;
}
 
f
 
/* src/components/AddProperty.css */
.property-card {
    border: 1px solid #ddd;
    border-radius: 10px;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
    padding: 20px;
    margin: 20px;
    width: 300px;
}
 
.property-card h2 {
    margin-bottom: 15px;
    color: #333;
}
 
.property-card label {
    display: block;
    margin-bottom: 10px;
    color: #333;
}
 
.property-card input {
    width: 100%;
    padding: 8px;
    margin-bottom: 10px;
    box-sizing: border-box;
}
 
.property-card button {
    background-color: #007BFF;
    color: #fff;
    border: none;
    padding: 10px;
    cursor: pointer;
    border-radius: 4px;
}
 
.property-card button:hover {
    background-color: #0056b3;
}
 
 
 
 
 
 
/* AddProperty.css */
.form-container {
    max-width: 300px;
    margin: 20px auto;
    padding: 20px;
    background-color: #f7f7f7;
    border-radius: 8px;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
 
.form-row {
    display: flex;
    gap: 20px;
    margin-bottom: 15px;
}
 
.form-row label {
    flex: 1;
}
 
.form-row input {
    flex: 2;
    padding: 8px;
    width: 100%;
    box-sizing: border-box;
}
 
button {
 
    background-color: #007BFF;
    color: #fff;
    border: none;
    padding: 10px;
    cursor: pointer;
    border-radius: 4px;
}
 
button:hover {
    background-color: #0056b3;
}
 
 
form {
 
    padding: 10px;
}
 
.gfg {
    background-color: green;
    text-align: center;
    color: white;
    padding: 15px;
    border-radius: 10px;
    margin-bottom: -20px;
}
 
.list {
    margin-top: -50px;
}


Sep to Start Server and React App:

  • To start backend server: Go to server folder inside terminal and write below command
node server.js
  • To start frontend server: Go to client folder inside terminal and write below command
npm start

Output:

houseGif

Output

Output of Data saved in Database:

housedb

Db



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads