Open In App

Health Tracker using MEAN Stack

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

In the fast-paced world, maintaining a healthy lifestyle is more important than ever. Technology can play a significant role in helping individuals monitor and improve their health. In this article, we’ll explore how to use the power of the MEAN (MongoDB, Express.js, Angular, Node.js) stack to build a robust and feature-rich Health Tracker application.

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

preview1

PROJECT PREVIEW IMAGE

Prerequisites:

Approach to create Health Tracker:

Backend:

  • Set up a new node js projects
  • Create the server using express module with cors as the middleware in server.js file
  • Create the app instance using express()
  • Create models folder to create set up for database schema
  • Create controllers folder to create methods or functions for the already defined routes
  • Create routes folder to define different routes for different functionality and called different controller methods for the same
  • Set up Mongo DB database locally
  • Connect to this locally set up database in server.js file using the database connection string
  • Create database and collections to store and retrieve the data from the database
  • Two collections are created – User, Activities
  • Implement the core logic including CRUD operations for health related activity and user authentication
  • Test the API endpoints using postman

Frontend:

  • Create a new Angular project
  • Create components for implementing various functionalities and create HTML, CSS and .ts files for the same
  • Create services for effective connection between frontend and backend
  • Create various routes in app.routes.js file along with components to be loaded
  • Test the frontend application in the browser at http://localhost:3000

Steps to create the project:

Step 1: Create the main folder for the project which will consist of both backend and frontend

mkdir health-tracker
cd health-tracker

Step 2: Initialize the node js project

npm init -y

Step 3: Install the required dependencies

npm install express mongoose cors body-parser jsonwebtoken bcryptjs nodemon

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

"dependencies": {
"bcryptjs": "^2.4.3",
"body-parser": "^1.20.2",
"cors": "^2.8.5",
"express": "^4.18.3",
"jsonwebtoken": "^9.0.2",
"mongoose": "^8.2.1",
"nodemon": "^3.1.0"
}

Project Structure (Backend):

Screenshot-2024-03-20-225747

PROJECT STRUCTURE IMAGE FOR BACKEND

Example: Create the required files as seen on the project structure and add the following codes.

Node
// authController.js

const User = require("../models/User");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");

exports.register = async (req, res) => {
    try {
        const { username, email, password } = req.body;
        let user = await User.findOne({ email });
        if (user) {
            console.log("User already exists:", email);
            return res.status(400).json({ msg: "User already exists" });
        }
        user = new User({
            username,
            email,
            password,
        });
        const salt = await bcrypt.genSalt(10);
        user.password = await bcrypt.hash(password, salt);
        await user.save();
        const token = generateJwtToken(user.id);
        res.json({ token });
    } catch (err) {
        console.error("Error registering user:", err.message);
        res.status(500).send("Server Error");
    }
};

exports.login = async (req, res) => {
    try {
        const { email, password } = req.body;
        const user = await User.findOne({ email });
        if (!user) {
            return res.status(400).json({ msg: "Invalid credentials" });
        }
        const isMatch = await bcrypt.compare(password, user.password);
        if (!isMatch) {
            return res.status(400).json({ msg: "Invalid credentials" });
        }
        const token = generateJwtToken(user.id);
        res.status(200).json({ token });
    } catch (err) {
        console.error("Error logging in user:", err.message);
        res.status(500).send("Server Error");
    }
};

function generateJwtToken(userId) {
    const payload = {
        user: {
            id: userId,
        },
    };
    return jwt.sign(payload, "jwtSecret", { expiresIn: 3600 });
}
Node
// activityController.js

const Activity = require("../models/Activity");
const jwt = require("jsonwebtoken");
const secretKey = "jwtSecret";

exports.getAllActivities = async (req, res) => {
    let userId;
    try {
        jwt.verify(
            req.headers["authorization"].substring(7),
            secretKey,
            (err, decodedToken) => {
                if (err) {
                    console.error("Error decoding token:", err);
                } else {
                    userId = decodedToken.user.id;
                }
            }
        );
        const activities = await Activity.find({ userId: userId });
        res.json(activities);
    } catch (err) {
        console.error(err.message);
        res.status(500).send("Server Error");
    }
};

exports.createActivity = async (req, res) => {
    try {
        let activity = {};
        const { activityName, duration, caloriesBurned, steps, distance, date } =
            req.body;
        if (
            req.headers["authorization"] &&
            req.headers["authorization"].startsWith("Bearer ")
        ) {
            jwt.verify(
                req.headers["authorization"].substring(7),
                secretKey,
                (err, decodedToken) => {
                    if (err) {
                        console.error("Error decoding token:", err);
                    } else {
                        activity = new Activity({
                            userId: decodedToken.user.id,
                            activityName,
                            duration,
                            caloriesBurned,
                            steps,
                            distance,
                            date,
                        });
                    }
                }
            );
            await activity.save();
            res.json(activity);
        }
    } catch (err) {
        console.error(err.message);
        res.status(500).send("Server Error");
    }
};

exports.getActivityById = async (req, res) => {
    try {
        const id = req.params.id;
        const activities = await Activity.findById(id);
        res.json(activities);
    } catch (err) {
        console.error(err.message);
        res.status(500).send("Server Error");
    }
};

exports.updateActivity = async (req, res) => {
    try {
        const { id } = req.params;
        const { activityName, duration, caloriesBurned, steps, distance } =
            req.body;
        const activity = await Activity.findByIdAndUpdate(
            id,
            {
                activityName,
                duration,
                caloriesBurned,
                steps,
                distance,
            },
            { new: true }
        );
        res.json(activity);
    } catch (err) {
        console.error(err.message);
        res.status(500).send("Server Error");
    }
};

exports.deleteActivity = async (req, res) => {
    try {
        const { id } = req.params;
        await Activity.findByIdAndDelete(id);
        res.json({ msg: "Activity deleted" });
    } catch (err) {
        console.error(err.message);
        res.status(500).send("Server Error");
    }
};
Node
// models/Activity.js

const mongoose = require('mongoose');

const activitySchema = new mongoose.Schema({
    userId: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'User',
        required: true
    },
    activityName: {
        type: String,
        required: true,
    },
    duration: {
        type: Number,
        required: true
    },
    caloriesBurned: {
        type: Number,
        required: true
    },
    steps: {
        type: Number,
        required: false
    },
    distance: {
        type: Number,
        required: false
    },
    date: {
        type: Date,
        default: Date.now
    }
});

module.exports = mongoose.model('Activity', activitySchema);
Node
// models/User.js

const mongoose = require('mongoose');

const userSchema = new mongoose.Schema({
    username: {
        type: String,
        required: true
    },
    email: {
        type: String,
        required: true,
        unique: true
    },
    password: {
        type: String,
        required: true
    }
});

module.exports = mongoose.model('User', userSchema);
Node
// routes/activityRoutes.js

const express = require('express');
const router = express.Router();
const activityController = require('../controllers/activityController');

router.get('/', activityController.getAllActivities);

router.post('/create', activityController.createActivity);

router.get('/:id', activityController.getActivityById);

router.put('/update/:id', activityController.updateActivity);

router.delete('/delete/:id', activityController.deleteActivity);

module.exports = router;
Node
// routes/authRoutes.js

const express = require('express');
const router = express.Router();
const authController = require('../controllers/authController');

router.post('/register', authController.register);

router.post('/login', authController.login);

module.exports = router;
Node
// server.js

const express = require('express');
const mongoose = require('mongoose');
const authRoutes = require('./routes/authRoutes');
const activityRoutes = require('./routes/activityRoutes');
const cors = require('cors');

const app = express();
app.use(cors());
app.use(express.json());

mongoose.connect('mongodb://localhost:27017/health-tracker', {
    useNewUrlParser: true,
    useUnifiedTopology: true,
    family: 4,
})
    .then(() => console.log('MongoDB Connected'))
    .catch(err => console.log(err));

app.use('/api/auth', authRoutes);
app.use('/api/activities', activityRoutes);

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

To start the backend server use the following command:

nodemon server.js

Step 4: Install the angular CLI

npm install -g @angular/cli

Step 5: Create a new angular project

ng new frontend

Step 6: Create components for various functionalities in angular application

Syntax - ng generate component <component-name>
ng generate component auth
ng generate component activities

Step 7: Create service in frontend to establish communication between frontend and backend

Syntax - ng generate service <service-name>
ng generate service auth
ng generate service activities

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

"dependencies": {
"@angular/animations": "^17.2.4",
"@angular/common": "^17.2.4",
"@angular/compiler": "^17.2.4",
"@angular/core": "^17.2.4",
"@angular/forms": "^17.2.4",
"@angular/platform-browser": "^17.2.4",
"@angular/platform-browser-dynamic": "^17.2.4",
"@angular/platform-server": "^17.2.4",
"@angular/router": "^17.2.4",
"@angular/ssr": "^17.2.3",
"@auth0/angular-jwt": "^5.2.0",
"express": "^4.18.2",
"jwt-decode": "^4.0.0",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.14.2"
}

Project Structure (Frontend):

Screenshot-2024-03-20-231004

PROJECT STRUCTURE IMAGE FOR FRONTEND

