This video is available to students only

Disabling booked dates on the client

In this lesson, we'll begin to work on the client-side to facilitate the booking of a listing. We'll begin by first disabling any dates in the listing page datepickers that have been previously booked by other users.

Gameplan#

We built the /listing/:id page a few modules before which is the page that surfaces and displays information about a certain listing. In the <Listing /> component rendered in the /listing/:id page, we retrieve the value of the dynamic id parameter available in the URL which we use as an argument for the listing query that allows us to retrieve information about a certain listing.

We also set up the component called <ListingCreateBooking />, rendered as a child of <Listing />, as the surface area where we allow a user to select the dates to check-in and check-out of a listing. We haven't built functionality further than that.

There are a few things we want to achieve here. When the user selects the check-in and check-out dates for a booking and clicks the "Request to book!" button, we'll want to surface a modal. This modal will be the confirmation modal where the user can confirm their booking and the dates they want to be booked. A summary of the total price it will cost them for the booking is to be displayed as well as an element where the user can provide their credit or debit card information to book their listing.

The element where the user will be able to provide their card information will be a component that we'll get and use from Stripe itself. There are a few reasons why this is helpful:

  1. The card payment component we'll use will ensure that the user is to provide valid credit or debit card information. If invalid information is presented, the component would have client-side UI to reflect what is incorrect. This won't be handled by us through custom means since it'll be taken care of by the component we'll use from Stripe.

  2. More importantly, when someone is to provide their payment information - it is sensitive information. By using elements provided to us from Stripe, we can create the capability for users to provide their information without us having to worry about handling sensitive card data on the client.

When the listing is confirmed to be booked through the "Book" action in the modal, this is where we'll fire the createBooking mutation and pass in the variables the createBooking mutation expects. The createBooking mutation expects an input that contains the id of the listing being booked, the source of the payment (which we'll get from the Stripe element), and the checkIn and checkOut dates being booked.

This are some of the main remaining things we want to handle. Additionally, there are a few other things we'll need to look into as well. In our server, we've provided some capability when a user shouldn't be able to book a listing. We'll also want to provide some client-side validations as well to prevent the user from even launching the booking modal if they shouldn't be booking the listing in the first place. For example, we can disable the check-in and check-out datepickers and the "Request to Book" button when:

  • A user is not signed in to our application.

  • A user attempts to book a listing of their own.

  • A user attempts to book a listing where the host of the listing has disconnected from Stripe. In this context, we'll be unable to facilitate the payment to the host so we shouldn't allow someone to make a booking.

When bookings have been made to a listing, the listing bookingsIndex object will be updated to reflect which bookings have been made for it. We'll need to update the check-in and check-out datepicker inputs to prevent users from booking dates that have already been booked!

Prevent user from booking a listing#

Since there are a couple of things for us to do, we'll take it step by step. We'll first look to handle the client-side checks for disabling the check-in/check-out datepickers and the `"Request to book!" button when the user should not be able to book a listing.

In the <ListingCreateBooking /> component in the src/sections/Listing/components/ListingCreateBooking/index.tsx file, we'll create a constant element called buttonMessage that we'll provide a value for "You won't be charged yet". This will be the message we want to show under the "Request to book!" button when a user can book a listing but is how we'll convey that this won't confirm the booking yet. We'll place the buttonMessage element within a <Text /> component that has the "secondary" type and a mark prop.

Prevent booking when user is not logged in#

We'll look to prevent a user from booking a listing if they aren't logged in. For this, we'll need access to the viewer object we have in our client app that keeps context of the status of the viewer (i.e. the user viewing the app). The parent <Listing /> component doesn't have the viewer object available so we'll need to pass it two levels down from the root parent <App /> component.

In the parent <App /> component, we'll employ the render props pattern to render the <Listing /> component for its route and we'll pass an additional viewer prop down.

In the <Listing /> component file, we'll declare that it is to expect the viewer prop object.

We'll declare the viewer prop as a value of the props argument and we'll pass it further down to the child <ListingCreateBooking /> component.

In the <ListingCreateBooking /> component, we'll specify it is to also accept a viewer prop object.

With the viewer object available in the <ListingCreateBooking /> component, we can check to see if the viewer is available by simply seeing if the id property of the viewer object is available. We'll set up a checkInInputDisabled constant in the component that will be true when viewer.id doesn't exist.

We can then say if the checkInInputDisabled value is ever true, so would be the checkOutInputDisabled value (i.e. if the user can't select a date for check-in, they shouldn't be able to select a date for check-out). If the checkOutInputDisabled property is ever true, the "Request to book!" button will be disabled as well. Finally, we'll say if the viewer.id value doesn't exist, the buttonMessage property will be updated to say - "You have to be signed in to book a listing!".

We'll then add the checkInInputDisabled property as the value for the disabled prop for the check-in <DatePicker /> input component.

If we were to now take a look at the /listing/:id page of a listing when not logged in, we'll notice the check-in datepicker input is disabled since viewer.id doesn't exist. The check-out datepicker input and the "Request to book!" button are also disabled. The text below the confirmation button will say "You have to be signed in to book a listing!".

Prevent booking when viewer attempts to book own listing#

The next thing we'll check for is that the viewer isn't attempting to book their own listing. From the GraphQL query we make for the listing information, the host field is to have the user information of the user who owns the listing. To verify a viewer isn't booking a listing of their own, we can check viewer.id isn't equal to the host.id.

In the parent <Listing /> component, we'll pass a prop labeled host that reflects the listing host.

In the <ListingCreateBooking /> component, we'll state that it is to accept a prop labeled host. We'll describe the shape of the host prop by importing the shape of the Listing data interface from the autogenerated types for the listing query and declare a lookup type to access the type of the host field within this listing data interface.

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