Введение

Next.js — это популярный фреймворк для разработки интерфейса, который позволяет быстро разрабатывать приложения, расширяя библиотеку React. Кроме того, известно, что приложение Next.js отличается высокой производительностью благодаря различным методам рендеринга, таким как предварительный рендеринг, рендеринг на стороне сервера и рендеринг на стороне клиента.

В этой статье вы создадите веб-приложение, призывающее добровольцев присоединиться к совместной уборке красивых достопримечательностей Вьетнама. Вы также создаете службу уведомлений для отправки электронных писем и SMS-сообщений волонтерам, когда они успешно регистрируются в программе, и уведомляете их за день до дня уборки. Чтобы создать службу уведомлений, вы будете использовать Sendgrid API для отправки электронных писем и Twilio SMS API для отправки SMS-сообщений.

Перейти к заявке

Приложение представляет собой веб-приложение, в котором перечислены все вьетнамские места, которые необходимо очистить из-за недавнего загрязнения.

Добровольцы могут пройти по списку мест.

Затем они могут зарегистрироваться, чтобы присоединиться к уборке, если захотят. Они даже могут зарегистрироваться в нескольких местах, если смогут запланировать свое время.

После того, как волонтеры зарегистрируются на месте, они получат уведомление по электронной почте о том, что они успешно зарегистрировались в качестве волонтеров для уборки места.

За день до даты уборки волонтеры получат уведомления от организаторов по электронной почте и SMS, чтобы быть предупрежденными о приближающейся дате уборки.

Вы создадите приложение Next.js и службу уведомлений для отправки электронных писем и SMS-сообщений волонтерам. Приложение Next.js создает пользовательский интерфейс для веб-приложения и веб-API, чтобы помочь добровольцам взаимодействовать с приложением. Служба уведомлений на самом деле представляет собой задание cron, которое запускается каждый день в 20:00 для отправки уведомлений добровольцам с помощью API уведомлений.

Предпосылки

Для реализации приложения необходимо наличие следующих предпосылок:

  • Иметь компьютер с Linux для разработки приложения, желательно Ubuntu версии 22.04, так как мы тестировали на нем демонстрационное приложение.
  • Установите Git для клонирования кода с GitHub
  • Иметь учетную запись Sendgrid для отправки электронных писем волонтерам.
  • Иметь учетную запись Twilio для отправки SMS-сообщений волонтерам.
  • Имейте Node.js версии 14.6.0 выше для создания приложения Next.js, предпочтительно используя Node.js версии 16.8.1, так как мы тестировали эту версию.

Обзор шагов для создания приложения

Чтобы создать веб-приложение для регистрации волонтеров на уборку мест и оповещения их о предстоящей дате уборки, необходимо выполнить следующие шаги:

  1. Создайте учетную запись Sendgrid
    Вам необходимо настроить учетную запись Sendgrid, чтобы вы могли отправлять электронные письма волонтерам.
  2. Создайте учетную запись Twilio
    Чтобы отправлять SMS-сообщения волонтерам, вам необходимо иметь учетную запись Twilio.
  3. Клонируйте код приложения Next.js
    Существует довольно много файлов для реализации приложения Next.js. Вы должны клонировать код из существующего репозитория GitHub, чтобы запустить приложение Next.js.
  4. Изучите структуру приложения Next.js
    Вы узнаете об общей структуре приложения Next.js и роли каждого файла в проекте кода.
  5. Запустите приложение
    Наконец-то вы сможете запустить приложение Next.js
  6. Взаимодействуйте с приложением
    Вы попытаетесь зарегистрироваться и посмотреть, что произойдет, если вы введете повторяющийся адрес электронной почты. Вы также проверяете, получили ли вы уведомление по электронной почте после регистрации на месте.
  7. Клонирование кода для приложения Next.js
    Вы клонируете код из существующего репозитория GitHub для службы уведомлений.
  8. Запуск службы уведомлений
    Вы запускаете код службы уведомлений. После получения уведомления вы можете изменить дату уборки зарегистрированного места, чтобы узнать, получили ли вы уведомление по электронной почте и SMS.

Шаг 1: Создайте учетную запись SendGrid

Перейдите на страницу регистрации SendGrid, чтобы создать новую учетную запись SendGrid. После успешного создания учетной записи SendGrid вам необходимо связаться со службой поддержки, чтобы включить функциональность SendGrid, чтобы вы могли отправлять электронные письма волонтеру через приложение Next.js.

Теперь, когда у вас есть учетная запись SendGrid, готовая к отправке электронных писем, вам нужно создать токен SendGrid для аутентификации API SendGrid внутри вашего приложения. Для этого войдите в свою учетную запись SendGrid и перейдите на страницу Node.js интеграции SendGrid.

