Creating a scheduling app with React, Nodejs and EmailJS 🧨
A scheduling app is a software tool that helps individuals or organizations manage and organize appointments, meetings, and tasks efficiently by providing a centralized platform to schedule, view, and modify schedules.
What is this article about?
In this article, you’ll learn how to build a scheduling application that allows you to set your availability and share your profile links to enable others to book an appointment with you. You will also be notified via email when someone schedules an appointment.
Novu – the first open-source notification infrastructure
Just a quick background about us. Novu is the first open-source notification infrastructure. We basically help to manage all the product notifications. It can be In-App (the bell icon like you have in Facebook – Websockets), Emails, SMSs and so on.
I would be super happy if you could give us a star! And let me also know in the comments ❤️
https://github.com/novuhq/novu
Project Setup
Here, I’ll guide you through creating the project environment for the scheduling application.
Create the project folder for the scheduling application by running the code below:
1mkdir scheduling-app
2cd scheduling-app
3mkdir client server
Setting up the Node.js server
Navigate into the server folder and create a package.json
file.
1cd server & npm init -y
Install Express, Nodemon, and the CORS library.
1npm install express cors nodemon
ExpressJS is a fast, minimalist framework that provides several features for building web applications in Node.js, CORS is a Node.js package that allows communication between different domains, and Nodemon is a Node.js tool that automatically restarts the server after detecting file changes.
Create an index.js
file – the entry point to the web server.
1touch index.js
Set up a Node.js server using Express.js. The code snippet below returns a JSON object when you visit the http://localhost:4000/api
in your browser.
1//👇🏻index.js
2const express = require("express");
3const app = express();
4const PORT = 4000;
5
6app.use(express.urlencoded({ extended: true }));
7app.use(express.json());
8
9app.get("/api", (req, res) => {
10 res.json({
11 message: "Hello world",
12 });
13});
14
15app.listen(PORT, () => {
16 console.log(`Server listening on ${PORT}`);
17});
We’ll create the various API routes later in this tutorial. For now, let’s design the user interface for the application.
Setting up the React application
Here, I’ll guide you through creating the application’s user interface with React.js.
Navigate into the client folder via your terminal and create a new React.js project.
1cd client
2npx create-react-app ./
Install React Router, React-Toastify, and the React Timezone Select.
1npm install react-toastify react-router-dom react-timezone-select
React Router is a JavaScript library that enables us to navigate between pages in a React application, and React Toastify is used to display colourful notifications to the users. React Timezone Select is a simple library that provides various available timezones per location.
Delete the redundant files, such as the logo and the test files from the React app, and update the App.js
file to display Hello World as below.
1function App() {
2 return (
3 <div>
4 <p>Hello World!</p>
5 </div>
6 );
7}
8export default App;
Copy the CSS file required for styling the project here into the src/index.css
file.
Building the user interface
In this section, I’ll walk you through creating the various components required to build the application.
Update the App.js
file to render the following components below:
1import React from "react";
2import { BrowserRouter, Routes, Route } from "react-router-dom";
3//👇🏻 component
4import Dashboard from "./components/Dashboard";
5import Login from "./components/Login";
6import Signup from "./components/Signup";
7import Profile from "./components/Profile";
8import BookUser from "./components/BookUser";
9//👇🏻 React-Toastify configuration
10import { ToastContainer } from "react-toastify";
11import "react-toastify/dist/ReactToastify.css";
12
13const App = () => {
14 return (
15 <div>
16 <BrowserRouter>
17 <Routes>
18 <Route path='/' element={<Login />} />
19 <Route path='/register' element={<Signup />} />
20 <Route path='/dashboard' element={<Dashboard />} />
21 <Route path='/profile/:id' element={<Profile />} />
22 <Route path='/book/:user' element={<BookUser />} />
23 </Routes>
24 </BrowserRouter>
25 <ToastContainer />
26 </div>
27 );
28};
29
30export default App;
From the code snippet above, I imported the Login
, Signup
, Dashboard
, Profile
, and BookUser
components. Create a components folder containing the files as done below:
1cd client
2mkdir components
3cd components
4touch Login.js Signup.js Dashboard.js Profile.js BookUser.js
- The Login and Signup components are the authentication routes.
- The Dashboard component is the homepage displayed to authenticated users – where they can set their availability.
- The Profile component displays the availability to the user, and the BookUser component allows others to schedule an appointment with them.
The Authentication components – Login and Signup
Copy the code below into the Login.js
file to accept the user’s username and password.
1import React, { useState } from "react";
2import { useNavigate, Link } from "react-router-dom";
3
4const Login = () => {
5 const [username, setUsername] = useState("");
6 const [password, setPassword] = useState("");
7 const navigate = useNavigate();
8
9 const handleSubmit = (e) => {
10 if (username.trim() && password.trim()) {
11 e.preventDefault();
12 console.log({ username, password });
13 setPassword("");
14 setUsername("");
15 }
16 };
17
18 return (
19 <main className='login'>
20 <form className='login__form' onSubmit={handleSubmit}>
21 <h2 className='login__title'>Log into your account</h2>
22 <label htmlFor='username'>Username</label>
23 <input
24 id='username'
25 name='username'
26 type='text'
27 value={username}
28 onChange={(e) => setUsername(e.target.value)}
29 className='username'
30 />
31 <label htmlFor='password'>Password</label>
32 <input
33 id='password'
34 type='password'
35 name='password'
36 value={password}
37 onChange={(e) => setPassword(e.target.value)}
38 className='password'
39 />
40 <button className='loginButton'>LOG IN</button>
41 <p style={{ textAlign: "center", marginTop: "30px" }}>
42 Don't have an account?{" "}
43 <Link className='link' to='/register'>
44 Create one
45 </Link>
46 </p>
47 </form>
48 </main>
49 );
50};
51
52export default Login;
Update the Signup.js
component to allow users to create an account using their username, email, and password.
1import React, { useState } from "react";
2import { useNavigate, Link } from "react-router-dom";
3import { handleRegister } from "../utils/resource";
4
5const Signup = () => {
6 const [username, setUsername] = useState("");
7 const [password, setPassword] = useState("");
8 const [email, setEmail] = useState("");
9 const navigate = useNavigate();
10
11 const handleSubmit = (e) => {
12 e.preventDefault();
13 if (username.trim() && password.trim() && email.trim()) {
14 console.log(email, username, password);
15 setPassword("");
16 setUsername("");
17 setEmail("");
18 }
19 };
20
21 return (
22 <main className='signup'>
23 <form className='signup__form' onSubmit={handleSubmit}>
24 <h2 className='signup__title'>Create an account</h2>
25 <label htmlFor='email'>Email Address</label>
26 <input
27 id='email'
28 name='email'
29 type='email'
30 required
31 value={email}
32 onChange={(e) => setEmail(e.target.value)}
33 />
34 <label htmlFor='username'>Username</label>
35 <input
36 id='username'
37 name='username'
38 required
39 type='text'
40 value={username}
41 onChange={(e) => setUsername(e.target.value)}
42 />
43 <label htmlFor='password'>Password</label>
44 <input
45 id='password'
46 type='password'
47 name='password'
48 required
49 value={password}
50 onChange={(e) => setPassword(e.target.value)}
51 />
52 <button className='signupButton'>REGISTER</button>
53 <p style={{ textAlign: "center", marginTop: "30px" }}>
54 Already have an account?{" "}
55 <Link className='link' to='/'>
56 Sign in
57 </Link>
58 </p>
59 </form>
60 </main>
61 );
62};
63
64export default Signup;
The Dashboard component
Here, we’ll create a user interface that allows users to set their availability according to their location or preferred timezone. React Timezone Select enables us to select from a list of time zones per location.
Update the Dashboard.js
component as done below:
1import React, { useState, useEffect } from "react";
2import TimezoneSelect from "react-timezone-select";
3import { useNavigate } from "react-router-dom";
4
5const Dashboard = () => {
6 const [selectedTimezone, setSelectedTimezone] = useState({});
7 const navigate = useNavigate();
8
9 //👇🏻 Runs when a user sign out
10 const handleLogout = () => {
11 localStorage.removeItem("_id");
12 localStorage.removeItem("_myEmail");
13 navigate("/");
14 };
15
16 return (
17 <div>
18 <nav className='dashboard__nav'>
19 <h2>BookMe</h2>
20 <button onClick={handleLogout} className='logout__btn'>
21 Log out
22 </button>
23 </nav>
24 <main className='dashboard__main'>
25 <h2 className='dashboard__heading'>Select your availability</h2>
26
27 <div className='timezone__wrapper'>
28 <p>Pick your timezone</p>
29 <TimezoneSelect
30 value={selectedTimezone}
31 onChange={setSelectedTimezone}
32 />
33 </div>
34 </main>
35 </div>
36 );
37};
The code snippet above displays the component, as shown in the image below. The handleLogout
function logs a user out of the application by removing the email and id from the local storage.
Below the time zone selection field, we need to create a group of input fields that allow users to set their availability or working hours for each day.
To do this, create a state within the Dashboard component that holds the schedule for each day.
1const [schedule, setSchedule] = useState([
2 { day: "Sun", startTime: "", endTime: "" },
3 { day: "Mon", startTime: "", endTime: "" },
4 { day: "Tue", startTime: "", endTime: "" },
5 { day: "Wed", startTime: "", endTime: "" },
6 { day: "Thu", startTime: "", endTime: "" },
7 { day: "Fri", startTime: "", endTime: "" },
8 { day: "Sat", startTime: "", endTime: "" },
9]);
Create a utils
folder containing a resource.js
file. The file will contain the asynchronous functions needed to make API requests to the server.
1cd src
2mkdir utils
3cd utils
4touch resource.js
Create a list of possible working hours from which users can select.
1export const time = [
2 { id: "null", t: "Select" },
3 { id: "7", t: "7:00am" },
4 { id: "8", t: "8:00am" },
5 { id: "9", t: "9:00am" },
6 { id: "10", t: "10:00am" },
7 { id: "11", t: "11:00am" },
8 { id: "12", t: "12:00pm" },
9 { id: "13", t: "13:00pm" },
10 { id: "14", t: "14:00pm" },
11 { id: "15", t: "15:00pm" },
12 { id: "16", t: "16:00pm" },
13 { id: "17", t: "17:00pm" },
14 { id: "18", t: "18:00pm" },
15 { id: "19", t: "19:00pm" },
16];
Update the Dashboard.js
file to display the list of working hours for each day.
1import { time } from "../utils/resource";
2import { toast } from "react-toastify";
3
4const Dashboard = () => {
5 const [selectedTimezone, setSelectedTimezone] = useState({})
6 //👇🏻 This updates the schedule array with the start and end time.
7 const handleTimeChange = (e, id) => {
8 const { name, value } = e.target;
9 if (value === "Select") return;
10 const list = [...schedule];
11 list[id][name] = value;
12 setSchedule(list);
13 };
14 //👇🏻 Logs the user's schedule to the console after setting the availability
15 const handleSaveSchedules = () => {
16 if (JSON.stringify(selectedTimezone) !== "{}") {
17 console.log(schedule);
18 } else {
19 toast.error("Select your timezone");
20 }
21 };
22 return (
23 <div>
24 <nav className='dashboard__nav'>
25 <h2>BookMe</h2>
26 <button onClick={handleLogout} className='logout__btn'>
27 Log out
28 </button>
29 </nav>
30 <main className='dashboard__main'>
31 <h2 className='dashboard__heading'>Select your availability</h2>
32 <div className='timezone__wrapper'>
33 <p>Pick your timezone</p>
34 <TimezoneSelect
35 value={selectedTimezone}
36 onChange={setSelectedTimezone}
37 />
38
39 {schedule.map((sch, id) => (
40 <div className='form' key={id}>
41 <p>{sch.day}</p>
42 <div className='select__wrapper'>
43 <label htmlFor='startTime'>Start Time</label>
44 <select
45 name='startTime'
46 id='startTime'
47 onChange={(e) => handleTimeChange(e, id)}
48 >
49 {time.map((t) => (
50 <option key={t.id} value={t.t} id={t.id}>
51 {t.t}
52 </option>
53 ))}
54 </select>
55 </div>
56 <div className='select__wrapper'>
57 <label htmlFor='endTime'>End Time</label>
58 <select
59 name='endTime'
60 id='endTime'
61 onChange={(e) => handleTimeChange(e, id)}
62 >
63 {time.map((t) => (
64 <option key={t.id} value={t.t} id={t.id}>
65 {t.t}
66 </option>
67 ))}
68 </select>
69 </div>
70 </div>
71 ))}
72 </div>
73
74 <div className='saveBtn__container'>
75 <button onClick={handleSaveSchedules}>SAVE SCHEDULES</button>
76 </div>
77 </main>
78 </div>
79 );
80};
The Profile component
The Profile component is a simple component that displays the user’s schedule as shown below:
Copy the code below into the Profile.js
file. Later in the tutorial, we’ll fetch its data from the server.
1import React from "react";
2import { useParams } from "react-router-dom";
3
4const Profile = () => {
5 //👇🏻 The ID is the URL parameter for fetching the user's details.
6 const { id } = useParams();
7
8 return (
9 <main className='profile'>
10 <div style={{ width: "70%" }}>
11 <h2>Hey, nevodavid</h2>
12 <p>Here is your schedule: WAT</p>
13 <table>
14 <tbody>
15 <tr>
16 <td>MON</td>
17 <td>8:00am</td>
18 <td>10:00pm</td>
19 </tr>
20 </tbody>
21 </table>
22 </div>
23 </main>
24 );
25};
26
27export default Profile;
The BookUser component
This page shows a user’s availability according to the username from the URL and allows people to book a session with the user.
Copy the code below into the BookUser.js
component.
1import React, { useState } from "react";
2import { useParams } from "react-router-dom";
3
4const BookUser = () => {
5 const [fullName, setFullName] = useState("");
6 const [email, setEmail] = useState("");
7 const [message, setMessage] = useState("");
8 const { user } = useParams();
9
10 //👇🏻 logs the user's details to the console
11 const handleSubmit = (e) => {
12 e.preventDefault();
13 console.log(email, fullName, message);
14 setFullName("");
15 setMessage("");
16 };
17
18 return (
19 <div className='bookContainer'>
20 <h2 className='bookTitle'>Book a session with {user}</h2>
21 <form onSubmit={handleSubmit} className='booking__form'>
22 <label htmlFor='fullName'>Full Name</label>
23 <input
24 id='fullName'
25 name='fullName'
26 type='text'
27 required
28 value={fullName}
29 onChange={(e) => setFullName(e.target.value)}
30 />
31 <label htmlFor='email'>Email Address</label>
32 <input
33 id='email'
34 name='email'
35 required
36 type='email'
37 value={email}
38 onChange={(e) => setEmail(e.target.value)}
39 />
40
41 <label htmlFor='message'>Any important note? (optional)</label>
42 <textarea
43 rows={5}
44 name='message'
45 id='message'
46 value={message}
47 onChange={(e) => setMessage(e.target.value)}
48 />
49
50 <label htmlFor='session'>
51 Select your preferred session - GMT+2 Jerusalem
52 </label>
53
54 <button className='bookingBtn'>SEND</button>
55 </form>
56 </div>
57 );
58};
59
60export default BookUser;
The code snippet above displays a booking form that accepts the client’s full name, email, and message. Later in this tutorial, we’ll improve the component to book a session with a user and send a confirmation email to the user.
The ErrorPage component
This component is displayed to users when an error occurs.
1import React from "react";
2import { Link } from "react-router-dom";
3
4const ErrorPage = ({ error }) => {
5 return (
6 <div className='errorContainer'>
7 <h2 style={{ marginBottom: "30px" }}>{error}</h2>
8 <Link to='/'>Go Home</Link>
9 </div>
10 );
11};
12
13export default ErrorPage;
User authentication with React and Node.js
Here, I’ll guide you through authenticating users and how to allow only authorized users to access protected pages within the web application.
Creating new users
Add a register POST route on the server that accepts the user’s username, email, and password from the React application.
1app.post("/register", (req, res) => {
2 const { username, email, password } = req.body;
3 console.log(req.body);
4});
Create an asynchronous function within the utils/resource.js
file that accepts the user’s credentials.
1export async function handleRegister(email, username, password, navigate) {
2 //...data
3}
Import the handleRegister
function into the Signup
component and pass in the required parameters.
1import { handleRegister } from "../utils/resource";
2import { useNavigate } from "react-router-dom";
3
4const navigate = useNavigate();
5
6const handleSubmit = (e) => {
7 e.preventDefault();
8 if (username.trim() && password.trim() && email.trim()) {
9 handleRegister(email, username, password, navigate);
10 setPassword("");
11 setUsername("");
12 setEmail("");
13 }
14};
Update the handleRegister
function to make a POST request to the server.
1export async function handleRegister(email, username, password, navigate) {
2 try {
3 const request = await fetch("http://localhost:4000/register", {
4 method: "POST",
5 body: JSON.stringify({
6 email,
7 username,
8 password,
9 }),
10 headers: {
11 Accept: "application/json",
12 "Content-Type": "application/json",
13 },
14 });
15 const data = await request.json();
16 if (data.error_message) {
17 toast.error(data.error_message);
18 } else {
19 toast.success(data.message);
20 navigate("/");
21 }
22 } catch (err) {
23 console.error(err);
24 toast.error("Account creation failed");
25 }
26}
Accept the user’s credentials and create an account on the server.
1//👇🏻 array representing the data
2const database = [];
3//👇🏻 generates a random string as ID
4const generateID = () => Math.random().toString(36).substring(2, 10);
5
6app.post("/register", (req, res) => {
7 const { username, email, password } = req.body;
8 //👇🏻 checks if the user does not exist
9 let result = database.filter(
10 (user) => user.email === email || user.username === username
11 );
12 //👇🏻 creates the user's data structure on the server
13 if (result.length === 0) {
14 database.push({
15 id: generateID(),
16 username,
17 password,
18 email,
19 timezone: {},
20 schedule: [],
21 });
22 return res.json({ message: "Account created successfully!" });
23 }
24 //👇🏻 returns an error
25 res.json({ error_message: "User already exists!" });
26});
Logging users into the application
Add a login POST route on the server that accepts the username and password from the React application.
1app.post("/login", (req, res) => {
2 const { username, password } = req.body;
3 console.log(req.body);
4});
Create an asynchronous function that accepts the username and password from the user within the utils/resource.js
file.
1export async function handleLogin(username, password, navigate) {
2 //...data
3}
Import the handleLogin
function into the Login component as follows:
1import { handleLogin } from "../utils/resource";
2import { useNavigate } from "react-router-dom";
3
4const navigate = useNavigate();
5
6//👇🏻 The Login button function
7const handleSubmit = (e) => {
8 if (username.trim() && password.trim()) {
9 e.preventDefault();
10 //👇🏻 accepts the user's password and email
11 handleLogin(username, password, navigate);
12 setPassword("");
13 setUsername("");
14 }
15};
Update the handleLogin
function to make a POST request to the server.
1export async function handleLogin(username, password, navigate) {
2 try {
3 const request = await fetch("http://localhost:4000/login", {
4 method: "POST",
5 body: JSON.stringify({
6 username,
7 password,
8 }),
9 headers: {
10 Accept: "application/json",
11 "Content-Type": "application/json",
12 },
13 });
14 const data = await request.json();
15 if (data.error_message) {
16 toast.error(data.error_message);
17 } else {
18 //👇🏻If login successful
19 toast.success(data.message);
20 //👇🏻 saves the email and id for identification
21 localStorage.setItem("_id", data.data._id);
22 localStorage.setItem("_myEmail", data.data._email);
23 navigate("/dashboard");
24 }
25 } catch (err) {
26 console.error(err);
27 }
28}
Accept and verify the user’s credentials on the server.
1app.post("/login", (req, res) => {
2 const { username, password } = req.body;
3 let result = database.filter(
4 (user) => user.username === username && user.password === password
5 );
6 //👇🏻 user doesn't exist
7 if (result.length !== 1) {
8 return res.json({
9 error_message: "Incorrect credentials",
10 });
11 }
12 //👇🏻 user exists
13 res.json({
14 message: "Login successfully",
15 data: {
16 _id: result[0].id,
17 _email: result[0].email,
18 },
19 });
20});
Since we’ll be making requests that require authentication on the server, add the code snippet below to the Dashboard
and Profile
components.
1useEffect(() => {
2 if (!localStorage.getItem("_id")) {
3 navigate("/");
4 }
5}, [navigate]);
Creating schedules
In this section, I’ll walk you through creating the process of schedules and displaying them to the user.
Add a POST route on the server that allows users to create a new schedule.
1app.post("/schedule/create", (req, res) => {
2 const { userId, timezone, schedule } = req.body;
3 console.log(req.body);
4});
Create a handleCreateSchedule
function within the utils/resource.js
file that accepts the user’s timezone and schedule.
1export async function handleCreateSchedule(
2 selectedTimezone,
3 schedule,
4 navigate
5) {
6 //..other data
7}
Import the handleCreateSchedule
function within the Dashboard component.
1import { handleCreateSchedule } from "../utils/resource";
2
3const handleSaveSchedules = () => {
4//👇🏻 ensures the user's timezone has been selected
5 if (JSON.stringify(selectedTimezone) !== "{}") {
6 handleCreateSchedule(selectedTimezone, schedule, navigate);
7 } else {
8 toast.error("Select your timezone");
9 }
10};
Update the handleCreateSchedule
function to make a POST request containing the schedules and the timezone.
1export async function handleCreateSchedule(
2 selectedTimezone,
3 schedule,
4 navigate
5) {
6 try {
7 await fetch("http://localhost:4000/schedule/create", {
8 method: "POST",
9 body: JSON.stringify({
10 userId: localStorage.getItem("_id"),
11 timezone: selectedTimezone,
12 schedule,
13 }),
14 headers: {
15 Accept: "application/json",
16 "Content-Type": "application/json",
17 },
18 });
19 //👇🏻 navigates to the profile page
20 navigate(`/profile/${localStorage.getItem("_id")}`);
21 } catch (err) {
22 console.error(err);
23 }
24}
Update the POST route on the server to accept the data from the React app and create a new schedule for the user.
1app.post("/schedule/create", (req, res) => {
2 const { userId, timezone, schedule } = req.body;
3 //👇🏻 filters the database via the id
4 let result = database.filter((db) => db.id === userId);
5 //👇🏻 updates the user's schedule and timezone
6 result[0].timezone = timezone;
7 result[0].schedule = schedule;
8 res.json({ message: "OK" });
9});
Congratulations! 🎉 We’ve been able to update the user’s schedule and timezone.
Displaying the schedules
Here, I will walk you through fetching the user’s schedules from the server.
Add a GET route on the server that retrieves the user’s data from the database array.
1app.get("/schedules/:id", (req, res) => {
2 const { id } = req.params;
3 //👇🏻 filters the array via the ID
4 let result = database.filter((db) => db.id === id);
5 //👇🏻 returns the schedule, time and username
6 if (result.length === 1) {
7 return res.json({
8 message: "Schedules successfully retrieved!",
9 schedules: result[0].schedule,
10 username: result[0].username,
11 timezone: result[0].timezone,
12 });
13 }
14 //👇🏻 if user not found
15 return res.json({ error_message: "Sign in again, an error occured..." });
16});
Create a function within the Profile.js
file that sends a request to the GET route when the page is loaded.
1const [schedules, setSchedules] = useState([]);
2const [loading, setLoading] = useState(true);
3const [username, setUsername] = useState("");
4const [timezone, setTimezone] = useState("");
5
6useEffect(() => {
7 function getUserDetails() {
8 if (id) {
9 fetch(`http://localhost:4000/schedules/${id}`)
10 .then((res) => res.json())
11 .then((data) => {
12 setUsername(data.username);
13 setSchedules(data.schedules);
14 setTimezone(data.timezone.label);
15 setLoading(false);
16 })
17 .catch((err) => console.error(err));
18 }
19 }
20 getUserDetails();
21}, [id]);
And display the data as shown below:
1return (
2 <main className='profile'>
3 {loading ? (
4 <p>Loading...</p>
5 ) : (
6 <div>
7 <h2>Hey, {username}</h2>
8 <p>Here is your schedule: - {timezone}</p>
9 <table>
10 <tbody>
11 {schedules.map((sch) => (
12 <tr key={sch.day}>
13 <td style={{ fontWeight: "bold" }}>{sch.day.toUpperCase()}</td>
14 <td>{sch.startTime || "Unavailable"}</td>
15 <td>{sch.endTime || "Unavailable"}</td>
16 </tr>
17 ))}
18 </tbody>
19 </table>
20 </div>
21 )}
22 </main>
23);
Booking appointments with EmailJS
In this section, you’ll learn how to send email notifications via EmailJS when clients book an appointment with a user.
EmailJS is a JavaScript library that enables us to send emails via client-side technologies only – without a server. With EmailJS, you can send texts and email templates and add attachments to the emails.
Create a POST route on the server that fetches the user’s data.
1app.post("/schedules/:username", (req, res) => {
2 const { username } = req.body;
3 //👇🏻 filter the databse via the username
4 let result = database.filter((db) => db.username === username);
5 if (result.length === 1) {
6 const scheduleArray = result[0].schedule;
7 //👇🏻 return only the selected schedules
8 const filteredArray = scheduleArray.filter((sch) => sch.startTime !== "");
9 //return the schedules and other information
10 return res.json({
11 message: "Schedules successfully retrieved!",
12 schedules: filteredArray,
13 timezone: result[0].timezone,
14 receiverEmail: result[0].email,
15 });
16 }
17 return res.json({ error_message: "User doesn't exist" });
Add a fetchBookingDetails
function within the resource.js
file.
1export function fetchBookingDetails(
2 user,
3 setError,
4 setTimezone,
5 setSchedules,
6 setReceiverEmail
7) {
8 //...data
9}
Import the function into the BookUser.js
component and call it with its necessary parameters on page load.
1const [schedules, setSchedules] = useState([]);
2const [timezone, setTimezone] = useState("");
3const [error, setError] = useState(false);
4const [receiverEmail, setReceiverEmail] = useState("");
5
6useEffect(() => {
7 fetchBookingDetails(
8 user,
9 setError,
10 setTimezone,
11 setSchedules,
12 setReceiverEmail
13 );
14}, [user]);
15
16if (error) {
17 return <ErrorPage error="User doesn't exist" />;
18}
Update the fetchBookingDetails
function to retrieve the information from the server and update the state parameters.
1export function fetchBookingDetails(
2 user,
3 setError,
4 setTimezone,
5 setSchedules,
6 setReceiverEmail
7) {
8 fetch(`http://localhost:4000/schedules/${user}`, {
9 method: "POST",
10 body: JSON.stringify({
11 username: user,
12 }),
13 headers: {
14 Accept: "application/json",
15 "Content-Type": "application/json",
16 },
17 })
18 .then((res) => res.json())
19 .then((data) => {
20 if (data.error_message) {
21 toast.error(data.error_message);
22 setError(true);
23 } else {
24 setTimezone(data.timezone.label);
25 setSchedules(data.schedules);
26 setReceiverEmail(data.receiverEmail);
27 }
28 })
29 .catch((err) => console.error(err));
30}
Render the schedule within the form to enable users to select their preferred appointment time.
1<select name='duration' onChange={(e) => setDuration(e.target.value)}>
2 {schedules.map((schedule) => (
3 <option
4 value={`${schedule.day} - ${schedule.startTime} : ${schedule.endTime}`}
5 key={schedule.day}
6 >{`${schedule.day} - ${schedule.startTime} : ${schedule.endTime}`}</option>
7 ))}
8</select>
Sending email notifications with EmailJS
Here, I’ll guide you through adding EmailJS to the React.js application and how to send emails to users whenever someone books an appointment with them.
Install EmailJS to the React application by running the code below:
1npm install @emailjs/browser
Create an EmailJS account here and add an email service provider to your account.
Add an email template as done in the image below:
The words in curly brackets represent variables that can hold dynamic data.
Import EmailJS into the utils/resource.js
file and create a function that sends an email notification to the user.
1import emailjs from "@emailjs/browser";
2
3export const sendEmail = (
4 receiverEmail,
5 email,
6 fullName,
7 message,
8 duration
9) => {
10 emailjs
11 .send(
12 "YOUR_SERVICE_ID",
13 "YOUR_TEMPLATE_ID",
14 {
15 to_email: receiverEmail,
16 from_email: email,
17 fullName,
18 message,
19 duration,
20 },
21 "YOUR_PUBLIC_KEY"
22 )
23 .then(
24 (result) => {
25 console.log(result.text);
26 toast.success("Session booked successfully!");
27 },
28 (error) => {
29 console.log(error.text);
30 toast.error(error.text);
31 }
32 );
33};
You can get your EmailJS Public key from the Account section of your EmailJS dashboard.
Add the sendEmail
function into the BookUser
component to send an email to the user containing the booking information whenever the form is submitted.
1const handleSubmit = (e) => {
2 e.preventDefault();
3 sendEmail(receiverEmail, email, fullName, message, duration);
4 setFullName("");
5 setMessage("");
6};
Congratulations! 🎉 You’ve completed the project for this tutorial.
Conclusion
So far, you’ve learnt how to create a scheduling application that enables users to set their availability and get notified via EmailJS when they have an appointment.
This tutorial walks you through a mini project you can build using React and Node.js. You can improve the application by adding an authentication library and storing the data in a database.
The source code for this tutorial is available here:
Thank you for reading!
Help me out!
If you feel like this article helped you understand WebSockets better! I would be super happy if you could give us a star! And let me also know in the comments ❤️
https://github.com/novuhq/novu