Open In App

Neighborhood Safety Rating App with MERN Stack

Last Updated : 15 Apr, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

In this project, we developed a web application called the Neighborhood Safety Rating App using the MERN stack, which includes MongoDB, Express.js, React.js, and Node.js. The app allows users to view and manage safety ratings for different neighborhoods.

Project Preview:

imresizer-1712233549659

Prerequisites:-

Approach

  • Setting up the back-end API using Express.js to handle CRUD operations for managing neighborhoods in MongoDB.
  • Creating a React front-end to interact with the back-end API.
  • Designing the UI for the neighborhood list and form to add new neighborhoods.
  • Implementing functionality to add, delete, and edit neighborhoods.
  • Sorting neighborhoods by safety rating.
  • Styling the UI to enhance the user experience.

Steps to Create the Project:

Step 1: Create the backend folder using the following command.

mkdir neighborhood-safety

Step 2: Initialize a new Node.js project using the following command.

npm init -y

Step 3: Install the required dependencies:

npm i express mongoose cors

Depedencies:

"dependencies": {
"cors": "^2.8.5",
"express": "^4.19.2",
"mongoose": "^8.3.1",
"nodemon": "^3.1.0"
}

Folder Structure:

Screenshot-2024-03-23-142208

Step 4: To start the server run the following command.

node server.js


Step 5: Create the frontend application using the following command.

npx create-react-app frontend

Step 6: Install the required dependencies:

npm i axios

Step 7: To start the application run the following command.

npm start

Folder Structure:

Screenshot-2024-03-23-142145

Dependencies:

"dependencies": {
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"axios": "^1.6.8",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
}

Example Code:

FrontEnd:

CSS
/* App.css */

body {
    font-family: Arial, sans-serif;
    background-color: #f4f4f4;
    margin: 0;
    padding: 0;
}

.app-container {
    max-width: 1200px;
    margin: 0 auto;
    padding: 20px;
    background-color: #ffffff;
    border-radius: 5px;
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}

.heading {
    font-size: 24px;
    color: #009688;
    margin-bottom: 20px;
}

.form {
    display: flex;
    flex-wrap: wrap;
    margin-bottom: 20px;
}

.input {
    flex: 1;
    padding: 8px;
    margin-right: 10px;
    border: 1px solid #ccc;
    border-radius: 3px;
}

.button {
    background-color: #009688;
    color: #ffffff;
    border: none;
    margin-top: 1%;
    padding: 8px 16px;
    cursor: pointer;
    transition: background-color 0.3s;
    border-radius: 3px;
}

.button:hover {
    background-color: #00796b;
}

.sort-button {
    margin-bottom: 10px;
}

.neighborhood-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
    grid-gap: 20px;
}

.neighborhood-box {
    background-color: #ffffff;
    border: 1px solid #ccc;
    padding: 20px;
    border-radius: 5px;
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}

.neighborhood-name {
    font-weight: bold;
    margin-bottom: 10px;
}

.neighborhood-rating {
    font-size: 14px;
    margin-bottom: 10px;
}

.button-group {
    display: flex;
    justify-content: space-between;
}
JavaScript
//App.js

import React, { useState, useEffect } from "react";
import axios from "axios";
import "./App.css";