Дайте имя вашему ключу API SendGrid в поле «Имя моего первого ключа API» и нажмите «Создать ключ», чтобы сгенерировать значение ключа. Затем сохраните значение этого ключа в безопасном месте. Вы будете использовать этот ключ на шаге 5 этой статьи.

Шаг 2. Создайте учетную запись Twilio

Перейдите на Страницу регистрации Twilio, чтобы создать учетную запись Twilio.

После создания пробной учетной записи Twilio вы получили 15 $, чтобы опробовать функции, предоставляемые Twilio. Вам необходимо создать номер телефона Twilio, чтобы отправлять SMS-сообщения волонтерам. Чтобы создать номер телефона Twilio, обратитесь к данному руководству. Обратите внимание, что с пробной учетной записью Twilio вы можете отправлять сообщения только на подтвержденный Twilio номер телефона.

Наконец, вам нужно получить SID учетной записи и Токен аутентификации на странице проекта Twilio, чтобы пройти аутентификацию для Twilio SMS API в приложении Next.js.

Сохраните значения «SID учетной записи» и «Токен аутентификации» в безопасном месте. Вы будете использовать эти значения в шаге 5 этой статьи.

Шаг 3. Клонируйте код приложения Next.js.

Чтобы узнать, как работает волонтерское веб-приложение с использованием фреймворка Next.js, вам необходимо клонировать существующий код из этого репозитория GitHub. Для этого, открыв консоль терминала, выполните следующую команду:

mkdir ~/projects
cd ~/projects
git clone https://github.com/cuongld2/twilio-earthly-app.git
cd twilio-earthly-app

Теперь, когда вы стоите на проекте приложения Next.js, давайте перейдем к следующему шагу, чтобы увидеть структуру приложения Next.js и то, как работает каждый из файлов.

Шаг 4. Просмотрите структуру приложения Next.js

Типичное приложение Next.js включает следующие компоненты:

1.Файл конфигурации Next.js
Файл конфигурации Next.js, часто называемый файлом `next.config.js`, находится в корневом каталоге приложения Next.js. В файле конфигурации Next.js вы можете установить переменные среды или сопоставить путь входящего запроса с другим путем назначения, используя функцию rewrites для приложения Next.js. Ознакомьтесь с этим руководством, чтобы узнать больше о том, как работает файл конфигурации Next.js.

2.Pages
В среде Next.js вы определяете все страницы своего приложения в каталоге `pages` вашего проекта. Страницы могут иметь статический или динамический контент, загружаемый из веб-API. Например, на странице О нас со статическим содержимым вы можете написать код для файла about-us.js, как показано ниже:

const AboutUsPage = () => {

return (
<div>
<h1> About us Page</h1>
<p>
We are a group of volunteers who would like to help Vietnam to gain back the beautiful places so that citizens in Vietnam can enjoy living in cities and attract tourists to come visit Vietnam in the future.
</p>
<h2>Contact</h2>
<p>
For more information about our group, or if you would like to join us, please send an email to [email protected].
</p>
</div>
);
};
export default AboutUsPage;

Вы также можете создавать веб-API с использованием маршрутов API Next.js, создав каталог pages/api.
Например, чтобы создать API hello-world с путем /api/hello-world, вы создаете файл с именем hello-world.js в каталоге pages/api. Файл содержит пример кода:

//hello-world.js
export default function handler(req, res) {
res.status(200).json({ text: 'Hello World' });
}

3. Файлы компонентов
Файлы компонентов будут реализовывать базовую компоновку страниц в приложении Next.js. Вы определяете файлы компонентов в каталоге `src/components`.

Например, вы определяете основной макет каждой страницы в приложении Next.js с помощью `src/components/layout/main-layout.js` как:

import React from 'react';
import { Footer } from '../footer/footer';
import { Header } from '../header/header';
const MainLayout = ({ children }) => {
return (
<>
<Header />
<main>{children}</main>
<Footer />
</>
);
};
export default MainLayout;

4. Общедоступные файлы
Общедоступные файлы обычно представляют собой изображения и логотип вашего приложения. Вы храните эти файлы в каталоге public проекта.

5. Файлы стилей
Файлы стилей используются для определения цветов и других параметров CSS для приложения Next.js. Файлы стилей лежат в каталоге styles проекта.

Чтобы увидеть структуру приложения волонтера, из текущего открытого терминала запустите команду `tree`:

tree

Вы должны увидеть текущую структуру проекта, как показано ниже:

