initial changes to add an option to select either context or project as grouping of todos on home or tag page

* new menu item in view menu
* js to toggle view
* store setting in cookie to keep de choice of view persistent
* refactor index and tag page of todos to look more alike
* refactor context and project partials to look more alike
This commit is contained in:
Reinier Balt 2013-03-05 14:04:01 +01:00
parent 55aa387ab9
commit f22dfc1f9b
15 changed files with 160 additions and 84 deletions

View file

@ -318,6 +318,17 @@ var TracksPages = {
} }
}); });
$("a#group_view_by_link").click(function () {
var state = $(this).attr("x_current_group_by")
if(state =='context'){
state='project';
} else {
state='context';
}
$.cookie('group_view_by', state);
refresh_page();
});
/* fade flashes and alerts in automatically */ /* fade flashes and alerts in automatically */
$(".alert").fadeOut(8000); $(".alert").fadeOut(8000);
} }
@ -696,7 +707,7 @@ var ProjectListPage = {
}, },
setup_behavior: function() { setup_behavior: function() {
/* in-place edit of project name */ /* in-place edit of project name */
$('div#project_name').editable(ProjectListPage.save_project_name, { $('span#project_name').editable(ProjectListPage.save_project_name, {
style: 'padding: 0px; width=100%;', style: 'padding: 0px; width=100%;',
submit: i18n['common.ok'], submit: i18n['common.ok'],
cancel: i18n['common.cancel'], cancel: i18n['common.cancel'],

View file

@ -128,9 +128,9 @@ class ProjectsController < ApplicationController
init_data_for_sidebar unless mobile? init_data_for_sidebar unless mobile?
@page_title = t('projects.page_title', :project => @project.name) @page_title = t('projects.page_title', :project => @project.name)
@not_done = @project.todos.active_or_hidden.includes(Todo::DEFAULT_INCLUDES) @not_done_todos = @project.todos.active_or_hidden.includes(Todo::DEFAULT_INCLUDES)
@deferred = @project.todos.deferred.includes(Todo::DEFAULT_INCLUDES) @deferred_todos = @project.todos.deferred.includes(Todo::DEFAULT_INCLUDES)
@pending = @project.todos.pending.includes(Todo::DEFAULT_INCLUDES) @pending_todos = @project.todos.pending.includes(Todo::DEFAULT_INCLUDES)
@done = {} @done = {}
@done = @project.todos.completed. @done = @project.todos.completed.
@ -138,8 +138,8 @@ class ProjectsController < ApplicationController
limit(current_user.prefs.show_number_completed). limit(current_user.prefs.show_number_completed).
includes(Todo::DEFAULT_INCLUDES) unless current_user.prefs.show_number_completed == 0 includes(Todo::DEFAULT_INCLUDES) unless current_user.prefs.show_number_completed == 0
@count = @not_done.size @count = @not_done_todos.size
@down_count = @count + @deferred.size + @pending.size @down_count = @count + @deferred_todos.size + @pending_todos.size
@next_project = current_user.projects.next_from(@project) @next_project = current_user.projects.next_from(@project)
@previous_project = current_user.projects.previous_from(@project) @previous_project = current_user.projects.previous_from(@project)
@default_tags = @project.default_tags @default_tags = @project.default_tags
@ -159,9 +159,9 @@ class ProjectsController < ApplicationController
end end
format.xml do format.xml do
render :xml => @project.to_xml(:except => :user_id) { |xml| render :xml => @project.to_xml(:except => :user_id) { |xml|
xml.not_done { @not_done.each { |child| child.to_xml(:builder => xml, :skip_instruct => true) } } xml.not_done { @not_done_todos.each { |child| child.to_xml(:builder => xml, :skip_instruct => true) } }
xml.deferred { @deferred.each { |child| child.to_xml(:builder => xml, :skip_instruct => true) } } xml.deferred { @deferred_todos.each { |child| child.to_xml(:builder => xml, :skip_instruct => true) } }
xml.pending { @pending.each { |child| child.to_xml(:builder => xml, :skip_instruct => true) } } xml.pending { @pending_todos.each { |child| child.to_xml(:builder => xml, :skip_instruct => true) } }
xml.done { @done.each { |child| child.to_xml(:builder => xml, :skip_instruct => true) } } xml.done { @done.each { |child| child.to_xml(:builder => xml, :skip_instruct => true) } }
} }
end end

View file

@ -8,49 +8,18 @@ class TodosController < ApplicationController
def index def index
@source_view = params['_source_view'] || 'todo' @source_view = params['_source_view'] || 'todo'
@group_view_by = cookies['group_view_by'] || 'context'
init_data_for_sidebar unless mobile? init_data_for_sidebar unless mobile?
@todos = current_user.todos.includes(Todo::DEFAULT_INCLUDES) @todos = current_user.todos.includes(Todo::DEFAULT_INCLUDES)
@todos = @todos.limit(sanitize(params[:limit])) if params[:limit]
# TODO: refactor text feed for done todos to todos/done.text, not /todos.text?done=true @not_done_todos = get_not_done_todos
if params[:done]
@not_done_todos = current_user.todos.completed.completed_after(Time.zone.now - params[:done].to_i.days)
else
@not_done_todos = current_user.todos.active.not_hidden
end
@not_done_todos = @not_done_todos.
reorder("todos.due IS NULL, todos.due ASC, todos.created_at ASC").
includes(Todo::DEFAULT_INCLUDES)
if params[:limit]
@not_done_todos = @not_done_todos.limit(sanitize(params[:limit]))
@todos = @todos.limit(sanitize(params[:limit]))
end
if params[:due]
due_within_when = Time.zone.now + params['due'].to_i.days
@not_done_todos = @not_done_todos.where('todos.due <= ?', due_within_when)
end
if params[:tag]
tag = Tag.where(:name => params['tag']).first
@not_done_todos = @not_done_todos.where('taggings.tag_id = ?', tag.id)
end
if params[:context_id]
context = current_user.contexts.find(params[:context_id])
@not_done_todos = @not_done_todos.where('context_id' => context.id)
end
if params[:project_id]
project = current_user.projects.find(params[:project_id])
@not_done_todos = @not_done_todos.where('project_id' => project)
end
@projects = current_user.projects.includes(:default_context) @projects = current_user.projects.includes(:default_context)
@contexts = current_user.contexts @contexts = current_user.contexts
@contexts_to_show = current_user.contexts.active @contexts_to_show = current_user.contexts.active
@projects_to_show = current_user.projects.active
# If you've set no_completed to zero, the completed items box isn't shown # If you've set no_completed to zero, the completed items box isn't shown
# on the home page # on the home page
@ -674,11 +643,11 @@ class TodosController < ApplicationController
hidden. hidden.
reorder('todos.completed_at DESC, todos.created_at DESC'). reorder('todos.completed_at DESC, todos.created_at DESC').
includes(Todo::DEFAULT_INCLUDES) includes(Todo::DEFAULT_INCLUDES)
@deferred = todos_with_tag_ids. @deferred_todos = todos_with_tag_ids.
deferred. deferred.
reorder('todos.show_from ASC, todos.created_at DESC'). reorder('todos.show_from ASC, todos.created_at DESC').
includes(Todo::DEFAULT_INCLUDES) includes(Todo::DEFAULT_INCLUDES)
@pending = todos_with_tag_ids. @pending_todos = todos_with_tag_ids.
blocked. blocked.
reorder('todos.show_from ASC, todos.created_at DESC'). reorder('todos.show_from ASC, todos.created_at DESC').
includes(Todo::DEFAULT_INCLUDES) includes(Todo::DEFAULT_INCLUDES)
@ -692,7 +661,8 @@ class TodosController < ApplicationController
@projects = current_user.projects @projects = current_user.projects
@contexts = current_user.contexts @contexts = current_user.contexts
@contexts_to_show = @contexts.reject {|c| c.hidden? } @contexts_to_show = @contexts.active
@projects_to_show = @projects.active
# Set defaults for new_action # Set defaults for new_action
@initial_tags = @tag_name @initial_tags = @tag_name
@ -959,6 +929,8 @@ class TodosController < ApplicationController
@single_tag = @tag_expr.size == 1 && @tag_expr[0].size == 1 @single_tag = @tag_expr.size == 1 && @tag_expr[0].size == 1
@tag_name = @tag_expr[0][0] @tag_name = @tag_expr[0][0]
@tag_title = @single_tag ? @tag_name : tag_title(@tag_expr) @tag_title = @single_tag ? @tag_name : tag_title(@tag_expr)
@group_view_by = cookies['group_view_by'] || 'context'
end end
def get_ids_from_tag_expr(tag_expr) def get_ids_from_tag_expr(tag_expr)
@ -1372,6 +1344,44 @@ class TodosController < ApplicationController
start_of_this_week = Time.zone.now.beginning_of_week start_of_this_week = Time.zone.now.beginning_of_week
completed_todos.completed_before(start_of_this_week).completed_after(start_of_this_month).all(includes) completed_todos.completed_before(start_of_this_week).completed_after(start_of_this_month).all(includes)
end end
def get_not_done_todos
# TODO: refactor text feed for done todos to todos/done.text, not /todos.text?done=true
if params[:done]
not_done_todos = current_user.todos.completed.completed_after(Time.zone.now - params[:done].to_i.days)
else
not_done_todos = current_user.todos.active.not_hidden
end
not_done_todos = not_done_todos.
reorder("todos.due IS NULL, todos.due ASC, todos.created_at ASC").
includes(Todo::DEFAULT_INCLUDES)
not_done_todos = not_done_todos.limit(sanitize(params[:limit])) if params[:limit]
if params[:due]
due_within_when = Time.zone.now + params['due'].to_i.days
not_done_todos = not_done_todos.where('todos.due <= ?', due_within_when)
end
if params[:tag]
tag = Tag.where(:name => params['tag']).first
not_done_todos = not_done_todos.where('taggings.tag_id = ?', tag.id)
end
if params[:context_id]
context = current_user.contexts.find(params[:context_id])
not_done_todos = not_done_todos.where('context_id' => context.id)
end
if params[:project_id]
project = current_user.projects.find(params[:project_id])
not_done_todos = not_done_todos.where('project_id' => project)
end
return not_done_todos
end
class TodoCreateParamsHelper class TodoCreateParamsHelper

View file

@ -1,6 +1,28 @@
# The methods added to this helper will be available to all templates in the # The methods added to this helper will be available to all templates in the
# application. # application.
module ApplicationHelper module ApplicationHelper
def group_view_by_menu_entry
# not set, no menu entry
return "" if @group_view_by.nil?
# if view == context, the menu shows Order By Project
menu_name = @group_view_by == 'context' ? 'project' : 'context'
content_tag(:li) do
link_to(
t("layouts.navigation.group_view_by_#{menu_name}"),
'#',
{:id => "group_view_by_link", :accesskey => "g", :title => t('layouts.navigation.group_view_by_title'), :x_current_group_by => @group_view_by} )
end
end
def container_toggle(id)
link_to(
image_tag("blank.png", :alt => t('common.collapse_expand')),
"#",
{:class => "container_toggle", :id => id} )
end
def navigation_link(name, options = {}, html_options = nil, *parameters_for_method_reference) def navigation_link(name, options = {}, html_options = nil, *parameters_for_method_reference)
link_to name, options, html_options link_to name, options, html_options

View file

@ -1,4 +1,12 @@
module ContextsHelper module ContextsHelper
def show_context_name(context)
if source_view_is :context
content_tag(:span, :id => "context_name"){context.name}
else
link_to_context( context )
end
end
def link_to_delete_context(context, descriptor = sanitize(context.name)) def link_to_delete_context(context, descriptor = sanitize(context.name))
link_to(descriptor, link_to(descriptor,

View file

@ -1,5 +1,19 @@
module ProjectsHelper module ProjectsHelper
def show_project_name(project)
if source_view_is :project
content_tag(:span, :id => "project_name"){project.name}
else
link_to_project( project )
end
end
def show_project_settings(project)
content_tag(:div, :id => dom_id(project, "container"), :class=>"list") do
render :partial => "projects/project_settings", :object => project
end
end
def project_next_prev def project_next_prev
html = "" html = ""
html << link_to_project(@previous_project, "&laquo; #{@previous_project.shortened_name}".html_safe) if @previous_project html << link_to_project(@previous_project, "&laquo; #{@previous_project.shortened_name}".html_safe) if @previous_project

View file

@ -1,5 +1,19 @@
module TodosHelper module TodosHelper
def empty_message_holder(show)
content_tag(:div, :id => "no_todos_in_view", :class => "container context", :style => "display:" + (show ? "block" : "none") ) do
content_tag(:h2) { t('todos.no_actions_found_title') }
content_tag(:div, :class => "message") do
content_tag(:p) { t('todos.no_actions_found') }
end
end
end
def show_grouped_todos
collection = (@group_view_by == 'context') ? @contexts_to_show : @projects_to_show
render(:partial => collection, :locals => { :collapsible => true })
end
def remote_star_icon(todo=@todo) def remote_star_icon(todo=@todo)
link_to( image_tag_for_star(todo), link_to( image_tag_for_star(todo),
toggle_star_todo_path(todo), toggle_star_todo_path(todo),

View file

@ -6,14 +6,8 @@ cache [context, @source_view, current_user.date.strftime("%Y%m%d"), @tag_name] d
%> %>
<div id="c<%= context.id %>" class="container context" style="display:<%= (collapsible && @not_done.empty?) ? "none" : "block" %>"> <div id="c<%= context.id %>" class="container context" style="display:<%= (collapsible && @not_done.empty?) ? "none" : "block" %>">
<h2> <h2>
<% if collapsible -%> <%= container_toggle("toggle_c#{context.id}") %>
<a href="#" class="container_toggle" id="toggle_c<%= context.id %>"><%= image_tag("blank.png", :alt => t('common.collapse_expand')) %></a> <%= show_context_name(context) %>
<% end -%>
<% if source_view_is :context %>
<span id="context_name"><%= context.name %></span>
<% else %>
<%= link_to_context( context ) %>
<% end %>
</h2> </h2>
<div id="c_<%=context.id%>_target" class="context_target drop_target"></div> <div id="c_<%=context.id%>_target" class="context_target drop_target"></div>
<div id="c<%= context.id %>items" class="items toggle_target"> <div id="c<%= context.id %>items" class="items toggle_target">

View file

@ -71,6 +71,8 @@
<li><%= navigation_link( t('layouts.navigation.completed_tasks'), done_overview_path, {:accesskey=>"d", :title=>t('layouts.navigation.completed_tasks_title')} ) %></li> <li><%= navigation_link( t('layouts.navigation.completed_tasks'), done_overview_path, {:accesskey=>"d", :title=>t('layouts.navigation.completed_tasks_title')} ) %></li>
<li><%= navigation_link( t('layouts.navigation.feeds'), feeds_path, :title => t('layouts.navigation.feeds_title')) %></li> <li><%= navigation_link( t('layouts.navigation.feeds'), feeds_path, :title => t('layouts.navigation.feeds_title')) %></li>
<li><%= navigation_link( t('layouts.navigation.stats'), stats_path, :title => t('layouts.navigation.stats_title')) %></li> <li><%= navigation_link( t('layouts.navigation.stats'), stats_path, :title => t('layouts.navigation.stats_title')) %></li>
<li><hr/></li>
<%= group_view_by_menu_entry %>
</ul> </ul>
</li> </li>
<li><a href="#"><%= t('layouts.navigation.admin') %></a> <li><a href="#"><%= t('layouts.navigation.admin') %></a>

View file

@ -1,21 +1,16 @@
<% <%
@not_done = @not_done_todos.select {|t| t.project_id == project.id }
# invalidate the cache every day because of staleness or # invalidate the cache every day because of staleness or
# rendering of "due in x days" that change without touching updated at of the todo # rendering of "due in x days" that change without touching updated at of the todo
cache [project, current_user.date.strftime("%Y%m%d")] do %> cache [project, @source_view, current_user.date.strftime("%Y%m%d")] do
<div class="container"> %>
<h2 id="project_name_container"> <%= render :partial => "project_settings_container", :locals => {:project => project} if source_view_is :project %>
<% if collapsible -%>
<a href="#" class="container_toggle" id="toggle_p<%= project.id %>"><%= image_tag("blank.png", :alt => t('common.collapse_expand')) %></a>
<% end -%>
<div id="project_name" style="width: 100%;"><%= project.name -%></div>
</h2>
<div id="<%= dom_id(project, "container")%>" class="list">
<%= render :partial => "projects/project_settings", :object => project, :locals => { :collapsible => collapsible } %>
</div>
</div>
<div class="container"> <div id="p<%= project.id %>" class="container project" style="display:<%= (collapsible && @not_done.empty?) ? "none" : "block" %>">
<h2><%= t('projects.actions_in_project_title') %></h2> <h2>
<%= container_toggle("toggle_p#{project.id}") if collapsible %>
<%= source_view_is(:project) ? t('projects.actions_in_project_title') : show_project_name(project) %>
</h2>
<div id="p<%= project.id %>items" class="items toggle_target"> <div id="p<%= project.id %>items" class="items toggle_target">
<div id="p<%= project.id %>empty-nd" style="display:<%= @not_done.empty? ? 'block' : 'none'%>;"> <div id="p<%= project.id %>empty-nd" style="display:<%= @not_done.empty? ? 'block' : 'none'%>;">
<div class="message"><p><%= t('projects.no_actions_in_project') %></p></div> <div class="message"><p><%= t('projects.no_actions_in_project') %></p></div>
@ -23,4 +18,5 @@ cache [project, current_user.date.strftime("%Y%m%d")] do %>
<%= render :partial => "todos/todo", :collection => @not_done, :locals => { :parent_container_type => "project" } %> <%= render :partial => "todos/todo", :collection => @not_done, :locals => { :parent_container_type => "project" } %>
</div> </div>
</div> </div>
<% end %> <% end %>

View file

@ -0,0 +1,6 @@
<div class="container project">
<h2 id="project_name_container">
<%= show_project_name(project) %>
</h2>
<%= show_project_settings(project) %>
</div>

View file

@ -4,7 +4,7 @@
</div> </div>
<%= render :partial => @project, :locals => {:collapsible => false } %> <%= render :partial => @project, :locals => {:collapsible => false } %>
<%= render :partial => "todos/deferred", :object => @deferred, :locals => { :collapsible => false, :append_descriptor => t('projects.todos_append'), :parent_container_type => 'project', :pending => @pending } %> <%= render :partial => "todos/deferred", :object => @deferred_todos, :locals => { :collapsible => false, :append_descriptor => t('projects.todos_append'), :parent_container_type => 'project', :pending => @pending_todos } %>
<% unless @max_completed==0 -%> <% unless @max_completed==0 -%>
<%= render :partial => "todos/completed", :object => @done, :locals => { :collapsible => false, :suppress_project => true, :append_descriptor => t('projects.todos_append') } %> <%= render :partial => "todos/completed", :object => @done, :locals => { :collapsible => false, :suppress_project => true, :append_descriptor => t('projects.todos_append') } %>
<% end -%> <% end -%>

View file

@ -1,14 +1,14 @@
<div id="display_box"> <div id="display_box">
<div id="no_todos_in_view" class="container context" style="display:<%= @not_done_todos.empty? ? "block" : "none" %>"> <%= empty_message_holder(@not_done_todos.empty?) %>
<h2><%= t('todos.no_actions_found_title')%></h2>
<div class="message"><p><%= t('todos.no_actions_found') %></p></div> <%= show_grouped_todos %>
</div>
<%= render(:partial => @contexts_to_show, :locals => { :collapsible => true }) -%>
<% unless @done.nil? -%> <% unless @done.nil? -%>
<%= render(:partial => "todos/completed", :object => @done, <%= render(:partial => "todos/completed", :object => @done,
:locals => { :collapsible => true, :append_descriptor => nil }) -%> :locals => { :collapsible => true, :append_descriptor => nil }) -%>
<% end -%> <% end -%>
</div> </div>
<div id="input_box"> <div id="input_box">
<%= render :partial => "shared/add_new_item_form" %> <%= render :partial => "shared/add_new_item_form" %>
<%= render :file => "sidebar/sidebar" %> <%= render :file => "sidebar/sidebar" %>

View file

@ -1,16 +1,12 @@
<div id="display_box"> <div id="display_box">
<div id="no_todos_in_view" class="container context" <%= "style=\"display:none\"".html_safe unless @not_done_todos.empty? %> > <%= empty_message_holder(@not_done_todos.empty?) %>
<h2><%= t('todos.no_actions_found_title')%></h2>
<div class="message"><p><%= t('todos.no_actions_with', :tag_name=>@tag_name) %></p></div>
</div>
<%= render :partial => "contexts/context", :collection => @contexts_to_show, <%= show_grouped_todos %>
:locals => { :collapsible => true } %>
<% unless @deferred.nil? -%> <% unless @deferred_todos.nil? -%>
<%= render :partial => "todos/deferred", :locals => { <%= render :partial => "todos/deferred", :locals => {
:deferred => @deferred, :deferred => @deferred_todos,
:pending => @pending, :pending => @pending_todos,
:collapsible => true, :collapsible => true,
:append_descriptor => t('todos.tagged_with', :tag_name => @tag_title), :append_descriptor => t('todos.tagged_with', :tag_name => @tag_title),
:parent_container_type => 'tag' :parent_container_type => 'tag'

View file

@ -51,6 +51,9 @@ en:
admin: Admin admin: Admin
help: "?" help: "?"
mobile: Mobile Site mobile: Mobile Site
group_view_by_context: "Order by context"
group_view_by_project: "Order by project"
group_view_by_title: "Change the ordering of the actions on this page"
integrations: integrations:
opensearch_description: Search in Tracks opensearch_description: Search in Tracks
applescript_next_action_prompt: "Description of next action:" applescript_next_action_prompt: "Description of next action:"