function App() {
    const [neighborhoods, setNeighborhoods] = useState([]);
    const [newNeighborhood, setNewNeighborhood] = useState({
        name: "",
        safetyRating: "",
        crimeStatistics: "",
        accidentReports: "",
        demographics: "",
        communityFeedback: "",
    });

    useEffect(() => {
        fetchNeighborhoods();
    }, []);

    const fetchNeighborhoods = async () => {
        try {
            const response = await axios.get("http://localhost:5000/neighborhoods");
            setNeighborhoods(response.data);
        } catch (error) {
            console.error("Error fetching neighborhoods:", error);
        }
    };

    const handleInputChange = (e) => {
        setNewNeighborhood({ ...newNeighborhood, [e.target.name]: e.target.value });
    };

    const handleSubmit = async (e) => {
        e.preventDefault();
        try {
            const response = await axios.post(
                "http://localhost:5000/neighborhoods",
                newNeighborhood
            );
            setNeighborhoods([...neighborhoods, response.data]);
            setNewNeighborhood({
                name: "",
                safetyRating: "",
                crimeStatistics: "",
                accidentReports: "",
                demographics: "",
                communityFeedback: "",
            });
        } catch (error) {
            console.error("Error adding neighborhood:", error);
        }
    };

    const handleDelete = async (id) => {
        try {
            await axios.delete(`http://localhost:5000/neighborhoods/${id}`);
            setNeighborhoods(
                neighborhoods.filter((neighborhood) => neighborhood._id !== id)
            );
        } catch (error) {
            console.error("Error deleting neighborhood:", error);
        }
    };

    const handleEdit = async (id, updatedNeighborhood) => {
        try {
            await axios.put(
                `http://localhost:5000/neighborhoods/${id}`,
                updatedNeighborhood
            );
            fetchNeighborhoods();
        } catch (error) {
            console.error("Error editing neighborhood:", error);
        }
    };

    const sortNeighborhoodsBySafetyRating = () => {
        const sortedNeighborhoods = [...neighborhoods].sort(
            (a, b) => b.safetyRating - a.safetyRating
        );
        setNeighborhoods(sortedNeighborhoods);
    };

    return (
        <div className="app-container">
            <h1 className="heading">Neighborhood Safety Rating App</h1>
            <form className="form" onSubmit={handleSubmit}>
                <input
                    className="input"
                    type="text"
                    placeholder="Neighborhood Name"
                    name="name"
                    value={newNeighborhood.name}
                    onChange={handleInputChange}
                />
                <input
                    className="input"
                    type="number"
                    placeholder="Safety Rating"
                    name="safetyRating"
                    value={newNeighborhood.safetyRating}
                    onChange={handleInputChange}
                />
                <input
                    className="input"
                    type="text"
                    placeholder="Crime Statistics"
                    name="crimeStatistics"
                    value={newNeighborhood.crimeStatistics}
                    onChange={handleInputChange}
                />
                <input
                    className="input"
                    type="text"
                    placeholder="Accident Reports"
                    name="accidentReports"
                    value={newNeighborhood.accidentReports}
                    onChange={handleInputChange}
                />
                <input
                    className="input"
                    type="text"
                    placeholder="Demographics"
                    name="demographics"
                    value={newNeighborhood.demographics}
                    onChange={handleInputChange}
                />
                <input
                    className="input"
                    type="text"
                    placeholder="Community Feedback"
                    name="communityFeedback"
                    value={newNeighborhood.communityFeedback}
                    onChange={handleInputChange}
                />

                <button className="button" type="submit">
                    Add Neighborhood
                </button>
            </form>
            <button
                className="button sort-button"
                onClick={sortNeighborhoodsBySafetyRating}
            >
                Sort by Safety Rating
            </button>
            <h2 className="heading">Neighborhoods:</h2>
            <div className="neighborhood-grid">
                {neighborhoods.map((neighborhood) => (
                    <div key={neighborhood._id} className="neighborhood-box">
                        <span className="ne">
                            <b>{neighborhood.name} &nbsp;&#9733;&nbsp;</b>
                        </span>
                        <span className="ne">{neighborhood.safetyRating}</span>
                        <br />
                        <br />
                        <span className="ne">
                            <u>Crime Statistics:</u> {neighborhood.crimeStatistics}
                        </span>
                        <br />
                        <br />
                        <span className="ne">
                            <u>Accident Reports:</u> {neighborhood.accidentReports}
                        </span>
                        <br />
                        <br />
                        <span className="ne">
                            <u>Demographics:</u> {neighborhood.demographics}
                        </span>
                        <br />
                        <br />
                        <span className="ne">
                            <u>Community Feedback:</u> {neighborhood.communityFeedback}
                        </span>
                        <br />
                        <br />
                        <div className="button-group">
                            <button
                                className="button delete-button"
                                onClick={() => handleDelete(neighborhood._id)}
                            >
                                Delete
                            </button>
                            <button
                                className="button edit-button"
                                onClick={() =>
                                    handleEdit(neighborhood._id, {
                                        name: neighborhood.name,
                                        safetyRating: prompt("Enter new safety rating:"),
                                        crimeStatistics: prompt("Enter new crime statistics:"),
                                        accidentReports: prompt("Enter new accident reports:"),
                                        demographics: prompt("Enter new demographics:"),
                                        communityFeedback: prompt("Enter new community feedback:"),
                                    })
                                }
                            >
                                Edit
                            </button>
                        </div>
                    </div>
                ))}
            </div>
        </div>
    );
}