.
├── data
│ └── data.json
├── next.config.js
├── package.json
├── package-lock.json
├── pages
│ ├── about-us.js
│ ├── api
│ │ ├── registration.js
│ │ └── notification.js
│ ├── _app.js
│ ├── index.js
│ └── places
│ ├── [detail]
│ │ ├── [id].js
│ │ └── index.js
│ └── index.js
├── public
│ ├── favicon.ico
│ ├── images
│ │ └── logo_black.png
│ └── vercel.svg
├── src
│ └── components
│ ├── footer
│ │ └── footer.jsx
│ ├── header
│ │ └── header.jsx
│ ├── home
│ │ └── home-page.jsx
│ ├── layout
│ │ └── main-layout.jsx
│ └── places
│ ├── detailPlace.jsx
│ ├── places-page.jsx
│ └── single-place.jsx
├── styles
│ ├── general.sass
│ └── globals.css
└── utils
├── email.js
└── phone.js

Давайте узнаем об обязанностях этих файлов.

Файлы конфигурации

Для приложения Next.js есть два файла конфигурации:

  • Общий файл package.json для проекта Node.js
  • Файл next.config.js, который является стандартным файлом конфигурации для платформы Next.js.

файл package.json

Чтобы просмотреть содержимое файла package.json, выполните следующую команду:

cat package.json

Вы должны увидеть следующий вывод:

{
"name": "polluted_sightseeing_cleaning_volunteer",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start -p 3001",
"lint": "next lint"
},
"dependencies": {
"@sendgrid/mail": "⁷.7.0",
"next": "12.3.1",
"react": "18.2.0",
"react-dom": "18.2.0",
"twilio": "⁴.8.0"
},
"devDependencies": {
"eslint": "8.26.0",
"eslint-config-next": "12.3.1",
"sass": "¹.55.0"
}
}

Блок кода dependencies показывает необходимые зависимости, которые необходимо запустить приложению Next.js.

  • @sendgrid/mail зависимость предназначена для отправки уведомлений по электронной почте волонтерам.
  • next зависимость предназначена для предоставления функций, которые помогут вам быстро создать приложение Next.js.
  • react зависимость предназначена для определения компонентов React, поскольку среда Next.js использует React внутри.
  • react-dom зависимость для работы с объектной моделью документа React (React DOM)
  • twilio зависимость для работы с Twilio API, чтобы вы могли отправлять SMS-сообщения волонтерам

файл next.config.js

Чтобы просмотреть содержимое файла `next.config.js`, выполните следующую команду:

cat next.config.js

Вы должны увидеть следующий вывод:

/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
swcMinify: true,
images: {
domains: ['images.unsplash.com'],
},
};
module.exports = nextConfig;

Текущее значение reactStrictMode равно true, что говорит Next.js выделить потенциальные проблемы в приложении. Значение swcMinify равно true, что говорит Next.js применить SWC — компрессор на основе Rust, который повышает производительность компиляции и сборки приложения Next.js. Домен изображений приложения — images.unsplash.com, так как вы будете использовать изображение Unsplash в своем приложении для изображений вьетнамских мест.

Файл базы данных

Ваше приложение использует файл data/data.json в качестве файла базы данных. Структура файла data/data.json выглядит так:

{
"places_categories": [
{
"id": "hanoi",
"title": "Places in Hanoi",
"description": "Several places in hanoi were heavily impacted by pollution and needs a help from you as a volunteer.",
"image": "https://images.unsplash.com/photo-1616486410185-81af2d32a2af?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1972&q=80"
},
…
],
"allPlaces": [
{
"id": "hoan-kiem-lake",
"title": "Hoan Kiem Lake",
"city": "hanoi",
"description": "Hoan Kiem Lake now is polutted with trash after the holiday.",
"cleaningDate": "2024–03–11",
"image": "https://images.unsplash.com/photo-1616486410185-81af2d32a2af?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=686&q=80",
"emails_registered": [
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]"
],
"phoneNumbers_registered": []
},
…
]
}

Общая информация по всем городам хранится в поле places_categories. Подробная информация о каждом месте в каждом городе хранится в поле `allPlaces`.

Страницы

Каталог pages содержит следующие каталоги и файлы:

  • api каталог
  • places каталог
  • _app.js файл
  • about-us.js файл
  • index.js файл

каталог API

Этот каталог содержит два файла:

  • registration.js файл
    Этот файл предназначен для определения API регистрации по электронной почте, позволяя добровольцам регистрироваться, чтобы присоединиться к уборке места. API будет иметь путь /api/registration с методом `POST`. В теле запроса API должно быть email, phoneNumber добровольцев, а также placeId места, на которое они хотят зарегистрироваться. Логика API заключается в функции «обработчик».
