Neighborhood Safety Rating App with MERN Stack
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:
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:
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:
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:
/* 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;
}
//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} ★ </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:
//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:
Database:
Contact Us