export default App;

BackEnd:

JavaScript
//server.js

const express = require("express");
const mongoose = require("mongoose");
const bodyParser = require("body-parser");
const cors = require("cors");

const app = express();

app.use(cors());
app.use(bodyParser.json());

mongoose
    .connect(
        "Your MongoDB URI",
        { useNewUrlParser: true, useUnifiedTopology: true }
    )
    .then(() => console.log("MongoDB connected"))
    .catch((err) => console.log(err));

const Neighborhood = mongoose.model("Neighborhood", {
    name: { type: String, required: true },
    safetyRating: { type: Number, required: true },
    crimeStatistics: { type: String },
    accidentReports: { type: String },
    demographics: { type: String },
    communityFeedback: { type: String },
});

app.get("/neighborhoods", async (req, res) => {
    try {
        const neighborhoods = await Neighborhood.find();
        res.json(neighborhoods);
    } catch (err) {
        res.status(500).json({ message: err.message });
    }
});

app.post("/neighborhoods", async (req, res) => {
    const neighborhood = new Neighborhood({
        name: req.body.name,
        safetyRating: req.body.safetyRating,
        crimeStatistics: req.body.crimeStatistics,
        accidentReports: req.body.accidentReports,
        demographics: req.body.demographics,
        communityFeedback: req.body.communityFeedback,
    });

    try {
        const newNeighborhood = await neighborhood.save();
        res.status(201).json(newNeighborhood);
    } catch (err) {
        res.status(400).json({ message: err.message });
    }
});

app.put("/neighborhoods/:id", async (req, res) => {
    try {
        const neighborhood = await Neighborhood.findById(req.params.id);
        if (neighborhood == null) {
            return res.status(404).json({ message: "Neighborhood not found" });
        }
        if (req.body.name != null) {
            neighborhood.name = req.body.name;
        }
        if (req.body.safetyRating != null) {
            neighborhood.safetyRating = req.body.safetyRating;
        }
        if (req.body.crimeStatistics != null) {
            neighborhood.crimeStatistics = req.body.crimeStatistics;
        }
        if (req.body.accidentReports != null) {
            neighborhood.accidentReports = req.body.accidentReports;
        }
        if (req.body.demographics != null) {
            neighborhood.demographics = req.body.demographics;
        }
        if (req.body.communityFeedback != null) {
            neighborhood.communityFeedback = req.body.communityFeedback;
        }
        const updatedNeighborhood = await neighborhood.save();
        res.json(updatedNeighborhood);
    } catch (err) {
        res.status(400).json({ message: err.message });
    }
});

app.delete("/neighborhoods/:id", async (req, res) => {
    try {
        const deletedNeighborhood = await Neighborhood.findByIdAndDelete(
            req.params.id
        );
        res.json(deletedNeighborhood);
    } catch (err) {
        res.status(500).json({ message: err.message });
    }
});

const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));

Output:24354-ezgifcom-video-to-gif-converter

Database:

imresizer-1712233660028



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

Similar Reads