export default function handler(req, res) {

const { method } = req;
const filePath = buildPath();
const { places_categories, allPlaces } = extractData(filePath);
if (!allPlaces) {
return res.status(404).json({
status: 404,
message: 'Places data not found',
});
}
if (method === 'POST') {
const { email, phoneNumber, placeId } = req.body;
if (!email | !email.includes('@')) {
res.status(422).json({ message: 'Invalid email address' });
}
const newAllPlaces = allPlaces.map((ev) => {
if (ev.id === placeId) {
if (ev.emails_registered.includes(email)) {
res.status(409).json({ message: `This email ${email} has already been registered` });
return ev;
}
if (ev.phoneNumbers_registered.includes(phoneNumber)) {
res.status(409).json({ message: `This phoneNumber ${phoneNumber} has already been registered` });
return ev;
}
sendEmail(email, '<strong>Thanks for your help to clean Vietnamese places together</strong>',`Successfully sign up to be volunteer for ${placeId}`)
return {
…ev,
emails_registered: […ev.emails_registered, email],
phoneNumbers_registered: […ev.phoneNumbers_registered, phoneNumber],
};
}
return ev;
});
fs.writeFileSync(filePath, JSON.stringify({ places_categories, allPlaces: newAllPlaces }));
res.status(201).json({
message: `You have been registered successfully with the email: ${email} and the phone number: ${phoneNumber} for the place: ${placeId}`,
});
}
}

Когда волонтеры успешно зарегистрируются, приложение отправит им электронные письма, подтверждающие, что они успешно зарегистрировались.

sendEmail(email, '<strong>Thanks for your help to clean Vietnamese places together</strong>',`Successfully sign up to be volunteer for ${placeId}`)
  • notification.js файл
    Этот файл предназначен для определения API уведомлений, который будет отправлять SMS-сообщения и электронные письма зарегистрированным волонтерам, чтобы напомнить им, что дата уборки состоится завтра. URL-адрес API — api/notification, а метод — POST. Реализация логики заключается в функции handler.
export default function handler(req, res) {
const { method } = req;
const filePath = buildPath();
const { allPlaces } = extractData(filePath);
if (!allPlaces) {
return res.status(404).json({
status: 404,
message: 'Places data not found',
});
}
if (method === 'POST') {
allPlaces.map((ev) => {
if(ev.cleaningDate===getTodayDateAfterOneDay()){
ev.emails_registered.forEach((email) => {
sendEmail(email, '<strong>Cleaning date will occur tomorrow. Please be well prepared. Thanks for your support to clean the environment in Vietnam</strong>',`Cleaning date will occur tomorrow for ${ev.title}`)
})
ev.phoneNumbers_registered.forEach((phone)=>{
sendMessage(phone,'The cleaning date will happen tomorrow. Please well prepared')
})
}
});
res.status(200).json({
message: `Successfully sent notification`,
});
}
}

Приложение просмотрит все места в базе данных и проверит, есть ли место, где cleaningDate будет завтра. Затем приложение отправит волонтеру уведомление по электронной почте с помощью функции sendEmail:

ev.emails_registered.forEach((email) => {
sendEmail(email, '<strong>Cleaning date will occur tomorrow. Please be well prepared. Thanks for your support to clean the environment in Vietnam</strong>',`Cleaning date will occur on tomorrow for ${ev.title}`)
})

И отправьте SMS-сообщения волонтерам с помощью функции `sendMessage`:

ev.phoneNumbers_registered.forEach((phone)=>{
sendMessage(phone,'The cleaning date will happen tomorrow. Please well prepared')
})

каталог мест

Этот каталог реализует динамические страницы для страницы города и страницы мест.

  • `места/index.js`

places/index.js реализует страницу, на которой перечислены все города Вьетнама, нуждающиеся в уборке.

import AllPlaces from '../../src/components/places/places-page';
const PlacesPage = ({ data }) => {
return <AllPlaces data={data} />;
};
export default PlacesPage;
export async function getStaticProps() {
const { places_categories } = await import('/data/data.json');
return {
props: {
data: places_categories,
},
};
}

Приложение Next.js извлечет информацию о городах из файла data/data.json, используя поле places_categories.

  • places/details/index.js файл

Этот файл реализует логику отображения всех мест в конкретном городе.

