Building a Full-stack Invoicing Application using the Novu API
Tags: react, node, tailwindcss, novu
Published: 2023-06-12 22:23:03

Invoicing is an essential aspect of running a business, and having an efficient invoicing system can greatly streamline operations. 

With the advancements in technology, developers now have access to powerful APIs that simplify the process of creating and sending invoices.

One such API is the Novu API, which empowers developers to build robust and feature-rich invoicing applications. 

This article explores the power of the Novu API and how it can be leveraged to create a full-stack invoicing application.

You will be building a fullstack invoicing application for creating and Sending invoices to customers via email. This application will be useful for Startups, Business Owners and Freelancers.

### Application Features
#### Invoice Creation:
The application provides a user-friendly interface for creating invoices.

Users can input relevant details such as the customer's name, billing information, item descriptions, quantities, prices, and notes.

#### Invoice Sending:
You will integrate the Novu API, that would enable you to send invoices in PDF format directly to customers via email.

#### Edit Invoice Functionality:
To cater to for changing requirements or corrections, you will incorporate an edit invoice functionality.

Here is the link to the repository of the application with the full code implementation -[Repo](https://github.com/jamesoyanna/novu-invoice)

Here is a link to the demo of the application - [Novu Invoice Application](https://novu-invoice.netlify.app/)

Here is a screenshot of the final look of the application:

Frontend Page View :
 

![Front page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r2z842dby51bp0k066n3.PNG)

Invoice Creation View:

![Invoice Creation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eoahwnhnkuzki7e6842x.PNG)

Created Invoice View:

![Created Invoice](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d0yjb6g1caz85n2u89bx.PNG)

Sample Invoice:

![Sample Invoice](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/375yjmeudvfunlsq605i.PNG)

#### Overview of the Novu API:
The Novu API is a unified delivery API that would allow you to send invoices as email messages to customers seamlessly. 

You can read more about it on the [Novu Website](https://novu.co/). You can also read-up on the documentation [Here](https://docs.novu.co/overview/introduction/)

It provides a straightforward integration process, making it an excellent choice for developers looking to incorporate invoicing functionality into their applications. 

The API handles the complexities of email delivery, ensuring that invoices reach customers' inboxes reliably.

**The technologies you will be using for the development of this application are; **

- React JS -Library for building your user interface
- Axios - Promise based HTTP client for the browser and node.js
- React-router - Enables the implementation of dynamic routing in a web applications.
- Tailwind CSS - A utility-first CSS framework for your styling.
- Novu/node - Simple components and APIs for managing all communication channels in one place: Email, SMS, Direct, and Push

- Express JS - flexible Node.js web application framework that provides a robust set of features for web and mobile applications.

- HTML-PDF - HTML to PDF converter that uses phantomjs

- Mongoose - Provides a straight-forward, schema-based solution to model our application data.

- Node.js- Cross-platform, back-end JavaScript runtime environment that runs on the V8 engine and executes JavaScript code outside a web browser.

**Let's start building.**
Create a folder for your project. Name it `novu-invoice` or you can name it anything you like.

Now, you will start with building the API or backend of the application first. 

Inside the novu-invoice folder you have just created, create another folder. Name it `backend`.

Open your command-line inside the backend folder and type:
` npm  init --y`  to initiate your API application.

A `package.json` file is automatically generated for you.

Your package.json file should look like this -
` {
  "name": "backend",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}`

### Setting up the Server:
Now, install the necessary dependencies you will be needing for this project by running this command:
`npm i  npm i express mongoose cors html-pdf moment pdfkit uuid @novu/node dotenv nodemon`.

This will install all the dependencies.

Inside your `backend` folder, create a file. Name it `app.js` and setup a simple express server in it by adding the code below:

{% gist https://gist.github.com/jamesoyanna/7950935373817ae4f43b4449279e47e4  file=app.js%}

Here, you imported the Express.js module, which is a web application framework for Node.js.

You created an instance of the Express application with the code - `const app = express();`

You add a middleware to our application. In this case, `express.json()`, used to parse incoming JSON data. 

This allows you to easily handle JSON payloads in your application routes.

You then setup a simple route for the root URL ("/") such that when a GET request is made to the root URL, the callback function `(req, res) => { ... }` is executed. 

And then lastly, you specify a port on which the server will listen for incoming requests.
 
In this case, it is set to port 5000. The `app.listen()` function starts the server and listens on the specified port. 

Once the server is running, it logs a message to the console indicating that the app is running and on that port.

Update your package.json by adding this start script - `"start": "nodemon app.js"` in the script section. Your package.json file should now look like this.

{% gist https://gist.github.com/jamesoyanna/373b1d48f85eeed7c2c8221961b42717 file=package.json.js%}

To start your server, run the command - `npm start`

This would display this on your command line, indicating the server is working.

![Server Running](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gdrxljdrco98oz6ybadk.PNG)

### Establish Database Connection:
Now, implement a database connection to your application.

In your root directory, create a file. Name it `db.js` and add the following code inside:
{% gist https://gist.github.com/jamesoyanna/9de6c3fa137c445c258215d51479537b file=db.js%}

Here in this code, you are establishing a connection to a MongoDB database using Mongoose, a popular MongoDB object modeling tool for Node.js.

Firstly, you imported the Mongoose module. You then define an asynchronous function called `connectToDatabase`. 

You establish a connection to the MongoDB database using the `mongoose.connect()` method. The connection string is provided as the argument, which is stored in the `MONGODB_URI` environment variable.

If the database connection is successful, you log a success message to the console.

You exported the `connectToDatabase` function, allowing other parts of our application to import and use it.

In your `app.js` file, import the `connectToDatabase` function and invoke it. Add the following code to your `app.js` file like this:

`const connectToDatabase = require("./db");
.......
connectToDatabase();`

Your `app.js` file should now look like this:

{% gist https://gist.github.com/jamesoyanna/10a4b92f57a8c1756a2da55b9df8deee file=app.js %}

Now create a file. Name it `.env` in your root directory and add the following code:
`MONGODB_URI = mongodb://localhost:27017/novu-invoice`

The `.env` file you created is to securely manage configuration variables or environment-specific settings for your application.

It allows you to separate sensitive or environment-specific configuration from your application code in this case, you have store MongoDB connection url on it.

Please ensure you setup a MongoDB Database either locally using using the MongoDB Compass(You can do that [here](https://medium.com/@LondonAppBrewery/how-to-download-install-mongodb-on-windows-4ee4b3493514) or on the cloud using the MongoDB Atlas.

You have also installed `Nodemon`, a library that helps you to automatically restart our server. You can also run `npm start` if you have terminate the process before.

If everything works fine, you should see this in your console indicating the database is successfully connected.

![DB Connected](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8gt516kak85kofcz3bz0.PNG)

### Implement Database Model:
Now, implement your database model. 
Create a folder in your project directory. Name it `model`. Inside this folder, create a file. Name it `invoiceModel.js` and add the following code:

{% gist https://gist.github.com/jamesoyanna/0926da18485b309da25a5c5ebd4b19ec file=invoiceModel.js%}

Here in this code, you imported the necessary modules from the Mongoose library. 

`const invoiceSchema = new Schema({
  // Schema properties and their types
});`

Here, you created a new Mongoose schema named `invoiceSchema` using the Schema object. The schema defines the structure and properties of the "Invoice" document stored in the MongoDB collection.

You then define the properties of the Invoice schema. Each property corresponds to a field in the "Invoice" document. 

The properties can have various types such as Date, String, or Number. The `items` property is an array that contains objects with properties such as `itemName`, `unitPrice`, and `quantity`.

The `createdAt `property is a special field that automatically gets assigned the current date and time when a new document is created.

`module.exports = model('Invoice', invoiceSchema);`

Here, you exported the Mongoose model for the "Invoice" schema. The model function is used to create a model from the `invoiceSchema`. 

The model is given a name 'Invoice', which corresponds to the MongoDB collection name. The exported model can then be used to perform database operations on the "Invoice" collection.

### Implement Invoice Controller:
Create a folder and name it `controller`. Inside the controller folder, create a file. Name it `invoiceController.js` and add the following code:
{% gist https://gist.github.com/jamesoyanna/32bdee3e422f14caa019469050d91fb6 file=invoiceController.js %}

Here, you require the Mongoose library and an invoice model from the model directory.

You define a function called `createInvoice` that handles the creation of an invoice. 

It extracts the necessary data from the request body, generates a random 6-digit invoice number with the prefix "INV," and then creates a new invoice using the InvoiceModel. 

The new invoice is then sent as a JSON response with a status code of 201 (created).

You then define another function to handle the editing of an invoice. 
It extracts the invoice ID from the request parameters and the invoice data from the request body. 

It then checks if the provided invoice ID is a valid MongoDB object ID. If not, it sends a 404 response with the message "No invoice with that id." 

If the ID is valid, it updates the invoice using the InvoiceModel's `findByIdAndUpdate` method, passing the ID, the invoice data, and the option { new: true } to return the updated invoice. The updated invoice is then sent as a JSON response.

### Implement Routing:
To implement your routes, create a folder. Name it `routes`. Inside it, create a file. Name it `invoiceRoute.js` and add the following code:
{% gist https://gist.github.com/jamesoyanna/4dcc524c5b757b1f96bbe144b54fff00 file=invoiceRoute.js%}

Here in this code, you imported the Express library and the other controller functions from controllers folder. 

The controller functions handle the logic for creating and editing an invoice.

You created an instance of the Express router by calling `express.Router()`. The router allows you to define routes for specific HTTP methods and attach the corresponding controller functions to them.

You define the following routes using the router:
 
#####  POST: / 
- This route is used to create a new invoice. It calls the `createInvoice` controller function.

##### PATCH /:id 
- This route is used to update an existing invoice based on the id parameter. It calls the `editInvoice` controller function.

Now make use of your routes in the `app.js` file. 
In your `app.js` file import the invoice route by requiring it like this:

`const invoiceRoutes = require('./routes/invoiceRoute')`
And use it like this:
`app.use('/api/invoices', invoiceRoutes);`

Your `app.js` file should now look like this:

{% gist https://gist.github.com/jamesoyanna/b221d8e87d328e9bd39d243a478cc6a1 file=app.js%}

### Designing the Invoice Template:
In this section you will be designing the way your invoice will look like. 

Inside your project folder, create a folder. Name it `document`. Inside the document folder create a file. Name it `index.js` and add the following code:

{% gist https://gist.github.com/jamesoyanna/7484a190da87b183a9531bf797fa92ef file=index.js%}

Here in this code you are basically defining a JavaScript function called `pdfTemplate` that generates an HTML template for your invoice.

The pdfTemplate function takes an object as an argument containing various properties related to an invoice, such as name, phoneNumber, email, notes, items, dueDate, issuedDate, totalQuantity, totalAmount, and invoiceNumber. 

It uses these properties to populate the invoice template.

The returned HTML template can then be used to generate PDF documents or display the invoice in an HTML format.

You will import the invoice template into our `app.js` file and use it later on.

### Implementing the Novu API:
Novu provides a unified API that makes it simple to send notifications through multiple channels, including In-App, Push, Email, SMS, and Chat. 

With Novu, you can create custom workflows and define conditions for each channel, ensuring that your notifications are delivered in the most effective way possible. You can read more about the Novu API in their [Official Doc](https://docs.novu.co).

You will need to sign up for a [Novu account](https://web.novu.co/auth/login) to get started

After setting up an account, the next step involves creating a workflow within the workflow tab.

![Workflow tab](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ewr5qp4ajf8ewt6r637z.PNG)

Here, you added the email event below the Trigger by dragging and dropping it as the event that will happen whenever a notification is triggered. 

So you are setting up one that works for sending emails, as you want to send invoice as an email.

Click on the email channel you just added and fill-in the required information and click on the update button to save your changes like this :

![Workflow Email field](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lkpaifbtpxxltdjou3tn.PNG)

Go to your settings tab still in your Novu Dashboard, copy the API Key and add it to your environment variable like this :

![API Key](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3j5mzyhj2pmouzwl80nv.PNG)

In your `.env` file, add the api key you have just copied like this:

`NOVU_API_KEY= your-novu-api`. Replace the value with your `Novu` API Key.

One more thing you will add to your implementation is the `TRIGGER_NAME`. You will get this by clicking on the Workflow tab and locate the settings area like this:

![Workflow settings](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h75g17wtbe5olh8uogjm.PNG)

In this case, the `TRIGGER_NAME` which is our identifier is` invoice-notification`. Copy your identifier and save it somewhere. You will need it for your implementation. 

Now, implement the Novu API. But before that, require some of the dependencies you will be using. Add the following to your `app.js` file
`
const { Novu } = require('@novu/node');
const pdf = require('html-pdf');
const cors = require('cors');
const fs = require('fs');
const pdfTemplate = require('./document/index');`

`// Enable CORS middleware
app.use(cors());`

Here in this code above, you imported several Node.js modules: @novu/node, html-pdf, cors, fs, and a custom module pdfTemplate.

You also imported or require the pdfTemplate you have previously created.

Then you added the CORS middleware to the Express application using this code:

`app.use(cors());`

The application enables CORS and allows cross-origin requests from different domains.

**Add the logic for sending the invoice as PDF like this:**
{% gist https://gist.github.com/jamesoyanna/bf1a145962bacfaab2813ad285905f38 file=app.js%}

Here in this code, you define an object `options` with a property format set to 'A4'. These options are used when generating the PDF document using the html-pdf library. In this case, the A4 format is specified.

`app.post('/send-pdf', (req, res) => { ... });`

You then sets up a route handler for the POST request to the `/send-pdf` endpoint. 

You listen for incoming requests and executes the provided callback function when the endpoint is accessed via a POST request.

`const { email } = req.body;`
You extracted the email property from the request body using destructuring assignment.

`pdf.create(pdfTemplate(req.body), options).toFile('invoice.pdf', (err) => { ... });
`
You generated a PDF document using the `pdfTemplate` function and the options defined earlier. 

You executed the `pdfTemplate` function with the request body as an argument. The resulting PDF document is saved to a file named 'invoice.pdf'.

`const novu = new Novu(process.env.NOVU_API_KEY);`

With this code above, you created a new instance of the `Novu` object, which was imported earlier from the `@novu/node `module. 

`novu.trigger('invoice-notification', { ... });`

Here, you invoke the trigger method of `the novu` object, passing 'invoice-notification' as the first argument and an object containing notification details as the second argument. (Remember to replace the identifier with your own TRIGGER_NAME or identifier you had earlier saved.

The details include the recipient's subscriber ID (Remember to replace the subscriber ID with the one on your Novu dashboard) and email address, and an attachment object representing the invoice in PDF format.

It reads the contents of the PDF file using `fs.readFileSync` and converts it to a base64-encoded string before attaching it to the payload.

What you have basically done was to handle a POST request to generate a PDF invoice, save it to a file, and trigger a notification using the `Novu` object.

Your `app.js` should finally look like this:
{% gist https://gist.github.com/jamesoyanna/6538992dcf1206f063a12b12fc6336e2 file=app.js%}

You are done with the backend of our Novu Invoice project. You can test your endpoints using Postman like this:

#### Testing the create invoice endpoint:

![Create Invoice Endpoint](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m90qdmeowo2oczzxre7b.PNG)


### Developing the frontend of your Invoice Application:
In this section, you will be developing the frontend of your invoice application. 

Inside your project directory where you have your backend folder,
initialize a new react application by running:
` npx create-react-app frontend` on your command line.
 
This would create a new `frontend `folder for your application.

Navigate into the `frontend` folder from your command line and install the Tailwindcss  to add styling to your application using the following command: 

`npm install -D tailwindcss` and then run
`npx tailwindcss init` to initialize the Tailwindcss. This will automatically generate a `tailwind.config` file for the setup.

Now open the application in VSCode or in any of your favorite editor. 

Remove the default content in the tailwind.config and add the following code:

`/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    "./src/**/*.{js,jsx,ts,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}`

Open the `index.css` file in the `src`folder of your project directory. Remove its content and add the following code:
`
@tailwind base;
@tailwind components;
@tailwind utilities;`

Now start your application by running this command:
`npm start`

This will start the application on port 3000. Go to your browser. You should see your application running on http://localhost:3000/

You should see this:

![React App](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uqapz797kvvvqq42pl1w.PNG)

Now do some cleaning on your application and also test your tailwind styling to ensure its working.

In the `app.js` file, remove it's content and add the following code:

`export default function App() {
  return (
    <h1 className="text-3xl font-bold underline bg-green-600">
      Novu Invoice
    </h1>
  )
}`

After applying the tailwind styling, your application should now look like this on your browser.

![Tailwind style](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r6r7la13dd7ppnb3snuj.PNG)


Now implement routing:
Firstly, you will need to install the `react-router-dom` library for your routing. Stop your server and run the command:

`npm i react-router-dom`

In the `src` folder, create a folder. Name it `components`. Inside the components folder, create a file and name it `Invoice.jsx` and add the following code. 

{% gist https://gist.github.com/jamesoyanna/a4b5b3705644885d22fb4a1e4fc69e6d file=Invoice.jsx%}

Here you just create a simple component to render invoice as text.

Now, update the `index.js` file with the following code:

{% gist https://gist.github.com/jamesoyanna/21ec73480c00b7420ba6f9a4c662583d  file=index.js%}

Here in this code, you imported the necessary modules from the react, react-dom, and react-router-dom libraries.

You also imported the `Invoice` component you had created earlier.

You created a router using `createBrowserRouter` from `react-router-dom`.

Router is configured with an array containing a single route object:

The route object specifies the path as "/" (root path) and associates it with the `Invoice` component meaning that when the root path is accessed, the `Invoice` component will be rendered.

#### Add Logo:
In the `src` folder, create a folder name it `images` and add any image of your choice as logo. 

#### Creating the Invoice Header:
Now, create a file inside your component folder. Name it `InvoiceHeader.jsx` and add the following code:

{% gist https://gist.github.com/jamesoyanna/388671fb4ee979bb164560107a6c72f6  file=InvoiceHeader.jsx%}

Here in this code, you define a React functional component called `InvoiceHeader`.

You added your logo by importing it from the images folder. 

You imported `Link` from "react-router-dom", a package used for routing in React applications. It will allow you create links to different routes within the application.

`InvoiceHeader` component renders a header section for your invoice which includes a logo, title, location information, and an invoice number label. 

The logo is wrapped in a link that navigates to the root path when clicked. 

Import the `InvoiceHeader` component into the Invoice component. Your Invoice component should now look like this:

{% gist https://gist.github.com/jamesoyanna/51fab59d7a4f9532c58c8a29a81f3750  file=Invoice.jsx%}

Here in this code, you imported the `InvoiceHeader` from the './InvoiceHeader' file.

 A container is rendered with a specified width and styling, and includes the `InvoiceHeader` component as its child, which renders the header section of the invoice. 

Your application should now look like this on your browser:

![Front page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pcn9woxen2wwt6jfrjfv.PNG)

#### Add Invoice FormField:
Now, create a file inside your component folder. Name it `FormField.jsx` and add the following code to it:

{% gist https://gist.github.com/jamesoyanna/c75063bc6b6bf030d542a204a66ef189 file=FormField.jxs%}

Here  in this code, you define the `FormField` component as an arrow function that takes in a destructured object as its parameter. 

This object contains the following properties; 
label: Represents the label text for the form field.

name: Represents the name attribute of the input field.

type: Represents the type attribute of the input field (e.g., "text", "number", "email").

value: Represents the current value of the input field.

onChange: Represents the callback function to handle the onChange event of the input field.

placeholder: Represents the placeholder text for the input field.

error: Represents an optional error message to be displayed.

This `FormField` component represents a form field with a label, input field, and optional error message. 

It provides props for configuring various attributes and behavior of the form field, such as label text, input type, value, onChange event handler, placeholder, and error message.

You will be defining these `props` in the `Invoice` component shortly.

Now use your `FormField`component in the Invoice component. You will also be adding a table and a button. Update your `Invoice.jsx` file  with this code: 

{% gist https://gist.github.com/jamesoyanna/f1eeed0d19fa9049bb295eeb2b2cf79d  file=Invoice.jsx%}

Here in this code, you imported the `FormField`. You rendered a form layout inside a flex container with centered content and a margin-top. 

You included the `InvoiceHeader` component to display the invoice header section.

The form includes fields for billing information, such as the recipient's name and the issued date. It also includes fields for contact information, such as email address, phone number, and due date.

You added a table to be used for inputting item descriptions, quantities, prices, and amounts. `Add Item` button is present to add new items to the invoice.

Invoice summary section displays the total quantity and total amount of the invoice.

You added "Create Invoice" button at the end of the form for creating the invoice.

Your application should now look like this on your browser:

![Invoice](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eib6aaanru6t2ze3eggw.PNG)

Now, define the different states to be used in your application. 

You will be adding the `invoiceData`, error and isLoading to track the state in your application and to also update the state.

Update your `Invoice.jsx` file with the following code:

{% gist https://gist.github.com/jamesoyanna/6e09859ea9b7e2e16e206f960afd246d file=Invoice.jsx%}

Here in this code , you imported `useState` hook from the "react" module and `useNavigate` from `"react-router-dom"` module.

Then you initialized state variables using the `useState` hook:
- isLoading: 
A boolean state to track the loading state of the component.

- errors: 
An object that holds error messages for form fields.

 invoiceData:
 An object that stores the data related to the invoice, including name, phoneNumber, email, issuedDate, dueDate, items (an array of item details), notes, total, invoiceNumber, totalQuantity, and totalAmount.

You defined a navigate function using the `useNavigate` hook from `"react-router-dom"`. It allows you to navigate to different routes in our application.

You added event handlers as follows:

- handleInputChange:
 Handles the input change for form fields and updates the invoiceData state accordingly.

- handleItemChange: 
Handles the input change for individual item fields in the items array and updates the `invoiceData` state. It also calls the`updateTotals` function to recalculate the total quantity and total amount.

- handleAddItem:
Adds a new item object to the `invoiceData` state's items array. 

handleRemoveItem:
Removes an item object from the invoiceData state's items array.

 updateTotals:
Calculates the total quantity and total amount based on the items' quantity and unit price. It updates the `invoiceData` state accordingly.

Now, you will refactor our code a bit. You will extract the invoice items into it's own component. 

Create a file inside the component folder. Name it `InvoiceItem.jsx` and add the following code to it:

{% gist https://gist.github.com/jamesoyanna/480197d5907243c9ed3891f2329284cd file=InvoiceItem.jsx%}

Here in this code, you defined the `InvoiceItem` functional component, which represents a single row in the invoice table for an item.

The component receives `props` as its parameter. The props include:

  item: An object containing the details of the item (e.g., itemName, quantity, unitPrice).

  index: The index of the item in the list of items.

  onChange: A callback function to handle changes in the item's input fields.

 onRemove: A callback function to handle removing the item from the list.

This component returns JSX markup, representing a table row (<tr>) with several table cells (<td>).

Each table cell contains an <input> element for user input or display of item details.


You will use this component inside your Invoice component later.

Now before you start using our state variables and the functions, you defined in the `Invoice` component, integrate your backend API with the frontend. 

Run the command `npm i axios` to install the axios library for making http calls to the server. If your React app is still running, you will need to stop the server before installing the library.

Inside the `src`, create a file. Name it `api.js `and add the following code to it:-

{% gist https://gist.github.com/jamesoyanna/00ad30d84647ff75346a24a60f346d95 file=api.js%}

Here in this code, you imported `axios` for making HTTP requests to our API.

Using the `create` method, you created an Axios instance. The method accepts an object with configuration options as its parameter. 

In this case, the configuration object contains a single property, `baseURL`  which Specifies the base URL for all the requests made using the Axios instance.

Now update your `Invoice.jsx` with the following code:

{% gist https://gist.github.com/jamesoyanna/8adcc7103655b7d88eaf2bce3792f2bb file=Invoice.jsx%}

Here in this code, you defined several event handler functions used in the form:

navigateTo: Use this navigate function to navigate to a specified route with optional state data.

handleCreateInvoice: An asynchronous function that handles the creation of the invoice. 

It performs validation on the form fields, sends a POST request to the server using the api object, handles the response, and redirects to the invoice details page.

handleInputChange: function updates the corresponding field value in the invoiceData state object when an input field's value changes.

handleItemChange: function to update the details of an individual invoice item in the `invoiceData` state object when the corresponding input field's value changes. 

It also calls the `updateTotals` function to recalculate the total quantity and total amount.

handleAddItem: A function that adds a new empty item to the items array in the `invoiceData` state object.

handleRemoveItem: this function removes an item from the items array in the invoiceData state object.

updateTotals: function calculates the total quantity and total amount based on the items' quantities and unit prices and updates the corresponding fields in the `invoiceData` state object.

Upon clicking the `Create Invoice` button to create your invoice, you need to set a page that you will use to display the detail of the invoice you have created.

 Now set it up. Inside your component folder, create a file. Name it `InvoiceDetails.jsx` and add the following code:

{% gist https://gist.github.com/jamesoyanna/0af5b67a5f874ba8f173649200d2cb09 file=InvoiceDetails.jsx %}

Here in this code, you imported several dependencies and defined the `InvoiceDetails` component that renders the invoice details form.

Inside the component, you used the `useLocation` hook to get the current location object from the React Router.

You initialized the `initialInvoiceData` variable with the `invoiceData` value from the location state. The ?. operator is used for optional chaining to prevent errors if `location.state` or `location.state.invoiceData `is undefined.

You defined the `navigateTo` function to navigate to a specified route when called. It uses the navigate function from `useNavigate`.

You defined the `handleGoBack` function to navigate back to the home page when called. It calls the `navigateTo` function with the "/" route.


 You defined the `handleSendInvoice` function as an asynchronous function. It is called when the `"Send Invoice"` button is clicked

It sends the invoice data to the server using the `api.post` method and updates the loading, error, and success states based on the response.

You also defined the `handleEditInvoice` function as an asynchronous function. It toggles the `editMode` state when called. 

If `editMode` is true, it calculates the updated total quantity and total amount based on the items in the invoice data, sends a patch request to update the invoice data on the server using the `api.patch` method, and updates the success and error states accordingly.

Also ensure you update your index.js file by importing `InvoiceDetails` into it. You use it to handle routing to the details page. 

Your `index.js` file should now look like this:

{% gist https://gist.github.com/jamesoyanna/1461961e2f2e60a9b852a272730d1b58 file=index.js%}

YOU have just completed your fullstack application. You can test its functionalities by creating and invoice, add your email and other information and send the invoice to your email. 

A Message will display showing an Email has been sent to the email you supplied for the receiving of the Invoice.

Here is the link to the repository of the application with the full code implementation -[Repo](https://github.com/jamesoyanna/novu-invoice)

Here is a link to the demo of the application - [Novu Invoice Application](https://novu-invoice.netlify.app/)

Conclusion: This tutorial provides a details explanation on how you can integrate the Novu API on an Invoice application for sending of invoice via email.