Updating a Booking Status in Rails
Tags: rails, css
Published: 2022-12-07 01:51:54

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ko6jz4gw4wo10ro00qzw.png)

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]
```

## 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`. 

```
<% @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.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rlgopstn9bkrfrgizkz4.png)
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](https://github.com/heartcombo/simple_form)).

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>
```

## 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
```
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:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tmlt4brsp2l45rxrcelj.png)

## 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>
```
### 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;
  }
}
```