import DetailPlace from '../../../src/components/places/detailPlace';
const PlacesDetailPage = ({ data, pageName }) => <DetailPlace data={data} pageName={pageName} />;
export default PlacesDetailPage;
export async function getStaticPaths() {
const { places_categories } = await import('/data/data.json');
const allPaths = places_categories.map((ev) => {
return {
params: {
detail: ev.id.toString(),
},
};
});
return {
paths: allPaths,
fallback: false,
};
}
export async function getStaticProps(context) {
const id = context?.params.detail;
const { allPlaces } = await import('/data/data.json');
const data = allPlaces.filter((ev) => ev.city === id);
return { props: { data, pageName: id } };
}

Приложение Next.js извлечет информацию о местах определенного города из файла data/data.json, используя поле allPlaces.

  • places/details/[id].js файл

Этот файл предназначен для определения страницы регистрации для определенного места, в котором добровольцы хотели бы зарегистрироваться. Путь маршрута страницы регистрации является динамическим и основан на идентификаторе места.

import SinglePlace from '../../../src/components/places/single-place';
const PlacePage = ({ data }) => <SinglePlace data={data} />;
export default PlacePage;
export async function getStaticPaths() {
const data = await import('/data/data.json');
const allPlaces = data.allPlaces;
const allPaths = allPlaces.map((path) => {
return {
params: {
detail: path.city,
id: path.id,
},
};
});
return {
paths: allPaths,
fallback: false,
};
}
export async function getStaticProps(context) {
const id = context.params.id;
const { allPlaces } = await import('/data/data.json');
const placeData = allPlaces.find((ev) => id === ev.id);
return {
props: { data: placeData },
};
}

Next.js будет получать информацию о каждом месте, используя поле allPlaces внутри файла data/data.json.

файл _app.js

В этом файле вы реализуете общий макет каждой страницы приложения.

import MainLayout from '../src/components/layout/main-layout';
import '../styles/globals.css';
import '../styles/general.sass';
function MyApp({ Component, pageProps }) {
return (
<>
<MainLayout>
<Component {…pageProps} />
</MainLayout>
</>
);
}
export default MyApp;

Макет каждой страницы имеет заголовок, содержимое страницы и нижний колонтитул.

файл about-us.js

Этот файл определяет статическое содержимое страницы о нас.

const AboutUsPage = () => {
return (
<div>
<h1> About us Page</h1>
<p>
We are a group of volunteers who would like to help Vietnam to gain back the beautiful places so that citizens in Vietnam can enjoy living in cities and attract tourists to come visit Vietnam in the future.
</p>
<h2>Contact</h2>
<p>
For more information about our group, or if you would like to join us, please send an email to [email protected].
</p>
</div>
);
};
export default AboutUsPage;

файл index.js

Этот файл определяет макет домашней страницы.

import Head from 'next/head';
import { HomePage } from '../src/components/home/home-page';
export default function Home({ data }) {
return (
<div>
<Head>
<title>Lets clean Vietnam together</title>
<meta name="description" content="Created by Donald Le" />
<link rel="icon" href="/favicon.ico" />
</Head>
<HomePage data={data} />
</div>
);
}
export async function getServerSideProps() {
const { places_categories } = await import('/data/data.json');
return {
props: {
data: places_categories,
},
};
}

Файл будет отображать информацию обо всех трех городах: «Ханой», «Хошимин», «Дананг» с использованием поля «places_categories» из файла данных data/data.json.

Файлы компонентов

Каталог src/components предназначен для определения логики отображения данных о каждом компоненте приложения, включая:

  • Файл `footer.jsx`: определяет нижний колонтитул приложения.
export const Footer = () => {
return (
<footer>
<p> © 2023 Lets clean Vietnam cities together</p>
</footer>
);
};
  • Файл `header.jsx`: определяет заголовок приложения.
import Link from 'next/link';
import Image from 'next/image';
export const Header = () => {
return (
<header>
<div>
<div className="topNav">
<Image alt="logo" src={'/images/logo_black.png'} width={50} height={50} />
<nav>
<ul>
<li>
<Link href="/" passHref>
<a> Home</a>
</Link>
</li>
<li>
<Link href="/places" passHref>
<a> Places</a>
</Link>
</li>
<li>
<Link href="/about-us" passHref>
<a> About us</a>
</Link>
</li>
</ul>
</nav>
</div>
<p className="title">Lets clean Vietnam together</p>
</div>
</header>
);
};
  • Файл `home-page.js`: определяет отображаемую логику домашней страницы.
