Building Tempo with Rails, Part VI
Posted by Brian Sam-Bodden

In this installment we are going to build the Dashboard page of the Tempo application. The first incarnation of the Dashboard page will serve as the entry point/main page of the application and its main function will be to provide an interface for users to report time against a project-activity combination.
The user story that we are targeting is:
TMPO-29: “User enters Time” Business Critical [OPEN] 40 points 0% unassigned
The description above is from the Agile Project Management and Collaboration tool Caimito One. The “User enters Time” story is business critical, it is still OPEN, we have estimated 40 complexity points to it, is 0% completed and it has not been assigned to any team member (or no team member has volunteered for it!)
The description of the user story is "As a User I would like to enter time against a project and activity by providing a starting and ending time"
User Interface
Based on the story description I’ve come up with a rough sketch of what I picture the Dashboard page looking like after the first pass:

The sketch above shows 3 different areas:
- Date Selection: A Calendar-style component that will enable the user to select a given (active) date to enter time
- Time Entry Details: A form that will provide drop-downs for the Project, Activity, Start Time, End Time for the TimeEntry as well as a text area for a description. Optionally the user will be allowed to enter time as a number of hours to be reported against the given date (with no specific start or end times(1)).
- Daily Summary: A table displaying the time entries reported for the active date.
(1) Notice that I’ve just created a new requirement that was not present in the original User Story. In our work to fulfill the main User Story we will set the stage to satisfy this new requirement but we should really create a new issue (story) to tackle that new piece of functionality (of course first we should check with the project owner and/or stakeholders)
Dashboard Controller
I started by creating the skeleton code and tests for the Dashboard Controller:
script/generate rspec_controller dashboard
exists app/controllers/
exists app/helpers/
create app/views/dashboard
exists spec/controllers/
exists spec/helpers/
create spec/views/dashboard
create spec/controllers/dashboard_controller_spec.rb
create spec/helpers/dashboard_helper_spec.rb
create app/controllers/dashboard_controller.rb
create app/helpers/dashboard_helper.rb
Since we want the dashboard to be the default page for our application we can modify the config/routes.rb accordingly:
map.connect '', :controller => "dashboard", :action => "index"
With the skeleton in place I can now do a little planning about what I need to build.
A bit of Design
Here's what I know about the Dashboard page so far:
- The Dashboard page can only be shown if there is a user logged in. (done, taken care by the restful authentication plug-in)
- When the user first navigates to the Dashboard page, the current date should be shown in the calendar date picker and on the “blog” style display on the time entry form
- The Daily Work table should show all TimeEntries for the current user for the selected date
- When the user selects a new date form the calendar date picker the "current date" (the date which under the times entered will be reported) should change.
Create the Views
Let’s start by creating an index.rhtml template in the Views/dashboard directory. The index template will render three partials; one for a blog-style date, the TimeEntry form and one for the Daily Work table. The partials will be _current_date.rhtml, _form.rhtml and _time_entries.rhtml respectively.
views/dashboard/index.rhtml
The listing below shows the contents for index.rhtml. I used some of the CSS styles provided by ActiveScaffold to keep the look and feel consistent.
<% form_tag :action => 'create' do %>
<%= render :partial => "current_date" %><%= render :partial => 'form' %>
<% end %>
<%= render :partial => "time_entries" %>
The index.rhtml view renders a form (which body is contained in the _form.rhtml partial), the current date which is render by the partial _current_date.rhtml. Finally at the bottom we have a section that contains the daily work table summary which is rendered by the _time_entries.rhtml partial. Let's take a look at each one of those partials next:
views/dashboard/_current_date.rhtml
The idea here is to render a blog style data block as shown below:

To render the blog style date I have a CSS snippet that I use to style a partial containing the current date. The partial _current_date.rhtml is shown below:
<%= "#{Time::RFC2822_MONTH_NAME[@current_date.month-1]}" %><%= "#{@current_date.day}" %><%= "#{@current_date.year}" %>
Notice that I use an instance variable current_date that will be set in the index method of the dashboard controller if it cannot be retrieved from the session:
def index
unless session[:current_date]
session[:current_date] = Time.today
end
@current_date = session[:current_date]
end
In the controller the method to render the partial and update the instance and the session variable follows:
def current_date
session[:current_date] = Time.parse(params[:current_date])
@current_date = session[:current_date]
render :partial => 'current_date'
end
The CSS code for the dateblock is shown below (I've found it on a CSS site but I can't remember where):
/*-------------------------------------------------
DATE BLOCK
-------------------------------------------------*/
.dateblock {
text-align: center;
width: 50px;
font-family: sans-serif;
border: solid 1px black;
padding-top: 5px;
color: white;
background-color:black;
margin-top: 10px;
margin-bottom: 10px;
line-height: 1.22em;
font-family: sans-serif;
}
.dateblock_month {
font-size: 12px;
}
.dateblock_day {
font-size: 26px;
position: relative;
top: -2px;
}
.dateblock_year {
font-size: 12px;
position: relative;
top: -2px;
}
views/_time_entries.rhtml
For the Daily Work table showing all TimeEntries for the current user for the selected date we use the _time_entries.rhtml partial. This partial makes use of ActiveScaffold's ability to embed a scaffold in another controller, or view. To accomplish this we use the
render :activescaffold => 'timeentries'which will call the render_component passing a specific set of conditions (to be appended to the SQL select statement) and a custom label for the scaffold table:
<%= render :active_scaffold => 'time_entries',
:conditions =>
["user_id = ? AND YEAR(start) = ? AND MONTH(start) = ? AND DAY(start) = ?",
@current_user.id, @current_date.year, @current_date.month, @current_date.mday],
:label =>
"Time Entries for #{Time::RFC2822_DAY_NAME[@current_date.wday]}, #{Time::RFC2822_MONTH_NAME[@current_date.month-1]} #{@current_date.day} #{@current_date.year}" %>
In the controller we need to modify the index method to also filter the collection of TimeEntry objects retrieved:
def index
unless session[:current_date]
session[:current_date] = Time.today
end
@current_date = session[:current_date]
@time_entries = TimeEntry.find(:all, :conditions => ["user_id = ? AND YEAR(start) = ? AND MONTH(start) = ? AND DAY(start) = ?", @current_user.id, @current_date.year, @current_date.month, @current_date.mday])
end
We also need a time_entries method to render the TimeEntry(s) based on the current_date in the session:
def time_entries
session[:current_date] = Time.parse(params[:current_date])
@current_date = session[:current_date]
render :partial => 'time_entries'
end
The daily work table should now display all the TimeEntry(s) for the current/selected date:


