Online Food Ordering App (8)
Tags: node, express, react, reactnative
Published: 2021-04-12 20:41:43

*<span>Photo by <a href="https://unsplash.com/@abillion?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">abillion</a> on <a href="https://unsplash.com/s/photos/food-mobile-app?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span>*

<br/>
Hi, welcome to the last post of Online Food Ordering App series.<br/>

In the previous posts we built API endpoints for authentication and managing orders. We've also built our frontend react and react-native apps and connected them to the API endpoints.<br/>

In this post, we are going to implement the view orders list, view single order, update order status, view menu, and place order features on our front-end apps.<br/>

***
| Project Steps |
| ----------- |
| [1. Backend - Project Setup](https://dev.to/the22mastermind/online-food-ordering-app-1-594j) :heavy_check_mark: |
| [2. Backend - Authentication - Sign Up](https://dev.to/the22mastermind/online-food-ordering-app-2-28po) :heavy_check_mark: |
| [3. Backend - Authentication - Login & Logout](https://dev.to/the22mastermind/online-food-ordering-app-3-1g8h) :heavy_check_mark: |
| [4. Backend - Place Order](https://dev.to/the22mastermind/online-food-ordering-app-4-5dc5) :heavy_check_mark: |
| [5. Backend - View Orders List & View a Specific Order](https://dev.to/the22mastermind/online-food-ordering-app-5-24gg) :heavy_check_mark: |
| [6. Backend - Update Order]() :heavy_check_mark: |
| [7. Front-end - Authentication]() :heavy_check_mark: |
| [8. Front-end - Place Order, View Orders List, and View Order Details]() :pushpin: |

***

Before we start, take a look at [this PR](https://github.com/the22mastermind/gourmet-api/pull/12) and update the [backend code](https://github.com/the22mastermind/gourmet-api). We added the payments endpoint, a script to create the menu and we updated the fetch orders list to accommodate both the admin and the customer.<br/><br/>

**Mobile app**

Before we begin our implementation, let's think for a minute about the user flow we want for our customer.<br/>

*A logged-in customer launches the app and they immediately see a list of menu items divided into 3 tabs (Breakfast, Lunch/Dinner, and Drinks). Each item has an image, a name, a short description, a cost/price, and size. To switch to a different tab, a user swipes the screen left or right, or they tap on the tab name. To add an item to the cart, a user simply taps on the item. Tapping on the item already in the cart increases its quantity by 1. To remove an item from the cart a user simply taps on the item from the cart screen. From the cart screen, a user can navigate to the payment screen where they will be able to confirm their order by making payment by card.*<br/>

A user can also see the list of orders he/she has placed and their details by tapping on the basket icon in the bottom navigator. Finally, a user will be able to see his/her account information by tapping on the account icon in the bottom navigator.<br/>

The screens of our app will be divided into 2 main parts (`AuthStack` and `HomeStack`) whereby `AuthStack` will contain all the screens related to authentication (`LoginScreen`, `SignupScreen`, and `VerifyScreen`) and `HomeStack` will contain nested stacks (`MainStack`, `OrdersStack`, and `AccountStack`).<br/>

`MainStack` will contain screens allowing the user to view the menu, interact with the cart, and make a payment.<br/>

`OrdersStack` as the name suggest will contain screens for viewing the list of orders a user has placed and each order details.<br/>

`AccountStack` will contain just one screen to display the user's account information and a logout button.<br/>

<br/>

Great! Let's get started.<br/>
<br/>

- Install the dependencies we are going to need:
```
yarn add react-native-dotenv react-native-credit-card-input react-native-paper-tabs react-native-pager-view @react-navigation/material-bottom-tabs react-native-stripe-payments
```

- In the context directory, create a `dataReducer.js` file and paste the following code inside:

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y2xu20jug9qnhwcukbyl.png)

*`ADD_MENU`*: will receive an array of menu categories and their items and will save it in our state in a variable called menu.<br/>

*`GET_MENU`*: will receive a category name then loops through the menu categories to find if that category exists then will save it's items in a variable called menuItems.<br/>

*`ADD_TO_CART`*: will receive a menu item and append it to a variable called cart.<br/>

*`UPDATE_CART`*: will receive an item then checks if that item is in the cart before replacing it with the new item.<br/>

*`REMOVE_FROM_CART`*: will receive an item id then loops through the cart array to find an item with that id and delete it.<br/>

*`CLEAR_CART`*: will remove all items in the cart array.<br/>

*`ADD_ORDERS`*: will receive an array of orders list the save it in the state in a variable called ordersList.<br/>

- Create a `DataProvider.js` file and paste the following code:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7lp5qk1qbn7nffbp5vls.png)

- Create a `src/navigation/menuTabs.js` file like this:
 ![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w7mwx0nj5uy1uakjzktw.png)

In this file we create a `MenuTabs` component that receive 2 props: `menuItems` (an array of menu items for the selected category) and `handleChangeIndex` (a function to switch tabs). We created a `handleAddTocart` function which will help us to modify an item before adding it to the cart and to dispatch messages after the item is added to the cart.<br/>

The component returns 3 tab screens where each tab screen will use the [ListItems](https://github.com/the22mastermind/gourmet/blob/main/src/components/ListItems/ListItems.js) component to display the data or the [`CustomCaption`](https://github.com/the22mastermind/gourmet/blob/main/src/components/CustomCaption/CustomCaption.js) component to display that items were not found. Also, each tab screen is associated with an index number starting from 0. We'll see how this index number will be useful in a minute.<br/>

Let us now create the main screen and use the menu tabs we just created above.<br/>

- Create a `src/screens/MainScreen/MainScreen.js` file like this:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/159adosptqjq0rqlhyt1.png)

In this file we created a `MainScreen` component that fetches user data, cart and menu items from our global state. We created a `handleChangeIndex` function that receives an index number (tab screen index) and dispatches a function that will trigger the `GET_MENU` action. We used the useEffect hook to trigger the handleChangeIndex function when this component mounts to get data for the first tab screen.<br/>

This component will render a welcome message, the user's address, the menuTabs component and the [`CartButton`](https://github.com/the22mastermind/gourmet/blob/main/src/components/CartButton/CartButton.js) component to view the cart contents if the cart is not empty.<br/>

Let us now create the last screen for `MainStack`.<br/>

- Create a `src/screens/PaymentScreen/PaymentScreen.js` file like this:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wyuy8qbkl8nqhsl6oteu.png)

In this file we created a `PaymentScreen` component that has 2 functions: `handlePaymentInit` and `handleCreditCardForm`. When this component mounts, it displays a title, an image of credit/debit cards accepted, and a button to make payment. When the button is clicked, it triggers the `handlePaymentInit` function which triggers an internal state update of `showCardForm` to display the [`CreditCardForm`](https://github.com/the22mastermind/gourmet/blob/main/src/components/CreditCardForm/CreditCardForm.js) component.<br/>

The `CreditCardForm` component receives an `onChange` props which is a function that gets executed as we fill the form and returns a `formData` object composed of 3 properties: `valid`, `values`, and `status`. We are interested in `valid` and `values` properties.<br/>

`valid` is a boolean which will be true once all the fields of the form are filled correctly.<br/>

`values` is an object of the form field values. It has the following properties: `number` (card number), `expiry` (MM/YY), and `cvc` (3-digit cvc/ccv). Learn more [here](https://github.com/sbycrosz/react-native-credit-card-input).<br/>

So, in the `handleCreditCardForm` function we check if the user has filled the form correctly, then we extract the form values and build a `cardDetails` object. We then proceed to validate the cardDetails object by using the `isCardValid` method from [react-native-stripe-payments](https://github.com/Fitpassu/react-native-stripe-payments).<br/>

If the cardDetails are valid, we hit our API endpoint for payments to create a `paymentIntent`. The payment intent returned from calling our API contains a `clientSecret` string that we use along with the cardDetails object to confirm the payment with stripe.<br/>

If the response returned from confirming the payment contains an id, that means the payment is successful then we proceed to prepare the payload for the order and hit our backend endpoint to place an order. If the order is placed successfully, we reset our stack navigation then navigate to ordersListScreen.<br/>

**NOTE:** *this implementation is a bit naive because there are some edge cases we did not account for, for example, what if the payment is successful but the order cannot be placed? What happens then?*<br/>

One solution would be to extend our order statuses and allow a user to place an order before making the payment then once the payment is confirmed we approve the order.<br/>

Cool!<br/>

Lastly, we wrapped everything in a try and catch so if anything goes wrong the user will be notified via the `Alert` component.

**NOTE:** our services in `src/utils/api.js` were starting to become messy, so we refactored the code to look like this:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c5qiwf7nyt1vn1vy6bsc.png)
 
**Make sure you update the authentication feature to use the updated services as well.**<br/>

The screens for our MainStack are now done. Let us implement `OrdersListScreen` and `OrderDetailsScreen` for `OrdersStack` next.<br/>

- Create a `src/screens/OrdersListScreen/OrdersListScreen.js` file like this:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7botjynvjonpd0ymp0vs.png)

In the `OrdersListScreen` component we used the useEffect hook to add a `focus` event listener which will be triggered every time this screen is focused. In the event listener we fetch the list of orders and dispatch an action to save the data in `ordersList` global state variable. The component will display the list of orders if found or a no orders found text. We have also implemented a `handleOrder` function which will receive an id then navigates to `OrderDetailsScreen`.<br/>

- Create a `src/screens/OrderDetailsScreen/OrderDetailsScreen.js` file like this:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9g60jl9w9xvwyt0c587u.png)

In this component we fetch an order's details by using the orderId parameter from props, save the data in internal state variable orderDetails then we render the information.<br/>

The screens for `OrdersStack` are now done. Let us create the one screen for `AccountStack`.<br/>

- Create a `src/AccountScreen/AccountScreen.js` file like this:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1i01etpzg0pe2vsbmf9t.png)
 
In this component we are just displaying the user information from the global state variable `auth`. We have also moved our logout implementation in this component.<br/>

Now that our screens are done, let us create the stacks mentioned above.<br/>

- Inside `src/navigation` create a new directory called `stacks` and inside stacks create the following files: `MainStack.js`, `OrdersStack.js`, and `AccountStack.js`.<br/>

- Inside `AccountStack.js` paste the following:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mb9tefwhdk94wo1e27hn.png)

- Inside `OrdersStack.js` paste the following:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pacpspiy6wa6avson7m2.png)
  
