Commit 4a1dc7326479d8fe54f888f5622c0d2a56bca8ad

Authored by Dmitriy Zaporozhets
2 parents abc5edab b76948e6

Merge pull request #5661 from jasonblanchard/sorting-project-issues

Added sorting to project issues page
  1 +v 6.4.0
  2 + - Added sorting to project issues page
  3 +
1 v 6.3.0 4 v 6.3.0
2 - API for adding gitlab-ci service 5 - API for adding gitlab-ci service
3 - Init script now waits for pids to appear after (re)starting before reporting status (Rovanion Luckey) 6 - Init script now waits for pids to appear after (re)starting before reporting status (Rovanion Luckey)
app/contexts/issues/list_context.rb
@@ -29,8 +29,26 @@ module Issues @@ -29,8 +29,26 @@ module Issues
29 if params[:milestone_id].present? 29 if params[:milestone_id].present?
30 @issues = @issues.where(milestone_id: (params[:milestone_id] == '0' ? nil : params[:milestone_id])) 30 @issues = @issues.where(milestone_id: (params[:milestone_id] == '0' ? nil : params[:milestone_id]))
31 end 31 end
  32 +
  33 + # Sort by :sort param
  34 + @issues = sort(@issues, params[:sort])
32 35
33 @issues 36 @issues
34 end 37 end
  38 +
  39 + private
  40 +
  41 + def sort(issues, condition)
  42 + case condition
  43 + when 'newest' then issues.except(:order).order('created_at DESC')
  44 + when 'oldest' then issues.except(:order).order('created_at ASC')
  45 + when 'recently_updated' then issues.except(:order).order('updated_at DESC')
  46 + when 'last_updated' then issues.except(:order).order('updated_at ASC')
  47 + when 'milestone_due_soon' then issues.except(:order).joins(:milestone).order("milestones.due_date ASC")
  48 + when 'milestone_due_later' then issues.except(:order).joins(:milestone).order("milestones.due_date DESC")
  49 + else issues
  50 + end
  51 + end
  52 +
35 end 53 end
36 end 54 end
app/controllers/projects/issues_controller.rb
@@ -23,6 +23,9 @@ class Projects::IssuesController < Projects::ApplicationController @@ -23,6 +23,9 @@ class Projects::IssuesController < Projects::ApplicationController
23 assignee_id, milestone_id = params[:assignee_id], params[:milestone_id] 23 assignee_id, milestone_id = params[:assignee_id], params[:milestone_id]
24 @assignee = @project.team.find(assignee_id) if assignee_id.present? && !assignee_id.to_i.zero? 24 @assignee = @project.team.find(assignee_id) if assignee_id.present? && !assignee_id.to_i.zero?
25 @milestone = @project.milestones.find(milestone_id) if milestone_id.present? && !milestone_id.to_i.zero? 25 @milestone = @project.milestones.find(milestone_id) if milestone_id.present? && !milestone_id.to_i.zero?
  26 + sort_param = params[:sort] || 'newest'
  27 + @sort = sort_param.humanize unless sort_param.empty?
  28 +
26 29
27 respond_to do |format| 30 respond_to do |format|
28 format.html # index.html.erb 31 format.html # index.html.erb
app/helpers/projects_helper.rb
@@ -70,6 +70,8 @@ module ProjectsHelper @@ -70,6 +70,8 @@ module ProjectsHelper
70 scope: params[:scope], 70 scope: params[:scope],
71 label_name: params[:label_name], 71 label_name: params[:label_name],
72 milestone_id: params[:milestone_id], 72 milestone_id: params[:milestone_id],
  73 + assignee_id: params[:assignee_id],
  74 + sort: params[:sort],
73 } 75 }
74 76
75 options = exist_opts.merge(options) 77 options = exist_opts.merge(options)
app/views/projects/issues/_issues.html.haml
@@ -78,6 +78,26 @@ @@ -78,6 +78,26 @@
78 %strong= milestone.title 78 %strong= milestone.title
79 %small.light= milestone.expires_at 79 %small.light= milestone.expires_at
80 80
  81 + .dropdown.inline.prepend-left-10
  82 + %a.dropdown-toggle.btn.btn-small{href: '#', "data-toggle" => "dropdown"}
  83 + %span.light sort:
  84 + = @sort
  85 + %b.caret
  86 + %ul.dropdown-menu
  87 + %li
  88 + = link_to project_filter_path(sort: 'newest') do
  89 + Newest
  90 + = link_to project_filter_path(sort: 'oldest') do
  91 + Oldest
  92 + = link_to project_filter_path(sort: 'recently_updated') do
  93 + Recently updated
  94 + = link_to project_filter_path(sort: 'last_updated') do
  95 + Last updated
  96 + = link_to project_filter_path(sort: 'milestone_due_soon') do
  97 + Milestone due soon
  98 + = link_to project_filter_path(sort: 'milestone_due_later') do
  99 + Milestone due later
  100 +