import Link from 'next/link';
import Image from 'next/image';
export const HomePage = ({ data }) => (
<div className="home_body">
{data?.map((ev) => (
<Link key={ev.id} href={`/places/${ev.id}`} passHref>
<a className="card" href={`/places/${ev.id}`}>
<div className="image">
<Image width={600} height={400} alt={ev.title} src={ev.image} />
</div>
<div className="content">
<h2> {ev.title} </h2>
<p> {ev.description} </p>
</div>
</a>
</Link>
))}
</div>
);
  • Файл `layout/main-layout.js`: определяет отображаемую логику макета на каждой странице.
import React from 'react';
import { Footer } from '../footer/footer';
import { Header } from '../header/header';
const MainLayout = ({ children }) => {
return (
<>
<Header />
<main>{children}</main>
<Footer />
</>
);
};
export default MainLayout;
  • Файл `places-page.jsx`: реализовать логику отображения для страницы «Все места».
import React from 'react';
import Image from 'next/image';
import Link from 'next/link';
const AllPlaces = ({ data }) => {
return (
<div className="places_page">
{data?.map((ev) => (
<Link key={ev.id} href={`/places/${ev.id}`} passHref>
<a className="card">
<Image src={ev.image} alt={ev.title} width={500} height={500} /> <h2>{ev.title} </h2>
</a>
</Link>
))}
</div>
);
};
export default AllPlaces;

detailPlace.jsx файл: отображает страницу, на которой показаны все «места», принадлежащие каждому городу.

import React from 'react';
import Image from 'next/image';
import Link from 'next/link';
const DetailPlace = ({ data, pageName }) => {
return (
<div className="detail_places">
<h1> Places in {pageName} </h1>
<div className="content">
{data.map((ev) => (
<Link key={ev.id} href={`/places/${ev.city}/${ev.id}`} passHref>
<a className="card">
<Image width={300} height={300} alt={ev.title} src={ev.image} />
<h2> {ev.title} </h2>
<p> {ev.description} </p>
</a>
</Link>
))}
</div>
</div>
);
};
export default DetailPlace;
  • Файл single-place.jsx: отображает страницу регистрации для каждого места.
import Image from 'next/image';
import { useRouter } from 'next/router';
import React, { useRef, useState } from 'react';
const SinglePlace = ({ data }) => {
const inputEmail = useRef();
const inputPhoneNumber = useRef();
const router = useRouter();
const [message, setMessage] = useState('');
const onSubmit = async (e) => {
e.preventDefault();
const emailValue = inputEmail.current.value;
const phoneNumberValue = inputPhoneNumber.current.value;
const placeId = router?.query.id;
const validRegex = /^[a-zA-Z0–9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0–9-]+(?:\.[a-zA-Z0–9-]+)*$/;
if (!emailValue.match(validRegex)) {
setMessage('Please introduce a correct email address');
}
try {
const response = await fetch('/api/email-registration', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ email: emailValue, phoneNumber:phoneNumberValue, placeId }),
});
const data = await response.json();
setMessage(data.message);
inputEmail.current.value = '';
} catch (e) {
console.log('ERROR', e);
}
inputPhoneNumber.current.value="";
inputEmail.current.value="";
};
return (
<div className="place_single_page">
<h1> {data.title} </h1>
<Image src={data.image} width={1000} height={500} alt={data.title} />
<p> {data.description} </p>
<p> Cleaning Date : {data.cleaningDate} </p>
<form onSubmit={onSubmit} className="email_registration">
<label> Get Registered for this place!</label>
<input
ref={inputEmail}
type="email"
id="email"
placeholder="Please insert your email here"
/>
<br />
<br />
<input
ref={inputPhoneNumber}
type="phoneNumber"
id="phoneNumber"
placeholder="Please insert your phone number here"
/>
<br />
<br />
<button type="submit">Submit</button>
</form>
<p>{message}</p>
</div>
);
};
export default SinglePlace;

Когда волонтеры нажимают кнопку «Отправить», приложение Next.js вызывает API регистрации, чтобы добавить адреса электронной почты и номера телефонов волонтеров в базу данных. Затем приложение также отправит волонтеру электронное письмо с подтверждением успешной регистрации.

Общедоступные файлы

Все общедоступные файлы, такие как логотип приложения, фавикон, хранятся в каталоге public.

Файлы стилей

Все файлы стилей для определения цвета текста, цвета фона, общей конфигурации CSS хранятся в каталоге styles.

Файлы утилит

Ваше приложение волонтера имеет два служебных файла:

  • utils/email.js: определяет функцию `sendEmail` для отправки уведомлений по электронной почте волонтерам.
import sgMail from '@sendgrid/mail';
export function sendEmail(recipientEmail, htmlContent, mailSubject){
sgMail.setApiKey(process.env.SENDGRID_API_KEY)
const msg = {
to: recipientEmail, // Change to your recipient
from: process.env.SENDGRID_EMAIL, // Change to your verified sender
subject: mailSubject,
html: htmlContent,
}
sgMail
.send(msg)
.then(() => {
console.log('Email sent')
})
.catch((error) => {
console.error(error)
})
}

Ключ API SendGrid и адрес электронной почты SendGrid вы будете передавать из переменных среды, используя ключи SENDGRID_API_KEY и SENDGRID_EMAIL.

  • utils/phone.js файл: определяет sendMessage функцию для отправки SMS-сообщений волонтерам.
export function sendMessage(recipientPhoneNumber,message){
const accountSid = process.env.TWILIO_ACCOUNT_SID;
const authToken = process.env.TWILIO_AUTH_TOKEN;
const client = require('twilio')(accountSid, authToken);
client.messages
.create({body: message, from: process.env.TWILIO_PHONE_NUMBER, to: recipientPhoneNumber})
.then(message => console.log(message.sid));
}

SID учетной записи, токен аутентификации вашей учетной записи Twilio и номер телефона Twilio, которые вы будете передавать из переменных среды, используя TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN и TWILIO-PHONE_NUMBER .

Шаг 5: Запустите приложение

Чтобы запустить приложение, откройте терминал и выполните следующую команду:

npm install
export SENDGRID_API_KEY=your_sendgrid_api_key
export SENDGRID_EMAIL=your_sendgrid_email
export TWILIO_ACCOUNT_SID=your_account_sid
export TWILIO_AUTH_TOKEN=your_auth_token
export TWILIO_PHONE_NUMBER=your_twilio_phone_number
npm run dev

Вы должны увидеть следующий вывод из журнала консоли (Next.js автоматически попытается запустить приложение на порту 3000, если порт 3000 используется, он будет использовать другой порт. В этом случае Next.js запускает приложение на порту 3000. порт 30001 вместо 3000):

> [email protected] dev
> next dev
warn - Port 3000 is in use, trying 3001 instead.
ready - started server on 0.0.0.0:3001, url: http://localhost:3001
event - compiled client and server successfully in 2.6s (247 modules)
wait - compiling / (client and server)…
event - compiled client and server successfully in 221 ms (256 modules)
wait - compiling /places…

Теперь, когда ваше приложение запущено и работает, вы можете попробовать взаимодействовать с приложением.

Шаг 6. Взаимодействие с приложением

Откройте браузер и перейдите на localhost:3001 (по умолчанию номер порта будет 3000). Вы должны увидеть домашнюю страницу приложения Next.js, как показано ниже:

Нажмите «Места» в параметрах меню, вы должны увидеть все места в Ханое, которые призывают добровольцев к очистке окружающей среды.

Нажмите на изображение «Озеро Хоанкьем», и вы перейдете на страницу регистрации для регистрации в качестве волонтера по очистке «Озера Хоанкьем».

Давайте попробуем ввести свой адрес электронной почты и номер телефона в поле электронной почты и поле номера телефона. Обратите внимание, что вы должны ввести подтвержденный Twilio номер телефона, чтобы иметь возможность получать SMS-сообщения, если вы используете пробную учетную запись Twilio. Затем вы нажимаете кнопку «ОТПРАВИТЬ».

Вы должны увидеть сообщение об успешном завершении.

Вы также должны увидеть подтверждение по электронной почте об успешной регистрации.

Если вы попытаетесь ввести тот же адрес электронной почты еще раз, вы должны получить сообщение об ошибке, как показано ниже:

Теперь веб-приложение Next.js работает должным образом. Давайте перейдем к реализации службы уведомлений для отправки SMS-сообщений и электронных писем, чтобы уведомить волонтеров за день до даты уборки.

Шаг 7. Клонируйте код службы уведомлений

Вы создадите службу уведомлений, реализуя задание cron с использованием node-cron и отправляя запросы API к API уведомлений, который вы создали ранее при реализации приложения Next.js. Код службы уведомлений находится в этом репозитории GitHub.

Чтобы клонировать код службы уведомлений, откройте новый терминал и выполните следующие команды:

cd ~/projects
git clone https://github.com/cuongld2/twilio-cron-job.git
cd twilio-cron-job

Затем выполните следующую команду, чтобы увидеть структуру службы уведомлений:

tree

Вы должны увидеть следующий вывод:

.
├── index.js
├── package.json
└── package-lock.json
0 directories, 3 files

В файле package.json перечислены зависимости, необходимые для запуска проекта. Выполните следующую команду, чтобы просмотреть содержимое файла package.json:

cat package.json

Вы должны увидеть следующее содержимое:

