diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index 5e08193b5cfc374f6175a36d5c142e4212614b78..7613283443a087829b802a96d78ab9ecf15efdf7 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -118,7 +118,19 @@ class IssuableFinder end def filter_by_no_due_date? - due_date? && params[:due_date] == Issue::NO_DUE_DATE[1] + due_date? && params[:due_date] == Issue::NoDueDate.name + end + + def filter_by_overdue? + due_date? && params[:due_date] == Issue::OverDue.name + end + + def filter_by_due_this_week? + due_date? && params[:due_date] == Issue::DueThisWeek.name + end + + def filter_by_due_this_month? + due_date? && params[:due_date] == Issue::DueThisMonth.name end def labels? @@ -295,11 +307,13 @@ class IssuableFinder def by_due_date(items) if due_date? if filter_by_no_due_date? - items = items.no_due_date - else - items = items.has_due_date - # Must use issues prefix to avoid ambiguous match with Milestone#due_date - items = items.where("issues.due_date > ? AND issues.due_date <= ?", Date.today, params[:due_date]) + items = items.without_due_date + elsif filter_by_overdue? + items = items.overdue + elsif filter_by_due_this_week? + items = items.due_between(Date.today.beginning_of_week, Date.today.end_of_week + 1.day) + elsif filter_by_due_this_month? + items = items.due_between(Date.today.beginning_of_month, Date.today.end_of_month + 1.day) end end items diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index 2a193e12ec96c304af252a405b0016ec93266edd..d3779eda91f4fa5f56faf0bafe31f20b5a5988a9 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -174,12 +174,14 @@ module IssuesHelper def due_date_options options = [ - ["Due to tomorrow", 1.day.from_now.to_date], - ["Due in this week", 1.week.from_now.to_date] + Issue::AnyDueDate, + Issue::NoDueDate, + Issue::DueThisWeek, + Issue::DueThisMonth, + Issue::OverDue ] - options.unshift(Issue::ANY_DUE_DATE) - options.unshift(Issue::NO_DUE_DATE) - options_for_select(options, params[:due_date]) + + options_from_collection_for_select(options, 'name', 'title', params[:due_date]) end diff --git a/app/helpers/sorting_helper.rb b/app/helpers/sorting_helper.rb index 624cb7bb8475a3b7a804a6fb6cc2802e9945cf48..630e10ea892b62448a733a850c3190fd3c0c943f 100644 --- a/app/helpers/sorting_helper.rb +++ b/app/helpers/sorting_helper.rb @@ -53,11 +53,11 @@ module SortingHelper end def sort_title_due_date_soon - 'Due date soon' + 'Due soon' end def sort_title_due_date_later - 'Due date due later' + 'Due later' end def sort_title_name diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 691b7e104e4681f5b2ab52701a894872c1bf1509..03d8a573a4d09c3c9e6eb7fd1dd659674c128ed6 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -39,8 +39,10 @@ module Issuable scope :order_milestone_due_asc, -> { joins(:milestone).reorder('milestones.due_date ASC, milestones.id ASC') } scope :with_label, ->(title) { joins(:labels).where(labels: { title: title }) } scope :without_label, -> { joins("LEFT OUTER JOIN label_links ON label_links.target_type = '#{name}' AND label_links.target_id = #{table_name}.id").where(label_links: { id: nil }) } - scope :has_due_date, ->{ where("issues.due_date IS NOT NULL") } - scope :no_due_date, ->{ where("issues.due_date IS NULL")} + scope :without_due_date, ->{ where("issues.due_date IS NULL")} + scope :due_before, ->(date){ where("issues.due_date IS NOT NULL AND issues.due_date < ?", date)} + scope :due_between, ->(from_date, to_date){ where("issues.due_date >= ?", from_date).due_before(to_date) } + scope :overdue, ->{ where("issues.due_date < ?", Date.today)} scope :join_project, -> { joins(:project) } scope :references_project, -> { references(:project) } diff --git a/app/models/concerns/sortable.rb b/app/models/concerns/sortable.rb index 86c7e38adc8102fe25d7fb653309dac7a591cb4f..f1f09011c1b995ea73f0e20ab24230a9c328bbc9 100644 --- a/app/models/concerns/sortable.rb +++ b/app/models/concerns/sortable.rb @@ -18,8 +18,8 @@ module Sortable scope :order_updated_asc, -> { reorder(updated_at: :asc) } scope :order_name_asc, -> { reorder(name: :asc) } scope :order_name_desc, -> { reorder(name: :desc) } - scope :due_date_asc, -> { reorder("due_date IS NULL, due_date ASC") } - scope :due_date_desc, -> { reorder("due_date IS NULL, due_date DESC") } + scope :order_due_date_asc, -> { reorder("issues.due_date IS NULL, issues.due_date ASC") } + scope :order_due_date_desc, -> { reorder("issues.due_date IS NULL, issues.due_date DESC") } end module ClassMethods @@ -33,8 +33,8 @@ module Sortable when 'created_desc' then order_created_desc when 'id_desc' then order_id_desc when 'id_asc' then order_id_asc - when 'due_date_asc' then due_date_asc - when 'due_date_desc' then due_date_desc + when 'due_date_asc' then order_due_date_asc + when 'due_date_desc' then order_due_date_desc else all end diff --git a/app/models/issue.rb b/app/models/issue.rb index ee5be904330c636c7d2dfa1b8180006ae8ba8da3..b9350e191b7b79509cc886cfc51d2538aa244457 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -28,8 +28,12 @@ class Issue < ActiveRecord::Base include Sortable include Taskable - NO_DUE_DATE = ['No Due Date', '0'] - ANY_DUE_DATE = ['Any Due Date', ''] + DueDateStruct = Struct.new(:title, :name) + NoDueDate = DueDateStruct.new('No Due Date', '0') + AnyDueDate = DueDateStruct.new('Any Due Date', '') + OverDue = DueDateStruct.new('Overdue', 'overdue') + DueThisWeek = DueDateStruct.new('Due This Week', 'week') + DueThisMonth = DueDateStruct.new('Due This Month', 'month') ActsAsTaggableOn.strict_case_match = true diff --git a/app/views/shared/_sort_dropdown.html.haml b/app/views/shared/_sort_dropdown.html.haml index 80971309da74fbf41e8843094a88f8acd2da66ae..922b02f9f3ede90c7589826686e29d2e4ae79dc3 100644 --- a/app/views/shared/_sort_dropdown.html.haml +++ b/app/views/shared/_sort_dropdown.html.haml @@ -21,9 +21,9 @@ = link_to page_filter_path(sort: sort_value_milestone_later) do = sort_title_milestone_later = link_to page_filter_path(sort: sort_value_due_date_soon) do - = sort_title_due_date_soon if controller_name == "issues" + = sort_title_due_date_soon if @issues = link_to page_filter_path(sort: sort_value_due_date_later) do - = sort_title_due_date_later if controller_name == "issues" + = sort_title_due_date_later if @issues = link_to page_filter_path(sort: sort_value_upvotes) do = sort_title_upvotes = link_to page_filter_path(sort: sort_value_downvotes) do diff --git a/app/views/shared/issuable/_filter.html.haml b/app/views/shared/issuable/_filter.html.haml index f832f430b2b8a7e2ad806944aba4a28446e89a9c..7b76e7e368b8ff452a738da3926e4c94f43d7512 100644 --- a/app/views/shared/issuable/_filter.html.haml +++ b/app/views/shared/issuable/_filter.html.haml @@ -24,7 +24,7 @@ .filter-item.inline.labels-filter = render "shared/issuable/label_dropdown" - - if controller.controller_name == 'issues' + - if @issues .filter-item.inline.due_date-filter = select_tag('due_date', due_date_options, class: 'select2 trigger-submit', include_blank: true, diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index fb2c727d57a0b9d78195fe1a73711331a64a5b0c..8923efd2fc23891bf95002190360fd836f0d8a82 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -80,7 +80,6 @@ = icon('calendar') %span - if issuable.due_date - = icon('calendar') = issuable.due_date.to_s(:medium) - else .light None diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb index ac54a0c2719ee2686a17e66653f71a0e110fbf7b..32c27d6e97ee7494d13535348e55476805f6c80a 100644 --- a/spec/features/issues_spec.rb +++ b/spec/features/issues_spec.rb @@ -187,33 +187,49 @@ describe 'Issues', feature: true do end it 'filters by none' do - visit namespace_project_issues_path(project.namespace, project, due_date: Issue::NO_DUE_DATE[1]) + visit namespace_project_issues_path(project.namespace, project, due_date: Issue::NoDueDate.name) expect(page).not_to have_content("foo") expect(page).not_to have_content("bar") expect(page).to have_content("baz") end it 'filters by any' do - visit namespace_project_issues_path(project.namespace, project, due_date: Issue::ANY_DUE_DATE[1]) + visit namespace_project_issues_path(project.namespace, project, due_date: Issue::AnyDueDate.name) expect(page).to have_content("foo") expect(page).to have_content("bar") expect(page).to have_content("baz") end - it 'filters by due to tomorrow' do - visit namespace_project_issues_path(project.namespace, project, due_date: Date.tomorrow.to_s) + it 'filters by due this week' do + foo.update(due_date: Date.today.beginning_of_week + 2.days) + bar.update(due_date: Date.today.end_of_week) + baz.update(due_date: Date.today - 8.days) + visit namespace_project_issues_path(project.namespace, project, due_date: Issue::DueThisWeek.name) expect(page).to have_content("foo") - expect(page).not_to have_content("bar") + expect(page).to have_content("bar") expect(page).not_to have_content("baz") end - it 'filters by due in this week' do - visit namespace_project_issues_path(project.namespace, project, due_date: 7.days.from_now.to_date.to_s) + it 'filters by due this month' do + foo.update(due_date: Date.today.beginning_of_month + 2.days) + bar.update(due_date: Date.today.end_of_month) + baz.update(due_date: Date.today - 50.days) + visit namespace_project_issues_path(project.namespace, project, due_date: Issue::DueThisMonth.name) expect(page).to have_content("foo") expect(page).to have_content("bar") expect(page).not_to have_content("baz") end + it 'filters by overdue' do + foo.update(due_date: Date.today + 2.days) + bar.update(due_date: Date.today + 20.days) + baz.update(due_date: Date.yesterday) + visit namespace_project_issues_path(project.namespace, project, due_date: Issue::OverDue.name) + expect(page).not_to have_content("foo") + expect(page).not_to have_content("bar") + expect(page).to have_content("baz") + end + end describe 'sorting by milestone' do