81 101
82 %ul.well-list.issues-list 102 %ul.well-list.issues-list
83 = render @issues 103 = render @issues
spec/contexts/issues/list_context_spec.rb 0 → 100644
@@ -0,0 +1,77 @@ @@ -0,0 +1,77 @@
  1 +require 'spec_helper'
  2 +
  3 +describe Issues::ListContext do
  4 +
  5 + let(:user) { create(:user) }
  6 + let(:project) { create(:project, creator: user) }
  7 +
  8 + titles = ['foo','bar','baz']
  9 + titles.each_with_index do |title, index|
  10 + let!(title.to_sym) { create(:issue, title: title, project: project, created_at: Time.now - (index * 60)) }
  11 + end
  12 +
  13 + describe 'sorting' do
  14 +
  15 + it 'sorts by newest' do
  16 + params = {:sort => 'newest'}
  17 +
  18 + issues = Issues::ListContext.new(project, user, params).execute
  19 + issues.first.should eq foo
  20 + end
  21 +
  22 + it 'sorts by oldest' do
  23 + params = {:sort => 'oldest'}
  24 +
  25 + issues = Issues::ListContext.new(project, user, params).execute
  26 + issues.first.should eq baz
  27 + end
  28 +
  29 + it 'sorts by recently updated' do
  30 + params = {:sort => 'recently_updated'}
  31 + baz.updated_at = Time.now + 10
  32 + baz.save
  33 +
  34 + issues = Issues::ListContext.new(project, user, params).execute
  35 + issues.first.should eq baz
  36 + end
  37 +
  38 + it 'sorts by least recently updated' do
  39 + params = {:sort => 'last_updated'}
  40 + bar.updated_at = Time.now - 10
  41 + bar.save
  42 +
  43 + issues = Issues::ListContext.new(project, user, params).execute
  44 + issues.first.should eq bar
  45 + end
  46 +
  47 + describe 'sorting by milestone' do
  48 +
  49 + let(:newer_due_milestone) { create(:milestone, :due_date => '2013-12-11') }
  50 + let(:later_due_milestone) { create(:milestone, :due_date => '2013-12-12') }
  51 +
  52 + before :each do
  53 + foo.milestone = newer_due_milestone
  54 + foo.save
  55 + bar.milestone = later_due_milestone
  56 + bar.save
  57 + end
  58 +
  59 + it 'sorts by most recently due milestone' do
  60 + params = {:sort => 'milestone_due_soon'}
  61 +
  62 + issues = Issues::ListContext.new(project, user, params).execute
  63 + issues.first.should eq foo
  64 +
  65 + end
  66 +
  67 + it 'sorts by least recently due milestone' do
  68 + params = {:sort => 'milestone_due_later'}
  69 +
  70 + issues = Issues::ListContext.new(project, user, params).execute
  71 + issues.first.should eq bar
  72 + end
  73 +
  74 + end
  75 + end
  76 +
  77 +end
spec/features/issues_spec.rb
@@ -95,4 +95,87 @@ describe "Issues" do @@ -95,4 +95,87 @@ describe "Issues" do
95 page.should have_content 'gitlab' 95 page.should have_content 'gitlab'
96 end 96 end
97 end 97 end
  98 +
  99 + describe 'filter issue' do
  100 + titles = ['foo','bar','baz']
  101 + titles.each_with_index do |title, index|
  102 + let!(title.to_sym) { create(:issue, title: title, project: project, created_at: Time.now - (index * 60)) }
  103 + end
  104 + let(:newer_due_milestone) { create(:milestone, :due_date => '2013-12-11') }
  105 + let(:later_due_milestone) { create(:milestone, :due_date => '2013-12-12') }
  106 +
  107 + it 'sorts by newest' do
  108 + visit project_issues_path(project, sort: 'newest')
  109 +
  110 + page.should have_selector("ul.issues-list li:first-child", :text => 'foo')
  111 + page.should have_selector("ul.issues-list li:last-child", :text => 'baz')
  112 + end
  113 +
  114 + it 'sorts by oldest' do
  115 + visit project_issues_path(project, sort: 'oldest')
  116 +
  117 + page.should have_selector("ul.issues-list li:first-child", :text => 'baz')
  118 + page.should have_selector("ul.issues-list li:last-child", :text => 'foo')
  119 + end
  120 +
  121 + it 'sorts by most recently updated' do
  122 + baz.updated_at = Time.now + 100
  123 + baz.save
  124 + visit project_issues_path(project, sort: 'recently_updated')
  125 +
  126 + page.should have_selector("ul.issues-list li:first-child", :text => 'baz')
  127 + end
  128 +
  129 + it 'sorts by least recently updated' do
  130 + baz.updated_at = Time.now - 100
  131 + baz.save
  132 + visit project_issues_path(project, sort: 'last_updated')
  133 +
  134 + page.should have_selector("ul.issues-list li:first-child", :text => 'baz')
  135 + end
  136 +
  137 + describe 'sorting by milestone' do
  138 +
  139 + before :each do
  140 + foo.milestone = newer_due_milestone
  141 + foo.save
  142 + bar.milestone = later_due_milestone
  143 + bar.save
  144 + end
  145 +
  146 + it 'sorts by recently due milestone' do
  147 + visit project_issues_path(project, sort: 'milestone_due_soon')
  148 +
  149 + page.should have_selector("ul.issues-list li:first-child", :text => 'foo')
  150 + end
  151 +
  152 + it 'sorts by least recently due milestone' do
  153 + visit project_issues_path(project, sort: 'milestone_due_later')
  154 +
  155 + page.should have_selector("ul.issues-list li:first-child", :text => 'bar')
  156 + end
  157 + end
  158 +
  159 + describe 'combine filter and sort' do
  160 +
  161 + let(:user2) { create(:user) }
  162 +
  163 + before :each do
  164 + foo.assignee = user2
  165 + foo.save
  166 + bar.assignee = user2
  167 + bar.save
  168 + end
  169 +
  170 + it 'sorts with a filter applied' do
  171 + visit project_issues_path(project, sort: 'oldest', assignee_id: user2.id)
  172 +
  173 + page.should have_selector("ul.issues-list li:first-child", :text => 'bar')
  174 + page.should have_selector("ul.issues-list li:last-child", :text => 'foo')
  175 + page.should_not have_content 'baz'
  176 +
  177 + end
  178 + end
  179 + end
  180 +
98 end 181 end