This video is available to students only

Building the UI of the Host page

We'll now begin to work on the form on the client application where a user can create (i.e. host) a new listing. The form we'll build will essentially be the UI of the `/host` route of our application.

We'll now begin to work on the form on the client app where a user can create (i.e. host) a new listing. The form we'll build will essentially be the UI of the /host route of our application.

The form isn't going to be very difficult to establish. We'll essentially provide different form inputs that represent the information we'll want the user to provide for their new listing. The entire form will be part of a single component called the <Host /> component.

If a user is either not signed in to our application or hasn't connected with Stripe, they'll be unable to see the form and instead will be told that they'll need to sign and connect with Stripe to create a listing.

In this lesson, we'll begin by establishing the UI of the entire /host page before we plan to introduce the hostListing mutation and the capability to run the mutation.

<Host />#

In the <Host /> component within the src/sections/Host/index.tsx file, let's import the first few Ant Design components we'll need. We'll import the <Layout /> and <Typography /> components. We'll destruct the <Content /> sub-component from <Layout /> and we'll destruct the <Text /> and <Title /> sub-components from <Typography />.

In the <Host /> component return statement, we'll return the <Content /> component that is to contain a <div /> element that further contains a <Title /> that says "Hi! Let's get started listing your place." We'll add some secondary text right below it that says "In this form, we'll collect some basic and additional information about your listing".

If we were to take a look at our app now, we'll see the title section we'll want to show for this page.

As we mentioned, we only want the user to create a listing in this /host page when they're logged in our application and connected with Stripe. The viewer state object we have in the parent <App /> instance has information for both of these cases. If the viewer is not logged in, there won't be a viewer.id field (or any other field in the viewer object). If the viewer is not connected with Stripe, the hasWallet field in the viewer object will return false. With that said, let's pass this viewer object down as props to the <Host /> component rendered in the /host route from the parent <App /> component.

We'll employ the render props pattern to render the <Host/> component and we'll pass the viewer state object down.

In the <Host /> component file, we'll declare that the viewer prop is expected to be passed in. We'll import the Viewer interface from the src/lib/types.ts file to describe the shape of the viewer object prop.

In the <Host /> component, we'll check if the viewer.id doesn't exist or the viewer.hasWallet field isn't true. If either of these conditions is met, we'll have our component return a <Title /> that says "You'll have to be signed in and connected with Stripe to host a listing!" and some secondary text that says "We only allow users who've signed in to our application and have connected with Stripe to host new listings". We'll also provide a link to the /login route of our app to tell the user that they can log-in from the login page. To provide a link to the /login page, we'll be sure to import the <Link /> component from react-router-dom.

If we were either not logged in OR not connected with Stripe, we'll see the title and text notifying us that we'll need to log-in and connect with Stripe.

We want the user to be logged in since every listing that is created needs to be attached to a certain user in our application. We want users to be connected with Stripe since this is the only way users can receive payments for their listings. However, we can't prevent them from disconnecting from Stripe after they've already created their listing which is why later on when we build out the booking capability, we'll look to prevent a user from booking a listing where the host has pulled away their Stripe information.

Host Form#

We'll now set up the UI for the form we want to show in the /host page. Forms and form validations in client-side applications are an interesting topic. Often, they begin through very simple means but can become very complicated, very quickly. This is exacerbated when it comes to thinking about how we want to deal with form-level and/or field-level validations within forms.

We're going to utilize an important advantage thanks to a pretty powerful component Ant Design gives us, called the <Form /> component. The <Form /> component from Ant Design doesn't necessarily give us the UI for certain form elements, like a radio input or a checkbox since other components exist for this, but it provides the capability to validate fields with certain rules AND to collect information.

The <Form /> component from Ant Design provides a useful <Item /> sub-component that helps display a form label, help text, etc. As of the current version of Ant Design, v3, when this lesson was being screencast, the <Form /> component also contains a function, labeled Form.create() that acts as a Higher Order Component Function that receives a component and produces another component where a form prop object is provided. This form object provides the capability to introduce validations on form fields and capture values from the form fields.