Example: Create the required files as seen in project structure and add the following codes

HTML
<!-- register.component.html -->

<div class="container">
  <h2>Register</h2>
  <form (submit)="register()">
    <div class="form-group">
      <label for="username">Username</label>
      <input
        type="text"
        id="username"
        class="form-control"
        [(ngModel)]="username"
        name="username"
        required
      />
    </div>
    <div class="form-group">
      <label for="email">Email</label>
      <input
        type="email"
        id="email"
        class="form-control"
        [(ngModel)]="email"
        name="email"
        required
      />
    </div>
    <div class="form-group">
      <label for="password">Password</label>
      <input
        type="password"
        id="password"
        class="form-control"
        [(ngModel)]="password"
        name="password"
        required
      />
    </div>
    <button type="submit" class="btn btn-primary">Register</button>
  </form>
</div>
HTML
<!-- login.component.html -->

<div class="container">
  <h2>Login</h2>
  <form (ngSubmit)="login()">
    <div class="form-group">
      <label for="email">Email:</label>
      <input
        type="email"
        class="form-control"
        id="email"
        name="email"
        [(ngModel)]="email"
        required
      />
    </div>
    <div class="form-group">
      <label for="password">Password:</label>
      <input
        type="password"
        class="form-control"
        id="password"
        name="password"
        [(ngModel)]="password"
        required
      />
    </div>
    <button type="submit" class="btn btn-primary">Login</button>
  </form>
</div>
HTML
<!-- activities.component.html -->

<div class="container">
  <a class="link-button" 
     (click)="showAddFormFunction()">
    Add New Activity</a>
  <h2>Activities</h2>

  <div class="search-bar">
    <input type="text" [(ngModel)]="searchQuery" 
    placeholder="Search by name" />
    <button (click)="applyFilter()" 
    class="link-button-search">Search</button>
    <button (click)="clearFilter()" 
    class="link-button-clear">Clear</button>
  </div>
  <div class="search-bar">
    <input
      type="date"
      [(ngModel)]="searchQueryDate"
      placeholder="Search by date"
    />
    <button (click)="applyFilter()" 
    class="link-button-search">Search</button>
    <button (click)="clearFilterDate()" 
    class="link-button-clear">Clear</button>
  </div>
  <div></div>
  <div *ngIf="activities.length > 0; 
  else noActivities" class="table-container">
    <table class="activity-table">
      <thead>
        <tr>
          <th>Activity Name</th>
          <th>Duration</th>
          <th>Calories Burned</th>
          <th>Steps</th>
          <th>Distance Covered</th>
          <th>Date</th>
          <th>Actions</th>
        </tr>
      </thead>
      <tbody>
        <tr *ngFor="let activity of displayedActivities">
          <td>{{ activity.activityName }}</td>
          <td>{{ activity.duration }}</td>
          <td>{{ activity.caloriesBurned }}</td>
          <td>{{ activity.steps }}</td>
          <td>{{ activity.distance }}</td>
          <td>{{ activity.date.toString().slice(0, 10) }}</td>
          <td>
            <button class="btn" 
            (click)="getActivityById(activity._id)">
              Get Activity
            </button>
            <button class="btn" 
            (click)="populateForm(activity)">Update</button>
            <button
              class="btn delete-btn"
              (click)="confirmDelete(activity._id)"
            >
              Delete
            </button>
          </td>
        </tr>
      </tbody>
    </table>
  </div>

  <ng-template #noActivities>
    <div class="no-activities-container">
      <p>No activities available</p>
    </div>
  </ng-template>

  <div *ngIf="showUpdateForm" 
  class="update-form-container">
    <button class="close-button" 
    (click)="cancelUpdate()">X</button>
    <h2>Update Activity</h2>
    <form class="update-form" 
    (ngSubmit)="updateActivity()">
      <div>
        <label for="updateActivityName">
            Activity Name:</label>
        <input
          type="text"
          id="updateActivityName"
          name="updateActivityName"
          [(ngModel)]="updatedActivity.activityName"
          required
        />
      </div>
      <div>
        <label for="updateDuration">
            Duration (minutes):</label>
        <input
          type="number"
          id="updateDuration"
          name="updateDuration"
          [(ngModel)]="updatedActivity.duration"
          required
        />
      </div>
      <div>
        <label for="updateCaloriesBurned">
            Calories Burned:</label>
        <input
          type="number"
          id="updateCaloriesBurned"
          name="updateCaloriesBurned"
          [(ngModel)]="updatedActivity.caloriesBurned"
          required
        />
      </div>
      <div>
        <label for="updateSteps">Steps:</label>
        <input
          type="number"
          id="updateSteps"
          name="updateSteps"
          [(ngModel)]="updatedActivity.steps"
          required
        />
      </div>
      <div>
        <label for="updateDistance">Distance:</label>
        <input
          type="number"
          id="updateDistance"
          name="updateDistance"
          [(ngModel)]="updatedActivity.distance"
          required
        />
      </div>
      <div>
        <button type="submit" 
        (click)="ifLoggedIn()">Update Activity</button>
        <button type="button" 
        (click)="cancelUpdate()">Cancel</button>
      </div>
    </form>
  </div>

  <div
    *ngIf="!showUpdateForm && activityById._id"
    class="view-activity-container"
  >
    <button class="close-button" 
    (click)="closeView()">X</button>
    <h2>View Activity</h2>
    <form class="view-activity-form">
      <div>
        <label for="activityName">Activity Name:</label>
        <input
          type="text"
          id="activityName"
          name="activityName"
          [(ngModel)]="activityById.activityName"
          readonly
        />
      </div>
      <div>
        <label for="duration">Duration (minutes):</label>
        <input
          type="number"
          id="duration"
          name="duration"
          [(ngModel)]="activityById.duration"
          readonly
        />
      </div>
      <div>
        <label for="caloriesBurned">Calories Burned:</label>
        <input
          type="number"
          id="caloriesBurned"
          name="caloriesBurned"
          [(ngModel)]="activityById.caloriesBurned"
          readonly
        />
      </div>
      <div>
        <label for="steps">Steps:</label>
        <input
          type="number"
          id="steps"
          name="steps"
          [(ngModel)]="activityById.steps"
          readonly
        />
      </div>
      <div>
        <label for="distance">Distance:</label>
        <input
          type="number"
          id="distance"
          name="distance"
          [(ngModel)]="activityById.distance"
          readonly
        />
      </div>
      <div>
        <label for="date">Date:</label>
        <input
          type="text"
          id="date"
          name="date"
          [(ngModel)]="getActivityByIdDate"
          readonly
        />
      </div>
      <div>
        <a href="/activities" 
        class="link-button">Get All Activities</a>
      </div>
    </form>
  </div>

  <div
    class="update-form-container"
    *ngIf="showAddForm && !showUpdateForm && !activityById._id"
  >
    <button class="close-button" 
    (click)="closeAddForm()">X</button>
    <h2>Add Activity</h2>
    <form class="update-form" 
    (ngSubmit)="addActivity()">
      <div>
        <label for="activityName">Activity Name:</label>
        <input
          type="text"
          id="activityName"
          name="activityName"
          [(ngModel)]="activity.activityName"
          required
        />
      </div>
      <div>
        <label for="duration">Duration (minutes):</label>
        <input
          type="number"
          id="duration"
          name="duration"
          [(ngModel)]="activity.duration"
          required
        />
      </div>
      <div>
        <label for="caloriesBurned">Calories Burned:</label>
        <input
          type="number"
          id="caloriesBurned"
          name="caloriesBurned"
          [(ngModel)]="activity.caloriesBurned"
          required
        />
      </div>
      <div>
        <label for="steps">Steps:</label>
        <input
          type="number"
          id="steps"
          name="steps"
          [(ngModel)]="activity.steps"
          required
        />
      </div>
      <div>
        <label for="distance">Distance:</label>
        <input
          type="number"
          id="distance"
          name="distance"
          [(ngModel)]="activity.distance"
          required
        />
      </div>
      <div>
        <label for="date">Date:</label>
        <input
          type="date"
          id="date"
          name="date"
          [(ngModel)]="activity.date"
          required
        />
      </div>
      <div>
        <button type="submit" 
        (click)="ifLoggedIn()">Add Activity</button>
        <button type="button" 
        (click)="resetForm()">Clear</button>
      </div>
    </form>
  </div>
</div>
HTML
<!-- app.component.html -->

<main class="main" *ngIf="componentReload">
    <div class="content">
        <div class="left-side">

            <h1>{{ title }}</h1>
            <div>
                <ul>
                    <li><a href="login" *ngIf="!isLoggedIn">Login</a></li>
                    <li><a href="register" *ngIf="!isLoggedIn">Register</a></li>
                    <li><a (click)="logout()" *ngIf="isLoggedIn">Logout</a></li>
                </ul>
            </div>
        </div>

    </div>
</main>
<router-outlet></router-outlet>
CSS
/* app.component.css */

.main {
    display: flex;
    flex-direction: column;
    align-items: center;
    background-color: darkslategrey;
    color: white;
}

