How to Build a Booking System with Strapi and Nuxt
Tags: nuxt, jamstack, strapi
Published: 2021-08-19 20:07:53

In this tutorial, we will learn how to build a simple hotel booking system with Strapi and NuxtJs. The hotel booking system will allow us to enter new guests into our database, send emails to our guests to notify them of their booking, and check out guests from the hotel.


## Prerequisites

What'll you need for this tutorial:

- Basic knowledge of [Vue.js](https://vuejs.org/)
- [Nuxt.js](https://nuxtjs.org/)
- Knowledge of JavaScript
- [Node.js](https://nodejs.org/en/) (v14 recommended for Strapi)


## **Table of Contents:**
- Getting started with Strapi
- Setting up our Strapi backend
- Setting users permission
- Building the front-end
- Using the [Nuxt-Strapi](https://strapi.nuxtjs.org/) module for authentication
- Setting up emails using [Nodemailer](https://nodemailer.com/)


## **What you’ll Build**
![](https://paper-attachments.dropbox.com/s_A29BAC50F53CD4243F6F89A54632AE0B49769F86697DF88620D47726E115FFB5_1626738853900_C6AC1DA2-4A47-4DD7-A26B-B179D842D4F3.png.jpg)



![](https://paper-attachments.dropbox.com/s_A29BAC50F53CD4243F6F89A54632AE0B49769F86697DF88620D47726E115FFB5_1626738877327_DEF1E774-250D-4742-B327-DCAF9CE96451.png.jpg)

![](https://paper-attachments.dropbox.com/s_A29BAC50F53CD4243F6F89A54632AE0B49769F86697DF88620D47726E115FFB5_1626738896950_1B503EA0-F779-4FF3-B4F4-B6C59D8F827B.png.jpg)


I hope you're excited about this project. Let's get started.

The Github repositories for this project can be found here:
[Front-end](https://github.com/oviecodes/nuxt-strapi-booking-system) 
[Backend](https://github.com/oviecodes/strapi-booking-system)


## Getting Started with Strapi
### What is Strapi?

The [Strapi documentation](https://strapi.io/documentation/developer-docs/latest/getting-started/introduction.html) says that "Strapi is a flexible, open-source Headless CMS that gives developers the freedom to choose their favorite tools and frameworks while also allowing editors to manage and distribute their content easily." 

Strapi enables the world's largest companies to accelerate content delivery while building beautiful digital experiences by making the admin panel and API extensible through a plugin system.

### Installing Strapi

The [documentation](https://strapi.io/documentation/developer-docs/latest/setup-deployment-guides/installation/cli.html) works you through installing Strapi from the CLI, the minimum requirements for running Strapi, and how to create a quickstart project.

The Quickstart project uses SQLite as the default database, but feel free to use whatever database you like.

```bash
    yarn create strapi-app my-project --quickstart //using yarn
    npx create-strapi-app my-project --quickstart //using npx
```

Replace `my-project` with the name you wish to call your application directory. Your package manager will create a directory with the name and will install Strapi.

If you have followed the instructions correctly, you should have Strapi installed on your machine.

```bash
    yarn develop //using yarn
    npm run develop //using npm
```

To start our development server, Strapi starts our app on `http://localhost:1337/admin`.

****
## **Setting up our Strapi backend**

Now let's start building the backend of our Application.


## **Creating the hotel Admin user for our application**

The user we are creating here is the admin for our hotel booking system. Say an account has access to our Application and can add guests to the system.


- Open up your Strapi admin panel in your browser by visiting `localhost:1337/admin`. 
- On the sidebar under `collection, types` click `Users`. 
- Click `Add new user`
    - Then add a `username`, `password`, `email`, and check the click the `confirm` button.

## **Building our Guests collection type**


- On the sidebar under `plugins`, click on `Content-Types builder`.
- Click on `create new collection type`.
- Fill in the display name as `guests` click `continue`.
    - Select the `text` field and label it `fullname`, leave the selection as `short text`, then click `add another field`
    - Repeat the procedure to create the `checkIn`, `leaveDate`, `status` fields as they are all `text` fields. Click on `add another field`.
    - Next, we add the `paid` field, which is a `boolean` value.
    - Finally, select `Number`, name the field `roomNo`, select `integer` type.

The final `Guests` collection type should look like this:


![](https://paper-attachments.dropbox.com/s_A29BAC50F53CD4243F6F89A54632AE0B49769F86697DF88620D47726E115FFB5_1626741356478_7785E703-8434-4906-8966-17BA462A6DD9.png.jpg)



## **Building our Rooms collection type**

Next, we'll build our `rooms` collection type.


- On the sidebar under `plugins`, click on `Content-Types builder`
- Click on `create new collection type`
- Fill in the display name as `rooms` click `continue`
    - Select the `text` field and label it `type`, leave the selection as `short text`, then click `add another field`
    - Next, we add the `occupied` field, which is a `boolean` value, click `add another field`
    - Select `Number`, name the field `roomNo`, select `integer` type, 
    - Repeat the previous instruction to create a `beds` field which is also an `integer`
    - Finally, choose `Relation`, name the field `guests`, then select a one-to-one relationship as follows
    
![](https://paper-attachments.dropbox.com/s_A29BAC50F53CD4243F6F89A54632AE0B49769F86697DF88620D47726E115FFB5_1626776541308_33335A65-4B80-4529-87EC-EE95561FDDD8.png.jpg)


The final version of your `rooms` collection type should look like the image below.


![](https://paper-attachments.dropbox.com/s_A29BAC50F53CD4243F6F89A54632AE0B49769F86697DF88620D47726E115FFB5_1626776907412_31475B3C-83DD-4396-9AD9-B4C1DA12B991.png.jpg)



## Seeding the database

We'll have to add some data to our `rooms` collection type. Follow the instructions below to do this

- In your Strapi admin panel, under `collection types`, click on `Rooms`
- Click `add new rooms` and fill in the data appropriately.

Your rooms collection type should have the following data but feel free to add as many rooms as possible.


![](https://paper-attachments.dropbox.com/s_A29BAC50F53CD4243F6F89A54632AE0B49769F86697DF88620D47726E115FFB5_1626780146896_C6B6BA8C-7812-4939-BA5A-CA6F7D28E494.png.jpg)


Next, we have to set up our user permission for authenticated users.


## **Setting user permission**
1. Click on Settings under `GENERAL` in the side menu
2. Click on `Roles` under Users and Permissions Plugin.
3. It will display a list of roles. Click on `authenticated`
4. Scroll down, under `Permissions`, click on `Application`,  
5. Under `guests` click `select all`,
6. Under `rooms`, click `select all`
7. Click `save`, then go back.

Your Permissions should look exactly like this:


![](https://paper-attachments.dropbox.com/s_A29BAC50F53CD4243F6F89A54632AE0B49769F86697DF88620D47726E115FFB5_1626778085559_5A62E04A-3717-447D-87AF-E9F55F720385.png.jpg)

## Installing Nuxt.js

Next, we will install and configure NuxtJs to work with our Strapi backend.

To install Nuxt.js, visit [the Nuxt.js docs](https://nuxtjs.org/docs/2.x/get-started/installation) or run one of these commands to get started.

```bash
    yarn create nuxt-app <project-name> //using yarn
    npx create-nuxt-app <project-name> //using npx
    npm init nuxt-app <project-name> //using npm
```

The command will ask you some questions (name, Nuxt options, U.I. framework, TypeScript, linter, testing framework, etc.).

We want to use Nuxt in SSR mode, Server Hosting, and Tailwind CSS as our preferred CSS framework, so select those, then choose whatever options for the rest. 

Preferably leave out C.I., commit-linting, style-linting, and the rest but do whatever you like. All we'll be needing in this tutorial is what I've mentioned above.

Once all questions are answered, it will install all the dependencies. The next step is to navigate to the project folder and launch it.

```bash
    cd <project-name
    
    yarn dev //using yarn
    npm run dev //using npm
```

We should have the Nuxt.js Application running on [http://localhost:3000](http://localhost:3000).


### Building our Index page

Run the following command in your terminal to open up your index page

```bash
    cd pages
    code index.vue
```

Fill up the file with the following code

```
    <template>
      <div>
        <Loading v-if="loading" />
        <Login v-if="!loading"/>
      </div>
    </template>
    <script>
    import Loading from '~/components/Loading.vue'
    export default {
      components: { Loading },
      middleware({ $auth, redirect }) {
        if($auth.$storage.getCookie("authenticated") === true) {
            redirect('/dashboard/guests')
        }
      },
      data() {
        return {
          loading: true,
          timeout: Math.floor(Math.random() * 1000),
        }
      },
      mounted() {
        setTimeout(() => {
          this.loading = false;
        }, this.timeout)
      }
    }
    </script>
  ```  


### Building the loading component

Run the following command in your terminal to create a `loading` component

```bash
    cd components
    touch Loading.vue
```

Fill up the file with the following code

```
    <template>
        <div>
            <div class="grid items-center justify-center h-screen">
                <div class="w-40 bg-blue-500 h-40 rounded-full"></div>
            </div>
        </div>
    </template>
    <script>
        export default {
            name: 'Loading'
        }
    </script>
    <style scoped>
    </style>
```

**Building out our vuex store**

Nuxt comes with a store directory pre-built for us. To activate our `vuex` store, we have to create an `index.js` file in the store directory as follows.

```bash
    cd store
    touch index.js
```

Fill it up with the following codes

```
    export const state = () => ({
        addGuest: false,
        allGuests: [],
        allRooms: []
    })
    export const getters = {
        addGuest: state => state.addGuest,
        // retrieve all guests from state
        allGuests: state => state.allGuests,
        // retrieve all rooms from state
        allRooms: state => state.allRooms,
        
    }
    export const actions = {
        toggleAddGuest({ commit }) {
            commit('toggleAddGuest')
        }
    }
    export const mutations = {
        toggleAddGuest(state) {
            state.addGuest = !state.addGuest
        },
        fetchGuests(state, guests) {
            state.allGuests = guests
        },
        fetchRooms(state, rooms) {
            state.allRooms = rooms
        },
        updateGuests(state, guest) {
            state.allGuests.unshift(guest)
        },
        updateRoom(state, data) {
            const { guest, active, room } = data
            const curRoom = state.allRooms.find(el => 
                el.id === room.id
            )
            state.allRooms[curRoom].guest = guest
            state.allRooms[curRoom].active = active
        }
    
    }
```

We'll build out the `login` component, but first, we need to install the `@nuxtjs/strapi` package to assist us with that.


## **Using the @Nuxtjs/Strapi module for authentication**

**Installing** [**@nuxtjs/strapi**](https://strapi.nuxtjs.org/) **module**
Run the following commands in order to install the module

```bash
    yarn add @nuxtjs/strapi //using yarn
    
    npm install @nuxtjs/strapi //using npm
```

Open up your `nuxt.config.js` file and add the following 

```
    export default {
      modules: [
        //.....
        '@nuxtjs/strapi'],
      strapi: {
        entities: ['guests', 'rooms']
      }
    }
```

Now we can use the module in our code as follows

```
    await this.$strapi.login({ identifier: '', password: '' }) //for login
```

We'll also install the [@nuxtjs/auth](https://auth.nuxtjs.org/) package for added protection on the front-end of our Application

Run the following commands in order to install the module

```bash
    yarn add @nuxtjs/auth-next //using yarn
    
    npm install @nuxtjs/auth-next //using npm
```

Open up your `nuxt.config.js` file and add the following 

```
     modules: [
        //...
        '@nuxtjs/auth-next'
      ],
```

## Building the login component

In order to create a `Login` component, run the following commands

```bash
    cd components
    touch Login.vue
```

Fill it up with the following code

```
    <template>
        <div class="grid w-4/5 md:w-3/5 mx-auto items-center justify-center h-screen">
            <div class="w-full">
                <h1 class="font-black text-6xl mb-10">Welcome Admin</h1>
                <form @submit="login">
                    <div class="">
                        <label for="username" class="w-full my-3">Username</label>
                        <input id="username" v-model="username" placeholder="Enter Username" type="text" class="w-full my-3 p-3 border-2">
                    </div>
                    <div class="">
                        <label for="password" class="my-3">Password</label> 
                        <span class=""> 
                            <font-awesome-icon v-if="!passwordVisible" :icon="['fas', 'eye']" class="cursor-pointer w-5" @click='switchVisibility' />
                            <font-awesome-icon v-if="passwordVisible" :icon="['fas', 'eye-slash']" class="cursor-pointer text-gray-400 w-5" @click='switchVisibility' />
                        </span>
                        <div class=""> 
                            <input 
                                id="password" 
                                v-model="password" placeholder="Enter password" 
                                :type="passwordFieldType" 
                                class="my-3 p-3 border-2 w-full"
                            >
                            
                        </div>
                    </div>
                    <button type="submit" class="flex items-center justify-center p-4 bg-blue-500 text-white my-3 rounded-lg">
                        Login <font-awesome-icon class="mx-3" :icon="['fas', 'arrow-right']" />
                    </button>              
                </form>
            </div>
        </div>
        
    </template>
    <script>
        export default {
            name: 'Login',
            middleware({ $auth }) {
                if($auth.$storage.getCookie("authenticated") === true) {
                    redirect('/dashboard/guest')
                }
            },
            data() {
                return {
                    username: '',
                    password: '',
                    passwordFieldType: 'password',
                    passwordVisible: false
                }
            },
            methods: {
                async login(e) {
                    e.preventDefault()
                    try {
                        await this.$strapi.login({ identifier: this.username, password: this.password })
                        if(this.$strapi.user)  {
                            this.$auth.$storage.setCookie("authenticated", true)
                            this.$router.push('/dashboard/guests')
                        }
                    } catch (e) {
                      alert('Wrong credentials', e)
                    }
                    
                },
                switchVisibility() {
                    this.passwordFieldType = this.passwordFieldType === 'password' ? 'text' : 'password'
                    this.passwordVisible = !this.passwordVisible
                }
            }
        }
    </script>
    <style scoped>
    </style>
```

What we're doing here is just enabling the Admin user to Login to our Application. On success, we redirect the user to the `dashboard/guest` page to perform admin functionalities. Then in the middleware function, we check if the user is logged in or not before granting access to the `dashboard/guests` page.

**Building our  `SideNav` component**
To create a `SideNav` component, run the following commands

```bash
    cd components
    touch SideNav.vue
```

Fill it up with the following code

```
    <template>
        <div>
            <!-- side nav -->
            <div class=" ">
                <div class="">
                    <NuxtLogo class="w-20 mx-auto" />
                </div>
                <div class="text-white text-center w-full mb-5">
                
                    <NuxtLink to="/dashboard">
                        <div ref="index" class="p-5 w-1/3 text-xl mx-auto rounded-full">
                            <font-awesome-icon :icon="['fas', 'home']" />
                        </div>
                        <p class="text-sm text-white">Home</p>
                    </NuxtLink>
                </div>
                
                <div class="text-white text-center w-full mb-5">
                    <NuxtLink to="/dashboard/guests">
                        <div ref="guests" class="p-5 w-1/3 text-xl mx-auto rounded-full">
                            <font-awesome-icon :icon="['fas', 'users']" />
                        </div>
                        <p class="text-sm text-white">Guests</p>
                    </NuxtLink>
                </div>
                <div class="text-white text-center w-full my-5">
                    <NuxtLink to="/dashboard/rooms">
                        <div ref="rooms"  class="w-1/3 text-xl mx-auto rounded-full p-5">
                            <font-awesome-icon :icon="['fas', 'bed']" /> 
                        </div>
                        <p class="text-sm text-white">Rooms</p>
                    </NuxtLink>
                </div>
                <div class="text-white text-center cursor-pointer w-full my-7">
                    <p @click="logout">Logout</p>
                </div>
            </div>
            <!-- end of first div -->
        </div>
    </template>
    <script>
        export default {
            name: 'SideNav',
            props: ['page'],
            methods: {
                async logout() {
                    await this.$strapi.logout()
                    .then(() => {
                        this.$auth.$storage.setCookie("authenticated", false)
                        this.$nuxt.$router.push('/')
                    })
                      
                }
            },
            mounted() {
                
                // get current link item
                const item = this.$refs[this.page]
                // change it's class
                const addClass = ['bg-purple-800', 'shadow-xl']
                addClass.forEach(e => {
                    item.classList.add(e)
                });
            }
        }
    </script>
    <style scoped>
    </style>
```

The `SideNav` component hold our `logout` method which calls the 
`this.$strapi.logout()` method.

**Building the `dashboard/index` page**

Run the following commands to create a `pages/dashboard/index` page

```bash
    cd pages
    mkdir dashboard
    cd dashboard
    touch index.vue
```

Fill it up with the following code

```
    <template>
        <div>
            <div class="flex w-screen flex-row">
                <!-- first div here    -->
                <!-- side menu -->
                
                <SideNav class="hidden sm:block sm:w-1/6 h-screen bg-purple-600" :page="routeName" />
                <!-- main display -->
                <!-- second div here -->
                <div class="w-full overflow-x-hidden md:w-5/6 h-screen">
                    <div class="w-screen h-10 m-8">
                        <h1 class="font-black">Welcome, user</h1>
                    </div>
                    <div class="w-4/5 mx-auto min-h-screen">
                        <div class="block sm:grid sm:grid-cols-2 sm:gap-4 mb-10 items-center justify-center">
                            <div class="mb-10 sm:mb-0 w-full rounded-xl p-28 bg-pink-300">
                                
                            </div>
                            <div class="mb-10 sm:mb-0 w-full rounded-xl p-32 h-full bg-green-300">
                                
                            </div>
                        </div>
                        <div class='w-full rounded-xl mb-20 p-32 h-full bg-blue-300'>
                        </div>
                    </div>
                </div>
                <!-- end of second div -->
            </div>
        </div>
    </template>
    <script>
        export default {
            data() {
                return {
                    routeName: 'index'
                }
            },
        }
    </script>
    <style scoped>
    </style>
```

**Building the `dashboard/rooms` page**
Run the following commands to create a `pages/dashboard/rooms` page

```bash
    cd pages
    cd dashboard
    touch rooms.vue
```

Fill it up with the following code

    
```
    <template>
        <div>
            <div class="flex w-screen flex-row">
                <!-- first div here    -->
                <!-- side menu -->
                
                <SideNav class="hidden sm:block sm:w-1/6 h-screen bg-purple-600" :page="routeName" />
                <!-- main display -->
                <!-- second div here -->
                <div class="w-full overflow-x-hidden md:w-5/6 h-screen">
                    <div class="w-screen h-10 m-8">
                        <h1 class="font-black">All Rooms</h1>
                    </div>
                    <div class="w-4/5 mx-auto min-h-screen">
                        <div class="block sm:grid sm:grid-cols-2 sm:gap-4 mb-10 items-center justify-center">
                            <div class="mb-10 sm:mb-0 w-full rounded-xl p-28 bg-pink-300">
                                
                            </div>
                            <div class="mb-10 sm:mb-0 w-full rounded-xl p-32 h-full bg-green-300">
                                
                            </div>
                        </div>
                        <div class='w-full rounded-xl mb-20 p-32 h-full bg-blue-300'>
                        </div>
                    </div>
                </div>
                <!-- end of second div -->
            </div>
        </div>
    </template>
    <script>
        export default {
            data() {
                return {
                    routeName: 'rooms'
                }
            },
        }
    </script>
    <style scoped>
    </style>
```

**Building the `dashboard/guests` page**
Run the following commands to create a `pages/dashboard/guests` page

```bash
    cd pages
    cd dashboard
    touch guests.vue
```

Fill it up with the following code

```    
    <template>
        <div>
            <div ref="guest" class="flex w-screen flex-row">
                <!-- first div here    -->
                <!-- side menu -->
                
                <SideNav class="hidden sm:block sm:w-1/6 h-screen bg-purple-600" :page="routeName" />
                <!-- main display -->
                <!-- second div here -->
                <div class="w-full relative overflow-x-hidden md:w-5/6 h-screen">
                    <div v-if="addGuest" class="w-screen h-full top-0 bottom-0 z-10 fixed bg-opacity-30 bg-gray-300">
                       <AddGuest class="z-10 top-5 left-0 overflow-y-scroll right-0 shadow-2xl bottom-5 bg-white mx-auto fixed" /> 
                    </div>
                    <div class="w-screen h-10 m-8">
                        <h1 class="text-2xl text-gray-500 font-black">Manage Guests</h1>
                    </div>
                    <div class="w-4/5 mx-auto min-h-screen">
                        <div class="block sm:grid sm:grid-cols-2 sm:gap-4 mb-10 items-center justify-center">
                            <!-- active users -->
                            <div class="mb-10 sm:mb-0 shadow-2xl grid grid-cols-2 items-center justify-center gap-6 text-white w-full rounded-xl p-8 lg:p-16 bg-pink-500">
                                <font-awesome-icon class="text-6xl lg:text-8xl" :icon="['fas', 'users']" />
                                <div class="text-2xl font-bold">
                                    <p>Over {{ allGuests.length }} Guests Lodged </p>
                                </div>
                            </div>
    
                            <!-- messages -->
                            <div class="">
                                <div class="my-3 font-black">
                                    <font-awesome-icon :icon="['fas', 'bell']" /> Notifications
                                </div>
                                <div class="mb-10 sm:mb-0 w-full divide-y divide-white text-sm relative rounded-xl text-white p-5 h-32 overflow-y-scroll bg-green-500">
                                    <p class="p-2">
                                       <font-awesome-icon :icon="['fas', 'circle']" />
                                       Alexander Godwin checked just checked into room 43
                                    </p>
                                    
                                    <p class="p-2">
                                       <font-awesome-icon :icon="['fas', 'circle']" />
                                       Alexander Godwin checked just checked into room 43
                                    </p>
                                     
                                </div>
                            </div>
                        </div>
                        
                        <!-- table part -->
                        <div class="w-full grid grid-cols-2 space-between items-center">
                          <h1 class="my-5 text-2xl font-black">All Guests</h1>
                        
                          <div class="text-right">
                            
                            <button class="p-3 text-white rounded-md bg-gray-500" @click="toggleAddGuest">
                              Add Guest <font-awesome-icon class="ml-2" :icon="['fas', 'plus']" />
                          </button>
                          </div>
                        </div>
                        
                        <div class='w-full rounded-xl overflow-x-scroll mb-20 min-h-full bg-white'>
                            <div class="table w-full">
                                <div class="w-full table-row-group">
                                   
                                   <!-- heading row -->
                                    <div class="table-row bg-black rounded-xl text-white">
                                        <div class="table-cell">
                                            <div class="m-3">Name</div>
                                        </div>
                                        <div class="table-cell">
                                            <div class="m-3">Room NO.</div>
                                        </div>
                                        <div class="table-cell">
                                            <div class="m-3">Status</div>
                                        </div>
                                        <div class="table-cell">
                                            <div class="m-3">Paid</div>
                                        </div>
                                        <div class="table-cell">
                                            <div class="m-3">Checked In</div>
                                        </div>
                                        <div class="table-cell">
                                            <div class="m-3">Leave Date</div>
                                        </div>
                                        <div class="table-cell">
                                            <div class="m-3">Action</div>
                                        </div>
                                    </div>
                                    <!-- end of heading row -->
                                
                                    <div v-for="(guest, i) in allGuests" :key="i" class="table-row bg-gray-500 text-white">
                                        
                                        <div class="table-cell">
                                            <div class="m-3">{{ guest.fullname }}</div>
                                        </div>
                                        <div class="table-cell">
                                            <div class="m-3">{{ guest.roomNo }}</div>
                                        </div>
                                        <div class="table-cell">
                                            <div class="m-3">{{ guest.status }}</div>
                                        </div>
                                        <div class="table-cell">
                                            <div class="m-3">{{ guest.paid }}</div>
                                        </div>
                                        <div class="table-cell">
                                            <div class="m-3">{{ guest.checkIn }} </div>
                                        </div>
                                        <div class="table-cell">
                                            <div class="m-3">{{ guest.leaveDate }}</div>
                                        </div>
                                        <div>
                                            <button v-if="guest.status === 'active'" @click="checkOut(guest)" class="p-2 m-3 bg-green-500">
                                                check-out
                                            </button>
                                        </div>
                            
                                    </div>
                                    
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
                <!-- end of second div -->
            </div>
        </div>
    </template>
    <script>
        import { mapActions, mapGetters } from 'vuex'
        export default {
            middleware({ $strapi, $auth, redirect }) {
                if($auth.$storage.getCookie("authenticated") === false) {
                    redirect('/')
                }
            },
            async asyncData({ $strapi, store }) {
                const guests = await $strapi.$guests.find({
                    _sort: 'published_at:DESC'
                })
                store.commit('fetchGuests', guests)
                const rooms = await $strapi.$rooms.find()
                store.commit('fetchRooms', rooms)
            },
            data() {
                return {
                    routeName: 'guests',
                }
            },
            methods: {
                ...mapActions(['toggleAddGuest']),
                async checkOut(guest) {
                    
                    await this.$strapi.$rooms.update(guest.roomNo, {
                        guests: null,
                        occupied: false
                    })
                    const rooms = await this.$strapi.$rooms.find()
                    this.$store.commit('fetchRooms', rooms)
                    await this.$strapi.$guests.update(guest.id, {
                        status: 'inactive'
                    })
                    const guests = await this.$strapi.$guests.find({
                        _sort: 'published_at:DESC'
                    }) 
                    this.$store.commit('fetchGuests', guests)
                },
               
            },
            computed: {
                ...mapGetters(['addGuest', 'allGuests', 'allRooms'])
            },
        }
    </script>
    <style scoped>
    </style>
```

This `guests` page has most of our functionality, as it allows us to:

- Fetch `guests` from our Strapi API and push them to our vuex store
- Display all our guests at a table.
- Fetch `rooms` from our Strapi API and push them to the vuex store
- Check out a `guest` from the hotel.

When a guest is checked-out from the hotel their status becomes inactive and the  check out button is not displayed for them.


![](https://paper-attachments.dropbox.com/s_A29BAC50F53CD4243F6F89A54632AE0B49769F86697DF88620D47726E115FFB5_1626790437418_22B3F53D-D671-4924-B8A6-B42992DC6F9C.png.jpg)


 
 

## Building the `add guest` component

We'll build the component to add the guest to a free hotel room in our database. Run the following commands to create a `AddGuests` component

```bash
    cd components
    touch AddGuests.vue
```

Fill it up with the following code


```
    <template>
        <div class="p-10 shadow-xl shadow-yellow-300 w-3/4 sm:w-1/2 rounded-xl max-h-screen">
            <div class="m-2">
                <h1 class="font-black text-yellow-800">New Guest</h1>
            </div>
            <div>
                <form @submit.prevent="newGuest">
                    <div class="p-2 text-sm">
                        <label class="font-bold text-yellow-800 text-sm" for="checkin">FullName</label>
                        <input v-model="fullname" type="text" class="py-3 pr-3 w-full border-b-2 border-gray-200 focus:outline-none hover:border-yellow-300">
                    </div>
                    <div class="p-2 text-sm">
                        <label class="font-bold text-yellow-800 text-sm" for="checkin">Email</label>
                        <input v-model="email" type="email" class="py-3 pr-3 w-full border-b-2 border-gray-200 focus:outline-none hover:border-yellow-300">
                    </div>
                    <!-- check in and check out -->
                    <div class="p-2">
                        <div class="mb-3 text-sm">
                            <label class="font-bold text-yellow-800" for="checkin">Check In</label>
                            <input v-model="checkIn" id="checkin" class="py-3 pr-3 w-full border-b-2 border-gray-200 focus:outline-none hover:border-yellow-300" type="date">
                        </div>
                        <div class="text-sm">
                            <label class="font-bold text-yellow-800 text-sm" for="leavedate">Leave Date</label>
                            <input v-model="leaveDate" id="leavedate" class="py-3 pr-3 w-full border-b-2 border-gray-200 focus:outline-none hover:border-yellow-300" type="date">
                        </div>
                    </div>
                    
                    <div class="text-sm p-2">
                        <label for="rooms" class="text-sm text-yellow-800 font-bold">Select Room</label>
                        <select v-model="RoomNo" id="rooms" name="" class="py-3 pr-3 w-full border-b-2 border-gray-200 focus:outline-none hover:border-yellow-300">
                            <option v-for="(room, i) in allRooms.filter(el => el.occupied === false).map(el => el.roomNo)" :key="i" :value="room">{{ room }}</option>
                        </select>
                    </div>
                    <div class="my-3">
                        <button class="p-3 bg-green-500 text-white" type="submit">
                            Submit 
                        </button>
                        <button class="p-3 bg-red-500 text-white" @click.prevent="toggleAddGuest">
                            Cancel 
                        </button>
                    </div>
                    
                </form>
            </div>
        </div>
    </template>
    <script>
        import { mapActions, mapGetters } from 'vuex'
        export default {
            name: 'AddGuest',
            data() {
                return {
                    fullname: '',
                    RoomNo: '',
                    checkIn: '',
                    leaveDate: '',
                    email: ''
                }
            },
            computed: {
                ...mapGetters(['allRooms'])
            },
            methods: {
                async newGuest() {
                    const newGuest = {
                        fullname: this.fullname,
                        checkIn: this.checkIn,
                        leaveDate: this.leaveDate,
                        roomNo: parseInt(this.RoomNo),
                        status: 'active',
                        paid: true,
                        email: this.email
                    }
                    const { guest } = await this.$strapi.$guests.create(newGuest)
                    console.log("guest", guest)
                    await this.$strapi.$rooms.update(this.RoomNo, {
                        occupied: true,
                        guest
                    })
                    this.$store.commit('updateGuests', guest)
                    const rooms = await this.$strapi.$rooms.find()
                    this.$store.commit('fetchRooms', rooms)
                    this.toggleAddGuest()
                },
                ...mapActions(['toggleAddGuest',])
            }
        }
    </script>
    <style scoped>
    </style>
```

The result of the code above. I have just one empty room left in my database since the other two rooms are occupied, so I can only select room 2.


![](https://paper-attachments.dropbox.com/s_A29BAC50F53CD4243F6F89A54632AE0B49769F86697DF88620D47726E115FFB5_1626859756783_DF9FC6D8-2302-4F77-B056-510C8F72169B.png.jpg)


Next, we'll build a feature into our backend that allows us to send emails to our guests once they are added to our database.


## **Setting up emails using Nodemailer**

To set up emails with [Nodemailer](https://nodemailer.com), open up the folder with your Strapi code in your favorite code editor. Then follow the instructions below:

**Installing Nodemailer**
To install nodemailer, run the following command from your terminal.

```bash
    yarn add nodemailer //using yarn
    
    npm install nodemailer //using npm
```

Once that's done, run the following commands to create an `api/emails/services/Email.js` file in your Strapi code base

```
    cd api
    mkdir emails
    cd emails
    mkdir services
    cd services
    touch Email.js
```

Open up the `Email.js` file and fill it up with the following code

```
    const nodemailer = require('nodemailer');
    const transporter = nodemailer.createTransport({
      service: 'gmail',
      port: '465',
      secure: true,
      host: 'smtp.gmail.com',
      auth: {
        user: process.env.USERNAME,
        pass: process.env.PASSWORD,
      },
    });
    module.exports = {
      send: (from, to, subject, text) => {
        // Setup e-mail data.
        const options = {
          from,
          to,
          subject,
          text,
        };
        // Return a promise of the function that sends the email.
        return transporter.sendMail(options);
      },
    };
```

Next up locate the `.env/example` file and rename it to `.env` the add the following lines.

```
    USERNAME=<YOUR_EMAIL>
    PASSWORD=<YOUR_PASSWORD>
```

Remember to replace both `<YOUR_EMAIL>` and `<YOUR_PASSWORD>` with your actual email address and password.

In order to use the email function in our code, open up the `api/guests/controllers/guest.js` file and fill it up with the following code

```
    'use strict';
    /**
     * Read the documentation (https://strapi.io/documentation/developer-docs/latest/development/backend-customization.html#core-controllers)
     * to customize this controller
     */
    module.exports = {
        async create(ctx) {
            
            let { fullname: name, email, roomNo, checkIn, leaveDate } = ctx.request.body
            
            strapi.services.email.send(
                'alecgee73@gmail.com',
                email,
                'Room booked successfully',
                `Hello ${name}, Welcome to Mars hotel,you booked ${roomNo} from ${checkIn} to ${leaveDate} enjoy yourself`
            )
                .then((res) => console.log(res))
                .catch(err => console.log(err))
            ctx.send({
                guest : await strapi.query('guests').create(ctx.request.body)
            })
    
        }
    };
```

What we're doing here is modifying the Strapi to `create` a function to do the following;


- Call the email service we created using the `strapi.services.email.send()` function, to send an email to our new guest,
- Create the new guest in our database using the `strapi.query().create()` function, 
- Finally returning the created guest using `ctx.send()` function.

Now whenever a new guest is created, they will receive an email from the hotel booking system.


## Conclusion

What we've seen so far is how to create a simple hotel booking system using Strapi. You could do more, add more features and tailor the Application according to your needs. No matter what front-end framework you are using, the Strapi logic remains the same, and that's nice, Be sure to do more and explore.