Let's look to see how this <Form /> component can help us. We'll first import the <Form /> component from Ant Design and wrap the uppermost <div /> element in the <Host /> component return statement (i.e. the template shown to the user when they've logged in and connected with Stripe). We'll specify in the layout prop for the <Form /> component that the form is going to be in the "vertical" layout.

We'll also destruct an Item sub-component from the Form component.

Title Input#

The <Form /> component from Ant Design doesn't provide the actual UI elements we'll show, like inputs or radio buttons, and those are to come from different components we'll import and use. We'll import and use the <Input /> component to capture the title of the listing.

We'll place the <Input /> component and provide a placeholder of an example of a title, something like "The iconic and luxurious Bel-Air mansion". Remember on the server, we added a server-side validation where the title can only have a maximum number of 100 characters. The server-side validation acts as a safeguard and on the client, we'll be more restrictive and add a maxlength of 45 characters for the title input. We'll then use the <Item /> sub-component from the form to provide a label to this form element saying Title and some extra help text that says "Max character count of 45".

If we take a look at our page now, we'll see the form input and we'll see the form label and extra help text shown along with it.

Description TextArea#

We'll display another input but this time we'll display the <TextArea /> sub-component within <Input /> to have a textarea be shown to capture more text for the description of the listing. We can declare the number of rows in this textarea to be 3 and we'll provide a max length of 400 characters (less than the server-side guard of 5000 characters). For the description input, we'll state a placeholder of an example description of a listing - something like "Modern, clean, and iconic home of the Fresh Prince. Situated in the heart of Bel-Air, Los Angeles.".

We'll wrap this <Input.TextArea /> with the <Item /> form component which is to have a title that says "Description of listing" and we'll provide extra help text of "Max character count of 400".

Address, City, State, Postal Code Inputs#

We'll look to display a series of other text inputs:

  • One to capture the direct address of the listing. We'll provide a placeholder here that says "251 North Bristol Avenue" and the the <Item /> label will be "Address".

  • Another to capture the city (or town). We'll provide a label of "City/Town" and an input placeholder of "Los Angeles".

  • An input to capture the state (or province). We'll provide a label of "State/Province" and an input placeholder of "California".

  • An input to capture the zip/postal code. We'll provide a label of "Zip/Postal Code" and an input placeholder of "Please enter a zip code for your listing!".

If we were to take a look at the /host page at this moment, we'll see the form inputs we've created presented and available to us. Great!

Price Input#

There are a few other inputs that are to be in our form that aren't direct text inputs. We'll look to capture the price value of the listing through an input but where the user is to only provide a value of a number.

With Ant Design, we can create a number input by using the <InputNumber /> component which can be used to help capture numbers between certain ranges. We'll import the <InputNumber /> component and use it to capture the price of the listing. We'll specify a minimum value of 0 and provide a placeholder of "120". In the form item label we'll say "Price" and for the extra help text we'll state "All prices in $USD/day".

If we took a look at the /host page now, we'll see a number input shown to us. If we type a negative number and click elsewhere, it'll be incremented to the minimum value of 0.

ListingType Radio Buttons#

When it comes to having the user decide between a listing type of a house or an apartment, we can leverage Ant Designs <Radio /> component to provide the user with radio inputs to pick one of two values for the listing type. We'll have these radio buttons be displayed right above the title input in the form.

Radio inputs don't have an inherent value so we'll have to provide concrete values for what each of these radio inputs will refer to when clicked. The two different listing types we have in our app are "APARTMENT" and "HOUSE". Instead of specifying these values directly, we can perhaps use the ListingType Enum available as a global type from our autogenerated TypeScript definitions. By using the ListingType Enum, it'll ensure we're providing a value that matches one of the available two.

We'll import the ListingType Enum from the globalTypes.ts file kept in our src/lib/graphql/ folder. We'll also provide icons to each Radio Button for presentational purposes. We'll import the <Icon /> component from Ant Design, and we'll import the iconColor constant we have in our src/lib/utils/ folder.

We'll use the ListingType Enum to declare the values of each radio button and we'll place a "bank" icon for the apartment option and a "home" icon for the house option.

Start a new discussion. All notification go to the author.