Using Bootstrap Panels to Organize Data in a Rails App

I’m creating a basic pantry application which will be able to catalog current items in the user’s pantry.  So, one of the pages needed will display different food items and the current quantities in the user’s pantry.  A way to organize this data is to use a Bootstrap Panel to display the current totals and when the selected panel is clicked, individual data values will be displayed.

The data structure consists of Store, Producer, and Unit models, which all just consist of a name.  Next, we have the Food model which has a name, upc, servings, serving_size, and references to a Producer and Unit.  Finally, we have the Stock model which has a price, quantity, discount, bought, and references to Food, Store, and User (for the User I’m just using the basic Devise User model).

Before we start sorting the data, we want to organize the data and get the totals.  The stock controller for creating the index looks like this:

 
  def index
    @stocks = Stock.where(user_id: current_user.id).order(:food_id,:bought).paginate(page: params[:page], :per_page => 10)
    @totals = Stock.select("food_id, SUM(price) as price, SUM(quantity) as quantity, SUM(discount) as discount").group(:food_id).order(:food_id)
  end

Now that we have the totals for each pantry item and the list of pantry items, we can begin displaying them.  Now, this isn’t the prettiest code and I couldn’t get it to work with haml.  But, this is what the index.html.erb looks like for my Stock view:

<h1>Listing Current Pantry</h1>
<% if @stocks.count == 0 %>
  <div class="row text-center">
    <h4>Your pantry is currently empty.</h4>
  </div>
<% else %>
  <%= will_paginate %>
  <div class="row">
    <div class="col-md-3 col-xs-3">Food</div>
    <div class="col-md-3 col-xs-3">Total Servings Left</div>
    <div class="col-md-3 col-xs-3">Total Amount Paid</div>
    <div class="col-md-3 col-xs-3">Total Discount Amount</div>
  </div>
  <div class="row">
    <div class="panel-group" id="#accordian">
      <% current_food, last_food = nil, nil %>
      <% @stocks.each do |stock| %>
        <% current_food = stock.food.id %>
        <% if current_food != last_food %>
          <% current_total = @totals.find_by(food_id: stock.food.id) %>
          <% if last_food != nil %>
            </div></div></div></div>
          <% end %><!-- end of closing divs -->
          <div class="panel panel-info">
            <div class="panel-heading">
              <h4 class="panel-title">
                <a class="accordian-toggle" data-toggle="collapse" data-parent="#accordian" href=<%= "#food_data_#{current_food}"%>>
                  <div class="row">
                    <div class="col-md-3 col-xs-3"><%= stock.food.name %></div>
                    <div class="col-md-3 col-xs-3"><%= current_total.quantity %></div>
                    <div class="col-md-3 col-xs-3"><%= number_to_currency(current_total.price-current_total.discount, precision: 2) %></div>
                    <div class="col-md-3 col-xs-3"><%= number_to_currency(current_total.discount, precision: 2) %></div>
                  </div>
                </a>
              </h4>
            </div>
            <% id = "food_data_#{current_food}" %>
            <div id="<%=id%>" class="panel-collapse collapse">
              <div class="panel-body">
                <div class="container">
                  <table class="table table-condensed">
                    <thead>
                      <th>Current Quantity</th>
                      <th>Price Paid</th>
                      <th>Discount</th>
                      <th>Store</th>
                      <th>Date Bought</th>
                    </thead>
                    <tbody>
        <% end %><!-- end of panel info -->
                      <tr>
                        <td><%= stock.quantity %></td>
                        <td><%= number_to_currency(stock.price-stock.discount, precision: 2) %></td>
                        <td><%= number_to_currency(stock.discount, precision: 2) %></td>
                        <td><%= stock.store.name %></td>
                        <td><%= stock.bought %></td>
                      </tr>
                      <% last_food = current_food %>
      <% end %><!-- end of loop -->
                    </tbody>
                  </table>
                </div><!-- end of container -->
              </div><!-- end of panel-body -->
            </div><!-- end of panel-collapse -->
          </div><!-- end of panel -->
    </div><!-- end of panel-group -->
  </div><!-- end of row -->
  <%= will_paginate %>
<% end %><!-- end of display for visible pantry items -->
</br>
<div class="row">
  <% if can? :create, @stocks %><%= link_to "Add Food to Pantry", new_stock_path, {:class => 'btn btn-primary'} %><% end %>
</div>

As you can see, the problem with getting it to work with haml is that you have to manually close the table and panel after you switch food items. The basic flow is to create the panel and table head whenever the current food item changes.  Then, for each pantry item we create the table row with current information from the pantry item.  Finally, once we’re done we have to close all the tags.

Since this code uses bootstrap 3, you can view the differences in the new version on the main Bootstrap page.  For more information on panels and panel styles check out the information here.

One thought on “Using Bootstrap Panels to Organize Data in a Rails App

  • April 22, 2016 at 1:43 pm
    Permalink

    Greetings! Very helpful advice in this particular article!
    It is the little changes that will make the largest changes.
    Thanks for sharing!

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *