How To Build Your Own Newsletter App? p.1
Tags: webdev, javascript, programming, tutorial
Published: 2024-05-16 11:02:07

## **TL;DR**

In this 2-part tutorial, I'll guide you through building a newsletter application that allows users to subscribe to a mailing list using a Google or GitHub account.

The application stores the user's details (email, first and last names) to Firebase, enabling the admin user to send notifications of beautifully designed email templates to every subscriber within the application using [Novu](https://docs.novu.co/echo/quickstart). 

You'll learn how to:

- implement Firebase Google, GitHub, and Email/Password authentication to your applications
- Save and fetch data from Firebase Firestore
- Create and modify email templates in the [Novu Dev Studio](https://docs.novu.co/echo/concepts/studio)
- Send email templates using the Novu


![Any Gif](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vokzaoec3yj3vdj4dusu.gif)

## Building the application interface with Next.js

In this section, I'll walk you through building the user interface for the newsletter application.

First, let's set up a Next.js project. Open your terminal and run the following command:

```bash
npx create-next-app newsletter-app
```

Follow the prompts to select your preferred configuration settings. For this tutorial, we'll use TypeScript and the [Next.js App Router](https://nextjs.org/docs/app) for navigation.


![CLI installation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nqsny1duu2w0wt5m8580.png)


Install the [React Icons package](https://react-icons.github.io/react-icons/), which allows us to use various icons within the application.

```bash
npm install react-icons
```

The application pages are divided into two parts - the subscriber routes and admin routes.

- Subscriber Routes:
    - `/` - The application home page that displays GitHub and Google sign-in buttons for user authentication.
    - `/subscribe` -  This page notifies users that they have successfully subscribed to the newsletter.
- Admin Routes:
    - `/admin` - This route displays the admin sign-up and login buttons.
    - `/admin/register` - Here, admins can access the sign-up form to create a new admin account.
    - `/admin/login` - Existing admin users can log into the application using their email and password.
    - `/admin/dashboard` - Admin users can access this route to view all subscribers and manage the creation and sending of newsletters.

### The Subscriber Routes

Copy the code snippet below into `app/page.tsx` file. It displays the GiHub and Google sign-in buttons.

```tsx
"use client";
import { useState } from "react";
import { FaGoogle, FaGithub } from "react-icons/fa";

export default function Home() {
	const [loading, setLoading] = useState<boolean>(false);

	const handleGoogleSignIn = () => {
		setLoading(true);
		//👉🏻 Firebase Google Sign in function
	};

	const handleGithubSignIn = () => {
		setLoading(true);
		//👉🏻 Firebase GitHub Sign in function
	};

	return (
		<main className='flex h-screen flex-col p-8 items-center justify-center'>
			<h2 className='text-3xl font-bold mb-6'>Sign up to the Newsletter</h2>
			
			<button
				className='py-3 px-6 flex items-center justify-center rounded-md border-gray-600 border-2 w-3/5 mb-4 text-lg hover:bg-gray-800 hover:text-gray-50'
				disabled={loading}
			>
				<FaGithub className='inline-block mr-2' />
				{loading ? "Signing in..." : "Sign Up with Github"}
			</button>

			<button
				className='py-3 px-6 flex items-center justify-center rounded-md border-gray-600 border-2 w-3/5 mb-4 text-lg hover:bg-gray-800 hover:text-gray-50'
				disabled={loading}
			>
				<FaGoogle className='inline-block mr-2' />
				{loading ? "Signing in..." : "Sign Up with Google"}
			</button>
		</main>
	);
}
```


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


Create a `subscribe` folder within the `app` directory. Inside the `subscribe` folder, add a `page.tsx` file and a `layout.tsx` file.

```bash
cd app
mkdir subscribe && cd subscribe
touch page.tsx layout.tsx
```

To indicate that the user has successfully subscribed to the newsletter, copy and paste the following code snippet into the `subscribe/page.tsx` file:

```tsx
"use client";

export default function Subscribe() {
	const handleSignOut = () => {
		//👉🏻 signout the subscriber
	};

	return (
		<main className='p-8 min-h-screen flex flex-col items-center justify-center'>
			<h3 className='text-3xl font-bold mb-4'>You&apos;ve Subscribed!</h3>
			<button
				className='bg-black text-gray-50 px-8 py-4 rounded-md w-[200px]'
				onClick={handleSignOut}
			>
				Back
			</button>
		</main>
	);
}
```


![Signed up page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3hcrkszy679sh2m9lvgj.png)

Finally, update the **`subscribe/layout.tsx`** file with the following changes to set the page title to **`Subscribe | Newsletter Subscription`**:

```tsx
import type { Metadata } from "next";
import { Sora } from "next/font/google";

const sora = Sora({ subsets: ["latin"] });

//👇🏻 changes the page title
export const metadata: Metadata = {
	title: "Subscribe | Newsletter Subscription",
	description: "Generated by create next app",
};

export default function RootLayout({
	children,
}: Readonly<{
	children: React.ReactNode;
}>) {
	return (
		<html lang='en'>
			<body className={sora.className}>{children}</body>
		</html>
	);
}
```

### The Admin Routes

Create an **`admin`** folder within the **`app`** directory. Inside the **`admin`** folder, add a **`page.tsx`** file to represent the Admin home page and a **`layout.tsx`** file to display the page title.

```bash
cd app
mkdir admin && cd admin
touch page.tsx layout.tsx
```

Copy the code snippet below into the `admin/page.tsx` file. It displays the links to the Admin Sign-up and Login pages.

```bash
import Link from "next/link";

export default function Admin() {
	return (
		<main className='flex min-h-screen flex-col items-center justify-center p-8'>
			<h2 className='text-3xl font-bold mb-6'>Admin Panel</h2>

			<Link
				href='/admin/register'
				className='py-3 px-6 flex items-center justify-center rounded-md border-gray-600 border-2 w-3/5 mb-4 text-lg hover:bg-gray-800 hover:text-gray-50'
			>
				Sign Up as an Admin
			</Link>

			<Link
				href='/admin/login'
				className='py-3 px-6 flex items-center justify-center rounded-md border-gray-600 border-2 w-3/5 mb-4 text-lg hover:bg-gray-800 hover:text-gray-50'
			>
				Log in as an Admin
			</Link>
		</main>
	);
}
```


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

Update the page title by copying this code snippet into the `admin/layout.tsx` file.

```tsx
import type { Metadata } from "next";
import { Sora } from "next/font/google";

const sora = Sora({ subsets: ["latin"] });

export const metadata: Metadata = {
	title: "Admin | Newsletter Subscription",
	description: "Generated by create next app",
};

export default function RootLayout({
	children,
}: Readonly<{
	children: React.ReactNode;
}>) {
	return (
		<html lang='en'>
			<body className={sora.className}>{children}</body>
		</html>
	);
}
```

Next, you need to create the Admin Register, Login, and Dashboard pages. Therefore, create the folders containing a `page.tsx` file.

```bash
cd admin
mkdir login register dashboard
cd login && page.tsx
cd ..
cd register && page.tsx
cd ..
cd dashboard && page.tsx
```

Within the `register/page.tsx` file, copy and pasted the code snippet provided below. The code block displays the sign-up form where users can enter their email and password to create an admin user account.

```tsx
"use client";
import React, { useState } from "react";
import { useRouter } from "next/navigation";
import Link from "next/link";

export default function Register() {
	const [email, setEmail] = useState<string>("");
	const [disabled, setDisabled] = useState<boolean>(false);
	const [password, setPassword] = useState<string>("");

	const handleSubmit = (e: React.FormEvent) => {
		e.preventDefault();
		setDisabled(true);
		//👉🏻 admin sign up function
	};
	return (
		<main className='flex min-h-screen flex-col items-center justify-center p-8'>
			<h2 className='font-bold text-3xl text-gray-700 mb-3'>Admin Sign Up</h2>
			<form
				className='md:w-2/3 w-full flex flex-col justify-center'
				onSubmit={handleSubmit}
			>
				<label htmlFor='email' className='text-lg'>
					Email
				</label>
				<input
					type='email'
					id='email'
					value={email}
					onChange={(e) => setEmail(e.target.value)}
					required
					className='w-full py-3 px-6 border-gray-600 border-[1px] rounded-md mb-4'
				/>

				<label htmlFor='password' className='text-lg'>
					Password
				</label>
				<input
					type='password'
					id='password'
					required
					value={password}
					onChange={(e) => setPassword(e.target.value)}
					className='w-full py-3 px-6 border-gray-600 border-[1px] rounded-md mb-4'
				/>

				<button
					className='py-3 px-6 rounded-md bg-black text-gray-50'
					disabled={disabled}
				>
					Register
				</button>
				<p className='text-md mt-4 text-center'>
					Already have an account?{" "}
					<Link href='/admin/login' className='text-blue-500'>
						Log in
					</Link>
				</p>
			</form>
		</main>
	);
}
```


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

Update the `login/page.tsx` file similarly to the admin register page. It accepts the user's email and password and logs the user into the application.

```tsx
"use client";
import React, { useState } from "react";
import Link from "next/link";

export default function Login() {
	const [email, setEmail] = useState<string>("");
	const [password, setPassword] = useState<string>("");
	const [disabled, setDisabled] = useState<boolean>(false);

	const handleSubmit = (e: React.FormEvent) => {
		e.preventDefault();
		setDisabled(true);
		//👉🏻 log user into the application
	};
	
	return (
		<main className='flex min-h-screen flex-col items-center justify-center p-8'>
			<h2 className='font-bold text-3xl text-gray-700 mb-3'>Admin Login</h2>
			<form
				className='md:w-2/3 w-full flex flex-col justify-center'
				onSubmit={handleSubmit}
			>
				<label htmlFor='email' className='text-lg'>
					Email
				</label>
				<input
					type='email'
					id='email'
					value={email}
					onChange={(e) => setEmail(e.target.value)}
					placeholder='test@gmail.com'
					required
					className='w-full py-3 px-6 border-gray-600 border-[1px] rounded-md mb-4'
				/>

				<label htmlFor='password' className='text-lg'>
					Password
				</label>
				<input
					type='password'
					id='password'
					required
					placeholder='test123'
					value={password}
					onChange={(e) => setPassword(e.target.value)}
					className='w-full py-3 px-6 border-gray-600 border-[1px] rounded-md mb-4'
				/>

				<button
					className='py-3 px-6 rounded-md bg-black text-gray-50'
					disabled={disabled}
				>
					Log in
				</button>
				<p className='text-md mt-4 text-center'>
					Don&apos;t have an account?{" "}
					<Link href='/admin/register' className='text-blue-500'>
						Create one
					</Link>
				</p>
			</form>
		</main>
	);
}
```


![Admin Login](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1nf1qs8rg795cfbdqq98.png)

Finally, let's create the admin dashboard page. It displays all the available subscribers and a form enabling admin users to create and view the existing newsletters.


![Create campaign page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/flfz97pyuq86loz38yoc.gif)

Copy the code snippet below into the `dashboard/page.tsx` file.

```tsx
"use client";
import { useState } from "react";
import Link from "next/link";
import Newsletters from "@/components/Newsletters";
import SubscribersList from "@/components/SubscribersList";

type Subscriber = {
	id: string;
	data: {
		email: string;
		firstName: string;
		lastName: string;
		topics: string[];
	};
};

export default function Dashboard() {
	const [subscribers, setSubscribers] = useState<Subscriber[]>([]);
	const [toggleView, setToggleView] = useState<boolean>(false);
	const handleToggleView = () => setToggleView(!toggleView);

	const fetchAllSubscribers = async () => {
		//👉🏻 fetch all subscribers
	};

	const handleLogout = () => {
		//👉🏻 sign user out
	};

	return (
		<div className='flex w-full min3-h-screen relative'>
			<div className='lg:w-[15%] border-r-2 lg:flex hidden flex-col justify-between min-h-[100vh]'>
				<nav className='fixed p-4 pb-8 flex flex-col justify-between h-screen'>
					<div className='flex flex-col space-y-4'>
						<h3 className='text-2xl font-bold text-blue-500 mb-6'>Dashboard</h3>
						<Link
							href='#subscribers'
							onClick={handleToggleView}
							className={`${
								toggleView ? "" : "bg-blue-400 text-blue-50"
							}  p-3 mb-2 rounded-md`}
						>
							Subscribers
						</Link>
						<Link
							href='#newsletters'
							onClick={handleToggleView}
							className={`${
								!toggleView ? "" : "bg-blue-400 text-blue-50"
							}  p-3 mb-2 rounded-md`}
						>
							Newsletters
						</Link>
					</div>

					<button className='text-red-500 block mt-10' onClick={handleLogout}>
						Log out
					</button>
				</nav>
			</div>

			<main className='lg:w-[85%] w-full bg-white h-full p-4'>
				<div className='flex items-center lg:justify-end justify-between mb-3'>
					<Link
						href='#'
						onClick={handleToggleView}
						className='lg:hidden block text-blue-700'
					>
						{!toggleView ? "View Newsletters" : "View Subscribers"}
					</Link>
					<button
						className='bg-red-500 text-white px-5 py-3 rounded-md lg:hidden block'
						onClick={handleLogout}
					>
						Log Out
					</button>
				</div>
				<div>
					{toggleView ? (
						<Newsletters />
					) : (
						<SubscribersList subscribers={subscribers} />
					)}
				</div>
			</main>
		</div>
	);
}
```

From the code snippet above, we have two components: the `Newsletters` and the `SubscribersList`. 

The `Newsletters` component renders the existing newsletters and a form that allows the admin to create new ones. The `SubscribersList` component displays all the existing subscribers within the application.

Therefore, create a `components` folder that contains the `Newsletters` and `SubscribersList` components within the `app` folder.

```bash
cd app
mkdir components && cd components
touch SubscribersList.tsx Newsletters.tsx
```

Copy the code snippet below into the `SubscribersList.tsx`. It accepts the existing subscribers' data as a prop and renders it within a table.

```tsx
"use client";

type Subscriber = {
	id: string;
	data: {
		email: string;
		firstName: string;
		lastName: string;
		topics: string[];
	};
};

export default function SubscribersList({
	subscribers,
}: {
	subscribers: Subscriber[];
}) {
	return (
		<main className='w-full'>
			<section className='flex items-center justify-between mb-4'>
				<h2 className='font-bold text-2xl mb-3'>All Subscribers</h2>
			</section>
			<div className='w-full'>
				<table className='w-full'>
					<thead>
						<tr>
							<th className='py-3'>First Name</th>
							<th className='py-3'>Last Name</th>
							<th className='py-3'>Email Address</th>
						</tr>
					</thead>
					<tbody>
						{subscribers.map((subscriber) => (
							<tr key={subscriber.id}>
								<td className='py-3'>{subscriber.data.firstName}</td>
								<td className='py-3'>{subscriber.data.lastName}</td>
								<td className='py-3'>{subscriber.data.email}</td>
							</tr>
						))}
					</tbody>
				</table>
			</div>
		</main>
	);
}
```

Lastly, update the `Newsletters.tsx` component to display the newsletter creation form and display the existing newsletters.

```tsx
"use client";
import React, { useEffect, useState } from "react";

export default function Newsletters() {
	const [recipients, setRecipients] = useState<string[]>([]);
	const [subject, setSubject] = useState<string>("");
	const [message, setMessage] = useState<string>("");
	const [disabled, setDisable] = useState<boolean>(false);
	const [subscribers, setSubscribers] = useState<SubscribersData[]>([]);
	const [newsLetters, setNewsletters] = useState<string[]>([]);

	const handleSendNewsletter = (e: React.FormEvent<HTMLFormElement>) => {
		e.preventDefault();
		emailNewsletter();
	};

	const emailNewsletter = async () => {
		//👉🏻 send newsletter
	};

	const fetchRecipients = async () => {
		//👉🏻 fetch all subscribers
	};

	const fetchNewsLetters = async () => {
		//👉🏻 fetch all newsletters
	};

	useEffect(() => {
		fetchRecipients();
		fetchNewsLetters();
	}, []);

	return (
		<main className='w-full'>
			<h2 className='font-bold text-2xl mb-4'>Create Campaign</h2>
			<form onSubmit={handleSendNewsletter} className='mb-8'>
				<label htmlFor='subject'>Subject</label>
				<input
					type='text'
					id='subject'
					value={subject}
					required
					onChange={(e) => setSubject(e.target.value)}
					className='w-full px-4 py-3 border-[1px] border-gray-600 rounded-sm mb-3'
				/>

				<label htmlFor='recipients'>Recipients</label>
				<input
					type='text'
					id='recipients'
					className='w-full px-4 py-3 border-[1px] border-gray-600 rounded-sm mb-3'
					disabled
					readOnly
					value={recipients.join(", ")}
				/>
				<label htmlFor='message'>Message</label>
				<textarea
					id='message'
					rows={5}
					value={message}
					required
					onChange={(e) => setMessage(e.target.value)}
					className='w-full px-4 py-3 border-[1px] border-gray-600 rounded-sm'
				></textarea>
				<button
					className='bg-blue-500 text-white py-3 px-6 rounded my-3'
					disabled={disabled}
				>
					{disabled ? "Sending..." : "Send Newsletter"}
				</button>
			</form>

			<h2 className='font-bold text-2xl '>Recent Newsletters</h2>
			<div className='flex flex-col gap-4'>
				{newsLetters.map((item, index) => (
					<div
						className='flex justify-between items-center bg-gray-100 p-4'
						key={index}
					>
						<h3 className='font-bold text-md'>{item}</h3>
						<button className='bg-green-500 text-gray-50 px-4 py-2 rounded-md'>
							Sent
						</button>
					</div>
				))}
			</div>
		</main>
	);
}
```


![Create campaign 2 gif](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/an05k6geje1fqulxuuig.gif)

Congratulations!🥳 You've successfully completed the user interface for the application. 

In the upcoming section, you'll learn how to connect the application to a Firebase backend and effectively manipulate data within the application.

---

## How to add Firebase authentication to a Next.js application

In this section, you'll learn how to implement multiple authentication methods using Firebase within your application. 

Subscribers will have the option to subscribe or sign in using either a Google or GitHub account, while Admin users will be able to sign in using the Email and Password authentication method.

### Setting up a Firebase project

Visit the [Firebase console](https://console.firebase.google.com/u/0/) and sign in with a Gmail account.

Create a Firebase project and select the `</>` icon to create a new Firebase web app.


![Firebase setup 1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mxxpaahqfr8onsy3g0yl.png)

Provide your app name and register it.


![Firebase setup 2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lczh1z7qv2j9crxumssi.png)

Install the Firebase SDK in your Next.js project by running the code snippet below.

```bash
npm install firebase
```

Create a `firebase.ts` file at the root of your Next.js project and copy the Firebase configuration code for your app into the file.


![Firebase setup 3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dhdlkeieh29wdpec6i1w.png)

```tsx
import { initializeApp, getApps } from "firebase/app";
import { getFirestore } from "firebase/firestore";
import { getAuth, GoogleAuthProvider, GithubAuthProvider } from "firebase/auth";

const firebaseConfig = {
	apiKey: "******",
	authDomain: "**********",
	projectId: "********",
	storageBucket: "******",
	messagingSenderId: "**********",
	appId: "********",
	measurementId: "********",
};
//👇🏻 gets the app config
const app =
	getApps().length === 0 ? initializeApp(firebaseConfig) : getApps()[0];
//👇🏻 creates a Firebase Firestore instance
const db = getFirestore(app);
//👇🏻 creates a Firebase Auth instance
const auth = getAuth(app);
//👇🏻 creates a Google & GitHub Auth instance
const googleProvider = new GoogleAuthProvider();
const githubProvider = new GithubAuthProvider();
//👇🏻 export them for use within the application
export { db, auth, googleProvider, githubProvider };
```

Before you can add Firebase Authentication to your application, you need to set it up in your Firebase console. 

To do this, navigate to the left-hand side panel, select `Build`, and then click on `Authentication` to add Firebase Authentication to your project.


![Firebase setup 4](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s0yh1p3m6n6ovw35775k.png)

Lastly, enable the Google and Email/Password authentication methods.


![Firebase setup 5](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lhshfnsghoncwloy2hig.png)

### Add Google authentication to Next.js

First, create a **`util.ts`** file within the **`app`** folder and copy the provided code snippet into the file.

```tsx
import { signInWithPopup } from "firebase/auth";
import { auth } from "../../firebase";

//👇🏻 Split full name into first name and last name
export const splitFullName = (fullName: string): [string, string] => {
	const [firstName, ...lastNamePart] = fullName.split(" ");
	return [firstName, lastNamePart.join(" ")];
};

//👇🏻 Handle Sign in with Google and Github
export const handleSignIn = (provider: any, authProvider: any) => {
	signInWithPopup(auth, provider)
		.then((result) => {
			const credential = authProvider.credentialFromResult(result);
			const token = credential?.accessToken;
			if (token) {
				const user = result.user;
				const [first_name, last_name] = splitFullName(user.displayName!);
				console.log([first_name, last_name, user.email]);
			}
		})
		.catch((error) => {
			const errorCode = error.code;
			const errorMessage = error.message;
			console.error({ errorCode, errorMessage });
			alert(`An error occurred, ${errorMessage}`);
		});
};
```

The code snippet enables users to sign into the application via Google or GitHub authentication. It accepts authentication providers as parameters and logs the user's email, first name, and last name to the console after a successful authentication process.

Next, you can execute the `handleSignIn` function within the `app/page.tsx` file when a user clicks the Sign Up with Google or GitHub buttons.

```tsx
import { GoogleAuthProvider, GithubAuthProvider } from "firebase/auth";
import { googleProvider, githubProvider } from "../../firebase";
import { handleSignIn } from "./util";

const handleGoogleSignIn = () => {
	setLoading(true);
	handleSignIn(googleProvider, GoogleAuthProvider);
};

const handleGithubSignIn = () => {
	setLoading(true);
	handleSignIn(githubProvider, GithubAuthProvider);
};
```

### Add GitHub authentication to Next.js

Before the GitHub authentication method can work as expected, you need to [create a GitHub OAuth App](https://github.com/settings/developers).


![Github 1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ju270h9101vwot5gut1p.png)

Once you've created the GitHub OAuth App, copy the client ID and client secret from the app settings. You'll need these credentials to activate the GitHub authentication method within the application.


![Github 2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nb1mvyktumpxply4vonf.png)

Go back to the Firebase Console. Enable the GitHub sign-in method and paste the client ID and secret into the input fields provided. Additionally, add the authorisation callback URL to your GitHub app.


![Github 3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/auym5dy4df2f4wyqclvt.png)

Congratulations! You've successfully added GitHub and Google authentication methods to the application. Next, let's add the Email/Password authentication for Admin users.

### Add email and password authentication to Next.js

Before we proceed, ensure you have enabled the email/password sign-in method within the Firebase project. Then, execute the functions below when a user signs up and logs in as an admin user.

```tsx
import {
	createUserWithEmailAndPassword,
	signInWithEmailAndPassword,
} from "firebase/auth";
import { auth } from "../../firebase";

//👇🏻 Admin Firebase Sign Up Function
export const adminSignUp = async (email: string, password: string) => {
	try {
		const userCredential = await createUserWithEmailAndPassword(
			auth,
			email,
			password
		);
		const user = userCredential.user;
		if (user) {
			//👉🏻 sign up successful
		}
	} catch (e) {
		console.error(e);
		alert("Encountered an error, please try again");
	}
};

//👇🏻 Admin Firebase Login Function
export const adminLogin = async (email: string, password: string) => {
	try {
		const userCredential = await signInWithEmailAndPassword(
			auth,
			email,
			password
		);
		const user = userCredential.user;
		if (user) {
			//👉🏻 log in successful
		}
	} catch (e) {
		console.error(e);
		alert("Encountered an error, please try again");
	}
};
```

The provided code snippets include functions for admin user sign-up (`adminSignUp`) and login (`adminLogin`) using Firebase authentication. You can trigger these functions when a user signs up or logs in as an admin user.

You can sign users (admin and subscribers) out of the application using the code snippet below.

```tsx
import { signOut } from "firebase/auth";
import { auth } from "../../firebase";

//👇🏻 Firebase Logout Function
export const adminLogOut = async () => {
	signOut(auth)
		.then(() => {
			//👉🏻 sign out successful
		})
		.catch((error) => {
			console.error({ error });
			alert("An error occurred, please try again");
		});
};
```

Congratulations! You've completed the authentication process for the application. You can read through the [concise documentation](https://modularfirebase.web.app/common-use-cases/authentication) if you encounter any issues.

---

## How to interact with Firebase Firestore

In this section, you'll learn how to save and retrieve data from Firebase Firestore by saving newsletters, subscribers, and admin users to the database.

Before we proceed, you need to add the Firebase Firestore to your Firebase project.


![Firestore 1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/836dq2evtrepny2itdm1.png)

Create the database in test mode, and pick your closest region.


![Firestore 2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g8w1742me307t4lpvo6t.png)

After creating your database, select `Rules` from the top menu bar, edit the rules, and publish the changes. This enables you to make requests to the database for a longer period of time.


![Firestore 3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lz59bqp7nklzd0iwobsr.png)

Congratulations!🎉 Your Firestore database is ready.

### Saving subscribers’ data to the database

After a user subscribes to the newsletter, you need to save their first name, last name, email, and user ID to Firebase. To do this, execute the `saveToFirebase` function after a subscriber successfully signs in via the GitHub or Google sign-in method.

```tsx
import { auth, db } from "../../firebase";
import { addDoc, collection, getDocs } from "firebase/firestore";

export type SubscribersData = {
	firstName: string;
	lastName: string;
	email: string;
	id: string;
};

//👇🏻 Save the subscriber data to Firebase
const saveToFirebase = async (subscriberData: SubscribersData) => {
	try {
		//👇🏻 checks if the subscriber already exists
		const querySnapshot = await getDocs(collection(db, "subscribers"));
		querySnapshot.forEach((doc) => {
			const data = doc.data();
			if (data.email === subscriberData.email) {
				window.location.href = "/subscribe";
				return;
			}
		});
		//👇🏻 saves the subscriber details
		const docRef = await addDoc(collection(db, "subscribers"), subscriberData);
		if (docRef.id) {
			window.location.href = "/subscribe";
		}
	} catch (e) {
		console.error("Error adding document: ", e);
	}
};
```

The `saveToFirebase` function validates if the user is not an existing subscriber to prevent duplicate entries before saving the subscriber's data to the database. 

To differentiate between existing subscribers and admin users within the application, you can save admin users to the database. 

Execute the provided function below when a user signs up as an admin.

```tsx
import { db } from "../../firebase";
import { addDoc, collection } from "firebase/firestore";

//👇🏻 Add Admin to Firebase Database
const saveAdmin = async (email: string, uid: string) => {
	try {
		const docRef = await addDoc(collection(db, "admins"), { email, uid });
		if (docRef.id) {
			alert("Sign up successful!");
			window.location.href = "/admin/dashboard";
		}
	} catch (e) {
		console.error("Error adding document: ", e);
	}
};
```

Next, update the admin log-in function to ensure that the user's data exists within the `admin` collection before granting access to the application.

```tsx
import { auth, db } from "../../firebase";
import { addDoc, collection, getDocs, where, query } from "firebase/firestore";

//👇🏻 Admin Firebase Login Function
export const adminLogin = async (email: string, password: string) => {
	try {
		const userCredential = await signInWithEmailAndPassword(
			auth,
			email,
			password
		);
		const user = userCredential.user;
		//👇🏻 Email/Password sign in successful
		if (user) {
			//👇🏻 check if the user exists within the database
			const q = query(collection(db, "admins"), where("uid", "==", user.uid));
			const querySnapshot = await getDocs(q);
			const data = [];
			querySnapshot.forEach((doc) => {
				data.push(doc.data());
			});
			if (data.length) {
				window.location.href = "/admin/dashboard";
				alert("Log in successful!");
			}
		}
	} catch (e) {
		console.error(e);
		alert("Encountered an error, please try again");
	}
};
```

To ensure that only authenticated users can access the Admin dashboard, Firebase allows you to retrieve the current user's data at any point within the application. This enables us to protect the Dashboard page from unauthorised access and listen to changes in the user's authentication state.

```tsx
import { onAuthStateChanged } from "firebase/auth";
import { auth } from "../firebase";

export default function Dashboard() {
	const router = useRouter();

	useEffect(() => {
		onAuthStateChanged(auth, (user) => {
			if (!user) {
				//👉🏻 redirect user to log in form
				router.push("/");
			}
		});
	}, [router]);
}
```

Congratulations on making it thus far! 🎉  In the upcoming sections, you'll learn how to create and send beautifully designed newsletters to subscribers using Novu Echo and React Email.