Updating a Booking Status

How to build accept/reject buttons for a booking

This is one of the most frequent tickets I receive. I'm going to start under the assumption you have a booking model in your application. (And hopefully you have a default status.). It should look something like this:

Bookings table

Really the only thing we're interested here is how to change the status to accepted or rejected as the owner of the offer. An AirBnb host isn't going to accept every offer than comes through. So how does this work? Do we create buttons? Links? Separate controller actions for accept and reject? No. I've seen a lot of different ways people try to solve this, but it's best to keep it simple.

We need a form. This form is just going to update our booking. This allows our bookings#update controller action to handle both the accepting and rejecting.

Setup the Route

Let's make sure we have the correct route in our routes.rb. This shouldn't be nested since the bookings already have an ID.

# routes.rb
resources :bookings, only: [:update]

Configure the Controller

Let's add the update action in the bookings controller (along with the strong params if they're missing). This is just standard CRUD, nothing special here.

# bookings_controller.rb
def update
  @booking = Booking.find(params[:id])
  if @booking.update(booking_params)
    # redirect_to # up to you...
  else
    # render # where was the booking update form?
  end
end

private

def booking_params
  # TODO: check your model, might be different than mine
  params.require(:booking).permit(:status, :start_time, :end_time)
end

Build your View

Now to the tough part. Let's go to your view where you want to accept and reject. This can be a couple places depending on how you build your routes.

I'm going to make another assumption here. You're either iterating over @bookings or something like current_user.bookings_as_owner. If you don't have the owner bookings setup yet, you can read the Kitt tutorial (Le Wagon students only for now).

<% @bookings.each do |booking| %>
  <!-- with the booking card here -->
<% end %>

You can design your cards how you see it fit with your application. But I'll let's just use a simple one to get started.

Douglas Berkley
Dec 12, 8:00am - 11:00am
Yann Klein
Dec 13, 10:00am - 11:30am

Okay now for those "buttons". We don't want just a normal button or link though. This is where we need the booking update form. Let's use simple_form_for (check out the docs here).

We're going to need two forms for each action. And we're going to hide the new status as a hidden field. The "button" is going to be the submit for the form. Like this:

<%= simple_form_for booking do |f| %>
  <%= f.input :status, as: :hidden, input_html: { value: 'accepted'} %>
  <%= f.submit 'Accept', class: 'btn btn-info' %>
<% end %>

<%= simple_form_for booking do |f| %>
  <%= f.input :status, as: :hidden, input_html: { value: 'rejected'} %>
  <%= f.submit 'Reject', class: 'btn btn-info' %>
<% end %>

Status Conditional

If your booking status is pending then we'll show the forms. Otherwise, we'll display the status.

Let's create a method in our model.
# booking.rb (model)
def pending?
  status == 'pending'
end

Now we can use that method in our conditional

<% if booking.pending? %>
  <!-- Display Forms-->
<% else %>
  <!-- Display Status-->
  <p class='booking-tag'><%= booking.status %></p>
<% end %>

We should get some bookings like this now:

Douglas Berkley
Dec 12, 8:00am - 11:00am
Yann Klein
Dec 13, 10:00am - 11:30am
Trouni Tiet
Dec 15, 9:30am - 11:30am

Accepted

Wrap Up

Now all we have to do is build these forms in our iteration.

ERB

<div class="bookings">
  <% @bookings.each do |booking| %>
    <div class="booking">
      <div class='booking-left'>
        <!-- if you're using cloudinary and activestorage -->
        <%= cl_image_tag booking.user.photo, class: 'avatar-bordered' %>
        <div class="booking-info">
          <div class="booking-info-title">
            <%= booking.user.name %>
          </div>
          <div class="booking-info-details">
            <%= booking.start_time.strftime('%l:%M %p') %> - <%= booking.end_time.strftime('%l:%M %p') %>
          </div>
        </div>
      </div>
      <div class="booking-actions">
        <% if booking.pending? %>
          <%= simple_form_for booking do |f| %>
            <%= f.input :status, as: :hidden, input_html: { value: 'accepted'} %>
            <%= f.submit 'Accept', class: 'btn btn-info' %>
          <% end %>
          <%= simple_form_for booking do |f| %>
            <%= f.input :status, as: :hidden, input_html: { value: 'rejected'} %>
            <%= f.submit 'Reject', class: 'btn btn-info' %>
          <% end %>
        <% else %>
          <p class='booking-tag'><%= booking.status %></p>
        <% end %>
      </div>
    </div>
  <% end %>
</div>

CSS

.booking {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin: 16px 0;

  .booking-left {
    display: flex;
    align-items: center;
  }

  .booking-actions {
    display: flex;
    align-items: center;
  }
  .booking-tag {
    margin: 1px 4px;
    padding: 2px 5px 0px 5px;
    background-color: #bfbfbf;
    color: white;
    border-radius: 8px;
    font-weight: lighter;
    font-size: 12px;
  }
}

.booking-info {
  display: flex;
  flex-direction: column;
  margin-left: 8px;

  .booking-info-title {
    font-weight: bolder;
    font-size: 1.2em;
  }

  .booking-info-details {
    font-weight: lighter;
  }
}