Mindfulness Meditation App with MERN Stack
Last Updated :
15 Apr, 2024
In today’s time, stress and anxiety have become common challenges for most of us. To promote mental well-being and mindfulness, we created a Mindfulness Meditation App using MERN. This app allows users to create, edit, and delete meditation sessions, helping them cultivate a sense of peace and calmness in their daily lives.
Project Preview:
Prerequisites:
Approach:
Our approach to building this app involves creating both frontend and backend components. The frontend is developed using React, providing a user-friendly interface for managing meditation sessions. The backend, built with Express and MongoDB, handles data storage, retrieval, editing, and deletion operations.
Key functionalities of the app include:
- Adding a new meditation session with a title, description, and duration.
- Editing existing meditation sessions by updating their details.
- Deleting meditation sessions to manage the list effectively.
Steps to Create the Project
Step 1: Create a new React app using the following command.
npx create-react-app frontend
Step 2: Navigate to the project directory
cd frontend
Step 3: Install Axios for making HTTP requests:
npm install axios.
Folder Structure:
Depedencies:
"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"
}
Step 4: To start the frontend run the following command.
npm start
Step 5: Create the backend folder using the following command.
mkdir backend
cd backend
Step 6: Initialize the Node APplication
npm init -y
Step 7: Install the required dependencies:
npm install cors express mongoose
Folder Structure:
Dependencies:
"dependencies": {
"body-parser": "^1.20.2",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.19.2",
"mongoose": "^8.2.3"
}
Step 8: To start the backend run the following command.
node server.js
Example Code
FrontEnd:
CSS
/* src/App.css */
.App {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100vh;
background-color: #eaf6f6;
color: #000000;
font-family: Arial, sans-serif;
}
.MeditationList {
margin-top: 20px;
}
.MeditationList ul {
display: flex;
flex-direction: row;
justify-content: space-evenly;
flex-wrap: wrap;
list-style-type: none;
padding: 0;
}
.MeditationList li {
background-color: #a5c9f0;
margin-bottom: 10px;
width: 300px;
padding: 10px;
margin: 1%;
border-radius: 5px;
}
.timer-container {
position: relative;
margin-top: 20px;
width: 100%;
}
.timer-container.blur {
filter: blur(5px);
display: flex;
justify-content: center;
align-items: center;
text-align: center;
}
.timer {
text-align: center;
font-size: 24px;
font-weight: bold;
background-color: rgba(200, 188, 254, 0.8);
padding: 10px 20px;
border-radius: 5px;
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.3);
}
.MeditationList h2 {
color: #333;
}
.MeditationForm {
background-color: rgb(160, 201, 246);
padding: 20px;
border-radius: 10px;
margin-bottom: 20px;
}
.MeditationForm label {
font-weight: bold;
margin-bottom: 5px;
display: block;
}
.MeditationForm input[type='text'],
.MeditationForm textarea,
.MeditationForm input[type='number'],
.MeditationForm button {
width: 100%;
padding: 10px;
margin-bottom: 10px;
border: none;
border-radius: 5px;
box-sizing: border-box;
}
.MeditationForm textarea {
height: 100px;
}
.MeditationForm button {
background-color: #eaf6f6;
color: #333;
cursor: pointer;
transition: background-color 0.3s ease;
}
.MeditationForm button:hover {
background-color: #d1eeea;
}
.action-buttons {
display: flex;
gap: 10px;
margin-top: 10px;
}
.action-buttons button {
padding: 8px 12px;
border: none;
border-radius: 5px;
cursor: pointer;
}
.action-buttons button.delete-btn {
background-color: #f44336;
color: #fff;
}
.action-buttons button.edit-btn {
background-color: #4caf50;
color: #fff;
}
JavaScript
// src/App.js
import React from 'react';
import MeditationList from './MeditationList';
import './App.css';
function App() {
return (
<div className="App">
<h1>Mindfulness Meditation App</h1>
<MeditationList />
</div>
);
}
export default App;
JavaScript
//MeditationList.js
import React, { useEffect, useState } from "react";
import axios from "axios";
import "./App.css";
const MeditationList = () => {
const [meditations, setMeditations] = useState([]);
const [formData, setFormData] = useState({
title: "",
description: "",
duration: 0,
});
const [timer, setTimer] = useState(null);
const [isTimerActive, setIsTimerActive] = useState(false);
useEffect(() => {
fetchMeditations();
}, []);
const fetchMeditations = () => {
axios
.get("http://localhost:5000/meditations")
.then((response) => {
setMeditations(response.data.data);
})
.catch((error) => {
console.error("Error fetching meditations:", error);
});
};
const handleInputChange = (e) => {
setFormData({ ...formData, [e.target.name]: e.target.value });
};
const handleSubmit = (e) => {
e.preventDefault();
axios
.post("http://localhost:5000/meditations", formData)
.then((response) => {
console.log("Meditation session created:", response.data);
fetchMeditations();
setFormData({ title: "", description: "", duration: 0 });
})
.catch((error) => {
console.error("Error creating meditation session:", error);
});
};
const handleDelete = (id) => {
axios
.delete(`http://localhost:5000/meditations/${id}`)
.then((response) => {
console.log("Meditation session deleted:", response.data);
fetchMeditations();
})
.catch((error) => {
console.error("Error deleting meditation session:", error);
});
};
const handleEdit = (id, newData) => {
const updatedData = {
...meditations.find((meditation) => meditation._id === id),
...newData,
};
axios
.put(`http://localhost:5000/meditations/${id}`, updatedData)
.then((response) => {
console.log("Meditation session updated:", response.data);
fetchMeditations();
})
.catch((error) => {
console.error("Error updating meditation session:", error);
});
};
const showEditPrompt = (id) => {
const meditationToEdit = meditations.find(
(meditation) => meditation._id === id
);
if (meditationToEdit) {
const newTitle = prompt("Enter the new title:", meditationToEdit.title);
const newDescription = prompt(
"Enter the new description:",
meditationToEdit.description
);
const newDuration = prompt(
"Enter the new duration (minutes):",
meditationToEdit.duration
);
if (newTitle && newDescription && newDuration) {
handleEdit(id, {
title: newTitle,
description: newDescription,
duration: parseInt(newDuration),
});
}
}
};
const startMeditation = (duration) => {
setIsTimerActive(true);
let seconds = duration * 60;
setTimer(
setInterval(() => {
const minutes = Math.floor(seconds / 60);
const remainingSeconds = seconds % 60;
document.getElementById("timer").innerHTML =
`${minutes}:${remainingSeconds < 10 ? "0" : ""
}${remainingSeconds}`;
if (seconds === 0) {
clearInterval(timer);
alert("Meditation session ended");
setIsTimerActive(false);
} else {
seconds--;
}
}, 1000)
);
};
return (
<>
<form className="MeditationForm" onSubmit={handleSubmit}>
<label>Title:</label>
<input
type="text"
name="title"
value={formData.title}
onChange={handleInputChange}
required
/>
<label>Description:</label>
<textarea
name="description"
value={formData.description}
onChange={handleInputChange}
required
></textarea>
<label>Duration (minutes):</label>
<input
type="number"
name="duration"
value={formData.duration}
onChange={handleInputChange}
required
/>
<button type="submit">Add Meditation Session</button>
</form>
{isTimerActive && (
<div className="timer-container">
<div id="timer" className="timer"></div>
</div>
)}
<div className={`MeditationList ${isTimerActive ? "blur" : ""}`}>
<h2>Meditation Sessions</h2>
<ul>
{meditations.map((meditation) => (
<li key={meditation._id}>
<h3>{meditation.title}</h3>
<p>{meditation.description}</p>
<p>Duration: {meditation.duration} minutes</p>
<div className="action-buttons">
<button onClick={() => showEditPrompt(meditation._id)}>
Edit
</button>
<button onClick={() => handleDelete(meditation._id)}>
Delete
</button>
<button onClick={() => startMeditation(meditation.duration)}>
Start Session
</button>
</div>
</li>
))}
</ul>
</div>
</>
);
};
export default MeditationList;
BackEnd:
JavaScript
// server.js
const express = require("express");
const bodyParser = require("body-parser");
const mongoose = require("mongoose");
const cors = require("cors");
require("dotenv").config();
const app = express();
const PORT = process.env.PORT || 5000;
app.use(cors());
app.use(bodyParser.json());
mongoose.connect("mongodb://localhost:27017/meditationDB", {
useNewUrlParser: true,
useUnifiedTopology: true,
});
const connection = mongoose.connection;
connection.once("open", () => {
console.log("MongoDB connection established successfully");
});
const Schema = mongoose.Schema;
const meditationSessionSchema = new Schema({
title: { type: String, required: true },
description: { type: String, required: true },
duration: { type: Number, required: true },
createdAt: { type: Date, default: Date.now },
});
const MeditationSession = mongoose.model(
"MeditationSession",
meditationSessionSchema
);
app.post("/meditations", (req, res) => {
const { title, description, duration } = req.body;
const newMeditationSession = new MeditationSession({
title,
description,
duration,
});
newMeditationSession
.save()
.then((meditationSession) =>
res.json({
message: "Meditation session created",
data: meditationSession,
})
)
.catch((err) => res.status(400).json({ error: err.message }));
});
app.get("/meditations", (req, res) => {
MeditationSession.find()
.then((meditationSessions) =>
res.json({
message: "Meditation sessions retrieved",
data: meditationSessions,
})
)
.catch((err) => res.status(400).json({ error: err.message }));
});
app.get("/meditations/:id", (req, res) => {
const { id } = req.params;
MeditationSession.findById(id)
.then((meditationSession) =>
res.json({
message: "Meditation session retrieved",
data: meditationSession,
})
)
.catch((err) => res.status(400).json({ error: err.message }));
});
app.put("/meditations/:id", (req, res) => {
const { id } = req.params;
const { title, description, duration } = req.body;
MeditationSession.findByIdAndUpdate(
id,
{ title, description, duration },
{ new: true }
)
.then((meditationSession) =>
res.json({
message: "Meditation session updated",
data: meditationSession,
})
)
.catch((err) => res.status(400).json({ error: err.message }));
});
app.delete("/meditations/:id", (req, res) => {
const { id } = req.params;
MeditationSession.findByIdAndDelete(id)
.then(() => res.json({ message: "Meditation session deleted" }))
.catch((err) => res.status(400).json({ error: err.message }));
});
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
Output:
Share your thoughts in the comments
Please Login to comment...