Building a customer support app with live chat and notifications using Next.js, Novu and Appwrite
Tags: react, javascript, tutorial, nextjs
Published: 2023-06-13 10:06:07

In this article, I'll walk you through how I built a customer support application with live chatting, in-app, and email notification features using Next.js, Novu, Appwrite, and EmailJS.

[Appwrite Cloud](https://appwrite.io) handles the authentication, database, and file storage aspects, [Novu](https://novu.co) handles in-app notifications, and [EmailJS](https://www.emailjs.com) for email messaging. 

Upon completion, you'll be able to build advanced applications that require a chat feature with Appwrite and add email and in-app notifications to your apps via EmailJS and Novu.

> _💡 PS: This tutorial assumes you have a basic knowledge of React or Next.js_

<img src="https://media.giphy.com/media/UoelmnMc7iOGsQ5HGT/giphy.gif" alt="Fully ready"/>

## Application Workflow
Before we start coding, let me summarise how the application works. The application does the following:

- allows customers to create support tickets,
- authenticates staff and ensures that no one other than the staff can log in to the application,
- notifies the staff when a customer creates a support ticket,
- allows both the staff and customers to settle disputes via the live-chatting feature powered by [Appwrite Cloud](https://appwrite.io), and
- sends a confirmation email to the customers when they open a ticket and also when a staff sends them a message regarding the open ticket.

![Customer support app with Next.js](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5sh7cyk2gxrfcg6eg395.png)

### Brief Demo
{% embed https://www.youtube.com/watch?v=tnncdRhvLOg %}

## The UI Design Process
Here, I'll walk you through creating the required pages for the web application.

First, you need to create a home page where customers can create support tickets, and staff can also navigate to the login page via the page.

![Home page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iise9qj5py1vf2y4wdq6.png)

Next, create the staff login page that authenticates visitors to ensure they are staff before allowing them access to the dashboard.

![Staff Login page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kc3sk50ipihqof03g39i.png)
You also need a dashboard page that shows all the tickets based on their status. Each support ticket must be clickable and redirects the staff to its details page.

![Dashboard page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6f8ofammdpaixabzya9c.png)

![Ticket details](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3nvpv73bunluaf8gud8d.gif)

Next, create a page that displays all the data related to a particular ticket. Staff can update the status of a support ticket and download its attachment; if available.

![Details page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0w8tope5atyviwww2e73.gif)

Then, create a chat page where a customer support staff and a customer can communicate in real time about an issue. 
The page should not require authentication but an access code sent to the customer's email. The chat URL will be similar to this `https://firm-support.vercel.app/chat/<ticket_ID>`.

![Chat Page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6osguile7j1n2xyhw0nv.gif)

Finally, you need an admin page that enables you to add or remove staff from the application. With this,  you can have more than one user manage the support tickets or respond to customers' queries.

![Admin Page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/drns7o7y6lbxnvl9nc0e.png)

## Adding Appwrite to a Next.js application
[Appwrite](https://appwrite.io) is a powerful open-source backend platform that enables you to create secured and scalable (web and mobile) applications. With Appwrite, you don't need to worry about the backend resources of your application because with Appwrite, you - "Build Fast. Scale Big. All in One Place."

Instead of setting up your backend server on your computer, Appwrite Cloud enables you to focus on developing your applications; while, it manages crucial backend functionalities such as user authentication, database management, file storage, and more.

### Installation and configuration
To add Appwrite to a Next.js app, follow the steps below:

First of all, create a Next.js project by running the code below.
```bash
npx create-next-app customer-support-app
``` 
Visit [Appwrite's website](https://appwrite.io) and create a new account.

Create a new organisation and project. Each project contains all the resources you need to build a fully functional application.

![Create Appwrite Project](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kf13usbo3tuk7atg4rwa.png)

Next, you need to choose where and how you want to use Appwrite, either as a web or mobile SDK or you need to integrate it with your (existing) server.

![Connect to a Web App](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nkg07t77y8k09hj8hjud.jpeg)

Since we are building a Next.js app with Appwrite Cloud, select Web App from the SDK Platform menu and register a new app under the project.

From the image below, I provided a name for the application and used an asterisk as the hostname. After deploying the application on Vercel, you can change the hostname to the URL provided by [Vercel](https://vercel.com).

![Connect Appwrite to Next.js](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q8yp0qizbvmk4dhiz7ho.png)

Install the Appwrite Node.js SDK into your Next.js project as done below.

```bash
npm install appwrite
```
Create a `.env.local` and `appwrite.js` file at the root of your project.

```bash
touch appwrite.js .env.local
```

Copy the code below into the `appwrite.js` file.

```js
import { Client, Account, Databases, Storage } from "appwrite";
const client = new Client();

client
    .setEndpoint("https://cloud.appwrite.io/v1")
    .setProject(process.env.NEXT_PUBLIC_PROJECT_ID);

export const account = new Account(client);

export const db = new Databases(client);

export const storage = new Storage(client);
```
The code snippet above enables us to access and interact with the authentication, database, and file storage features provided by Appwrite.

Copy the code below into the `.env.local` file.
```
NEXT_PUBLIC_PROJECT_ID=<your_project_id>
NEXT_PUBLIC_DB_ID=<your_database_id>
NEXT_PUBLIC_TICKETS_COLLECTION_ID=<your_collection_id>
NEXT_PUBLIC_USERS_COLLECTION_ID=<your_collection_id>
NEXT_PUBLIC_BUCKET_ID=<your_bucket_id>
```
The code snippet above contains environment variables containing all the private keys needed for interacting with Appwrite Cloud.

On your project dashboard, click the `Project ID` button to copy your project's id and paste it into the `.env.local` variable.

![Project Dashboard ID](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z4e8vzrpc6kuvct6ynq6.png)

### Setting up Authentication with Appwrite Cloud
Since you are using the Email and Password authentication method, you don't need to add any configurations on Appwrite Cloud before using the service because it has been configured by default.

However, let's add an extra layer of security to the project by updating the default Session length.

Select `Auth` from the sidebar menu and switch to the `Security` tab.

![Auth Settings](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a4nhzepn3qjhkm37n6g8.png)

Scroll down to the Session Length section and change it from `365 days` to `1 hour`. Users will need to be re-authenticated after an hour of using the application, and, in case a user doesn't log out of our application, they are logged out automatically after an hour.

![Session Length](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u0o7ip36kg2dbdjeln28.png)

Click `Update` to add the new setting, and you're ready to go.🚀

### Setting up the Appwrite Database

Here, you'll learn how to set up the database on Appwrite Cloud.

Select `Database` from the sidebar menu to create a new database.

![Create Database](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nx9mvqf7lrfwc3gwe51w.png)

Next, you need to create two database collections, one for the support tickets and the other for the staff within the application. 
You may be wondering why we need another collection for the users. What about the users saved on the Appwrite Auth?

The reason is that you can not [get all the users](https://appwrite.io/docs/server/users?sdk=nodejs-default#usersList) or [delete a user](https://appwrite.io/docs/server/users?sdk=nodejs-default#usersDelete) using Appwrite Cloud. It is only possible when you use the [Node.js SDK](https://appwrite.io/docs/getting-started-for-web#getSDK). 

However, since we need to view all the users (staff) and add or delete whenever necessary from the Admin page of the application; therefore, you need to create a `users` database containing similar information as the Auth section.

Create two database collections for the support tickets and users, and copy their IDs into the `env.local` file.

![Database Collections](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/slmjaymxefslgbqclz4p.png)

Select the `users` collection and create three required attributes, as shown below.

![User attributes](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0bkv2pu3j02v8c2qq0b0.png)

Finally, click on the Settings menu under the `users` and update the permission to allow only users to create, read, and delete users from the collection. 

![Appwrite Settings](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3k0nzf52znxhdp3y0ggr.png)

### Setting up Appwrite Storage

Select `Storage` from the sidebar menu and create a new bucket for images attached to each support ticket. Users will be able to upload screenshots of the issues they are facing when creating a support ticket.

![Create bucket](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/apy11nv1vwkqvx4h1m2l.png)

Copy the bucket ID and paste it into the `.env.local` file.

![Bucket ID](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ajz0eoilmmbiqfgkdq2j.png)

## Communicating with Appwrite: Authenticating users

Unlike conventional applications, this application does not have a sign-up page because it is exclusive to staff. Therefore, you can create the initial account via your dashboard on Appwrite Cloud.

In this section, you'll learn how to set up the authentication process for the application.
You can create a `utils` folder containing the functions and import them into the required components.

```bash
mkdir utils
cd utils
touch functions.js
```

Add the following imports into the file to enable us to interact with the backend features. We'll make use of them in the upcoming sections.

```js
import { account, db, storage } from "./appwrite";
import { ID } from "appwrite";
```

### Logging into the application
Remember, we have Appwrite Auth and the `users` collection on the database storing users' details. Therefore to log users into the application, you need to authenticate the user using Appwrite Auth, then filter the `users` collection to verify if the user's details exist before granting access to the application.

```js
//👇🏻 filters the users' list
const checkUserFromList = async (email, router) => {
	try {
		const response = await db.listDocuments(
			process.env.NEXT_PUBLIC_DB_ID,
			process.env.NEXT_PUBLIC_USERS_COLLECTION_ID
		);
		const users = response.documents;
		const result = users.filter((user) => user.email === email);

		//👉🏻 USER OBJECT ==> console.log(result[0])

		if (result.length > 0) {
			successMessage("Welcome back 🎉");
			router.push("/staff/dashboard");
		} else {
			errorMessage("Unauthorized...Contact Management.");
		}
	} catch (error) {
		errorMessage("An error occurred 😪");
		console.error(error);
	}
};

//👇🏻 authenticates the user
export const logIn = async (email, password, router) => {
	try {
		//👇🏻 Appwrite login method
		await account.createEmailSession(email, password);
		//👇🏻 calls the filter function
		await checkUserFromList(email, router);
	} catch (error) {
		console.log(error);
		errorMessage("Invalid credentials ❌");
	}
};

```
The code snippet above accepts the user's email and password from the form field, authenticates the user using Appwrite Auth, then checks if the user is on the staff list before granting permission to the application.

### Logging out

Logging users out still work the conventional way. Appwrite also provides an `account.deleteSession()` method that enables users to log out of an ongoing session. 

```js
export const logOut = async (router) => {
	try {
		await account.deleteSession("current");
		router.push("/");
		successMessage("See ya later 🎉");
	} catch (error) {
		console.log(error);
		errorMessage("Encountered an error 😪");
	}
};
```

### Protecting pages from unauthenticated users

To do this, you can store the user's information object to a state after logging in or use Appwrite's `account.get()` method.

Using the `account.get()` method:
```js
export const checkAuthStatus = async (setUser, setLoading, router) => {
	try {
		const response = await account.get();
		setUser(response);
		setLoading(false);
	} catch (err) {
		router.push("/");
		console.error(err);
	}
};
```
The code snippet above gets all the information related to the currently signed-in users. It checks if the user is active and returns the object containing all the user's details.

You can execute the function on page load for routes containing protected data, such as the Dashboard, Ticket Details, and Admin routes.

## Admin page: adding and removing staff
In this section, I'll guide you through how to add, get, and delete staff from the users' collection. We are technically building the backend functionality of the Admin page.

![Admin page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vfu6hftv4kxdxwkfy24n.png)

### Adding new staff
The code snippet below accepts the user's name, email, and password when you submit the form, create an account on Appwrite Auth, and save the email and the name in the `users` database.

```js
//👇🏻 generates random string as ID
const generateID = () => Math.random().toString(36).substring(2, 24);

export const addUser = async (name, email, password) => {
	try {
		//👇🏻 create a new acct on Appwrite Auth
		await account.create(generateID(), email, password, name);
		//👇🏻 adds the user's details to the users database
		await db.createDocument(
			process.env.NEXT_PUBLIC_DB_ID,
			process.env.NEXT_PUBLIC_USERS_COLLECTION_ID,
			ID.unique(),
			{ user_id: generateID(), name, email }
		);
		successMessage("User added successfully 🎉");
	} catch (error) {
		console.log(error);
	}
};
```

### Getting the staff list
The code snippet accesses the document on Appwrite Cloud and returns all the data in the `users` collection.

```js
export const getUsers = async (setUsers) => {
	try {
		const response = await db.listDocuments(
			process.env.NEXT_PUBLIC_DB_ID,
			process.env.NEXT_PUBLIC_USERS_COLLECTION_ID
		);
		setUsers(response.documents);
	} catch (error) {
		console.log(error);
	}
};
```

### Removing staff
To do this, you need to pass the ID of the selected staff into the function when you click the Remove button.

```js
export const deleteUser = async (id) => {
	try {
		await db.deleteDocument(
			process.env.NEXT_PUBLIC_DB_ID,
			process.env.NEXT_PUBLIC_USERS_COLLECTION_ID,
			id
		);
		successMessage("User removed 🎉"); // Success
	} catch (error) {
		console.log(error); // Failure
		errorMessage("Encountered an error 😪");
	}
};
```
> 💡 The user's credentials are still available in Appwrite Auth, but since we verify users' credentials against the `users` collection when they log in, this method is still valid.

## Support ticket: data structure, database and storage

In this section, you'll learn how to create the backend (database) for the customer support application.

### Creating a support ticket
When a customer creates a support ticket, you have to retrieve all the details from the form and save them to the database. Therefore, create a function that accepts all the ticket's details and ensures it works perfectly whether or not the customer uploads a screenshot of the problem they are facing.

```js
export const sendTicket = async (name, email, subject, message, attachment) => {
	if (attachment !== null) {
		//👇🏻 Customer attached an image
		console.log({ name, email, subject, message, attachment });
	} else {
		//👇🏻 No attachment
		console.log({ name, email, subject, message });
	}
};
```
To add the ticket's information to the `tickets` collection on Appwrite Cloud, add the following attributes to the collection.

| Name           | Required? | Type     |
| -------------- | --------- | -------- |
| name           | yes       | string   |
| email          | yes       | string   |
| subject        | yes       | string   |
| status         | yes       | string   |
| content        | yes       | string   |
| access_code    | yes       | string   |
| attachment_url | no        | string   |
| messages       |           | string[] |


![Ticket Attribute](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/apbtyv5zh0bxemdwtssg.png)

Switch to the Settings tab and update the permission on the `tickets` collection as shown below.

![Support Tickets Permission](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/znbgazwuhrs3iju6vd4t.png)

Finally, update the `sendTicket` function to add the event details to Appwrite Cloud.
```js
export const sendTicket = async (name, email, subject, message, attachment) => {
	const createTicket = async (file_url = "https://google.com") => {
		try {
			const response = await db.createDocument(
				process.env.NEXT_PUBLIC_DB_ID,
				process.env.NEXT_PUBLIC_TICKETS_COLLECTION_ID,
				ID.unique(),
				{
					name,
					email,
					subject,
					content: message,
					status: "open",
					messages: [
						JSON.stringify({
							id: generateID(),
							content: message,
							admin: false,
							name: "Customer",
						}),
					],
					attachment_url: file_url,
					access_code: generateID(),
				}
			);
			//👇🏻 send notification to the customer
			console.log("RESPONSE >>>", response);
			successMessage("Ticket created 🎉");
		} catch (error) {
			errorMessage("Encountered saving ticket ❌");
		}
	};

	if (attachment !== null) {
		try {
			const response = await storage.createFile(
				process.env.NEXT_PUBLIC_BUCKET_ID,
				ID.unique(),
				attachment
			);
			const file_url = `https://cloud.appwrite.io/v1/storage/buckets/${process.env.NEXT_PUBLIC_BUCKET_ID}/files/${response.$id}/view?project=${process.env.NEXT_PUBLIC_PROJECT_ID}&mode=admin`;
			//👇🏻 creates ticket with its image
			createTicket(file_url);
		} catch (error) {
			errorMessage("Error uploading the image ❌");
		}
	} else {
		//👇🏻 creates ticket even without an image (screenshot)
		await createTicket();
	}
};
```
- From the code snippet above:
  - The nested function, `createTicket` accepts all the ticket's attributes and creates a new document on the Appwrite Cloud. Since uploading an attachment is optional, the `flier_url` attribute has a default URL value.
  - The `messages` array attribute creates a new structure for the live chatting feature. It converts the content and the customer information into a JSON string and adds it to the `messages` array.
  - The if and else code block checks if the customer uploaded an image. If true, the code uploads the image to the Cloud Storage, retrieves its URL, and passes it into the `createTicket` function.
  - Otherwise, the `createTicket` function uses the default value (`https://google.com`) as the attachment URL.

### Getting the support tickets from Appwrite Cloud

On the Dashboard page, you need to retrieve all the support tickets. The function below retrieves and group them based on their status.

![Support Tickets](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l4msatb07laq65ym0ggb.png)

```js
export const getTickets = async (
	setOpenTickets,
	setInProgressTickets,
	setCompletedTickets
) => {
	try {
		const response = await db.listDocuments(
			process.env.NEXT_PUBLIC_DB_ID,
			process.env.NEXT_PUBLIC_TICKETS_COLLECTION_ID
		);
		const tickets = response.documents;
		const openTickets = tickets.filter((ticket) => ticket.status === "open");
		const inProgressTickets = tickets.filter(
			(ticket) => ticket.status === "in-progress"
		);
		const completedTickets = tickets.filter(
			(ticket) => ticket.status === "completed"
		);
		setCompletedTickets(completedTickets);
		setOpenTickets(openTickets);
		setInProgressTickets(inProgressTickets);
	} catch (error) {
		console.log(error); // Failure
	}
};
```

### Getting the ticket details

When you click on each support ticket on the Dashboard, it needs to redirect you to another page containing all information related to the support ticket. 

Therefore, you need to create a `ticket/[id].js` file that retrieves the ticket's details via server-side rendering using the `id` from the page route, as done below.

```js
export async function getServerSideProps(context) {
	let ticketObject = {};
	try {
		const response = await db.getDocument(
			process.env.NEXT_PUBLIC_DB_ID,
			process.env.NEXT_PUBLIC_TICKETS_COLLECTION_ID,
			context.query.id
		);

		ticketObject = response;
	} catch (err) {
		ticketObject = {};
	}

	return {
		props: { ticketObject },
	};
}
```

### Updating the ticket's status
On the details page, you can update the status of a support ticket using the HTML select tag and accept three types of values - `"open"`, `"in-progress"`, and `"completed"`. You can update the status using the function below:

```js
export const updateTicketStatus = async (id, status) => {
	try {
		await db.updateDocument(
			process.env.NEXT_PUBLIC_DB_ID,
			process.env.NEXT_PUBLIC_TICKETS_COLLECTION_ID,
			id,
			{ status }
		);
		successMessage("Status updated, refresh page 🎉");
	} catch (error) {
		console.log(error); // Failure
		errorMessage("Encountered an error ❌");
	}
};
```

## Live chatting feature with Next.js and Appwrite

In this section, I'll walk you through adding the chat feature to the application. To do this, create a chat page similar to the image below.

![Chat Page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dk6ksm99ctksjen2vrfk.png)

This page should require an access code on page load but doesn't require authentication to access the page. Run the code snippet below when a user sends a message.

```js
export const sendMessage = async (text, docId) => {
	//👇🏻 get the ticket ID
	const doc = await db.getDocument(
		process.env.NEXT_PUBLIC_DB_ID,
		process.env.NEXT_PUBLIC_TICKETS_COLLECTION_ID,
		docId
	);

	try {
		//👇🏻 gets the user's object (admin)
		const user = await account.get();
		const result = await db.updateDocument(
			process.env.NEXT_PUBLIC_DB_ID,
			process.env.NEXT_PUBLIC_TICKETS_COLLECTION_ID,
			docId,
			{
				messages: [
					...doc.messages,
					JSON.stringify({
						id: generateID(),
						content: text,
						admin: true,
						name: user.name,
					}),
				],
			}
		);
		//👇🏻 message was added successfully
		if (result.$id) {
			successMessage("Message Sent! ✅");
			//👉🏻 email the customer with access code and chat URL
		} else {
			errorMessage("Error! Try resending your message❌");
		}
	} catch (error) {
		//👇🏻 means the user is a customer
		const result = await db.updateDocument(
			process.env.NEXT_PUBLIC_DB_ID,
			process.env.NEXT_PUBLIC_TICKETS_COLLECTION_ID,
			docId,
			{
				messages: [
					...doc.messages,
					JSON.stringify({
						id: generateID(),
						content: text,
						admin: false,
						name: "Customer",
					}),
				],
			}
		);
		if (result.$id) {
			successMessage("Message Sent! ✅");
			//👉🏻 notify staff via notifications
		} else {
			errorMessage("Error! Try resending your message❌");
		}
	}
};
```
The code snippet above checks if the user is a staff or a customer before adding the message to the `messages` array. 

### Real-time messaging
To display the chat messages immediately you send them.

Create an event listener on the `tickets` collection and update the `messages` array with the changes, as done below.

```js
//👉🏻 "client" is from your appwrite.js file
useEffect(() => {
	const unsubscribe = client.subscribe(
		`databases.${process.env.NEXT_PUBLIC_DB_ID}.collections.${process.env.NEXT_PUBLIC_TICKETS_COLLECTION_ID}.documents`,
		(data) => {
			const messages = data.payload.messages;
			setMessages(messages.map(parseJSON));
		}
	);
	return () => {
		unsubscribe();
	};
}, []);
```
You can learn more about [real-time listeners in Appwrite](https://appwrite.io/docs/realtime).

### Auto-scroll feature

Add an empty `div` below the chat messages element.

```jsx
<div className='chat__container'>
	//👇🏻 chat messages element
	{/* {messages.map((message) => (
										<div>{message}</div>
									))} */}
	<div ref={lastMessageRef} />
</div>
```

Create a reference to the `div` element and shift the mouse focus when there is a new message, as done below.
```js
const lastMessageRef = useRef(null);

useEffect(() => {
	// 👇️ scroll to bottom every time messages change
	lastMessageRef.current?.scrollIntoView({ behavior: "smooth" });
}, [messages]);
```

## Adding in-app and email notifications with Novu and EmailJS
Here, you'll learn how to add in-app notifications with Novu and send emails using EmailJS. 
Recall that we need the in-app notification when a customer sends a message via the chat page and when they create a new ticket. 
The email notification is needed when the customer support staff sends a message and when a customer creates a new ticket.

### Setting up in-app notifications with Novu in Next.js
[Novu](https://novu.co) is the first open-source notification infrastructure that manages all forms of communications. In this article, we'll make use of its in-app notification feature.

Install Novu Node.js SDK and its Notification center by running the code below.

```bash
npm install @novu/node @novu/notification-center
```

Run `npx novu init` to create and access your dashboard.

Add your Novu Subscriber ID, App ID, and API Key into the `.env.local` file.

```txt
NEXT_PUBLIC_NOVU_SUBSCRIBER_ID=<subscriber_ID>
NEXT_PUBLIC_NOVU_APP_ID=<your_app_ID>
NEXT_PUBLIC_NOVU_API_KEY=<your_api_key>
```

Create two notification template on your Novu dashboard and edit its content for the two cases - when a customer creates a ticket and sends a chat message. Add the notification functions to your Next.js app, as shown below.

```js
const { Novu } = require("@novu/node");
const novu = new Novu(process.env.NEXT_PUBLIC_NOVU_API_KEY);

export default async function handler(req, res) {
	const { status, title, username } = req.body;
	const response = await novu
		.trigger("<template_name>", {
			to: {
				subscriberId: process.env.NEXT_PUBLIC_NOVU_SUBSCRIBER_ID,
			},
			payload: {
				status,
				title,
				username,
			},
		})
		.catch((err) => console.error(err));

	res.status(200).json(response.data);
}
```
Finally, create the notification bell to display the notifications within your application and add to the Nav component within your application.

```js
import {
	NovuProvider,
	PopoverNotificationCenter,
	NotificationBell,
} from "@novu/notification-center";

function Novu() {
	function onNotificationClick(message) {
		if (message?.cta?.data?.url) {
			window.location.href = message.cta.data.url;
		}
	}

	return (
		<NovuProvider
			subscriberId={process.env.NEXT_PUBLIC_NOVU_SUBSCRIBER_ID}
			applicationIdentifier={process.env.NEXT_PUBLIC_NOVU_APP_ID}
		>
			<PopoverNotificationCenter
				onNotificationClick={onNotificationClick}
				colorScheme='light'
			>
				{({ unseenCount }) => <NotificationBell unseenCount={unseenCount} />}
			</PopoverNotificationCenter>
		</NovuProvider>
	);
}
export default Novu;
```

### Sending email notifications with EmailJS

Here, you need to send an email to the customer when they create a support ticket and when a staff sends them a message. To do this follow the steps below.

Install EmailJS by running the code below.

```bash
npm install @emailjs/browser
```

Configure your [EmailJS account](https://www.emailjs.com/) and copy your credentials into the `.env.local` file.

```.env
NEXT_PUBLIC_EMAIL_SERVICE_ID=<your_service_id>
NEXT_PUBLIC_EMAIL_API_KEY=<your_api_key>
NEXT_PUBLIC_TICKET_CREATION_ID=<template_id>
NEXT_PUBLIC_NEW_MESSAGE_ID=<template_id>
```
Create the templates for both cases and [send your customers the email notifications](https://www.emailjs.com/docs/examples/reactjs).

[Check here for more guidance](https://dev.to/arshadayvid/building-an-event-ticketing-application-with-nextjs-and-appwrite-7m)

## The Wrap Up
The source code for the application is available [here](https://github.com/dha-stix/firm-support). Feel free to [check it out](https://firm-support.vercel.app)

### Open to work🙂
Did you enjoy this article or need an experienced React Technical Writer & Developer for a remote, full-time, or contract-based role? Feel free to contact me.
[GitHub](https://github.com/dha-stix) ||  [LinkedIn](https://www.linkedin.com/in/david-asaolu234/) ||  [Twitter](https://twitter.com/dayvid_JS)

[![Buy David a coffee](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cinwy8wfhl6ra51t6ml5.png)](https://www.buymeacoffee.com/asaoludavid)
<img src="https://media.giphy.com/media/fVtcfEXWQJQUbsF1sH/giphy.gif" alt="Thank You" width="auto" height="auto"/>