Building a Calorie Journal SaaS based project using MERN stack π₯
This blog article concentrates on the most significant tasks and ideas to assist you in better understanding and building MERN stack applications from the absolute ground up. It is intended for folks who are really curious about the MERN stack and want to focus on what they really need to know.
So, What is the MERN stack?
The MERN stack is a popular technology stack for creating modern Single Page Applications also known as SPA in short. MongoDB, Express, React, and Node.js are the acronyms for the βMERNβ stack.MERN is a variant of the very popular MEAN stack (MongoDB, Express, Angular, Node), with React replacing Angular as the frontend UI framework. The MEVN (MongoDB, Express, Vue, Node), which uses Vue as the frontend UI framework, is another very popular option. These frontends tech stack helps for building Single Page Applications (SPAs) which helps to avoid reloading the entire page and only fetch relevant pieces of information of the page from the server and displays freshly and newly updated stuff.
In this blog article, we'll build a full-stack calorie tracker application that users can use to keep track of users food habits and are able to track their entire calorie count by utilizing the absolute power of the MERN stack only. This blog tutorial should help you understand the fundamentals as well as advanced concepts and operations of the MERN stack technology. Here is our application's final sneak peek.
Configuring our folder structure
Create a two folder name client and server inside your project directory, then open it inside the Visual Studio Code or any code editor of your choice.
Now we'll set up our backend with npm and install the required packages, then configure a MongoDB database, set up a server with Node and Express, establish a database schema to describe our calorie tracker application, and set up API routes to create, read, update, and delete data and information from the database. So, using a command prompt, navigate to your server's directory and run the code below.
npm init -y
Configuring and updating our package.json file
Execute the following commands in the terminal to install the dependencies.
npm install cors dotenv express mongoose nodemon body-parser
The "package.json" file should look like this after the dependencies have been installed.
{
"name": "server",
"version": "1.0.0",
"description": "",
"main": "app.js",
"scripts": {
"start": "nodemon app.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"body-parser": "^1.19.0",
"cors": "^2.8.5",
"dotenv": "^10.0.0",
"express": "^4.17.1",
"mongoose": "^6.0.13",
"nodemon": "^2.0.15"
}
}
Also, don't forget to update the scripts.
Now go to your server directory and make a app.js file there.
The structure of your folders and file should resemble this.
Setting up app.js
Import express module.
Import mongoose module
Import and configure dotenv module
Import CORS module
Use express() to start our app.
//app.js
const express = require("express");
const cors = require("cors");
const mongoose = require("mongoose");
require("dotenv").config();
const app = express();
On that app instance, we can now use all of the different methods. Let's start with some basic setup. Don't forget to configure the port as well.
// app.js
const express = require("express");
const cors = require("cors");
const mongoose = require("mongoose");
require("dotenv").config();
const app = express();
const port = process.env.PORT || 5000;
app.use(cors());
app.use(express.json());
Setting up MongoDB cloud cluster
MongoDB is a document-oriented database that is open source and cross-platform. MongoDB is a NoSQL database that stores data in JSON-like documents with optional schemas. Versions prior to October 16, 2018 are released under the AGPL license. All versions released after October 16, 2018, including bug fixes for previous versions, are distributed under the SSPL license v1.
Selecting a cloud service provider
Finish and close to make a cluster and wait for the cluster to be built before proceeding (usually takes around 5 -10 minutes)
Navigate to the network access tab and select "Add IP address."
Now, select the Choose a connection method.
Connect your application by clicking on it and finally select the correct driver and version.
In the database, create a user. You'll need the username and password for the MongoDB URI and finally, create a database user.
Now, inside app.js create a new variable and name it DATABASE_CONNECTION. Inside it, create a string and simply paste the copied mongo DB connection URL or simply paste the link for the environment variables. Now, inside the link of Mongo Sb cloud atlas URL , enter your username and password, making sure to remove all the brackets and enter your own credentials. The second thing we need is a PORT, so simply enter the port number, for now, 6000, and finally, we will use mongoose to connect to our database, so enter mongoose. connect() which is a function with two different parameters. The first will be the DATABASE_CONNECTION, and the second will be an object with two different options. The first is useNewUrlParser, which we will set to true, and the second is useUnifiedTopology, which we will also set to true. These objects are not required, but we will see some errors or warnings on our console. Following that, let's chain a.then() and.catch() because this will return a promise, so inside .then() will call the app and invoke listen, which has two parameters, the first of which is PORT and the second of which is the callback function that will be executed if our application is successfully connected and finally, if the connection to the database is not successful we will simply console log our error message.
// app.js
const express = require("express");
const cors = require("cors");
const mongoose = require("mongoose");
require("dotenv").config();
const app = express();
// app config
app.use(cors());
app.use(express.json());
// port and DB config
const DATABASE_CONNECTION = process.env.DATABASE_URL;
const PORT = process.env.PORT || 5000;
// mongoose connection
mongoose
.connect(DATABASE_CONNECTION, {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(() =>
app.listen(PORT, () =>
console.log(`Server is running at : http://localhost:${PORT}`)
)
)
.catch((error) => console.error(error));
Insert mongodb+srv into the .env file.
PORT=6000
DATABASE_URL=mongodb+srv://pramit:<password>@cluster0.uauqv.mongodb.net/myFirstDatabase?retryWrites=true&w=majority
That's all there is to it; we've successfully connected our server to the database.
Now that we've successfully connected to our database, let's get started on building our backend application's routes. To do so, we'll need to create a new folder called routes on the server directory. We will create a file called calorie.routes.js within the routes folder.
This is how your folders should be organized.
Let's get started by importing the calorie and user routes into your app.js file . We can now connect calorie and user to our application using express middleware. Finally, your app.js file should like the following.
//app.js
const express = require("express");
const cors = require("cors");
const mongoose = require("mongoose");
require("dotenv").config();
const app = express();
// app config
app.use(cors());
app.use(express.json());
// port and DB config
const DATABASE_CONNECTION = process.env.DATABASE_URL;
const PORT = process.env.PORT || 5000;
// mongoose connection
mongoose
.connect(DATABASE_CONNECTION, {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(() =>
app.listen(PORT, () =>
console.log(`Server is running at : http://localhost:${PORT}`)
)
)
.catch((error) => console.error(error));
// routers
const calorie = require("./routes/calorie.routes.js");
const users = require("./routes/users.routes.js");
app.use("/calorie", calorie);
app.use("/users", users);
We are going to add all of the routes as well as its controllers inside of calorie.routes.js and user.routes.js , so first we must import express from "express" and also configure our router. But first, let's make a model for our users and calorie.So, create a folder named models, and inside that folder, create two files called calorie.model.js and users.model.js, and paste the following code into each of them.
Now, your folder structure should look something like this
//models/calorie.model.js
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const calorieSchema = new Schema({
username: {
type: String,
required: true
},
description: {
type: String,
required: true
},
calories: {
type: Number,
required: true
},
date: {
type: Date,
required: true
},
}, {
timestamps: true,
});
const Calorie = mongoose.model("CalorieJournal", calorieSchema);
module.exports = Calorie;
and
//models/users.model.js
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const userSchema = new Schema({
username: {
type: String,
required: true,
unique: true,
trim: true,
minlength: 2,
},
}, {
timestamps: true,
});
const User = mongoose.model("User", userSchema);
module.exports = User;
Now we can begin adding our routes to it.
//routes/calorie.routes.js
const router = require("express").Router();
let Calorie = require("../models/calorie.model.js");
router.route("/").get((req, res) => {
Calorie.find()
.then((meals) => res.json(meals))
.catch((err) => res.status(400).json("Error: " + err));
});
router.route("/add").post((req, res) => {
const username = req.body.username;
const description = req.body.description;
const calories = Number(req.body.calories);
const date = Date.parse(req.body.date);
const addCalorie = new Calorie({
username,
description,
calories,
date,
});
addCalorie
.save()
.then(() => res.json("Calories Added Successfully"))
.catch((err) => res.status(400).json("Error: " + err));
});
Fetching all the calorie information.
Deleting single calorie information.
Updating single calorie information.
Your calorie.route.js file should look like this.
//models/calorie.model.js
const router = require("express").Router();
let Calorie = require("../models/calorie.model.js");
router.route("/").get((req, res) => {
Calorie.find()
.then((meals) => res.json(meals))
.catch((err) => res.status(400).json("Error: " + err));
});
router.route("/add").post((req, res) => {
const username = req.body.username;
const description = req.body.description;
const calories = Number(req.body.calories);
const date = Date.parse(req.body.date);
const addCalorie = new Calorie({
username,
description,
calories,
date,
});
addCalorie
.save()
.then(() => res.json("Calories Added Successfully"))
.catch((err) => res.status(400).json("Error: " + err));
});
router.route("/:id").get((req, res) => {
Calorie.findById(req.params.id)
.then((calories) => res.json(calories))
.catch((err) => res.status(400).json("Error: " + err));
});
router.route("/:id").delete((req, res) => {
Calorie.findByIdAndDelete(req.params.id)
.then(() => res.json("Calories is deleted Successfully"))
.catch((err) => res.status(400).json("Error: " + err));
});
router.route("/update/:id").post((req, res) => {
Calorie.findById(req.params.id)
.then((calories) => {
calories.username = req.body.username;
calories.description = req.body.description;
calories.calories = Number(req.body.calories);
calories.date = Date.parse(req.body.date);
calories
.save()
.then(() => res.json("Calorie Updated Successfully"))
.catch((err) => res.status(400).json("Err: " + err));
})
.catch((err) => res.status(400).json("Err: " + err));
});
module.exports = router;
Now letβs update user routes.
//routes/user.routes.js
const router = require("express").Router();
let User = require("../models/users.model.js");
Your users.route.js file should look like this.
//routes/user.routes.js
const router = require("express").Router();
let User = require("../models/users.model.js");
// get user
router.route("/").get((req, res) => {
User.find()
.then((users) => res.json(users))
.catch((err) => res.status(400).json("Error: " + err));
});
// add user
router.route("/add").post((req, res) => {
const username = req.body.username;
const newUser = new User({
username
});
newUser
.save()
.then(() => res.json("User added Successfully"))
.catch((err) => res.status(400).json("Error: " + err));
});
module.exports = router;
You should see something like this after restarting the server:
Configuring our Frontend
We'll begin by using create-react-app to set up our frontend. We'll build the user interface and its features from the ground up. Let's go to work on our application right away.
Setting up react application bootstarpped using CRA
Let's start with the frontend and build it with react. The first thing you need do is install Node.js if it isn't already installed on your PC. So, head over to the official Node.js website and download the latest version. Node js is required in order to use the node package manager, generally known as NPM. Now open the client folder in your preferred code editor. I'll be using VScode . Next, open the integrated terminal and type npx create-react-app . This command will create a client application in the current directory, using the name client.
It normally only takes a few minutes to set up. Normally, we would use npm to get packages into a project, but in this case, we'll use npx, the package runner, which will download and configure everything for us so that we can get started with an excellent template right away. It's time to start our development server, so run npm start and the browser will open react-app instantly.
React boilerplate files cleanup
We must first tidy up our projects by eliminating some of the files provided by create-react-app before we can begin creating them. After you've cleaned up your files and folder , they should look like this.
Adding and Installing some packages
We will need to install a few third-party packages for this project. so copy and paste the following command into your terminal
npm install bootstrap react-chartjs-2 chart.js axios react-datepicker react-router-dom
After installing all these packages your packge.json file of client should look like this:
Let's construct seven separate folders / component inside the components folder after we've installed all of our project's dependencies and name it as Navbar, CalorieChart, UserChart, AddFood , AddUser , EditFood and DisplayFoodList .
Your file and folder structure should look something like this once you've added all of your components.
Now go to your app.js file and import the routers from react-router-dom and styles, as well as the bootstrap css file, also all the components as well and make the necessary changes to the code as follows.
// app.js
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import "./App.css";
import "bootstrap/dist/css/bootstrap.min.css";
import Navbar from "./components/Navbar";
import DisplayFoodList from "./components/DisplayFoodList";
import EditFood from "./components/EditFood";
import AddFood from "./components/AddFood";
import AddUser from "./components/AddUser";
function App() {
return (
<>
<Router>
<Navbar />
<br />
<Routes>
<Route path="/" exact element={<DisplayFoodList />} />
<Route path="/edit/:id" element={<EditFood />} />
<Route path="/create" element={<AddFood />} />
<Route path="/user" element={<AddUser />} />
</Routes>
</Router>
</>
);
}
export default App;
then go to the navbar component and paste the code below into it.
//components/Navbar/Navbar.js
import React from "react";
import { Link } from "react-router-dom";
const Navbar = () => {
return (
<nav
className="navbar navbar-expand-lg navbar-light static-top mb-0 shadow"
style={{ backgroundColor: "#8661d1" }}
>
<div className="container">
<Link to="/">
<img
alt="Calorie Journal Logo"
src="https://user-images.githubusercontent.com/37651620/142762093-45207811-0c6e-4b62-9cb2-8d0009efb4ea.png"
width="70"
height="70"
className="d-inline-block align-top"
/>
</Link>
<Link
className="navbar-brand"
to="/"
className="navbar-brand"
style={{
color: "white",
fontSize: "1.5rem",
marginRight: "15rem",
marginLeft: "30rem",
}}
>
<img
src="https://user-images.githubusercontent.com/37651620/142764762-fef8f764-4cd5-44c6-8b9a-cffcfab2ccf8.png"
alt="calorie journal"
style={{ height: "100px" }}
/>
</Link>
<div className="collapse navbar-collapse">
<ul className="navbar-nav ml-auto">
<li className="nav-item">
<Link
className="nav-link"
to="/"
className="nav-link"
style={{
fontSize: "0.2rem",
color: "white",
}}
>
<button type="button" className="btn btn-info">
Calorie Info
</button>
</Link>
</li>
<li className="nav-item active">
<Link
className="nav-link"
to="/create"
className="nav-link"
style={{
fontSize: "0.2rem",
color: "white",
}}
>
<button type="button" className="btn btn-info">
β Add food
</button>
</Link>
</li>
<li className="nav-item">
<Link
className="nav-link"
to="/user"
className="nav-link"
style={{
fontSize: "0.2rem",
color: "white",
}}
>
<button type="button" className="btn btn-warning">
β Add User
</button>
</Link>
</li>
</ul>
</div>
</div>
</nav>
);
};
export default Navbar;
It's time to define our AddFood component now that we've successfully introduced the navbar component to our application.
import React,{useState,useEffect,useRef} from "react";
import axios from "axios";
import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
In AddFood component, add a useState() hook, which will allow us to incorporate the state into our functional component. useState() does not operate with object values, unlike state in class components. We can use primitives directly to build multiple react hooks for multiple variables if necessary.
const [state, setState] = useState(initialState);
Hooks must always be declared at the beginning of a function in React. This also aids in the component's state maintenance as well as preservation between renderings.
what is useRef() hook ?
This hook simply returns a mutable ref object with the passed argument as its.current property (initialValue). The returned object will be retained for the duration of the component's lifetime.
const refContainer = useRef(initialValue);
Let us jump right back into the code and implement useRef functionality
const userInputRef = useRef("userInput");
Let's have a look at the useEffect() hook. You notify React that your component needs to perform something after it renders by using this Hook. After completing the DOM modifications, React will remember the function you gave (which we'll refer to as our "effect"). We set the document title to achieve this, but we could alternatively perform data fetching or call another imperative API. Using useEffect() within the component allows us to directly access the count state variable (or any props) from the effect. It's already in the function scope, so we don't need a new API to read it. Hooks make use of JavaScript closures rather than providing React-specific APIs where JavaScript already provides it. useEffect() The hook is comparable to the life-cycle methods for class components that we are familiar with. It executes after each component render, including the initial render. As a result, componentDidMount, componentDidUpdate, and componentWillUnmount can all be thought of as a single component. We can pass dependencies to the effect to determine the behavior of when the effect should execute (just on initial render, or only when a specific state variable changes). This hook also has a clean-up option, which allows resources to be cleaned up before the component is destroyed. useEffect(didUpdate) is the effect's fundamental syntax.
Let's make a function that fetches all the user information
useEffect(() => {
axios
.get("http://localhost:5000/users/")
.then((response) => {
if (response.data.length > 0) {
setUsers(response.data.map((user) => user.username));
setUsername(response.data[0].username);
}
})
.catch((error) => {
console.log(error);
});
}, []);
Now, create five function or handlers and name it as handleUsername, handlDescription, handleCalories, handleDate and handleSubmit
function handleUsername(e) {
setUsername(e.target.value);
}
function handleDescription(e) {
setDescription(e.target.value);
}
function handleCalories(e) {
setCalories(e.target.value);
}
function handleDate(date) {
setDate(date);
}
function handleSubmit(e) {
e.preventDefault();
const meal = {
username,
description,
calories,
date,
};
console.log(meal);
axios
.post("http://localhost:5000/calorie/add", meal)
.then((res) => console.log(res.data));
window.location = "/";
}
Finally, your AddFood component should look something like this
//components/AddFood
import React, { useState, useEffect, useRef } from "react";
import axios from "axios";
import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
const AddFood = () => {
const [username, setUsername] = useState("");
const [description, setDescription] = useState("");
const [calories, setCalories] = useState("");
const [date, setDate] = useState(new Date());
const [users, setUsers] = useState([]);
const userInputRef = useRef("userInput");
useEffect(() => {
axios
.get("http://localhost:5000/users/")
.then((response) => {
if (response.data.length > 0) {
setUsers(response.data.map((user) => user.username));
setUsername(response.data[0].username);
}
})
.catch((error) => {
console.log(error);
});
}, []);
function handleUsername(e) {
setUsername(e.target.value);
}
function handleDescription(e) {
setDescription(e.target.value);
}
function handleCalories(e) {
setCalories(e.target.value);
}
function handleDate(date) {
setDate(date);
}
function handleSubmit(e) {
e.preventDefault();
const meal = {
username,
description,
calories,
date,
};
console.log(meal);
axios
.post("http://localhost:5000/calorie/add", meal)
.then((res) => console.log(res.data));
window.location = "/";
}
return (
<>
<div className="container">
<div className="card border-0 shadow my-4">
<div className="card-body p-3"></div>
<div>
<h3 style={{ textAlign: "center" }}>
<img
src="https://user-images.githubusercontent.com/37651620/142764215-78f5b75f-4871-451e-9a4d-dd77cc667fc5.png"
alt="Food"
style={{ height: "150px" }}
/>{" "}
</h3>
<form onSubmit={handleSubmit}>
<div
className="form-group"
style={{
marginLeft: "20px",
marginBottom: "15px",
marginRight: "20px",
}}
>
<label>π€ User name: </label>
<select
ref={userInputRef}
required
className="form-control"
value={username}
onChange={handleUsername}
>
{users.map(function (user) {
return (
<option key={user} value={user}>
{user}
</option>
);
})}
</select>
</div>
<div
className="form-group"
style={{
marginLeft: "20px",
marginBottom: "25px",
marginRight: "20px",
}}
>
<label>π₯‘ Food Info: </label>
<input
type="text"
required
className="form-control"
value={description}
onChange={handleDescription}
/>
</div>
<div
className="form-group"
style={{
marginLeft: "20px",
marginBottom: "15px",
marginRight: "20px",
}}
>
<label>π₯ Calories: </label>
<input
type="text"
className="form-control"
value={calories}
onChange={handleCalories}
/>
</div>
<div
className="form-group"
style={{
marginLeft: "20px",
marginBottom: "15px",
marginRight: "20px",
}}
>
<div style={{ textAlign: "center", cursor: "pointer" }}>
<label>Date: </label>
<div>
<DatePicker selected={date} onChange={handleDate} />
</div>
</div>
</div>
<div className="form-group" style={{ textAlign: "center" }}>
<input
type="submit"
value="Add Meal"
className="btn"
style={{
color: "white",
backgroundColor: "#8661d1",
marginBottom: "25px",
}}
/>
</div>
</form>
</div>
</div>
</div>
</>
);
};
export default AddFood;
Now, It's time to define our AddUser component now that we've successfully introduced the AddFood component to our application. Copy the following code and paste it inside the AddUser component.
//components/AddUser
import React, { useState } from "react";
import axios from "axios";
const AddUser = () => {
const [username, setUsername] = useState("");
function handleUsername(e) {
setUsername(e.target.value);
}
function handleSubmit(e) {
e.preventDefault();
const user = {
username,
};
console.log(user);
axios
.post("http://localhost:5000/users/add", user)
.then((res) => console.log(res.data));
setUsername("");
}
return (
<>
<div class="container">
<div class="card border-0 shadow my-4">
<div class="card-body p-3"></div>
<div>
<h3 style={{ textAlign: "center", marginBottom: "15px" }}>
<img
src="https://user-images.githubusercontent.com/37651620/142767072-ff777861-7ee9-4355-b48e-a624e8de085b.png"
alt="Logo"
style={{ height: "150px" }}
/>
</h3>
<form onSubmit={handleSubmit}>
<div
className="form-group"
style={{
marginLeft: "20px",
marginBottom: "15px",
marginRight: "20px",
}}
>
<label>π€ User name:</label>
<input
type="text"
required
className="form-control"
value={username}
onChange={handleUsername}
/>
</div>
<div
className="form-group"
style={{
textAlign: "center",
}}
>
<input
type="submit"
value="Create User"
className="btn "
style={{
color: "white",
marginBottom: "25px",
backgroundColor: "#8661d1",
}}
/>
</div>
</form>
</div>
</div>
</div>
</>
);
};
export default AddUser;
Now that we've completed the AddUser component, it's time to construct a feature that allows us to change our data, therefore we'll make an EditFood component.
//components/EditFood
import React, { useState, useEffect, useRef } from "react";
import axios from "axios";
import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
const EditFood = (props) => {
const [username, setUsername] = useState("");
const [description, setDescription] = useState("");
const [calories, setCalories] = useState("");
const [date, setDate] = useState(new Date());
const [users, setUsers] = useState([]);
const userInputRef = useRef("userInput");
useEffect(() => {
axios
.get("http://localhost:5000/calorie/" + props.match.params.id)
.then((response) => {
setUsername(response.data.username);
setDescription(response.data.description);
setCalories(response.data.calories);
setDate(new Date(response.data.date));
})
.catch((error) => {
console.log(error);
});
axios
.get("http://localhost:5000/users/")
.then((response) => {
if (response.data.length > 0) {
setUsers(response.data.map((user) => user.username));
setUsername(response.data[0].username);
}
})
.catch((error) => {
console.log(error);
});
}, [props.match.params.id]);
function handleUsername(e) {
setUsername(e.target.value);
}
function handleDescription(e) {
setDescription(e.target.value);
}
function handleCalories(e) {
setCalories(e.target.value);
}
function handleDate(date) {
setDate(date);
}
function handleSubmit(e) {
e.preventDefault();
const food = {
username,
description,
calories,
date,
};
console.log(food);
axios
.post("http://localhost:5000/calorie/update", food)
.then((res) => console.log(res.data));
window.location = "/";
}
return (
<>
<div className="container">
<div className="card border-0 shadow my-4">
<div className="card-body p-3"></div>
<div>
<h3 style={{ textAlign: "center" }}>
<img
src="https://user-images.githubusercontent.com/37651620/142764215-78f5b75f-4871-451e-9a4d-dd77cc667fc5.png"
alt="Food"
style={{ height: "150px" }}
/>{" "}
</h3>
<form onSubmit={handleSubmit}>
<div
className="form-group"
style={{
marginLeft: "20px",
marginBottom: "15px",
marginRight: "20px",
}}
>
<label>π€ User name: </label>
<select
ref={userInputRef}
required
className="form-control"
value={username}
onChange={handleUsername}
>
{users.map(function (user) {
return (
<option key={user} value={user}>
{user}
</option>
);
})}
</select>
</div>
<div
className="form-group"
style={{
marginLeft: "20px",
marginBottom: "25px",
marginRight: "20px",
}}
>
<label>π₯‘ Food Info: </label>
<input
type="text"
required
className="form-control"
value={description}
onChange={handleDescription}
/>
</div>
<div
className="form-group"
style={{
marginLeft: "20px",
marginBottom: "15px",
marginRight: "20px",
}}
>
<label>π₯ Calories: </label>
<input
type="text"
className="form-control"
value={calories}
onChange={handleCalories}
/>
</div>
<div
className="form-group"
style={{
marginLeft: "20px",
marginBottom: "15px",
marginRight: "20px",
}}
>
<div style={{ textAlign: "center", cursor: "pointer" }}>
<label>Date: </label>
<div>
<DatePicker selected={date} onChange={handleDate} />
</div>
</div>
</div>
<div className="form-group" style={{ textAlign: "center" }}>
<input
type="submit"
value="Add Meal"
className="btn"
style={{
color: "white",
backgroundColor: "#8661d1",
marginBottom: "25px",
}}
/>
</div>
</form>
</div>
</div>
</div>
</>
);
};
export default EditFood;
Let's concentrate on visualizing the fetched data into charts using the react-chartjs-2 library before we start fetching and showing the whole information on our home page.
So let's make two distinct components, one for a bar graph and the other for a pie chart, and once you've done that, copy the following code into each component.
//components/UserChart.js
import React, { useEffect, useState } from "react";
import { Pie } from "react-chartjs-2";
import axios from "axios";
const Delayed = ({ children, waitBeforeShow = 4500 }) => {
const [isShown, setIsShown] = useState(false);
useEffect(() => {
setTimeout(() => {
setIsShown(true);
}, waitBeforeShow);
}, [waitBeforeShow]);
return isShown ? children : null;
};
const UserChart = () => {
const [chartData, setChartData] = useState({});
async function getData() {
let username = [];
let calories = [];
await axios
.get("http://localhost:5000/calorie/")
.then((res) => {
console.log(res);
for (const dataObj of res.data) {
username.push(dataObj.username);
calories.push(parseInt(dataObj.calories));
console.log(username, calories);
}
setChartData({
labels: username,
datasets: [
{
label: "Calories",
data: calories,
backgroundColor: [
"#f42f42",
"#5ab950",
"#fe812a",
"#ffc748",
"#6b71c7",
"#8661d1",
"#8a2cba",
],
borderColor: [
"#f42f42",
"#5ab950",
"#fe812a",
"#ffc748",
"#6b71c7",
"#8661d1",
"#8a2cba",
],
borderWidth: 2,
},
],
});
})
.catch((err) => {
console.log(err);
});
console.log(username, calories);
}
useEffect(() => {
getData();
}, []);
return (
<div className="App">
<div>
<h5
style={{
fontSize: "20",
textAlign: "center",
marginTop: "1em",
marginBottom: "1em",
}}
>
Calorie per user
</h5>
<Delayed>
<Pie
data={chartData}
options={{
title: "{"
text: "Calorie per User",
fontSize: 10,
fontColor: "#212529",
},
maintainAspectRatio: true,
}}
/>
</Delayed>
</div>
</div>
);
};
export default UserChart;
//components/CalorieChart
import React, { useEffect, useState } from "react";
import { Bar } from "react-chartjs-2";
import axios from "axios";
const Delayed = ({ children, waitBeforeShow = 4500 }) => {
const [isShown, setIsShown] = useState(false);
useEffect(() => {
setTimeout(() => {
setIsShown(true);
}, waitBeforeShow);
}, [waitBeforeShow]);
return isShown ? children : null;
};
const CalorieChart = () => {
const [chartData, setChartData] = useState({});
async function getData() {
let foodCal = [];
let caloriesCal = [];
await axios
.get("http://localhost:5000/calorie/")
.then((res) => {
console.log(res);
for (let dataObj of res.data) {
foodCal.push(dataObj.description);
caloriesCal.push(parseInt(dataObj.caloriesCal));
console.log("foodCal, caloriesCal", foodCal, caloriesCal);
}
setChartData({
labels: foodCal,
datasets: [
{
label: "Cal",
data: caloriesCal,
backgroundColor: [
"#f42f42",
"#5ab950",
"#fe812a",
"#ffc748",
"#6b71c7",
"#8661d1",
"#8a2cba",
],
},
],
});
})
.catch((err) => {
console.log(err);
});
}
useEffect(() => {
getData();
}, []);
return (
<div className="App">
<h4>Food Analytics</h4>
<h5
style={{
fontSize: "20",
textAlign: "center",
marginBottom: "1em",
}}
>
Calorie Intake per each Food
</h5>
<div>
<Delayed>
<Bar
data={chartData}
options={{
responsive: true,
title: "{"
text: "Calorie Per Food ",
fontSize: 20,
fontColor: "#212529",
},
scales: {
yAxes: [
{
ticks: {
autoSkip: true,
maxTicksLimit: 10,
beginAtZero: true,
},
gridLines: {
// display: true,
},
},
],
xAxes: [
{
gridLines: {
display: true,
},
},
],
},
}}
/>
</Delayed>
</div>
</div>
);
};
export default CalorieChart;
Finally, let's work on the DisplayFoodList component, so first import the link from react-router, then import the axios package, then import the two previously created chart components, then create a FoodTrack component inside the DisplayFoodList file and add the following code, and finally create three functions named DisplayFoodList, deleteMeal, and malList, and finally use all the imported data inside the return statement and donβt forget to invoke the mailList function inside the tbody.Finally, if you followed all the steps correctly then your DisplayFoodList component should resemble the following.
//components/DisplayFoodList
import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";
import axios from "axios";
import CalorieChart from "../CalorieChart";
import UserChart from "../UserChart";
const FoodTrack = (props) => (
<tr>
<td>
<Link to={"/edit/" + props.meal._id} style={{ color: " #a04949" }}>
<img
src="https://user-images.githubusercontent.com/37651620/142769270-6128d45e-3650-4b66-bc0b-a76e3991fa1f.png"
alt="edit"
style={{ height: "40px" }}
/>
</Link>{" "}
|{" "}
<a
href="#"
onClick={() => {
props.deleteMeal(props.meal._id);
window.location.reload(false);
}}
style={{ color: " #a04949" }}
>
<img
src="https://user-images.githubusercontent.com/37651620/142769328-23d55107-8bed-4fa0-92b8-cca7df931083.png"
alt="edit"
style={{ height: "40px" }}
/>
</a>
</td>
<td>{props.meal.username}</td>
<td>{props.meal.description}</td>
<td>{props.meal.calories}</td>
<td>{props.meal.date.substring(0, 10)}</td>
</tr>
);
const DisplayFoodList = () => {
const [foods, setFoods] = useState([]);
useEffect(() => {
axios
.get("http://localhost:5000/calorie/")
.then((response) => {
setFoods(response.data);
})
.catch((error) => {
console.log(error);
});
}, []);
function deleteMeal(id) {
axios.delete("http://localhost:5000/calorie/" + id).then((response) => {
console.log(response.data);
});
setFoods(foods.filter((el) => el._id !== id));
}
const mealList = () => {
return foods.map((currentmeal) => {
return (
<FoodTrack
meal={currentmeal}
deleteMeal={deleteMeal}
key={currentmeal._id}
/>
);
});
};
return (
<>
<>
<div className="container">
<div className="card border-0 shadow my-4">
<div className="card-body p-5">
<h3 style={{ textAlign: "center", marginBottom: "15px" }}>
Calorie Journal
</h3>
<table className="table" style={{ textAlign: "center" }}>
<thead className="thead" style={{ backgroundColor: "#8661d1" }}>
<tr>
<th>Edit/Delete</th>
<th>π€ Username</th>
<th>π Description</th>
<th>π₯ Calories</th>
<th>π
Date</th>
</tr>
</thead>
<tbody>{mealList()}</tbody>
</table>
</div>
</div>
</div>
<div className="container">
<div
className="card border-0 shadow my-2"
style={{ padding: "2rem" }}
>
<div className="card-body p-1"></div>
<UserChart />
<CalorieChart />
</div>
</div>
</>
</>
);
};
export default DisplayFoodList;
We've covered a lot of ground to provide you with the information you'll need to create a full-fledged MERN stack application from the ground up.
You can find the whole source code here.
https://github.com/aviyeldevrel/devrel-tutorial-projects/tree/main/MERN-saas-project
Main article available here => https://aviyel.com/post/1323
Happy Coding!!
Follow @aviyelHQ or sign-up on Aviyel for early access if you are a project maintainer, contributor, or just an Open Source enthusiast.
Join Aviyel's Discord => Aviyel's world
Twitter =>[https://twitter.com/AviyelHq]