使用 React、Nodejs 和 EmailJS 创建一个调度应用程序
这篇文章是关于什么的?
在本文中,您将学习如何构建一个日程安排应用程序,该应用程序允许您设置您的空闲时间并分享您的个人资料链接,以便其他人与您预约。当有人安排预约时,您还会收到电子邮件通知。
Novu——第一个开源通知基础设施
简单介绍一下我们。Novu 是第一个开源通知基础设施。我们主要负责管理所有产品通知。这些通知可以是应用内通知(类似 Facebook 的铃铛图标 - Websockets)、电子邮件、短信等等。
如果你能给我们一颗星,我会非常高兴!也请在评论区告诉我❤️
https://github.com/novuhq/novu
项目设置
在这里,我将指导您创建调度应用程序的项目环境。
通过运行以下代码为调度应用程序创建项目文件夹:
mkdir scheduling-app
cd scheduling-app
mkdir client server
设置 Node.js 服务器
导航到服务器文件夹并创建一个package.json
文件。
cd server & npm init -y
安装 Express、Nodemon 和 CORS 库。
npm install express cors nodemon
ExpressJS是一个快速、简约的框架,它提供了在 Node.js 中构建 Web 应用程序的多种功能, CORS是一个允许不同域之间通信的 Node.js 包, Nodemon是一个在检测到文件更改后自动重启服务器的 Node.js 工具。
创建一个index.js
文件——Web 服务器的入口点。
touch index.js
使用 Express.js 设置 Node.js 服务器。当您http://localhost:4000/api
在浏览器中访问时,下面的代码片段会返回一个 JSON 对象。
//👇🏻index.js
const express = require("express");
const app = express();
const PORT = 4000;
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.get("/api", (req, res) => {
res.json({
message: "Hello world",
});
});
app.listen(PORT, () => {
console.log(`Server listening on ${PORT}`);
});
我们将在本教程的后面创建各种 API 路由。现在,让我们设计应用程序的用户界面。
设置 React 应用程序
在这里,我将指导您使用 React.js 创建应用程序的用户界面。
通过终端导航到客户端文件夹并创建一个新的 React.js 项目。
cd client
npx create-react-app ./
安装 React Router、React-Toastify 和 React Timezone Select。
npm install react-toastify react-router-dom react-timezone-select
React Router 是一个 JavaScript 库,它使我们能够在 React 应用程序中的页面之间导航,而 React Toastify用于向用户显示彩色通知。React Timezone Select 是一个简单的库,它为每个位置提供各种可用的时区。
从 React 应用程序中删除冗余文件(例如徽标和测试文件),并更新App.js
文件以显示如下所示的 Hello World。
function App() {
return (
<div>
<p>Hello World!</p>
</div>
);
}
export default App;
将此处项目样式所需的 CSS 文件复制 到src/index.css
文件中。
构建用户界面
在本节中,我将引导您创建构建应用程序所需的各种组件。
更新App.js
文件以呈现以下组件:
import React from "react";
import { BrowserRouter, Routes, Route } from "react-router-dom";
//👇🏻 component
import Dashboard from "./components/Dashboard";
import Login from "./components/Login";
import Signup from "./components/Signup";
import Profile from "./components/Profile";
import BookUser from "./components/BookUser";
//👇🏻 React-Toastify configuration
import { ToastContainer } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
const App = () => {
return (
<div>
<BrowserRouter>
<Routes>
<Route path='/' element={<Login />} />
<Route path='/register' element={<Signup />} />
<Route path='/dashboard' element={<Dashboard />} />
<Route path='/profile/:id' element={<Profile />} />
<Route path='/book/:user' element={<BookUser />} />
</Routes>
</BrowserRouter>
<ToastContainer />
</div>
);
};
export default App;
从上面的代码片段中,我导入了Login
、、、和组件。创建一个包含这些文件的components 文件夹Signup
,如下所示:Dashboard
Profile
BookUser
cd client
mkdir components
cd components
touch Login.js Signup.js Dashboard.js Profile.js BookUser.js
- 登录和注册组件是身份验证路由。
- 仪表板组件是向经过身份验证的用户显示的主页 - 他们可以在此设置他们的可用性。
- Profile 组件向用户显示可用性,而 BookUser 组件允许其他人与他们安排约会。
身份验证组件 - 登录和注册
将以下代码复制到Login.js
文件中以接受用户的用户名和密码。
import React, { useState } from "react";
import { useNavigate, Link } from "react-router-dom";
const Login = () => {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const navigate = useNavigate();
const handleSubmit = (e) => {
if (username.trim() && password.trim()) {
e.preventDefault();
console.log({ username, password });
setPassword("");
setUsername("");
}
};
return (
<main className='login'>
<form className='login__form' onSubmit={handleSubmit}>
<h2 className='login__title'>Log into your account</h2>
<label htmlFor='username'>Username</label>
<input
id='username'
name='username'
type='text'
value={username}
onChange={(e) => setUsername(e.target.value)}
className='username'
/>
<label htmlFor='password'>Password</label>
<input
id='password'
type='password'
name='password'
value={password}
onChange={(e) => setPassword(e.target.value)}
className='password'
/>
<button className='loginButton'>LOG IN</button>
<p style={{ textAlign: "center", marginTop: "30px" }}>
Don't have an account?{" "}
<Link className='link' to='/register'>
Create one
</Link>
</p>
</form>
</main>
);
};
export default Login;
更新Signup.js
组件以允许用户使用他们的用户名、电子邮件和密码创建帐户。
import React, { useState } from "react";
import { useNavigate, Link } from "react-router-dom";
import { handleRegister } from "../utils/resource";
const Signup = () => {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [email, setEmail] = useState("");
const navigate = useNavigate();
const handleSubmit = (e) => {
e.preventDefault();
if (username.trim() && password.trim() && email.trim()) {
console.log(email, username, password);
setPassword("");
setUsername("");
setEmail("");
}
};
return (
<main className='signup'>
<form className='signup__form' onSubmit={handleSubmit}>
<h2 className='signup__title'>Create an account</h2>
<label htmlFor='email'>Email Address</label>
<input
id='email'
name='email'
type='email'
required
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<label htmlFor='username'>Username</label>
<input
id='username'
name='username'
required
type='text'
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
<label htmlFor='password'>Password</label>
<input
id='password'
type='password'
name='password'
required
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<button className='signupButton'>REGISTER</button>
<p style={{ textAlign: "center", marginTop: "30px" }}>
Already have an account?{" "}
<Link className='link' to='/'>
Sign in
</Link>
</p>
</form>
</main>
);
};
export default Signup;
仪表板组件
在这里,我们将创建一个用户界面,允许用户根据其位置或首选时区设置其可用性。React Timezone Select允许我们从每个位置的时区列表中进行选择。
Dashboard.js
按照如下所示更新组件:
import React, { useState, useEffect } from "react";
import TimezoneSelect from "react-timezone-select";
import { useNavigate } from "react-router-dom";
const Dashboard = () => {
const [selectedTimezone, setSelectedTimezone] = useState({});
const navigate = useNavigate();
//👇🏻 Runs when a user sign out
const handleLogout = () => {
localStorage.removeItem("_id");
localStorage.removeItem("_myEmail");
navigate("/");
};
return (
<div>
<nav className='dashboard__nav'>
<h2>BookMe</h2>
<button onClick={handleLogout} className='logout__btn'>
Log out
</button>
</nav>
<main className='dashboard__main'>
<h2 className='dashboard__heading'>Select your availability</h2>
<div className='timezone__wrapper'>
<p>Pick your timezone</p>
<TimezoneSelect
value={selectedTimezone}
onChange={setSelectedTimezone}
/>
</div>
</main>
</div>
);
};
上面的代码片段显示了组件,如下图所示。该handleLogout
函数通过从本地存储中删除电子邮件和 ID 来将用户从应用程序中注销。
在时区选择字段下方,我们需要创建一组输入字段,允许用户设置每天的可用时间或工作时间。
为此,请在仪表板组件内创建一个状态来保存每天的时间表。
const [schedule, setSchedule] = useState([
{ day: "Sun", startTime: "", endTime: "" },
{ day: "Mon", startTime: "", endTime: "" },
{ day: "Tue", startTime: "", endTime: "" },
{ day: "Wed", startTime: "", endTime: "" },
{ day: "Thu", startTime: "", endTime: "" },
{ day: "Fri", startTime: "", endTime: "" },
{ day: "Sat", startTime: "", endTime: "" },
]);
创建一个utils
包含resource.js
文件的文件夹。该文件将包含向服务器发出 API 请求所需的异步函数。
cd src
mkdir utils
cd utils
touch resource.js
创建一个可能的工作时间列表,供用户选择。
export const time = [
{ id: "null", t: "Select" },
{ id: "7", t: "7:00am" },
{ id: "8", t: "8:00am" },
{ id: "9", t: "9:00am" },
{ id: "10", t: "10:00am" },
{ id: "11", t: "11:00am" },
{ id: "12", t: "12:00pm" },
{ id: "13", t: "13:00pm" },
{ id: "14", t: "14:00pm" },
{ id: "15", t: "15:00pm" },
{ id: "16", t: "16:00pm" },
{ id: "17", t: "17:00pm" },
{ id: "18", t: "18:00pm" },
{ id: "19", t: "19:00pm" },
];
更新Dashboard.js
文件以显示每天的工作时间列表。
import { time } from "../utils/resource";
import { toast } from "react-toastify";
const Dashboard = () => {
const [selectedTimezone, setSelectedTimezone] = useState({})
//👇🏻 This updates the schedule array with the start and end time.
const handleTimeChange = (e, id) => {
const { name, value } = e.target;
if (value === "Select") return;
const list = [...schedule];
list[id][name] = value;
setSchedule(list);
};
//👇🏻 Logs the user's schedule to the console after setting the availability
const handleSaveSchedules = () => {
if (JSON.stringify(selectedTimezone) !== "{}") {
console.log(schedule);
} else {
toast.error("Select your timezone");
}
};
return (
<div>
<nav className='dashboard__nav'>
<h2>BookMe</h2>
<button onClick={handleLogout} className='logout__btn'>
Log out
</button>
</nav>
<main className='dashboard__main'>
<h2 className='dashboard__heading'>Select your availability</h2>
<div className='timezone__wrapper'>
<p>Pick your timezone</p>
<TimezoneSelect
value={selectedTimezone}
onChange={setSelectedTimezone}
/>
{schedule.map((sch, id) => (
<div className='form' key={id}>
<p>{sch.day}</p>
<div className='select__wrapper'>
<label htmlFor='startTime'>Start Time</label>
<select
name='startTime'
id='startTime'
onChange={(e) => handleTimeChange(e, id)}
>
{time.map((t) => (
<option key={t.id} value={t.t} id={t.id}>
{t.t}
</option>
))}
</select>
</div>
<div className='select__wrapper'>
<label htmlFor='endTime'>End Time</label>
<select
name='endTime'
id='endTime'
onChange={(e) => handleTimeChange(e, id)}
>
{time.map((t) => (
<option key={t.id} value={t.t} id={t.id}>
{t.t}
</option>
))}
</select>
</div>
</div>
))}
</div>
<div className='saveBtn__container'>
<button onClick={handleSaveSchedules}>SAVE SCHEDULES</button>
</div>
</main>
</div>
);
};
配置文件组件
Profile 组件是一个简单的组件,它显示用户的日程安排,如下所示:
将下面的代码复制到Profile.js
文件中。在本教程的后面,我们将从服务器获取其数据。
import React from "react";
import { useParams } from "react-router-dom";
const Profile = () => {
//👇🏻 The ID is the URL parameter for fetching the user's details.
const { id } = useParams();
return (
<main className='profile'>
<div style={{ width: "70%" }}>
<h2>Hey, nevodavid</h2>
<p>Here is your schedule: WAT</p>
<table>
<tbody>
<tr>
<td>MON</td>
<td>8:00am</td>
<td>10:00pm</td>
</tr>
</tbody>
</table>
</div>
</main>
);
};
export default Profile;
BookUser 组件
此页面根据 URL 中的用户名显示用户的可用性,并允许人们与该用户预约会议。
将下面的代码复制到BookUser.js
组件中。
import React, { useState } from "react";
import { useParams } from "react-router-dom";
const BookUser = () => {
const [fullName, setFullName] = useState("");
const [email, setEmail] = useState("");
const [message, setMessage] = useState("");
const { user } = useParams();
//👇🏻 logs the user's details to the console
const handleSubmit = (e) => {
e.preventDefault();
console.log(email, fullName, message);
setFullName("");
setMessage("");
};
return (
<div className='bookContainer'>
<h2 className='bookTitle'>Book a session with {user}</h2>
<form onSubmit={handleSubmit} className='booking__form'>
<label htmlFor='fullName'>Full Name</label>
<input
id='fullName'
name='fullName'
type='text'
required
value={fullName}
onChange={(e) => setFullName(e.target.value)}
/>
<label htmlFor='email'>Email Address</label>
<input
id='email'
name='email'
required
type='email'
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<label htmlFor='message'>Any important note? (optional)</label>
<textarea
rows={5}
name='message'
id='message'
value={message}
onChange={(e) => setMessage(e.target.value)}
/>
<label htmlFor='session'>
Select your preferred session - GMT+2 Jerusalem
</label>
<button className='bookingBtn'>SEND</button>
</form>
</div>
);
};
export default BookUser;
上面的代码片段显示了一个预订表单,用于输入客户的全名、电子邮件和消息。在本教程的后面,我们将改进该组件,以便与用户预订会话并向用户发送确认电子邮件。
ErrorPage 组件
当发生错误时,此组件会显示给用户。
import React from "react";
import { Link } from "react-router-dom";
const ErrorPage = ({ error }) => {
return (
<div className='errorContainer'>
<h2 style={{ marginBottom: "30px" }}>{error}</h2>
<Link to='/'>Go Home</Link>
</div>
);
};
export default ErrorPage;
使用 React 和 Node.js 进行用户身份验证
在这里,我将指导您验证用户身份以及如何仅允许授权用户访问 Web 应用程序中受保护的页面。
创建新用户
在服务器上添加一个注册 POST 路由,从 React 应用程序接受用户的用户名、电子邮件和密码。
app.post("/register", (req, res) => {
const { username, email, password } = req.body;
console.log(req.body);
});
在文件中创建一个utils/resource.js
接受用户凭证的异步函数。
export async function handleRegister(email, username, password, navigate) {
//...data
}
将handleRegister
函数导入Signup
组件并传入所需参数。
import { handleRegister } from "../utils/resource";
import { useNavigate } from "react-router-dom";
const navigate = useNavigate();
const handleSubmit = (e) => {
e.preventDefault();
if (username.trim() && password.trim() && email.trim()) {
handleRegister(email, username, password, navigate);
setPassword("");
setUsername("");
setEmail("");
}
};
更新handleRegister
函数以向服务器发出 POST 请求。
export async function handleRegister(email, username, password, navigate) {
try {
const request = await fetch("http://localhost:4000/register", {
method: "POST",
body: JSON.stringify({
email,
username,
password,
}),
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
});
const data = await request.json();
if (data.error_message) {
toast.error(data.error_message);
} else {
toast.success(data.message);
navigate("/");
}
} catch (err) {
console.error(err);
toast.error("Account creation failed");
}
}
接受用户的凭证并在服务器上创建一个帐户。
//👇🏻 array representing the data
const database = [];
//👇🏻 generates a random string as ID
const generateID = () => Math.random().toString(36).substring(2, 10);
app.post("/register", (req, res) => {
const { username, email, password } = req.body;
//👇🏻 checks if the user does not exist
let result = database.filter(
(user) => user.email === email || user.username === username
);
//👇🏻 creates the user's data structure on the server
if (result.length === 0) {
database.push({
id: generateID(),
username,
password,
email,
timezone: {},
schedule: [],
});
return res.json({ message: "Account created successfully!" });
}
//👇🏻 returns an error
res.json({ error_message: "User already exists!" });
});
将用户登录到应用程序
在服务器上添加一个登录 POST 路由,接受来自 React 应用程序的用户名和密码。
app.post("/login", (req, res) => {
const { username, password } = req.body;
console.log(req.body);
});
创建一个异步函数,接受文件中用户的用户名和密码utils/resource.js
。
export async function handleLogin(username, password, navigate) {
//...data
}
将该handleLogin
函数导入到 Login 组件中,如下所示:
import { handleLogin } from "../utils/resource";
import { useNavigate } from "react-router-dom";
const navigate = useNavigate();
//👇🏻 The Login button function
const handleSubmit = (e) => {
if (username.trim() && password.trim()) {
e.preventDefault();
//👇🏻 accepts the user's password and email
handleLogin(username, password, navigate);
setPassword("");
setUsername("");
}
};
更新handleLogin
函数以向服务器发出 POST 请求。
export async function handleLogin(username, password, navigate) {
try {
const request = await fetch("http://localhost:4000/login", {
method: "POST",
body: JSON.stringify({
username,
password,
}),
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
});
const data = await request.json();
if (data.error_message) {
toast.error(data.error_message);
} else {
//👇🏻If login successful
toast.success(data.message);
//👇🏻 saves the email and id for identification
localStorage.setItem("_id", data.data._id);
localStorage.setItem("_myEmail", data.data._email);
navigate("/dashboard");
}
} catch (err) {
console.error(err);
}
}
接受并验证服务器上的用户凭据。
app.post("/login", (req, res) => {
const { username, password } = req.body;
let result = database.filter(
(user) => user.username === username && user.password === password
);
//👇🏻 user doesn't exist
if (result.length !== 1) {
return res.json({
error_message: "Incorrect credentials",
});
}
//👇🏻 user exists
res.json({
message: "Login successfully",
data: {
_id: result[0].id,
_email: result[0].email,
},
});
});
由于我们将发出需要在服务器上进行身份验证的请求,因此将下面的代码片段添加到Dashboard
和Profile
组件中。
useEffect(() => {
if (!localStorage.getItem("_id")) {
navigate("/");
}
}, [navigate]);
创建时间表
在本节中,我将引导您完成创建计划并将其显示给用户的过程。
在服务器上添加一个 POST 路由,允许用户创建新的计划。
app.post("/schedule/create", (req, res) => {
const { userId, timezone, schedule } = req.body;
console.log(req.body);
});
在文件中创建一个接受用户时区和时间表的handleCreateSchedule
函数。utils/resource.js
export async function handleCreateSchedule(
selectedTimezone,
schedule,
navigate
) {
//..other data
}
handleCreateSchedule
在仪表板组件中导入该功能。
import { handleCreateSchedule } from "../utils/resource";
const handleSaveSchedules = () => {
//👇🏻 ensures the user's timezone has been selected
if (JSON.stringify(selectedTimezone) !== "{}") {
handleCreateSchedule(selectedTimezone, schedule, navigate);
} else {
toast.error("Select your timezone");
}
};
更新handleCreateSchedule
函数以发出包含时间表和时区的 POST 请求。
export async function handleCreateSchedule(
selectedTimezone,
schedule,
navigate
) {
try {
await fetch("http://localhost:4000/schedule/create", {
method: "POST",
body: JSON.stringify({
userId: localStorage.getItem("_id"),
timezone: selectedTimezone,
schedule,
}),
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
});
//👇🏻 navigates to the profile page
navigate(`/profile/${localStorage.getItem("_id")}`);
} catch (err) {
console.error(err);
}
}
更新服务器上的 POST 路由以接受来自 React 应用程序的数据并为用户创建新的时间表。
app.post("/schedule/create", (req, res) => {
const { userId, timezone, schedule } = req.body;
//👇🏻 filters the database via the id
let result = database.filter((db) => db.id === userId);
//👇🏻 updates the user's schedule and timezone
result[0].timezone = timezone;
result[0].schedule = schedule;
res.json({ message: "OK" });
});
恭喜!🎉 我们已经能够更新用户的日程安排和时区。
显示时间表
在这里,我将引导您从服务器获取用户的日程安排。
在服务器上添加一个 GET 路由,从数据库数组中检索用户的数据。
app.get("/schedules/:id", (req, res) => {
const { id } = req.params;
//👇🏻 filters the array via the ID
let result = database.filter((db) => db.id === id);
//👇🏻 returns the schedule, time and username
if (result.length === 1) {
return res.json({
message: "Schedules successfully retrieved!",
schedules: result[0].schedule,
username: result[0].username,
timezone: result[0].timezone,
});
}
//👇🏻 if user not found
return res.json({ error_message: "Sign in again, an error occured..." });
});
在文件中创建一个函数Profile.js
,当页面加载时向 GET 路由发送请求。
const [schedules, setSchedules] = useState([]);
const [loading, setLoading] = useState(true);
const [username, setUsername] = useState("");
const [timezone, setTimezone] = useState("");
useEffect(() => {
function getUserDetails() {
if (id) {
fetch(`http://localhost:4000/schedules/${id}`)
.then((res) => res.json())
.then((data) => {
setUsername(data.username);
setSchedules(data.schedules);
setTimezone(data.timezone.label);
setLoading(false);
})
.catch((err) => console.error(err));
}
}
getUserDetails();
}, [id]);
并显示数据如下图所示:
return (
<main className='profile'>
{loading ? (
<p>Loading...</p>
) : (
<div>
<h2>Hey, {username}</h2>
<p>Here is your schedule: - {timezone}</p>
<table>
<tbody>
{schedules.map((sch) => (
<tr key={sch.day}>
<td style={{ fontWeight: "bold" }}>{sch.day.toUpperCase()}</td>
<td>{sch.startTime || "Unavailable"}</td>
<td>{sch.endTime || "Unavailable"}</td>
</tr>
))}
</tbody>
</table>
</div>
)}
</main>
);
使用 EmailJS 预约
在本节中,您将了解当客户与用户预约时如何通过 EmailJS 发送电子邮件通知。
EmailJS 是一个 JavaScript 库,它使我们能够仅通过客户端技术发送电子邮件,而无需服务器。使用 EmailJS,您可以发送文本和电子邮件模板,并在电子邮件中添加附件。
在服务器上创建一个用于获取用户数据的 POST 路由。
app.post("/schedules/:username", (req, res) => {
const { username } = req.body;
//👇🏻 filter the databse via the username
let result = database.filter((db) => db.username === username);
if (result.length === 1) {
const scheduleArray = result[0].schedule;
//👇🏻 return only the selected schedules
const filteredArray = scheduleArray.filter((sch) => sch.startTime !== "");
//return the schedules and other information
return res.json({
message: "Schedules successfully retrieved!",
schedules: filteredArray,
timezone: result[0].timezone,
receiverEmail: result[0].email,
});
}
return res.json({ error_message: "User doesn't exist" });
});
在文件中添加一个fetchBookingDetails
函数resource.js
。
export function fetchBookingDetails(
user,
setError,
setTimezone,
setSchedules,
setReceiverEmail
) {
//...data
}
将函数导入BookUser.js
组件并在页面加载时使用其必要的参数调用它。
const [schedules, setSchedules] = useState([]);
const [timezone, setTimezone] = useState("");
const [error, setError] = useState(false);
const [receiverEmail, setReceiverEmail] = useState("");
useEffect(() => {
fetchBookingDetails(
user,
setError,
setTimezone,
setSchedules,
setReceiverEmail
);
}, [user]);
if (error) {
return <ErrorPage error="User doesn't exist" />;
}
更新fetchBookingDetails
函数以从服务器检索信息并更新状态参数。
export function fetchBookingDetails(
user,
setError,
setTimezone,
setSchedules,
setReceiverEmail
) {
fetch(`http://localhost:4000/schedules/${user}`, {
method: "POST",
body: JSON.stringify({
username: user,
}),
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
})
.then((res) => res.json())
.then((data) => {
if (data.error_message) {
toast.error(data.error_message);
setError(true);
} else {
setTimezone(data.timezone.label);
setSchedules(data.schedules);
setReceiverEmail(data.receiverEmail);
}
})
.catch((err) => console.error(err));
}
在表单中呈现时间表,以便用户选择他们喜欢的预约时间。
<select name='duration' onChange={(e) => setDuration(e.target.value)}>
{schedules.map((schedule) => (
<option
value={`${schedule.day} - ${schedule.startTime} : ${schedule.endTime}`}
key={schedule.day}
>{`${schedule.day} - ${schedule.startTime} : ${schedule.endTime}`}</option>
))}
</select>
使用 EmailJS 发送电子邮件通知
在这里,我将指导您将 EmailJS 添加到 React.js 应用程序,以及如何在有人与用户预约时向用户发送电子邮件。
通过运行以下代码将 EmailJS 安装到 React 应用程序:
npm install @emailjs/browser
在此创建一个 EmailJS 帐户 并将电子邮件服务提供商添加到您的帐户。
添加电子邮件模板,如下图所示:
花括号中的单词代表可以保存动态数据的变量。
将 EmailJS 导入utils/resource.js
文件并创建向用户发送电子邮件通知的函数。
import emailjs from "@emailjs/browser";
export const sendEmail = (
receiverEmail,
email,
fullName,
message,
duration
) => {
emailjs
.send(
"YOUR_SERVICE_ID",
"YOUR_TEMPLATE_ID",
{
to_email: receiverEmail,
from_email: email,
fullName,
message,
duration,
},
"YOUR_PUBLIC_KEY"
)
.then(
(result) => {
console.log(result.text);
toast.success("Session booked successfully!");
},
(error) => {
console.log(error.text);
toast.error(error.text);
}
);
};
您可以从 EmailJS 仪表板的帐户部分获取 EmailJS 公钥。
将该功能添加到组件sendEmail
中,BookUser
以便在每次提交表单时向用户发送包含预订信息的电子邮件。
const handleSubmit = (e) => {
e.preventDefault();
sendEmail(receiverEmail, email, fullName, message, duration);
setFullName("");
setMessage("");
};
恭喜!🎉您已完成本教程的项目。
结论
到目前为止,您已经学习了如何创建一个日程安排应用程序,使用户能够设置他们的可用性并在有预约时通过 EmailJS 收到通知。
本教程将引导您完成一个使用 React 和 Node.js 构建的迷你项目。您可以通过添加身份验证库并将数据存储在数据库中来改进该应用程序。
本教程的源代码可以在这里找到:
https://github.com/novuhq/blog/tree/main/scheduling-app-with-react-nodejs-emailjs
感谢您的阅读!
帮帮我!
如果您觉得这篇文章帮助您更好地理解了 WebSocket!请给我们一个 Star,我会非常高兴!也请在评论区告诉我❤️
https://github.com/novuhq/novu