{
"name": "cron-job",
"version": "1.0.0",
"description": "",
"main": "index.js",
"type": "module",
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"node-cron": "³.0.2",
"node-fetch": "³.3.0"
}
}

Для запуска службы уведомлений требуется 2 зависимости:

  • Зависимость «node-cron» — это библиотека, которая поддерживает создание задания cron.
  • Зависимость «node-fetch» ​​помогает вам сделать запрос API к существующему API уведомлений.

Файл package-lock.json предназначен только для блокировки зависимостей для определенных версий. Когда вы обновляете версию любой зависимости в файле package.json, файл package-lock.json также будет обновлен.

Файл index.js реализует всю логику службы уведомлений. Посмотрим текущий код в файле index.js:

cat index.js

Вы должны увидеть следующее содержимое:

import fetch from 'node-fetch';
import cron from 'node-cron';
function sendNotification(){
fetch('http://localhost:3001/api/notification',
{method: 'POST',});
}
cron.schedule('0 20 * * *', () => {
sendNotification();
});

Этот файл будет планировать cronjob в 20:00 каждый день, чтобы сделать запрос API к API уведомлений.

Шаг 8. Запустите службу уведомлений

Поскольку API уведомлений будет уведомлять добровольцев только за один день до даты уборки. Вам нужно изменить дату уборки места на значение, соответствующее завтрашней дате. Допустим, сегодняшняя дата 2023–03–11, вам нужно изменить дату уборки места на 2023–03–12, чтобы получить уведомление.

На шаге 6 вы зарегистрировались в качестве волонтера для уборки «Озеро Хоанкьем». Давайте теперь обновим cleaningDate «Озеро Хоанкьем». В терминале, который запускает приложение Next.js, вы останавливаете текущий процесс, нажимая «CTRL + C». Затем откройте файл `data/data.json` и найдите «hoan-kiem-lake».

nano data/data.json

Текущее значение json-объекта «hoan-kiem-lake» выглядит следующим образом:

"allPlaces": [
{
"id": "hoan-kiem-lake",
"title": "Hoan Kiem Lake",
"city": "hanoi",
"description": "Hoan Kiem Lake now is polutted with trash after the holiday.",
"cleaningDate": "2024–03–11",
"image": "https://images.unsplash.com/photo-1616486410185-81af2d32a2af?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=686&q=80",
"emails_registered": [
"[email protected]"
],
"phoneNumbers_registered": [
"your_phone_number"
]
},

Вам нужно обновить cleaningDate до завтрашней даты. Затем сохраните файл data/data.json и снова запустите приложение Next.js.

npm run dev

Переключитесь на терминал, который в данный момент открыт в проекте службы уведомлений. Вы редактируете расписание задания cron, чтобы задание cron запускалось каждую минуту для тестирования, обновляя файл index.js, как показано ниже:

import fetch from 'node-fetch';
import cron from 'node-cron';
function sendNotification(){
fetch('http://localhost:3001/api/notification',
{method: 'POST',});
}
cron.schedule('* * * * *', () => {
sendNotification();
});

Теперь запустите службу уведомлений, выполнив следующую команду:

npm install
node index.js

Через одну минуту вы должны получить новое электронное письмо с уведомлением о том, что завтра будет назначена дата уборки.

Вы также должны получить SMS-сообщение.

Вы, наконец, завершили внедрение службы уведомлений, чтобы уведомить волонтеров о завтрашней дате уборки.

Заключение

Из статьи вы узнали о том, как работает фреймворк Next.js. У вас также есть практическая практика создания веб-приложения, которое позволяет добровольцам регистрироваться для уборки мест во Вьетнаме, чтобы все вместе могли наслаждаться красивыми местами. Интеграция Twilio SDK и Sendgrid SDK с приложением Next.js позволяет легко отправлять электронные письма и SMS-сообщения волонтерам, написав всего несколько строк кода. Вы также можете узнать о других примерах использования API Twilio с инфраструктурой Next.js из блогов Twilio, таких как создание приложения для управления задачами с использованием Twilio WhatsApp, Strapi и Next.js.

Повышение уровня кодирования

Спасибо, что являетесь частью нашего сообщества! Перед тем, как ты уйдешь:

  • 👏 Хлопайте за историю и подписывайтесь на автора 👉
  • 📰 Смотрите больше контента в публикации Level Up Coding
  • 💰 Бесплатный курс собеседования по программированию ⇒ Просмотреть курс
  • 🔔 Подписывайтесь на нас: Twitter | ЛинкедИн | "Новостная рассылка"

🚀👉 Присоединяйтесь к коллективу талантов Level Up и найдите прекрасную работу