Cleaning up
Now that we have both a post page and user page, we can start cleaning up the Cases Index page. A user who creates a lot of content will see too much visual clutter:
And a new user may want to see briefly what's inside, without too much focus on the descriptions or authors.
So let's do just that.
Extracting a case partial
Create a new file, app/views/cases/_case.html.erb
- this will hold a short preview of the case - we will use it on the Cases Index page and on the User page.
In this partial, for every case c
, we will display a panel, where the panel title is the title
of the case and in the panel body we display the case_image
. Both the title and the image link to the case page. We would also like to constrain this panel to 4 (our of 12) grid columns on large screens and 6 columns on medium screens.
Therefore, this goes to the app/views/cases/_case.html.erb
file:
<div class="col-lg-4 col-md-6 col-sm-6 col-xs-12">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title"><%= link_to(c.title, c) %></h3>
</div>
<div class="panel-body">
<%= link_to(cl_image_tag(c.case_image.path, width: 500, class: 'img-responsive'), c) %>
</div>
</div>
</div>
Save the file. Let's reuse this partial. Open the app/views/cases/index.html.erb
file.
Now you can replace the following code:
<ul>
<% Case.all.each do |c| %>
<div class="col-lg-4 col-md-6 col-sm-6 col-xs-12">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title"><%= link_to(c.title, c) %></h3>
</div>
<div class="panel-body">
<%= link_to(cl_image_tag(c.case_image.path, width: 500, class: 'img-responsive img-thumbnail'), c) %>
<p>
<%= c.body %>
</p>
</div>
<div class="panel-footer">
<span class="label label-default"><%= link_to(c.user.name, user_path(c.user), style: "color: white") %></span>
<% if current_user == c.user %>
<%= link_to(edit_case_path(c), class: 'btn btn-default btn-xs pull-right') do %>
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit
<% end %>
<%= link_to(case_path(c), method: :delete, class: 'btn btn-danger btn-xs pull-right') do %>
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete
<% end %>
<% end %>
</div>
</div>
</div>
<% end %>
</ul>
With something shorter, namely:
<%= render :partial => :case, :collection => Case.all, :as => :c %>
For reference, this is the full content of my app/views/cases/index.html.erb
file:
<%= render "shared/internal_navbar" %>
<div class="container">
<h1>Cases</h1>
<%= render :partial => :case, :collection => Case.all, :as => :c %>
</div>
Save the file and reload the Case Index page:
Let's make one more adjustment to prettify this section:
We would like to crop the images to a certain maximum height, to gain more regularity in the grid. For this, replace the line:
<div class="panel-body">
with:
<div class="panel-body", style="height: 400px; overflow: hidden;">
Having saved the file, we can now reload the Cases Index page and see the changes:
The grid looks more regular.
Since we've extracted the case panel in a partial, we can also use it on the User page. Open the file app/views/users/show.html.erb
and replace the following code:
<% @user.cases.last(20).each do |c| %>
<div class="col-lg-4 col-md-4 col-sm-6 col-xs-12">
<div class="thumbnail", style="height: 500px; overflow: hidden;">
<div class="caption">
<h3><%= c.title %></h3>
</div>
<%= link_to(cl_image_tag(c.case_image.path, width: 500, class: 'img-responsive'), c) %>
</div>
</div>
<% end %>
with
<%= render :partial => :case, :collection => @user.cases.last(20), as: :c %>
For reference, this is the content of my app/views/users/show.html.erb
file:
<%= render "shared/internal_navbar" %>
<div class="container">
<h2><%= @user.name %></h2>
<h3>Recent updates:</h3>
<ul class="list-group">
<% @user.updates.last(20).each do |u| %>
<li class="list-group-item">
<span class="label label-primary"><%= u.domain %></span>
<%= link_to(u.case.title, u.case) %>: <%= u.title %>
</li>
<% end %>
</ul>
</div>
<div class="container">
<h3>Recent cases:</h3>
<%= render :partial => :case, :collection => @user.cases.last(20), as: :c %>
</div>
After we apply the changes, this is what the user page looks like:
Extracting more partials
Panel display of updates
In preparation for the next chapter, let's extract some other partials.
Create the folder app/views/updates
and the file app/views/updates/_update.html.erb
.
Open the app/views/cases/show.html.erb
file and extract the panel display of an update to the update partial. For this, copy the following code:
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title"> <%= u.title %> </h4>
</div>
<div class="panel-body">
<%= simple_format u.body %>
</div>
<div class="panel-footer">
<span class="label label-primary"><%= u.domain %></span>
<span class="label label-info">External link: <%= link_to(u.external_link, u.external_link)%></span>
<span class="label label-default"><%= link_to(u.user.name, user_path(u.user), style: "color: white") %></span>
</div>
</div>
and paste it to the app/views/updates/_update.html.erb
partial.
Now, in app/views/cases/show.html.erb
, you can replace the following lines:
<div class="col-lg-6 col-md-12 col-sm-12 col-xs-12">
<% @case.updates.reverse_each do |u| %>
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title"> <%= u.title %> </h4>
</div>
<div class="panel-body">
<%= simple_format u.body %>
</div>
<div class="panel-footer">
<span class="label label-primary"><%= u.domain %></span>
<span class="label label-info">External link: <%= link_to(u.external_link, u.external_link)%></span>
<span class="label label-default"><%= link_to(u.user.name, user_path(u.user), style: "color: white") %></span>
</div>
</div>
<% end %>
</div>
with just:
<div class="col-lg-6 col-md-12 col-sm-12 col-xs-12">
<%= render :partial => :update, :collection => @case.updates.reverse, as: :u %>
</div>
What we did above was render the collection of a case's updates on 6 (out of 12) grid columns. Each update u
will be displayed according to the definition in the views/updates/_update.html.erb
file, that is, in a panel.
Full panel display of a case
Let's extract another partial: the panel displaying the case with the full body text, edit and delete buttons. Create a new file, app/views/cases/_case_large.html.erb
.
Open the file app/views/cases/show.html.erb
, copy the following block of code:
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title"><%= @case.title %></h3>
</div>
<div class="panel-body">
<%= cl_image_tag(@case.case_image.path, width: 500, class: 'img-responsive img-thumbnail') %>
<p>
<%= simple_format @case.body %>
</p>
</div>
<div class="panel-footer">
<span class="label label-default"><%= "Created by: " + @case.user.email %></span>
<% if current_user == @case.user %>
<%= link_to(edit_case_path(@case), class: 'btn btn-default btn-xs pull-right') do %>
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit
<% end %>
<%= link_to(case_path(@case), method: :delete, class: 'btn btn-danger btn-xs pull-right') do %>
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete
<% end %>
<% end %>
</div>
</div>
and paste it into the _case_large.html.erb
partial. Save the file. Now, you have to replace, everywhere in this file, the instance variable @case
with a local variable c
. (You can use the find and replace function of your text editor.)
Finally, this is what I have in the _case_large.html.erb
file:
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title"><%= c.title %></h3>
</div>
<div class="panel-body">
<%= cl_image_tag(c.case_image.path, width: 500, class: 'img-responsive img-thumbnail') %>
<p>
<%= simple_format c.body %>
</p>
</div>
<div class="panel-footer">
<span class="label label-default"><%= "Created by: " + c.user.email %></span>
<% if current_user == c.user %>
<%= link_to(edit_case_path(c), class: 'btn btn-default btn-xs pull-right') do %>
<span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit
<% end %>
<%= link_to(case_path(c), method: :delete, class: 'btn btn-danger btn-xs pull-right') do %>
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete
<% end %>
<% end %>
</div>
</div>
Now, in app/views/cases/show.html.erb
we can replace the block above with just one statement:
<%= render :partial => "case_large", :locals => {:c => @case}%>
Finally, this is what is left in app/views/cases/show.html.erb
:
<%= render "shared/internal_navbar" %>
<div class="container">
<div class="col-lg-6 col-md-12 col-sm-12 col-xs-12">
<%= render :partial => "case_large", :locals => {:c => @case}%>
<div class="panel panel-default">
<div class="panel-body">
<h3> Post a new update </h3>
<%= bootstrap_form_for([@case, Update.new], layout: :horizontal, label_col: "col-sm-3", control_col: "col-sm-8") do |form| %>
<%= form.text_field :title, label: "Short title", required: true %>
<%= form.text_field :domain, label: "Domain", required: true %>
<%= form.text_area :body, label: "Full text", placeholder: "Enter the full text of your update", required: true %>
<%= form.text_field :external_link, label: "External link"%>
<%= form.submit %>
<% end %>
</div>
</div>
</div>
<div class="col-lg-6 col-md-12 col-sm-12 col-xs-12">
<%= render :partial => :update, :collection => @case.updates.reverse, as: :u %>
</div>
</div>
This is shorter and clearer. Now, to check, I go to a Case page:
I notice that I've somehow lost the user name and link to the user page in the panel footer. That's an easy fix, we've done it before.
In app/views/cases/_case_large.html.erb
replace the line:
<span class="label label-default"><%= "Created by: " + c.user.email %></span>
with
<span class="label label-default"><%= link_to(c.user.name, user_path(c.user), style: "color: white") %></span>
Media object - update and case
If we now open a user's page, we will see the updates displayed as a list group:
This was fast and convenient for prototyping, but we'd like something more - let's say, the case image, the title and text of the update. Bootstrap has a nice component for that, a Media object.
So let's create a new partial, app/views/updates/_update_with_case.html.erb
and paste the following:
<div class="panel panel-default">
<div class="media">
<div class="media-left">
<div style="width: 100px; overflow: hidden;">
<%= link_to(cl_image_tag(u.case.case_image.path, class: 'img-responsive'), u.case) %>
</div>
</div>
<div class="media-body">
<span class="label label-primary"><%= u.domain %></span>
<h4 class="media-heading">
<%= u.title %>
</h4>
<%= u.body %>
</div>
</div>
</div>
What the code does, essentially, is wrap in a box (a panel) a media object that links to the case through the case image and shows the update title, body and domain.
Now, in app/views/users/show.html.erb
, we can replace the following block:
<ul class="list-group">
<% @user.updates.last(20).each do |u| %>
<li class="list-group-item">
<span class="label label-primary"><%= u.domain %></span>
<%= link_to(u.case.title, u.case) %>: <%= u.title %>
</li>
<% end %>
</ul>
with only one statement:
<%= render :partial => "updates/update_with_case", :collection => @user.updates.last(20), :as => :u %>
After all these manipulations, this is what I have inside the app/views/users/show.html.erb
:
<%= render "shared/internal_navbar" %>
<div class="container">
<h2><%= @user.name %></h2>
<h3>Recent updates:</h3>
<%= render :partial => "updates/update_with_case", :collection => @user.updates.last(20), :as => :u %>
<h3>Recent cases:</h3>
<%= render :partial => :case, :collection => @user.cases.last(20), as: :c %>
</div>
And this is what the user's page looks like:
Final touches - user activity collections
If you now read the file app/views/users/show.html.erb
you will see that we've used @user.updates.last(20)
to collect the recent user updates.
Generally, it is not a good pracice to make these decisions in one view, we would like instead to use something like @user.recent_updates
- a reusable collection that will be the same, regardless of the view.
Open the file app/models/user.rb
and, after the validates ...
statement, add the following:
def recent_updates
updates.order(id: :desc).limit(20)
end
def recent_cases
cases.order(id: :desc).limit(20)
end
This defines the recent cases and recent updates of the user as being the last 20, in descending order by ID (so the most recent first).
Save the file and make the final changes in app/views/users/show.html.erb
:
<%= render "shared/internal_navbar" %>
<div class="container">
<h2><%= @user.name %></h2>
<h3>Recent updates:</h3>
<%= render :partial => "updates/update_with_case", :collection => @user.recent_updates, :as => :u %>
<h3>Recent cases:</h3>
<%= render :partial => :case, :collection => @user.recent_cases, as: :c %>
</div>
Small visual improvement
If you look at the pages, you will notice that most of the text is bold.
This style is defined and propagated from app/assets/stylesheets/pages/landing.scss
, the following block of code:
body,
h1,
h2,
h3,
h4,
h5,
h6 {
font-family: "Lato","Helvetica Neue",Helvetica,Arial,sans-serif;
font-weight: 700;
}
I changed font-weight
to 450 and saved the file. This is the result:
That's better. For now :)
Redirection after case creation or edit
Now that we have a case page, it seems wiser to redirect the user to the case page instead of the Cases Index page after she creates or modifies a profile.
Open the Cases Controller app/controllers/cases_controller.rb
and edit the create
action to the following:
def create
@case = Case.create(case_params)
@case.user = current_user
@case.save
redirect_to @case
end
and the update
action:
def update
@case = Case.find(params[:id])
if @case.update_attributes(case_params)
redirect_to @case
else
render 'edit'
end
end