DEV Community

Douglas Berkley
Douglas Berkley

Posted on • Updated on

Updating a Booking Status in Rails

When building an Airbnb clone app, we often have a booking model in our application.(And hopefully you have a default status.). It should look something like this:

Image description

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 that 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]
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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.

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

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.

Image description
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:

<div class="d-flex">
  <%= 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-light' %>
  <% end %>
</div>
Enter fullscreen mode Exit fullscreen mode

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.
⚠️ If you are using status as an enum, you don't need this. You already have this method available.

# booking.rb (model)
def pending?
  status == 'pending'
end
Enter fullscreen mode Exit fullscreen mode

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 %>
Enter fullscreen mode Exit fullscreen mode

We should get some bookings like this now:

Image description

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-light' %>
          <% end %>
        <% else %>
          <p class='booking-tag'><%= booking.status %></p>
        <% end %>
      </div>
    </div>
  <% end %>
</div>
Enter fullscreen mode Exit fullscreen mode

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;
  }
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)