- Inside `MainStack.js` paste the following:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wyprchwl4held8uf1one.png)


The last piece of the puzzle is to put together the stacks we created above and add the data context provider in `App.js`.<br/>

Let's do it.<br/>

- Move `HomeStack.js` in `src/navigation/stacks/` and update it to look like this:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ciplm7izg34kl58c432e.png)

Let's go over what we did in this file.<br/>

`HomeStack` is the component that will be mounted as soon as a user logs in or when a logged-in user launches the app and he/she is authenticated. There are a couple of things we want to do before this component renders:<br/>

1. We need to fetch the menu and save it in our global state.
2. While fetching the menu, if the user's token happens to be expired (from the backend) we automatically log out the user (on the frontend).
3. If the user's token is valid and the menu data is found or not, we proceed to render the tab navigator.<br/>

Finally, update `src/App.js` to look like this:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xegw2hk9i3xs02zuj43b.png)

- Run the app on an emulator or a physical device and you should see the screens below:

**MainScreen**
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gyvgz574iacf5o81hlmg.png)

**OrdersListScreen**
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1owj684u19l4qghoxg0b.png)

**OrderDetailsScreen**
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rxmp6xcf6jjj9nmthbda.png)

**AccountScreen**
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/addadrz3gnko7n3qig3h.png)

