Open In App

Mindfulness Meditation App with MERN Stack

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

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:

imresizer-1712236089317

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:

235

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:

434

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:

345678-ezgifcom-video-to-gif-converter



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

Similar Reads