In this part, I will recall how or frontend will be interacting with our backend regarding the Chatbot and Recommendation System and explain how you can implement a bubble-type chat 💬
What data we will be fetching
So remember what we had in part 1 & 2. We were able to accomplish two high-level functions:
- Respond to any user query using Natural Language Understanding a pseudo-random rules 📐
- Return back results to the users about either product Recommendations (here restaurants), or about chatbot-like queries 🤖
So how can we develop an interface for these 2 functionalities ?
Well, the most natural thing to do was to split our UI into two parts, one for interacting with the Chatbot, and the other to display the current results.
In this part, we will focus on the interactable part and implement a chat system. 🗣️
Creating a bubble-like chat with React
Okay, we now know what will compose our UI, and we also have some data to play with from our backend.
Remember that either with the deployed version of our API on Heroku, or with the local Docker configuration, we have a way to reaching the Chatbot endpoint at the POST endpoint:
We will refer to that endpoint from now on, since we are focusing on the frontend part 😎
Now we need to create a chat system.
So at that point, a good general practice is to mark clearly what we want the user to be able to accomplish with the chat.
In our case, and in most chatbot systems, the bubble-type chat is the way to go. So we wanted to mimic the features of what might be the best chat experience so far, Messenger (by Facebook).
Of course, the left part is not relevant because we will only be interacting with one entity, the Chatbot.
Then, we decided not to implement the seen functionality as it didn’t make much sense in that case, nor the new reaction feature.
So what came to our mind was a simplified version, that looked like this:
So we will dive into each part of this design and implement it.
There are essentially 3 parts here:
- The Header of the Chat, a stateless basic component.
- The Bubble Container along with its child bubbles, for the user to keep a record and get responses from the Chatbot
- The sending part, critical to the user-chatbot interaction
We chose React as the library for building the UI as we had a bit of experience with it, and just has been an amazing developer experience from the beginning, and even more with the introduction of React Hooks. 🎣
Here are some resources to get you started on the subject:
Back to our business, let’s implement our 3 parts. 👷🏻
Just as a note, the client service (that we are currently building) was initialized with CRA.
Before we begin, I had to show our main App Component:
Here we use the React Context API for sharing both userLocation and restaurants between the 2 parts of our application, but notice the
We created it a Hooks folder containing all the Hooks that we could isolate or reuse in the app. Here is the implementation of the
We make use of the native JS navigator object, available from the browser with any HTTPS connection. 🕸️
Focusing back to the 3 parts of our
Chat component, here is the rather straightforward implementation of the
Notice the use of
React.memo() around our components, as we want our function component to render the same result given the same props.
Now for the
Bubble component, we noticed 3 things:
- A bubble could have 3 states, either
THINKING. It is a way to differentiate them and to position appropriately them on the DOM.
- According to their states, there was a different look and implementation for each bubble.
- The bubbles from the
BOTentity could either be made of text, or made of an image such as a GIF for example.
Here is the implementation of that logic:
Now that we have covered the different styles and basic components of the
Chat component, let’s jump into its core logic. 🧠
There are a few things that we have to figure out before being done with the
Chat component. We need to figure out the flow of the user sending a message and calling our API as well as the flow of the bubbles and their arrangement in the
Before diving into how to implement those logics, we had to precise that we used a
useReducer hook to handle the state of our
Chat component, because multiple values in the state are related to each other, and it represented a cleaner approach overall:
Notice that we are getting the
userLocation data and
setRestaurants setter from the context, as we shared them through our
App component. ↗️
We are also storing what the user is typing into a ref, so that we can share its value across the
textareacomponent and the
Chat one without overhead.
So we can get now a cleaner overview of what actions could be performed in this component, and they go in hand with the two logics previously mentioned.
So now, how do we detect when the user wants to send a message?
Basically, you can extend some of the functionalities of a simple
textarea by auto-resizing the writing box as the user types, and knowing when he wants to send a message by listening for specific keyboard keys ⌨️
Here is the hook taking care of that, implemented in our
So when we know that the user wants to send a message, we update the
ref value and notify the parent component. 📨
But now, how to reproduce the traditional workflow of a Chatbot?
We came up with a basic behavior that goes like this:
User sends message -> update bubbles UI -> add a
THINKING bubble -> send the request to the Node API -> display response and results
To accomplish that, and starting when we know the user wants to send a message, we have a useEffect hook listening for the
shouldSendvalue in the
It does update the UI, displaying our sent message and the famous
THINKING bubble of the bot.
We now have a
useEffect hook for that specific bubble. Remember, we stated that if we encounter a
THINKING bubble, then we need to ask our query to the bot. 🤖
So here it is:
It is important that we check for the
userLocation as some user queries like “Show me the best restaurants around me” have to access this data.
So now, we only have to display the bot’s response as a bubble and to display the results (restaurants) on the other side.
In the next part, we will look more into how you can approach to display the results that we got from the Yelp API. 🦞
So far, we saw how to handle the main user-based interactions in our application, and it’s now time to focus on how to envision and display the results coming from the Chatbot ⚡
Prototyping our design
Basically, the data we are receiving is quite homogenous in its structure, because the restaurants the Chatbot is sending are coming from the Yelp API and we know what properties and datatypes to expect.
Given that, we always like to prototype our design before diving into coding almost blindly. We noticed that trying to implement the interface while still picturing it yields something like this:
So to overcome that issue, we found a tool that works really well because of how easy it is to get started, and how fast and reliable it is when working in teams. 💪🏻
We started prototyping our restaurants with it:
We started at a high level and just wanted to get the layout consistent and good looking. We ended up agreeing on something like:
It is essential to not go too deep in prototyping because usually, while coding, you encounter some little challenges that make you partially change the design, and you can just implement them while coding, having a basic idea with Figma
We also knew that a basic filtering feature would be nice, but we first started by implementing the restaurant containers 🧰
Implementing the Data Containers and Distance Filtering
We have two main components that we will discuss,
Restaurant . Obviously, the first is taking care of displaying all the restaurants and the latter is the look of 1 restaurant.
Let’s start with the
Restaurantcomponent, at a high level, here is how he’s implemented:
Few things to note:
- We are using react-icons because it really simplifies how you handle icons in a React project.
- We have integrated each of the restaurants with both its location (via Google Maps) and with its actual link on the Yelp website.
- We are also using react-tilt to wrap our restaurant because it simply makes them live, see below for an example
Now let’s look at the
Like before, 2 main takeaways:
- We have three states for the container’s content. No restaurant found, we display an empty placeholder from the awesome undraw.co website. Loading state, we display the same layout as our
Restaurantbut using Skeleton placeholders for a nice preload effect. Normal state, we map over our restaurants and display the
- We have a way for the user to sort the results on various criteria in our component and we will dive briefly into just below
You can almost with any kind of data implement a way to sort it. 🔢
Here are the sorts we implemented:
The most difficult one to implement was the sort by distance, and that is precisely why we needed to have the context API sharing the
For a large amount of data, it is recommended to have the sorting logic server-side but since we were limiting the number of results, we implemented it in a custom file:
We have a basic difference sort for the rating, a simple sort for the price but handling the fact that quite a lot of restaurants had no price data.
For the distance sort, we had to calculate the coordinate distance between the user’s location and the one of the restaurant.
Let’s now move on to the final part, deployment! 🚀
Deployment and serving the frontend
This part will actually be less complicated and verbose than the one for the backend, since the is already a lot of solutions for hosting bundled apps for free.
Locally, we just add a client service to our
docker-compose.yml file that you saw in part 2
But here it gets a bit tricky, since what we wanted for that service is to see the post-build result of the react-app, before deploying it it production.
That is why we defined a multi-staged build. Go ahead and read the information on the Docker website, because it is important to understand how it works. We are essentially serving the build result through nginx.
Now for production, it is actually quite simple, because of this amazing tool:
There is no config file to have in your project, and you can even pass the environment variables in a really simple way. You will also get the freedom of personalizing your domain name, for free! Below is a link to get you up and running 🏃🏻
That’s about it for the frontend part, hope you enjoyed it! 😊
That’s it for now! This is the end of the series on creating a framework on Chatbots and Recommendation Systems! 🧬
In case you need any help or have other questions about the project or really anything, I’m open to chat at firstname.lastname@example.org or any other social media.
As always, don’t hesitate to share 🔗 this post if liked it or felt you learned new things, and of course, don’t be afraid to experiment more! 🎉