<br/>

*To create a splash/launch screen, check out [this article](https://blog.logrocket.com/building-a-splash-screen-in-react-native/)*.<br/>


For reference, here's [the repo](https://github.com/the22mastermind/gourmet) for the project.<br/>

<br/>

***

<br/>
**Admin view orders list, view single order and update order**

<br/>
For the admin app we are going to use [Material UI's Collapsible Table](https://material-ui.com/components/tables/#collapsible-table) component to display the orders. Each row in the table will have a button to reveal the details where the admin will be able to see the contents of an order along with a `Update status` and `user info` buttons to update the status of the order and view the details of the user respectively.<br/>

We have also implemented pagination to 5 rows per page but you can change this value according to your needs.<br/>

Great. Let's start by installing React Spring to add small animations to our app.<br/>

- Install React Spring:
```
$ yarn add react-spring
```

- Update `src/utils/api.js` to look like this:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xbslybytn5iagu2j9lpx.png)

- Create a `src/pages/OrdersListPage/OrdersListPage.js` file and paste the following inside:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3ryitf5rt2z6kr6d74b0.png)
 
In this component we fetch the list of orders from the backend then use the [CustomTable](https://github.com/the22mastermind/gourmet-admin/blob/main/src/components/CustomTable/CustomTable.js) component to display the data.<br/>

we have also used the `useSpring` hook from [React Spring](https://www.react-spring.io/docs/hooks/use-spring) to add a fade animation to our component.<br/>

Among the new [components](https://github.com/the22mastermind/gourmet-admin/tree/main/src/components) we created include [CustomTableRow](https://github.com/the22mastermind/gourmet-admin/blob/main/src/components/CustomTableRow/CustomTableRow.js), [CustomTableFooter](https://github.com/the22mastermind/gourmet-admin/blob/main/src/components/CustomTableFooter/CustomTableFooter.js), and [TablePaginationActions](https://github.com/the22mastermind/gourmet-admin/blob/main/src/components/TablePaginationActions/TablePaginationActions.js) and the result will look like the image below:
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xbhidbuveq5hudxv7ik2.png)

And that's it, the admin will now be able to view and update orders.<br/>

**NOTE:** *There are lots of features we can add to improve our app. For instance, the first page of the dashboard could contain a summary or an overview of the data in our app like the number of orders for a given period of time, the amount of profit made, the most bought items, etc. We could also leverage the power of websockets to make our app show real-time data or add notifications for when an order is placed or any other action.*<br/>

<br/>

This concludes our series.

<br/>

To recap, we've built a REST API using Node, Express, and Postgres then we built frontend apps in React and React-Native to use the API. We've also covered JWT authentication, unit, integration, and end-to-end testing along with continuous integration and continuous delivery (CI/CD).<br/>

<br/>

I hope this series was useful to you. If you have a question, a comment, or a suggestion let me know in the comment box below.<br/>

<br/>

Thank you for your time, until next time, cheers!<br/>