.content {
    width: 100%;
    max-width: 1200px;
    padding: 20px;
}

.left-side {
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.left-side h1 {
    margin: 0;
    margin-left: 3%;
}

.left-side ul {
    list-style-type: none;
    padding: 0;
    margin: 0;
}

.left-side li {
    display: inline;
    margin-right: 20px;
}

.left-side li a {
    text-decoration: none;
    color: white;
    font-weight: bold;
    font-size: 1.5rem;
}

.left-side li a:hover {
    color: lightgray;
}

a {
    cursor: pointer;
}
CSS
/* app.component.css */

.main {
    display: flex;
    flex-direction: column;
    align-items: center;
    background-color: darkslategrey;
    color: white;
}

.content {
    width: 100%;
    max-width: 1200px;
    padding: 20px;
}

.left-side {
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.left-side h1 {
    margin: 0;
    margin-left: 3%;
}

.left-side ul {
    list-style-type: none;
    padding: 0;
    margin: 0;
}

.left-side li {
    display: inline;
    margin-right: 20px;
}

.left-side li a {
    text-decoration: none;
    color: white;
    font-weight: bold;
    font-size: 1.5rem;
}

.left-side li a:hover {
    color: lightgray;
}

a {
    cursor: pointer;
}
CSS
/* login.component.css */

.container {
    width: 50%;
    margin: 2rem auto;
    padding: 1.5vmax;
    padding-right: 3.5vmax;
    border: 1px solid #ccc;
    border-radius: 5px;
}

h2 {
    text-align: center;
    margin-bottom: 20px;
    font-size: 2rem;
}

.form-group {
    margin-bottom: 20px;
}

label {
    display: block;
    margin-bottom: 5px;
}

input[type="email"],
input[type="password"] {
    width: 99%;
    padding: 10px;
    border: 1px solid #ccc;
    border-radius: 5px;
}

button[type="submit"] {
    width: 20%;
    padding: 1.1vmax;
    background-color: #0056b3;
    color: #fff;
    border: none;
    border-radius: 5px;
    cursor: pointer;
    font-weight: bold;
    font-size: 1rem;
    align-self: center;
    margin-top: 1vmax;
}
CSS
/* activities.component.css */

.container {
    max-width: 100%;
    margin: 0 auto;
    padding: 20px;
}

.search-bar {
    display: flex;
    margin-bottom: 10px;
}

.search-bar input {
    flex: 1;
    margin-right: 10px;
}

.activity-table {
    width: 100%;
    border-collapse: collapse;
}

.activity-table th,
.activity-table td {
    padding: 10px;
    border: 1px solid #ccc;
    text-align: center;
}

.activity-table th {
    background-color: #f0f0f0;
}

.update-form-container,
.view-activity-container {
    margin-top: 20px;
    padding: 20px;
    border: 1px solid #ccc;
}

.update-form,
.view-activity-form {
    display: flex;
    flex-direction: column;
}

.update-form input,
.view-activity-form input {
    margin-bottom: 10px;
}

.btn {
    padding: 8px 16px;
    margin-right: 10px;
    margin-bottom: 1.5vmax;
    cursor: pointer;
    background-color: #0056b3;
    color: #fff;
    border: none;
    border-radius: 4px;
}

.delete-btn {
    background-color: #dc3545;
}

.link-button {
    display: inline-block;
    padding: 8px 16px;
    background-color: #1b599a;
    color: #fff;
    text-decoration: none;
    border-radius: 4px;
    cursor: pointer;
}

.link-button-search {
    display: inline-block;
    padding: 8px 16px;
    background-color: green;
    color: #fff;
    text-decoration: none;
    border-radius: 4px;
    cursor: pointer;
    border: none;
    margin-right: 1vmax;
}

.link-button-clear {
    display: inline-block;
    padding: 8px 16px;
    background-color: #dc3545;
    color: #fff;
    text-decoration: none;
    border-radius: 4px;
    cursor: pointer;
    border: none;
}

.no-activities-container {
    margin-top: 20px;
    padding: 10px;
    background-color: #f8d7da;
    color: #721c24;
    border: 1px solid #f5c6cb;
    border-radius: 4px;
}

.no-activities-container p {
    text-align: center;
    margin: 0;
}


.update-form-container {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 90%;
    background-color: rgba(255, 255, 255, 1);
    padding: 3.7vmax;
    border-radius: 5px;
}

.update-form-container h2 {
    text-align: center;
    font-size: 2rem;
}

.update-form-container .close-btn {
    position: absolute;
    top: 5px;
    right: 5px;
    font-size: 18px;
    color: #555;
    cursor: pointer;
}

.update-form-container .close-btn:hover {
    color: #333;
}

.update-form label {
    display: block;
    margin-bottom: 5px;
}

.update-form input[type="text"],
.update-form input[type="number"],
.update-form input[type="date"] {
    width: calc(100% - 12px);
    padding: 10px;
    margin-bottom: 10px;
    border: 1px solid #ccc;
    border-radius: 5px;
}

.update-form button[type="submit"],
.update-form button[type="button"] {
    width: 10%;
    padding: 10px;
    background-color: green;
    color: #fff;
    border: none;
    border-radius: 5px;
    cursor: pointer;
    margin-right: 10px;
}

.update-form button[type="button"] {
    background-color: #dc3545;
}

.view-activity-container {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 90%;
    background-color: rgba(255, 255, 255, 1);
    padding: 3.7vmax;
    border-radius: 5px;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}

.view-activity-container h2 {
    text-align: center;
    font-size: 2rem;
}

.view-activity-form label {
    display: block;
    margin-bottom: 5px;
}

.view-activity-form input[type="text"],
.view-activity-form input[type="number"],
.view-activity-form input[type="date"] {
    width: calc(100% - 12px);
    padding: 10px;
    margin-bottom: 10px;
    border: 1px solid #ccc;
    border-radius: 5px;
}

.view-activity-form button[type="submit"],
.view-activity-form a {
    width: 10%;
    padding: 10px;
    background-color: #0056b3;
    color: #fff;
    border: none;
    border-radius: 5px;
    cursor: pointer;
    text-align: center;
    display: inline-block;
    text-decoration: none;
}

.view-activity-form button[type="submit"]:hover,
.view-activity-form a:hover {
    background-color: #0056b3;
}

.close-button {
    position: absolute;
    top: 10px;
    right: 10px;
    padding: 5px 10px;
    background-color: #ccc;
    border: none;
    border-radius: 5px;
    cursor: pointer;
}

.close-button:hover {
    background-color: #aaa;
}
JavaScript
// register.component.ts

import { Component, OnInit } from '@angular/core';
import { AuthService } from '../../auth.service';
import { FormsModule } from '@angular/forms';
import { Router } from '@angular/router';

@Component({
    selector: 'app-register',
    standalone: true,
    imports: [FormsModule],
    templateUrl: './register.component.html',
    styleUrls: ['./register.component.css'],
})
export class RegisterComponent implements OnInit {
    username!: string;
    email!: string;
    password!: string;

    constructor(private authService: AuthService, 
        private router: Router) { }

    ngOnInit(): void {
    }

    register(): void {
        const userData = {
            username: this.username,
            email: this.email,
            password: this.password
        };

        this.authService.register(userData).subscribe(
            (res: any) => {
                this.router.navigate(["/login"]);
            },
            (err: any) => {
                console.error(err);
            }
        );
    }
}
JavaScript
// app/auth/login/login.component.ts

import { Component, EventEmitter, Output } from '@angular/core';
import { Router } from '@angular/router';
import { AuthService } from '../../auth.service';
import { FormsModule } from '@angular/forms';

@Component({
    selector: 'app-login',
    standalone: true,
    imports: [FormsModule],
    templateUrl: './login.component.html',
    styleUrls: ['./login.component.css']
})
export class LoginComponent {
    email!: string;
    password!: string;
    credentials: any = {};

    constructor(private authService: AuthService, 
        private router: Router) { }

    ngOnInit(): void {
    }
    login(): void {
        const credentials = {
            email: this.email,
            password: this.password
        };
        this.authService.login(credentials).subscribe(
            (response) => {
                const token = response.token;
                localStorage.setItem("token", token);
                this.authService.setAuthenticationStatus(true);
                this.router.navigate(['/activities']);
            },
            error => {
                console.error('Error logging in:', error);
            }
        );
    }
}
JavaScript
// activities.component.ts

import { Component, OnInit } from '@angular/core';
import { Activity } from '../activity.model';
import { ActivityService } from '../activity.service';
import { FormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { AuthService } from '../auth.service';
import { Router } from '@angular/router';

@Component({
    selector: 'app-activities',
    standalone: true,
    imports: [FormsModule, CommonModule],
    templateUrl: './activities.component.html',
    styleUrls: ['./activities.component.css']
})
export class ActivitiesComponent implements OnInit {
    activities: Activity[] = [];
    activityById: Activity = {
        _id: '',
        activityName: '',
        duration: 0,
        caloriesBurned: 0,
        steps: 0,
        distance: 0,
        date: new Date()
    };
    showUpdateForm: boolean = false;
    updatedActivity: Activity = {
        _id: '', activityName: '', duration: 0,
        caloriesBurned: 0, steps: 0, distance: 0, date: new Date()
    };
    searchQuery: any;
    searchQueryDate: any;
    displayedActivities: Activity[] = [];
    activity: Activity = {
        _id: '',
        activityName: '',
        duration: 0,
        caloriesBurned: 0,
        steps: 0,
        distance: 0,
        date: new Date()
    };
    showAddForm: boolean = false;
    getActivityByIdDate: string = '';

    constructor(private activityService: ActivityService,
        private authService: AuthService, private router: Router) { }

    ngOnInit(): void {
        if (this.authService.isAuthenticated()) {
            this.getAllActivities();
        }
    }

    ifLoggedIn(): void {
        this.authService.isAuthenticated().subscribe(isAuthenticated => {
            return isAuthenticated;
        });
    }

    getAllActivities(): void {
        if (typeof localStorage !== 'undefined') {
            const token = localStorage.getItem('token');
            if (token) {
                this.activityService.getAllActivities(token).subscribe(
                    (activities: Activity[]) => {
                        this.activities = activities;
                        this.displayedActivities = [...this.activities];
                    },
                    error => {
                        console.error('Error fetching activities:', error);
                    }
                );
            }
        }
    }

    getActivityById(id: string): void {
        const token = localStorage.getItem('token');
        this.showUpdateForm = false;
        this.showAddForm = false;
        console.log(id);
        if (token) {
            this.activityService.getActivityById(token, id).subscribe(
                (activity) => {
                    this.activityById = activity;
                    this.getActivityByIdDate = this.activityById.date.toString().slice(0, 10);
                },
                error => {
                    console.error('Error in getting activity:', error);
                }
            )
        }
    }

    populateForm(activity: Activity): void {
        this.updatedActivity = { ...activity };
        this.showUpdateForm = true;
    }

    updateActivity(): void {
        const token = localStorage.getItem('token');
        if (token) {
            this.activityService.updateActivity(token, this.updatedActivity).subscribe(
                (updatedActivity) => {
                    const index = this.activities.findIndex(a => a._id === updatedActivity._id);
                    if (index !== -1) {
                        this.router.navigate(["/activities"]);
                        this.activities[index] = updatedActivity;
                        this.getAllActivities();
                        this.showUpdateForm = false;
                    }
                    this.cancelUpdate();
                },
                error => {
                    console.error('Error updating activity:', error);
                }
            );
        }
    }

    cancelUpdate(): void {
        this.showUpdateForm = false;
        this.updatedActivity = {
            _id: '', activityName: '',
            duration: 0, caloriesBurned: 0, steps: 0, distance: 0,
            date: new Date()
        };
        this.activityById = {
            _id: '',
            activityName: '',
            duration: 0,
            caloriesBurned: 0,
            steps: 0,
            distance: 0,
            date: new Date()
        };
    }

    closeView(): void {
        this.activityById = {
            _id: '',
            activityName: '',
            duration: 0,
            caloriesBurned: 0,
            steps: 0,
            distance: 0,
            date: new Date()
        };
    }


    confirmDelete(activityId: string): void {
        const confirmDelete = window.confirm('Are you sure you want to delete this activity?');
        if (confirmDelete) {
            this.deleteActivity(activityId);
        }
    }

    deleteActivity(id: string): void {
        const token = localStorage.getItem('token');
        this.showUpdateForm = false;
        if (token) {
            this.activityService.deleteActivity(token, id).subscribe(
                () => {
                    this.activities = this.activities.filter(activity => activity._id !== id);
                    this.displayedActivities = [...this.activities]
                },
                error => {
                    console.error('Error deleting activity:', error);
                }
            );
        }
    }

    addActivity(): void {
        const token = localStorage.getItem('token');
        if (token) {
            this.showAddForm = true;
            this.showUpdateForm = false;
            this.activityService.addActivity(token, this.activity).subscribe(
                (newActivity: Activity) => {
                    this.closeAddForm();
                    this.getAllActivities();
                    this.router.navigate(["/activities"]);
                },
                error => {
                    console.error('Error adding activity:', error);
                }
            );
        }
    }

    resetForm(): void {
        this.activity = {
            _id: '',
            activityName: '',
            duration: 0,
            caloriesBurned: 0,
            steps: 0,
            distance: 0,
            date: new Date()
        };
    }

    closeAddForm(): void {
        this.activity = {
            _id: '',
            activityName: '',
            duration: 0,
            caloriesBurned: 0,
            steps: 0,
            distance: 0,
            date: new Date()
        };
        this.showAddForm = false;
    }

    showAddFormFunction(): void {
        this.showAddForm = true;
    }

    applyFilter(): void {
        if (!this.searchQuery?.trim() && !this.searchQueryDate?.trim()) {
            this.displayedActivities = [...this.activities];
        }
        else if (this.searchQueryDate?.trim() && !this.searchQuery?.trim()) {
            const queryDate = this.searchQueryDate ?
                this.searchQueryDate.toLowerCase().trim() : this.searchQueryDate;
            this.displayedActivities = this.activities.filter(activity =>
                activity.date.toString().toLowerCase().includes(queryDate)
            );
        }
        else if (!this.searchQueryDate?.trim() && this.searchQuery?.trim()) {
            const query = this.searchQuery ?
                this.searchQuery.toLowerCase().trim() : this.searchQuery;
            this.displayedActivities = this.activities.filter(activity =>
                activity.activityName.toLowerCase().includes(query)
            );
        }
        else {
            const query = this.searchQuery ?
                this.searchQuery.toLowerCase().trim() : this.searchQuery;
            const queryDate = this.searchQueryDate ?
                this.searchQueryDate.toLowerCase().trim() : this.searchQueryDate;
            this.displayedActivities = this.activities.filter(activity =>
                activity.activityName.toLowerCase().includes(query) &&
                activity.date.toString().toLowerCase().includes(queryDate)
            );
        }
    }

    clearFilter(): void {
        this.searchQuery = '';
        this.applyFilter();
    }

    clearFilterDate(): void {
        this.searchQueryDate = "";
        this.applyFilter();
    }
}
JavaScript
// app.component.ts

import { CommonModule } from '@angular/common';
import { Component, EventEmitter, Output } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { Router, RouterOutlet } from '@angular/router';
import { AuthService } from './auth.service';

@Component({
    selector: 'app-root',
    standalone: true,
    imports: [RouterOutlet, FormsModule, CommonModule,],
    templateUrl: './app.component.html',
    styleUrl: './app.component.css',
})
export class AppComponent {
    title = 'GeeksForGeeks Health Tracker';
    isLoggedIn: any;
    componentReload: boolean = false;
    constructor(private router: Router,
        private authService: AuthService) { }

    ngOnInit(): void {
        if (typeof localStorage !== 'undefined'
            && localStorage.getItem('token')) {
            this.isLoggedIn = true;
        }
        else {
            this.ifLoggedIn();
        }
        this.componentReload = true;
    }

    ifLoggedIn(): void {
        this.authService.isAuthenticated()
            .subscribe(isAuthenticated => {
                this.isLoggedIn = isAuthenticated;
            });
    }
    logout(): void {
        this.authService.setAuthenticationStatus(false);
        this.isLoggedIn = false;
        localStorage.removeItem('token');
        this.router.navigate(["/login"]);
    }
}
JavaScript
// app.routes.ts

import { Routes } from '@angular/router';
import { LoginComponent } from './auth/login/login.component';
import { RegisterComponent } from './auth/register/register.component';
import { ActivitiesComponent } from './activities/activities.component';
import { AppComponent } from './app.component';

export const routes: Routes = [
    { path: 'login', component: LoginComponent },
    { path: 'register', component: RegisterComponent },
    { path: '', redirectTo: '/login', pathMatch: 'full' },
    { path: 'activities', component: ActivitiesComponent },
    { path: 'activities/create', component: ActivitiesComponent },
    { path: 'activities/update/:id', component: ActivitiesComponent },
    { path: 'activities/delete/:id', component: ActivitiesComponent },
    { path: 'activities/:id', component: ActivitiesComponent },
];
JavaScript
// app.module.ts

import { InjectionToken, NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';

import { AppComponent } from './app.component';
import { RegisterComponent } from './auth/register/register.component';
import { LoginComponent } from './auth/login/login.component';
import { ActivitiesComponent } from './activities/activities.component';
import { JwtHelperService, JWT_OPTIONS } from '@auth0/angular-jwt';
import { RouterModule } from '@angular/router';
import { routes } from './app.routes';
@NgModule({
    declarations: [
        AppComponent,
        RegisterComponent,
        LoginComponent,
        ActivitiesComponent
    ],
    imports: [
        BrowserModule,
        FormsModule,
        RouterModule.forRoot(routes),
    ],
    exports: [RouterModule],
    providers: [{ provide: JWT_OPTIONS, useValue: JWT_OPTIONS },
        JwtHelperService],
    bootstrap: [AppComponent]
})
export class AppModule { }

To start the frontend run the following command.

ng serve

Output:

output-(2)

OUTPUT GIF FOR HEALTH-TRACKER PROJECT



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

Similar Reads