From a056dfa9a077def4c3ffb958d3f86f7c9d7c2096 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Mon, 22 Feb 2016 19:08:00 -0500 Subject: [PATCH 01/16] Refactor GlobalMilestone queries. Make methods return ActiveRecord Relations instead of Arrays. --- app/models/concerns/issuable.rb | 1 + app/models/global_milestone.rb | 26 +++++++++---------- app/models/merge_request.rb | 1 - app/models/project.rb | 1 + .../dashboard/milestones/_milestone.html.haml | 2 +- app/views/dashboard/milestones/show.html.haml | 2 +- .../groups/milestones/_milestone.html.haml | 2 +- app/views/groups/milestones/show.html.haml | 2 +- 8 files changed, 19 insertions(+), 18 deletions(-) diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 286d6655861..a3c4a3d2776 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -29,6 +29,7 @@ module Issuable scope :assigned, -> { where("assignee_id IS NOT NULL") } scope :unassigned, -> { where("assignee_id IS NULL") } scope :of_projects, ->(ids) { where(project_id: ids) } + scope :of_milestones, ->(ids) { where(milestone_id: ids) } scope :opened, -> { with_state(:opened, :reopened) } scope :only_opened, -> { with_state(:opened) } scope :only_reopened, -> { with_state(:reopened) } diff --git a/app/models/global_milestone.rb b/app/models/global_milestone.rb index 7ee276255a0..e4dd90b631e 100644 --- a/app/models/global_milestone.rb +++ b/app/models/global_milestone.rb @@ -28,27 +28,27 @@ class GlobalMilestone end def projects - milestones.map { |milestone| milestone.project } + @projects ||= Project.for_milestones(milestones.map(&:id)) end - def issue_count - milestones.map { |milestone| milestone.issues.count }.sum + def issues_count + issues.count end def merge_requests_count - milestones.map { |milestone| milestone.merge_requests.count }.sum + merge_requests.count end def open_items_count - milestones.map { |milestone| milestone.open_items_count }.sum + opened_issues.count + opened_merge_requests.count end def closed_items_count - milestones.map { |milestone| milestone.closed_items_count }.sum + closed_issues.count + closed_merge_requests.count end def total_items_count - milestones.map { |milestone| milestone.total_items_count }.sum + issues_count + merge_requests_count end def percent_complete @@ -76,11 +76,11 @@ class GlobalMilestone end def issues - @issues ||= milestones.map(&:issues).flatten.group_by(&:state) + @issues ||= Issue.of_milestones(milestones.map(&:id)) end def merge_requests - @merge_requests ||= milestones.map(&:merge_requests).flatten.group_by(&:state) + @merge_requests ||= MergeRequest.of_milestones(milestones.map(&:id)) end def participants @@ -88,19 +88,19 @@ class GlobalMilestone end def opened_issues - issues.values_at("opened", "reopened").compact.flatten + issues.opened end def closed_issues - issues['closed'] + issues.closed end def opened_merge_requests - merge_requests.values_at("opened", "reopened").compact.flatten + merge_requests.opened end def closed_merge_requests - merge_requests.values_at("closed", "merged", "locked").compact.flatten + merge_requests.with_states(:closed, :merged, :locked) end def complete? diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 025b522cf66..f575494e2bf 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -139,7 +139,6 @@ class MergeRequest < ActiveRecord::Base scope :of_projects, ->(ids) { where(target_project_id: ids) } scope :opened, -> { with_states(:opened, :reopened) } scope :merged, -> { with_state(:merged) } - scope :closed, -> { with_state(:closed) } scope :closed_and_merged, -> { with_states(:closed, :merged) } scope :join_project, -> { joins(:target_project) } diff --git a/app/models/project.rb b/app/models/project.rb index 148eab692ff..3a28d5d7fee 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -215,6 +215,7 @@ class Project < ActiveRecord::Base scope :public_only, -> { where(visibility_level: Project::PUBLIC) } scope :public_and_internal_only, -> { where(visibility_level: Project.public_and_internal_levels) } scope :non_archived, -> { where(archived: false) } + scope :for_milestones, ->(ids) { joins(:milestones).where('milestones.id' => ids) } state_machine :import_status, initial: :none do event :import_start do diff --git a/app/views/dashboard/milestones/_milestone.html.haml b/app/views/dashboard/milestones/_milestone.html.haml index 7c882a32702..ea6c304d7de 100644 --- a/app/views/dashboard/milestones/_milestone.html.haml +++ b/app/views/dashboard/milestones/_milestone.html.haml @@ -8,7 +8,7 @@ .row .col-sm-6 = link_to issues_dashboard_path(milestone_title: milestone.title) do - = pluralize milestone.issue_count, 'Issue' + = pluralize milestone.issues_count, 'Issue' · = link_to merge_requests_dashboard_path(milestone_title: milestone.title) do = pluralize milestone.merge_requests_count, 'Merge Request' diff --git a/app/views/dashboard/milestones/show.html.haml b/app/views/dashboard/milestones/show.html.haml index 3810267577c..bbe0b0905ce 100644 --- a/app/views/dashboard/milestones/show.html.haml +++ b/app/views/dashboard/milestones/show.html.haml @@ -52,7 +52,7 @@ %li.active = link_to '#tab-issues', 'data-toggle' => 'tab' do Issues - %span.badge= @milestone.issue_count + %span.badge= @milestone.issues_count %li = link_to '#tab-merge-requests', 'data-toggle' => 'tab' do Merge Requests diff --git a/app/views/groups/milestones/_milestone.html.haml b/app/views/groups/milestones/_milestone.html.haml index a20bf75bc39..50558d7dce8 100644 --- a/app/views/groups/milestones/_milestone.html.haml +++ b/app/views/groups/milestones/_milestone.html.haml @@ -8,7 +8,7 @@ .row .col-sm-6 = link_to issues_group_path(@group, milestone_title: milestone.title) do - = pluralize milestone.issue_count, 'Issue' + = pluralize milestone.issues_count, 'Issue' · = link_to merge_requests_group_path(@group, milestone_title: milestone.title) do = pluralize milestone.merge_requests_count, 'Merge Request' diff --git a/app/views/groups/milestones/show.html.haml b/app/views/groups/milestones/show.html.haml index 1233da85524..405df1d3433 100644 --- a/app/views/groups/milestones/show.html.haml +++ b/app/views/groups/milestones/show.html.haml @@ -58,7 +58,7 @@ %li.active = link_to '#tab-issues', 'data-toggle' => 'tab' do Issues - %span.badge= @milestone.issue_count + %span.badge= @milestone.issues_count %li = link_to '#tab-merge-requests', 'data-toggle' => 'tab' do Merge Requests From 70028d36bee51a9ed86d20fe5d6c895772cc476a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Mon, 22 Feb 2016 20:39:36 -0500 Subject: [PATCH 02/16] Recator Issues Tab into a custom partial. --- app/views/groups/milestones/show.html.haml | 15 ++------------- app/views/projects/milestones/show.html.haml | 9 +-------- .../milestones/_issue.html.haml | 5 +++-- .../milestones/_issues.html.haml | 2 +- app/views/shared/milestones/_issues_tab.html.haml | 7 +++++++ 5 files changed, 14 insertions(+), 24 deletions(-) rename app/views/{projects => shared}/milestones/_issue.html.haml (63%) rename app/views/{projects => shared}/milestones/_issues.html.haml (80%) create mode 100644 app/views/shared/milestones/_issues_tab.html.haml diff --git a/app/views/groups/milestones/show.html.haml b/app/views/groups/milestones/show.html.haml index 405df1d3433..0114e68b19d 100644 --- a/app/views/groups/milestones/show.html.haml +++ b/app/views/groups/milestones/show.html.haml @@ -68,20 +68,9 @@ Participants %span.badge= @milestone.participants.count -.tab-content +.tab-content.milestone-content .tab-pane.active#tab-issues - .gray-content-block.middle-block - .pull-right - = link_to 'Browse Issues', issues_group_path(@group, milestone_title: @milestone.title), class: "btn btn-grouped" - - .oneline - All issues in this milestone - - .row.prepend-top-default - .col-md-6 - = render 'issues', title: "Open", issues: @milestone.opened_issues - .col-md-6 - = render 'issues', title: "Closed", issues: @milestone.closed_issues + = render 'shared/milestones/issues_tab', unassigned: @milestone.opened_issues.unassigned, assigned: @milestone.opened_issues.assigned, closed: @milestone.closed_issues .tab-pane#tab-merge-requests .gray-content-block.middle-block diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml index 2cae1ac4e2c..2322946894e 100644 --- a/app/views/projects/milestones/show.html.haml +++ b/app/views/projects/milestones/show.html.haml @@ -94,14 +94,7 @@ .tab-content.milestone-content .tab-pane.active#tab-issues - .row.prepend-top-default - .col-md-4 - = render('issues', title: 'Unstarted Issues (open and unassigned)', issues: @issues.opened.unassigned, id: 'unassigned') - .col-md-4 - = render('issues', title: 'Ongoing Issues (open and assigned)', issues: @issues.opened.assigned, id: 'ongoing') - .col-md-4 - = render('issues', title: 'Completed Issues (closed)', issues: @issues.closed, id: 'closed') - + = render 'shared/milestones/issues_tab', unassigned: @issues.opened.unassigned, assigned: @issues.opened.assigned, closed: @issues.closed .tab-pane#tab-merge-requests .row.prepend-top-default .col-md-3 diff --git a/app/views/projects/milestones/_issue.html.haml b/app/views/shared/milestones/_issue.html.haml similarity index 63% rename from app/views/projects/milestones/_issue.html.haml rename to app/views/shared/milestones/_issue.html.haml index ca51b8c745d..07880404c9a 100644 --- a/app/views/projects/milestones/_issue.html.haml +++ b/app/views/shared/milestones/_issue.html.haml @@ -1,8 +1,9 @@ +- project = issue.project %li{ id: dom_id(issue, 'sortable'), class: 'issue-row', 'data-iid' => issue.iid, 'data-url' => issue_path(issue) } %span - = link_to_gfm issue.title, [@project.namespace.becomes(Namespace), @project, issue], title: issue.title + = link_to_gfm issue.title, [project.namespace.becomes(Namespace), project, issue], title: issue.title .issue-detail - = link_to [@project.namespace.becomes(Namespace), @project, issue] do + = link_to [project.namespace.becomes(Namespace), project, issue] do %span.issue-number ##{issue.iid} - issue.labels.each do |label| = render_colored_label(label) diff --git a/app/views/projects/milestones/_issues.html.haml b/app/views/shared/milestones/_issues.html.haml similarity index 80% rename from app/views/projects/milestones/_issues.html.haml rename to app/views/shared/milestones/_issues.html.haml index 6f8a341e478..e1b5c2eb66c 100644 --- a/app/views/projects/milestones/_issues.html.haml +++ b/app/views/shared/milestones/_issues.html.haml @@ -4,4 +4,4 @@ .pull-right= issues.size %ul{ class: "well-list issues-sortable-list", id: "issues-list-#{id}", "data-state" => id } - issues.sort_by(&:position).each do |issue| - = render 'issue', issue: issue + = render 'shared/milestones/issue', issue: issue diff --git a/app/views/shared/milestones/_issues_tab.html.haml b/app/views/shared/milestones/_issues_tab.html.haml new file mode 100644 index 00000000000..3e98a809ad5 --- /dev/null +++ b/app/views/shared/milestones/_issues_tab.html.haml @@ -0,0 +1,7 @@ +.row.prepend-top-default + .col-md-4 + = render('shared/milestones/issues', title: 'Unstarted Issues (open and unassigned)', issues: unassigned, id: 'unassigned') + .col-md-4 + = render('shared/milestones/issues', title: 'Ongoing Issues (open and assigned)', issues: assigned, id: 'ongoing') + .col-md-4 + = render('shared/milestones/issues', title: 'Completed Issues (closed)', issues: closed, id: 'closed') From 37d92d0b7b91c678ce71393e73aa94860051fd5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Tue, 23 Feb 2016 11:53:59 -0500 Subject: [PATCH 03/16] Refactor Merge Requests tab into a custom partial --- app/views/groups/milestones/show.html.haml | 13 +------------ app/views/projects/milestones/show.html.haml | 15 +-------------- .../milestones/_merge_request.html.haml | 6 ++++-- .../milestones/_merge_requests.html.haml | 2 +- .../shared/milestones/_merge_requests_tab.haml | 13 +++++++++++++ 5 files changed, 20 insertions(+), 29 deletions(-) rename app/views/{projects => shared}/milestones/_merge_request.html.haml (58%) rename app/views/{projects => shared}/milestones/_merge_requests.html.haml (73%) create mode 100644 app/views/shared/milestones/_merge_requests_tab.haml diff --git a/app/views/groups/milestones/show.html.haml b/app/views/groups/milestones/show.html.haml index 0114e68b19d..d9899fbd43d 100644 --- a/app/views/groups/milestones/show.html.haml +++ b/app/views/groups/milestones/show.html.haml @@ -73,18 +73,7 @@ = render 'shared/milestones/issues_tab', unassigned: @milestone.opened_issues.unassigned, assigned: @milestone.opened_issues.assigned, closed: @milestone.closed_issues .tab-pane#tab-merge-requests - .gray-content-block.middle-block - .pull-right - = link_to 'Browse Merge Requests', merge_requests_group_path(@group, milestone_title: @milestone.title), class: "btn btn-grouped" - - .oneline - All merge requests in this milestone - - .row.prepend-top-default - .col-md-6 - = render 'merge_requests', title: "Open", merge_requests: @milestone.opened_merge_requests - .col-md-6 - = render 'merge_requests', title: "Closed", merge_requests: @milestone.closed_merge_requests + = render 'shared/milestones/merge_requests_tab', unassigned: @milestone.opened_merge_requests.unassigned, assigned: @milestone.opened_merge_requests.assigned, closed: @milestone.closed_merge_requests, merged: @milestone.merge_requests.merged .tab-pane#tab-participants .gray-content-block.middle-block diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml index 2322946894e..02f330cce23 100644 --- a/app/views/projects/milestones/show.html.haml +++ b/app/views/projects/milestones/show.html.haml @@ -96,20 +96,7 @@ .tab-pane.active#tab-issues = render 'shared/milestones/issues_tab', unassigned: @issues.opened.unassigned, assigned: @issues.opened.assigned, closed: @issues.closed .tab-pane#tab-merge-requests - .row.prepend-top-default - .col-md-3 - = render('merge_requests', title: 'Work in progress (open and unassigned)', merge_requests: @merge_requests.opened.unassigned, id: 'unassigned') - .col-md-3 - = render('merge_requests', title: 'Waiting for merge (open and assigned)', merge_requests: @merge_requests.opened.assigned, id: 'ongoing') - .col-md-3 - = render('merge_requests', title: 'Rejected (closed)', merge_requests: @merge_requests.closed, id: 'closed') - .col-md-3 - .panel.panel-primary - .panel-heading Merged - %ul.well-list - - @merge_requests.merged.each do |merge_request| - = render 'merge_request', merge_request: merge_request - + = render 'shared/milestones/merge_requests_tab', unassigned: @merge_requests.opened.unassigned, assigned: @merge_requests.opened.assigned, closed: @merge_requests.closed, merged: @merge_requests.merged .tab-pane#tab-participants %ul.bordered-list - @users.each do |user| diff --git a/app/views/projects/milestones/_merge_request.html.haml b/app/views/shared/milestones/_merge_request.html.haml similarity index 58% rename from app/views/projects/milestones/_merge_request.html.haml rename to app/views/shared/milestones/_merge_request.html.haml index a1033607c5d..a58dadb4f84 100644 --- a/app/views/projects/milestones/_merge_request.html.haml +++ b/app/views/shared/milestones/_merge_request.html.haml @@ -1,8 +1,10 @@ +- project = @project || merge_request.project + %li{ id: dom_id(merge_request, 'sortable'), class: 'mr-row', 'data-iid' => merge_request.iid, 'data-url' => merge_request_path(merge_request) } %span.str-truncated - = link_to [@project.namespace.becomes(Namespace), @project, merge_request] do + = link_to [project.namespace.becomes(Namespace), project, merge_request] do %span.cgray ##{merge_request.iid} - = link_to_gfm merge_request.title, [@project.namespace.becomes(Namespace), @project, merge_request], title: merge_request.title + = link_to_gfm merge_request.title, [project.namespace.becomes(Namespace), project, merge_request], title: merge_request.title .pull-right.assignee-icon - if merge_request.assignee = image_tag avatar_icon(merge_request.assignee, 16), class: "avatar s16", alt: '' diff --git a/app/views/projects/milestones/_merge_requests.html.haml b/app/views/shared/milestones/_merge_requests.html.haml similarity index 73% rename from app/views/projects/milestones/_merge_requests.html.haml rename to app/views/shared/milestones/_merge_requests.html.haml index 9a5a02af215..c8df6c2e280 100644 --- a/app/views/projects/milestones/_merge_requests.html.haml +++ b/app/views/shared/milestones/_merge_requests.html.haml @@ -2,4 +2,4 @@ .panel-heading= title %ul{ class: "well-list merge_requests-sortable-list", id: "merge_requests-list-#{id}", "data-state" => id } - merge_requests.sort_by(&:position).each do |merge_request| - = render 'merge_request', merge_request: merge_request + = render 'shared/milestones/merge_request', merge_request: merge_request diff --git a/app/views/shared/milestones/_merge_requests_tab.haml b/app/views/shared/milestones/_merge_requests_tab.haml new file mode 100644 index 00000000000..5797aeb8295 --- /dev/null +++ b/app/views/shared/milestones/_merge_requests_tab.haml @@ -0,0 +1,13 @@ +.row.prepend-top-default + .col-md-3 + = render('shared/milestones/merge_requests', title: 'Work in progress (open and unassigned)', merge_requests: unassigned, id: 'unassigned') + .col-md-3 + = render('shared/milestones/merge_requests', title: 'Waiting for merge (open and assigned)', merge_requests: assigned, id: 'ongoing') + .col-md-3 + = render('shared/milestones/merge_requests', title: 'Rejected (closed)', merge_requests: closed, id: 'closed') + .col-md-3 + .panel.panel-primary + .panel-heading Merged + %ul.well-list + - merged.each do |merge_request| + = render 'shared/milestones/merge_request', merge_request: merge_request From e805becfe838b6ac351a15a01d58e355a0b1763b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Tue, 23 Feb 2016 12:15:19 -0500 Subject: [PATCH 04/16] Eager load Issues/MRs project for Milestone. With this change we avoid doing N+1 queries when viewing Milestone's Issues/MRs from a Group context. --- app/models/global_milestone.rb | 4 ++-- app/views/groups/milestones/show.html.haml | 2 +- app/views/shared/milestones/_issue.html.haml | 4 +++- app/views/shared/milestones/_merge_request.html.haml | 1 + 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/app/models/global_milestone.rb b/app/models/global_milestone.rb index e4dd90b631e..40193a6f050 100644 --- a/app/models/global_milestone.rb +++ b/app/models/global_milestone.rb @@ -76,11 +76,11 @@ class GlobalMilestone end def issues - @issues ||= Issue.of_milestones(milestones.map(&:id)) + @issues ||= Issue.of_milestones(milestones.map(&:id)).includes(:project) end def merge_requests - @merge_requests ||= MergeRequest.of_milestones(milestones.map(&:id)) + @merge_requests ||= MergeRequest.of_milestones(milestones.map(&:id)).includes(:target_project) end def participants diff --git a/app/views/groups/milestones/show.html.haml b/app/views/groups/milestones/show.html.haml index d9899fbd43d..c6016179ba6 100644 --- a/app/views/groups/milestones/show.html.haml +++ b/app/views/groups/milestones/show.html.haml @@ -73,7 +73,7 @@ = render 'shared/milestones/issues_tab', unassigned: @milestone.opened_issues.unassigned, assigned: @milestone.opened_issues.assigned, closed: @milestone.closed_issues .tab-pane#tab-merge-requests - = render 'shared/milestones/merge_requests_tab', unassigned: @milestone.opened_merge_requests.unassigned, assigned: @milestone.opened_merge_requests.assigned, closed: @milestone.closed_merge_requests, merged: @milestone.merge_requests.merged + = render 'shared/milestones/merge_requests_tab', unassigned: @milestone.opened_merge_requests.unassigned, assigned: @milestone.opened_merge_requests.assigned, closed: @milestone.merge_requests.closed, merged: @milestone.merge_requests.merged .tab-pane#tab-participants .gray-content-block.middle-block diff --git a/app/views/shared/milestones/_issue.html.haml b/app/views/shared/milestones/_issue.html.haml index 07880404c9a..be55c5b6fe0 100644 --- a/app/views/shared/milestones/_issue.html.haml +++ b/app/views/shared/milestones/_issue.html.haml @@ -1,4 +1,6 @@ -- project = issue.project +-# @project is present when viewing Project's milestone +- project = @project || issue.project + %li{ id: dom_id(issue, 'sortable'), class: 'issue-row', 'data-iid' => issue.iid, 'data-url' => issue_path(issue) } %span = link_to_gfm issue.title, [project.namespace.becomes(Namespace), project, issue], title: issue.title diff --git a/app/views/shared/milestones/_merge_request.html.haml b/app/views/shared/milestones/_merge_request.html.haml index a58dadb4f84..66e2a2955da 100644 --- a/app/views/shared/milestones/_merge_request.html.haml +++ b/app/views/shared/milestones/_merge_request.html.haml @@ -1,3 +1,4 @@ +-# @project is present when viewing Project's milestone - project = @project || merge_request.project %li{ id: dom_id(merge_request, 'sortable'), class: 'mr-row', 'data-iid' => merge_request.iid, 'data-url' => merge_request_path(merge_request) } From 834b5d49ecb486065e3ecbf8b36becb416eff366 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Tue, 23 Feb 2016 12:45:16 -0500 Subject: [PATCH 05/16] Refactor Merge Requests tab into a custom partial --- app/views/groups/milestones/show.html.haml | 13 +------------ app/views/projects/milestones/show.html.haml | 10 +--------- .../shared/milestones/_participants_tab.html.haml | 8 ++++++++ 3 files changed, 10 insertions(+), 21 deletions(-) create mode 100644 app/views/shared/milestones/_participants_tab.html.haml diff --git a/app/views/groups/milestones/show.html.haml b/app/views/groups/milestones/show.html.haml index c6016179ba6..0a15f49ff12 100644 --- a/app/views/groups/milestones/show.html.haml +++ b/app/views/groups/milestones/show.html.haml @@ -76,15 +76,4 @@ = render 'shared/milestones/merge_requests_tab', unassigned: @milestone.opened_merge_requests.unassigned, assigned: @milestone.opened_merge_requests.assigned, closed: @milestone.merge_requests.closed, merged: @milestone.merge_requests.merged .tab-pane#tab-participants - .gray-content-block.middle-block - .oneline - All participants to this milestone - - %ul.bordered-list - - @milestone.participants.each do |user| - %li - = link_to user, title: user.name, class: "darken" do - = image_tag avatar_icon(user, 32), class: "avatar s32" - %strong= truncate(user.name, lenght: 40) - %br - %small.cgray= user.username + = render 'shared/milestones/participants_tab', users: @milestone.participants diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml index 02f330cce23..6ca66885eb4 100644 --- a/app/views/projects/milestones/show.html.haml +++ b/app/views/projects/milestones/show.html.haml @@ -98,15 +98,7 @@ .tab-pane#tab-merge-requests = render 'shared/milestones/merge_requests_tab', unassigned: @merge_requests.opened.unassigned, assigned: @merge_requests.opened.assigned, closed: @merge_requests.closed, merged: @merge_requests.merged .tab-pane#tab-participants - %ul.bordered-list - - @users.each do |user| - %li - = link_to user, title: user.name, class: "darken" do - = image_tag avatar_icon(user, 32), class: "avatar s32" - %strong= truncate(user.name, lenght: 40) - %br - %small.cgray= user.username - + = render 'shared/milestones/participants_tab', users: @users .tab-pane#tab-labels %ul.bordered-list.manage-labels-list - @labels.each do |label| diff --git a/app/views/shared/milestones/_participants_tab.html.haml b/app/views/shared/milestones/_participants_tab.html.haml new file mode 100644 index 00000000000..67ae85ac276 --- /dev/null +++ b/app/views/shared/milestones/_participants_tab.html.haml @@ -0,0 +1,8 @@ +%ul.bordered-list + - users.each do |user| + %li + = link_to user, title: user.name, class: "darken" do + = image_tag avatar_icon(user, 32), class: "avatar s32" + %strong= truncate(user.name, lenght: 40) + %br + %small.cgray= user.username From ed4808555877c668366d98a5408937712ad10d52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Tue, 23 Feb 2016 16:22:36 -0500 Subject: [PATCH 06/16] Refactor Merge Requests tab into a custom partial * Add Labels tab to Groups * Add decorator for label so it's aware of Milestones. --- app/decorators/label_with_milestone.rb | 19 +++++++++++++++++++ app/models/global_milestone.rb | 6 ++++++ app/views/groups/milestones/show.html.haml | 8 ++++++-- app/views/projects/milestones/show.html.haml | 14 +------------- .../shared/milestones/_labels_tab.html.haml | 15 +++++++++++++++ config/application.rb | 1 + 6 files changed, 48 insertions(+), 15 deletions(-) create mode 100644 app/decorators/label_with_milestone.rb create mode 100644 app/views/shared/milestones/_labels_tab.html.haml diff --git a/app/decorators/label_with_milestone.rb b/app/decorators/label_with_milestone.rb new file mode 100644 index 00000000000..a70a4e2f50d --- /dev/null +++ b/app/decorators/label_with_milestone.rb @@ -0,0 +1,19 @@ +class LabelWithMilestone + attr_reader :milestone + + def initialize(label, milestone) + @label, @milestone = label, milestone + end + + def method_missing(meth, *args) + if @label.respond_to?(meth) + @label.send(meth, *args) + else + super + end + end + + def respond_to?(meth) + @label.respond_to?(meth) + end +end diff --git a/app/models/global_milestone.rb b/app/models/global_milestone.rb index 40193a6f050..e13aaf16732 100644 --- a/app/models/global_milestone.rb +++ b/app/models/global_milestone.rb @@ -87,6 +87,12 @@ class GlobalMilestone @participants ||= milestones.map(&:participants).flatten.compact.uniq end + def labels + @labels ||= milestones.map do |ms| + ms.labels.map { |label| LabelWithMilestone.new(label, ms) } + end.flatten.sort_by!(&:title) + end + def opened_issues issues.opened end diff --git a/app/views/groups/milestones/show.html.haml b/app/views/groups/milestones/show.html.haml index 0a15f49ff12..7ffa3f2d518 100644 --- a/app/views/groups/milestones/show.html.haml +++ b/app/views/groups/milestones/show.html.haml @@ -67,13 +67,17 @@ = link_to '#tab-participants', 'data-toggle' => 'tab' do Participants %span.badge= @milestone.participants.count + %li + = link_to '#tab-labels', 'data-toggle' => 'tab', 'data-show' => '.tab-issues-buttons' do + Labels + %span.badge= @milestone.labels.count .tab-content.milestone-content .tab-pane.active#tab-issues = render 'shared/milestones/issues_tab', unassigned: @milestone.opened_issues.unassigned, assigned: @milestone.opened_issues.assigned, closed: @milestone.closed_issues - .tab-pane#tab-merge-requests = render 'shared/milestones/merge_requests_tab', unassigned: @milestone.opened_merge_requests.unassigned, assigned: @milestone.opened_merge_requests.assigned, closed: @milestone.merge_requests.closed, merged: @milestone.merge_requests.merged - .tab-pane#tab-participants = render 'shared/milestones/participants_tab', users: @milestone.participants + .tab-pane#tab-labels + = render 'shared/milestones/labels_tab', labels: @milestone.labels diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml index 6ca66885eb4..4aa1a53e87e 100644 --- a/app/views/projects/milestones/show.html.haml +++ b/app/views/projects/milestones/show.html.haml @@ -100,16 +100,4 @@ .tab-pane#tab-participants = render 'shared/milestones/participants_tab', users: @users .tab-pane#tab-labels - %ul.bordered-list.manage-labels-list - - @labels.each do |label| - %li - = render_colored_label(label) - - args = [@milestone.project.namespace, @milestone.project, milestone_title: @milestone.title, label_name: label.title] - - options = args.extract_options! - - %span.issues-count - = link_to namespace_project_issues_path(*args, options.merge(state: 'opened')) do - = pluralize label.open_issues_count, 'open issue' - %span.issues-count - = link_to namespace_project_issues_path(*args, options.merge(state: 'closed')) do - = pluralize label.closed_issues_count, 'closed issue' + = render 'shared/milestones/labels_tab', labels: @labels diff --git a/app/views/shared/milestones/_labels_tab.html.haml b/app/views/shared/milestones/_labels_tab.html.haml new file mode 100644 index 00000000000..bb6ce0afb40 --- /dev/null +++ b/app/views/shared/milestones/_labels_tab.html.haml @@ -0,0 +1,15 @@ +%ul.bordered-list.manage-labels-list + - labels.each do |label| + - milestone = @milestone.is_a?(Milestone) ? @milestone : label.milestone + + %li + = render_colored_label(label) + - args = [milestone.project.namespace, milestone.project, milestone_title: milestone.title, label_name: label.title] + - options = args.extract_options! + + %span.issues-count + = link_to namespace_project_issues_path(*args, options.merge(state: 'opened')) do + = pluralize label.open_issues_count, 'open issue' + %span.issues-count + = link_to namespace_project_issues_path(*args, options.merge(state: 'closed')) do + = pluralize label.closed_issues_count, 'closed issue' diff --git a/config/application.rb b/config/application.rb index 28684a3e578..fee8637a4cb 100644 --- a/config/application.rb +++ b/config/application.rb @@ -15,6 +15,7 @@ module Gitlab # Custom directories with classes and modules you want to be autoloadable. config.autoload_paths.push(*%W(#{config.root}/lib + #{config.root}/app/decorators #{config.root}/app/models/hooks #{config.root}/app/models/concerns #{config.root}/app/models/project_services From 32f8fc1f9a493dca41f5b67a15bb7ff37e5d78c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Tue, 23 Feb 2016 16:35:36 -0500 Subject: [PATCH 07/16] Refactor Milestone view for Dashboard. --- .../dashboard/milestones/_issue.html.haml | 10 ---- .../dashboard/milestones/_issues.html.haml | 6 --- .../milestones/_merge_request.html.haml | 10 ---- .../milestones/_merge_requests.html.haml | 6 --- app/views/dashboard/milestones/show.html.haml | 48 ++++--------------- 5 files changed, 10 insertions(+), 70 deletions(-) delete mode 100644 app/views/dashboard/milestones/_issue.html.haml delete mode 100644 app/views/dashboard/milestones/_issues.html.haml delete mode 100644 app/views/dashboard/milestones/_merge_request.html.haml delete mode 100644 app/views/dashboard/milestones/_merge_requests.html.haml diff --git a/app/views/dashboard/milestones/_issue.html.haml b/app/views/dashboard/milestones/_issue.html.haml deleted file mode 100644 index 1408ebdd5dc..00000000000 --- a/app/views/dashboard/milestones/_issue.html.haml +++ /dev/null @@ -1,10 +0,0 @@ -%li{ id: dom_id(issue, 'sortable'), class: 'issue-row', 'data-iid' => issue.iid } - %span.milestone-row - - project = issue.project - %strong #{project.name_with_namespace} · - = link_to [project.namespace.becomes(Namespace), project, issue] do - %span.cgray ##{issue.iid} - = link_to_gfm issue.title, [project.namespace.becomes(Namespace), project, issue], title: issue.title - .pull-right.assignee-icon - - if issue.assignee - = image_tag avatar_icon(issue.assignee, 16), class: "avatar s16" diff --git a/app/views/dashboard/milestones/_issues.html.haml b/app/views/dashboard/milestones/_issues.html.haml deleted file mode 100644 index 9f350b772bd..00000000000 --- a/app/views/dashboard/milestones/_issues.html.haml +++ /dev/null @@ -1,6 +0,0 @@ -.panel.panel-default - .panel-heading= title - %ul{ class: "well-list issues-sortable-list" } - - if issues - - issues.each do |issue| - = render 'issue', issue: issue diff --git a/app/views/dashboard/milestones/_merge_request.html.haml b/app/views/dashboard/milestones/_merge_request.html.haml deleted file mode 100644 index 77c46de030b..00000000000 --- a/app/views/dashboard/milestones/_merge_request.html.haml +++ /dev/null @@ -1,10 +0,0 @@ -%li{ id: dom_id(merge_request, 'sortable'), class: 'mr-row', 'data-iid' => merge_request.iid } - %span.milestone-row - - project = merge_request.project - %strong #{project.name_with_namespace} · - = link_to [project.namespace.becomes(Namespace), project, merge_request] do - %span.cgray ##{merge_request.iid} - = link_to_gfm merge_request.title, [project.namespace.becomes(Namespace), project, merge_request], title: merge_request.title - .pull-right.assignee-icon - - if merge_request.assignee - = image_tag avatar_icon(merge_request.assignee, 16), class: "avatar s16" diff --git a/app/views/dashboard/milestones/_merge_requests.html.haml b/app/views/dashboard/milestones/_merge_requests.html.haml deleted file mode 100644 index 50057e2c636..00000000000 --- a/app/views/dashboard/milestones/_merge_requests.html.haml +++ /dev/null @@ -1,6 +0,0 @@ -.panel.panel-default - .panel-heading= title - %ul{ class: "well-list merge_requests-sortable-list" } - - if merge_requests - - merge_requests.each do |merge_request| - = render 'merge_request', merge_request: merge_request diff --git a/app/views/dashboard/milestones/show.html.haml b/app/views/dashboard/milestones/show.html.haml index bbe0b0905ce..3e9e1204408 100644 --- a/app/views/dashboard/milestones/show.html.haml +++ b/app/views/dashboard/milestones/show.html.haml @@ -61,45 +61,17 @@ = link_to '#tab-participants', 'data-toggle' => 'tab' do Participants %span.badge= @milestone.participants.count + %li + = link_to '#tab-labels', 'data-toggle' => 'tab', 'data-show' => '.tab-issues-buttons' do + Labels + %span.badge= @milestone.labels.count -.tab-content +.tab-content.milestone-content .tab-pane.active#tab-issues - .gray-content-block.middle-block - .pull-right - = link_to 'Browse Issues', issues_dashboard_path(milestone_title: @milestone.title), class: "btn btn-grouped" - - .oneline - All issues in this milestone - - .row.prepend-top-default - .col-md-6 - = render 'issues', title: "Open", issues: @milestone.opened_issues - .col-md-6 - = render 'issues', title: "Closed", issues: @milestone.closed_issues - + = render 'shared/milestones/issues_tab', unassigned: @milestone.opened_issues.unassigned, assigned: @milestone.opened_issues.assigned, closed: @milestone.closed_issues .tab-pane#tab-merge-requests - .gray-content-block.middle-block - .pull-right - = link_to 'Browse Merge Requests', merge_requests_dashboard_path(milestone_title: @milestone.title), class: "btn btn-grouped" - - .oneline - All merge requests in this milestone - - .row.prepend-top-default - .col-md-6 - = render 'merge_requests', title: "Open", merge_requests: @milestone.opened_merge_requests - .col-md-6 - = render 'merge_requests', title: "Closed", merge_requests: @milestone.closed_merge_requests - + = render 'shared/milestones/merge_requests_tab', unassigned: @milestone.opened_merge_requests.unassigned, assigned: @milestone.opened_merge_requests.assigned, closed: @milestone.merge_requests.closed, merged: @milestone.merge_requests.merged .tab-pane#tab-participants - .gray-content-block.middle-block - .oneline - All participants to this milestone - %ul.bordered-list - - @milestone.participants.each do |user| - %li - = link_to user, title: user.name, class: "darken" do - = image_tag avatar_icon(user, 32), class: "avatar s32" - %strong= truncate(user.name, lenght: 40) - %br - %small.cgray= user.username + = render 'shared/milestones/participants_tab', users: @milestone.participants + .tab-pane#tab-labels + = render 'shared/milestones/labels_tab', labels: @milestone.labels From e0a18829ee7f1d681500002a2b6bda122b64b4c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Tue, 23 Feb 2016 19:31:59 -0500 Subject: [PATCH 08/16] Show project name for Issues tab in Group and Dashboard context. --- app/views/dashboard/milestones/show.html.haml | 6 +++--- app/views/groups/milestones/show.html.haml | 6 +++--- app/views/shared/milestones/_issue.html.haml | 4 ++++ app/views/shared/milestones/_issues.html.haml | 2 +- app/views/shared/milestones/_issues_tab.html.haml | 9 ++++++--- 5 files changed, 17 insertions(+), 10 deletions(-) diff --git a/app/views/dashboard/milestones/show.html.haml b/app/views/dashboard/milestones/show.html.haml index 3e9e1204408..18cf6fcbfd3 100644 --- a/app/views/dashboard/milestones/show.html.haml +++ b/app/views/dashboard/milestones/show.html.haml @@ -68,10 +68,10 @@ .tab-content.milestone-content .tab-pane.active#tab-issues - = render 'shared/milestones/issues_tab', unassigned: @milestone.opened_issues.unassigned, assigned: @milestone.opened_issues.assigned, closed: @milestone.closed_issues + = render 'shared/milestones/issues_tab', unassigned: @milestone.opened_issues.unassigned, assigned: @milestone.opened_issues.assigned, closed: @milestone.closed_issues, show_full_project_name: true .tab-pane#tab-merge-requests - = render 'shared/milestones/merge_requests_tab', unassigned: @milestone.opened_merge_requests.unassigned, assigned: @milestone.opened_merge_requests.assigned, closed: @milestone.merge_requests.closed, merged: @milestone.merge_requests.merged + = render 'shared/milestones/merge_requests_tab', unassigned: @milestone.opened_merge_requests.unassigned, assigned: @milestone.opened_merge_requests.assigned, closed: @milestone.merge_requests.closed, merged: @milestone.merge_requests.merged, show_full_project_name: true .tab-pane#tab-participants = render 'shared/milestones/participants_tab', users: @milestone.participants .tab-pane#tab-labels - = render 'shared/milestones/labels_tab', labels: @milestone.labels + = render 'shared/milestones/labels_tab', labels: @milestone.labels, show_full_project_name: true diff --git a/app/views/groups/milestones/show.html.haml b/app/views/groups/milestones/show.html.haml index 7ffa3f2d518..021425cad4f 100644 --- a/app/views/groups/milestones/show.html.haml +++ b/app/views/groups/milestones/show.html.haml @@ -74,10 +74,10 @@ .tab-content.milestone-content .tab-pane.active#tab-issues - = render 'shared/milestones/issues_tab', unassigned: @milestone.opened_issues.unassigned, assigned: @milestone.opened_issues.assigned, closed: @milestone.closed_issues + = render 'shared/milestones/issues_tab', unassigned: @milestone.opened_issues.unassigned, assigned: @milestone.opened_issues.assigned, closed: @milestone.closed_issues, show_project_name: true .tab-pane#tab-merge-requests - = render 'shared/milestones/merge_requests_tab', unassigned: @milestone.opened_merge_requests.unassigned, assigned: @milestone.opened_merge_requests.assigned, closed: @milestone.merge_requests.closed, merged: @milestone.merge_requests.merged + = render 'shared/milestones/merge_requests_tab', unassigned: @milestone.opened_merge_requests.unassigned, assigned: @milestone.opened_merge_requests.assigned, closed: @milestone.merge_requests.closed, merged: @milestone.merge_requests.merged, show_project_name: true .tab-pane#tab-participants = render 'shared/milestones/participants_tab', users: @milestone.participants .tab-pane#tab-labels - = render 'shared/milestones/labels_tab', labels: @milestone.labels + = render 'shared/milestones/labels_tab', labels: @milestone.labels, show_project_name: true diff --git a/app/views/shared/milestones/_issue.html.haml b/app/views/shared/milestones/_issue.html.haml index be55c5b6fe0..dbef742ebe8 100644 --- a/app/views/shared/milestones/_issue.html.haml +++ b/app/views/shared/milestones/_issue.html.haml @@ -3,6 +3,10 @@ %li{ id: dom_id(issue, 'sortable'), class: 'issue-row', 'data-iid' => issue.iid, 'data-url' => issue_path(issue) } %span + - if show_project_name + %strong #{project.name} · + - elsif show_full_project_name + %strong #{project.name_with_namespace} · = link_to_gfm issue.title, [project.namespace.becomes(Namespace), project, issue], title: issue.title .issue-detail = link_to [project.namespace.becomes(Namespace), project, issue] do diff --git a/app/views/shared/milestones/_issues.html.haml b/app/views/shared/milestones/_issues.html.haml index e1b5c2eb66c..04053eb854b 100644 --- a/app/views/shared/milestones/_issues.html.haml +++ b/app/views/shared/milestones/_issues.html.haml @@ -4,4 +4,4 @@ .pull-right= issues.size %ul{ class: "well-list issues-sortable-list", id: "issues-list-#{id}", "data-state" => id } - issues.sort_by(&:position).each do |issue| - = render 'shared/milestones/issue', issue: issue + = render 'shared/milestones/issue', issue: issue, show_project_name: show_project_name, show_full_project_name: show_full_project_name diff --git a/app/views/shared/milestones/_issues_tab.html.haml b/app/views/shared/milestones/_issues_tab.html.haml index 3e98a809ad5..277cd81677b 100644 --- a/app/views/shared/milestones/_issues_tab.html.haml +++ b/app/views/shared/milestones/_issues_tab.html.haml @@ -1,7 +1,10 @@ +- args = { show_project_name: local_assigns.fetch(:show_project_name, false), + show_full_project_name: local_assigns.fetch(:show_full_project_name, false) } + .row.prepend-top-default .col-md-4 - = render('shared/milestones/issues', title: 'Unstarted Issues (open and unassigned)', issues: unassigned, id: 'unassigned') + = render 'shared/milestones/issues', args.merge({ title: 'Unstarted Issues (open and unassigned)', issues: unassigned, id: 'unassigned' }) .col-md-4 - = render('shared/milestones/issues', title: 'Ongoing Issues (open and assigned)', issues: assigned, id: 'ongoing') + = render 'shared/milestones/issues', args.merge({ title: 'Ongoing Issues (open and assigned)', issues: assigned, id: 'ongoing' }) .col-md-4 - = render('shared/milestones/issues', title: 'Completed Issues (closed)', issues: closed, id: 'closed') + = render 'shared/milestones/issues', args.merge({ title: 'Completed Issues (closed)', issues: closed, id: 'closed' }) From 7cc102a0e6568fdfa08e63ba3a0b31ced2c30a7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Tue, 23 Feb 2016 20:12:10 -0500 Subject: [PATCH 09/16] Make Merge Requests tab have the same look as Issues. --- app/assets/stylesheets/pages/milestone.scss | 8 ++++---- app/views/groups/milestones/_issue.html.haml | 10 ---------- app/views/groups/milestones/_issues.html.haml | 6 ------ .../groups/milestones/_merge_request.html.haml | 10 ---------- .../groups/milestones/_merge_requests.html.haml | 6 ------ .../shared/milestones/_merge_request.html.haml | 14 ++++++++++---- .../shared/milestones/_merge_requests.html.haml | 7 +++++-- .../shared/milestones/_merge_requests_tab.haml | 15 +++++++-------- 8 files changed, 26 insertions(+), 50 deletions(-) delete mode 100644 app/views/groups/milestones/_issue.html.haml delete mode 100644 app/views/groups/milestones/_issues.html.haml delete mode 100644 app/views/groups/milestones/_merge_request.html.haml delete mode 100644 app/views/groups/milestones/_merge_requests.html.haml diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss index d24adbf67e6..2c2a8e34d50 100644 --- a/app/assets/stylesheets/pages/milestone.scss +++ b/app/assets/stylesheets/pages/milestone.scss @@ -19,7 +19,7 @@ li.milestone { width: 105px; } - .issue-row { + .issue-row, .mr-row { .color-label { border-radius: 2px; padding: 3px !important; @@ -44,11 +44,11 @@ li.milestone { } } -.issues-sortable-list { - .issue-detail { +.issues-sortable-list, .merge_requests-sortable-list { + .issue-detail, .merge-request-detail { display: block; - .issue-number{ + .issue-number, .merge-request-number { color: rgba(0,0,0,0.44); margin-right: 5px; } diff --git a/app/views/groups/milestones/_issue.html.haml b/app/views/groups/milestones/_issue.html.haml deleted file mode 100644 index 9b85d83d6d8..00000000000 --- a/app/views/groups/milestones/_issue.html.haml +++ /dev/null @@ -1,10 +0,0 @@ -%li{ id: dom_id(issue, 'sortable'), class: 'issue-row', 'data-iid' => issue.iid } - %span.milestone-row - - project = issue.project - %strong #{project.name} · - = link_to [project.namespace.becomes(Namespace), project, issue] do - %span.cgray ##{issue.iid} - = link_to_gfm issue.title, [project.namespace.becomes(Namespace), project, issue], title: issue.title - .pull-right.assignee-icon - - if issue.assignee - = image_tag avatar_icon(issue.assignee, 16), class: "avatar s16", alt: '' diff --git a/app/views/groups/milestones/_issues.html.haml b/app/views/groups/milestones/_issues.html.haml deleted file mode 100644 index 9f350b772bd..00000000000 --- a/app/views/groups/milestones/_issues.html.haml +++ /dev/null @@ -1,6 +0,0 @@ -.panel.panel-default - .panel-heading= title - %ul{ class: "well-list issues-sortable-list" } - - if issues - - issues.each do |issue| - = render 'issue', issue: issue diff --git a/app/views/groups/milestones/_merge_request.html.haml b/app/views/groups/milestones/_merge_request.html.haml deleted file mode 100644 index e3aa4aad198..00000000000 --- a/app/views/groups/milestones/_merge_request.html.haml +++ /dev/null @@ -1,10 +0,0 @@ -%li{ id: dom_id(merge_request, 'sortable'), class: 'mr-row', 'data-iid' => merge_request.iid } - %span.milestone-row - - project = merge_request.project - %strong #{project.name} · - = link_to [project.namespace.becomes(Namespace), project, merge_request] do - %span.cgray ##{merge_request.iid} - = link_to_gfm merge_request.title, [project.namespace.becomes(Namespace), project, merge_request], title: merge_request.title - .pull-right.assignee-icon - - if merge_request.assignee - = image_tag avatar_icon(merge_request.assignee, 16), class: "avatar s16", alt: '' diff --git a/app/views/groups/milestones/_merge_requests.html.haml b/app/views/groups/milestones/_merge_requests.html.haml deleted file mode 100644 index 50057e2c636..00000000000 --- a/app/views/groups/milestones/_merge_requests.html.haml +++ /dev/null @@ -1,6 +0,0 @@ -.panel.panel-default - .panel-heading= title - %ul{ class: "well-list merge_requests-sortable-list" } - - if merge_requests - - merge_requests.each do |merge_request| - = render 'merge_request', merge_request: merge_request diff --git a/app/views/shared/milestones/_merge_request.html.haml b/app/views/shared/milestones/_merge_request.html.haml index 66e2a2955da..7b6c6606460 100644 --- a/app/views/shared/milestones/_merge_request.html.haml +++ b/app/views/shared/milestones/_merge_request.html.haml @@ -2,10 +2,16 @@ - project = @project || merge_request.project %li{ id: dom_id(merge_request, 'sortable'), class: 'mr-row', 'data-iid' => merge_request.iid, 'data-url' => merge_request_path(merge_request) } - %span.str-truncated - = link_to [project.namespace.becomes(Namespace), project, merge_request] do - %span.cgray ##{merge_request.iid} + %span + - if show_project_name + %strong #{project.name} · + - elsif show_full_project_name + %strong #{project.name_with_namespace} · = link_to_gfm merge_request.title, [project.namespace.becomes(Namespace), project, merge_request], title: merge_request.title - .pull-right.assignee-icon + .merge-request-detail + = link_to [project.namespace.becomes(Namespace), project, merge_request] do + %span.merge-request-number ##{merge_request.iid} + - merge_request.labels.each do |label| + = render_colored_label(label) - if merge_request.assignee = image_tag avatar_icon(merge_request.assignee, 16), class: "avatar s16", alt: '' diff --git a/app/views/shared/milestones/_merge_requests.html.haml b/app/views/shared/milestones/_merge_requests.html.haml index c8df6c2e280..71991516e90 100644 --- a/app/views/shared/milestones/_merge_requests.html.haml +++ b/app/views/shared/milestones/_merge_requests.html.haml @@ -1,5 +1,8 @@ -.panel.panel-default +- primary = local_assigns.fetch(:primary, false) +- panel_class = primary ? 'panel-primary' : 'panel-default' + +.panel{ class: panel_class } .panel-heading= title %ul{ class: "well-list merge_requests-sortable-list", id: "merge_requests-list-#{id}", "data-state" => id } - merge_requests.sort_by(&:position).each do |merge_request| - = render 'shared/milestones/merge_request', merge_request: merge_request + = render 'shared/milestones/merge_request', merge_request: merge_request, show_project_name: show_project_name, show_full_project_name: show_full_project_name diff --git a/app/views/shared/milestones/_merge_requests_tab.haml b/app/views/shared/milestones/_merge_requests_tab.haml index 5797aeb8295..d912e607963 100644 --- a/app/views/shared/milestones/_merge_requests_tab.haml +++ b/app/views/shared/milestones/_merge_requests_tab.haml @@ -1,13 +1,12 @@ +- args = { show_project_name: local_assigns.fetch(:show_project_name, false), + show_full_project_name: local_assigns.fetch(:show_full_project_name, false) } + .row.prepend-top-default .col-md-3 - = render('shared/milestones/merge_requests', title: 'Work in progress (open and unassigned)', merge_requests: unassigned, id: 'unassigned') + = render 'shared/milestones/merge_requests', args.merge({ title: 'Work in progress (open and unassigned)', merge_requests: unassigned, id: 'unassigned' }) .col-md-3 - = render('shared/milestones/merge_requests', title: 'Waiting for merge (open and assigned)', merge_requests: assigned, id: 'ongoing') + = render 'shared/milestones/merge_requests', args.merge({ title: 'Waiting for merge (open and assigned)', merge_requests: assigned, id: 'ongoing' }) .col-md-3 - = render('shared/milestones/merge_requests', title: 'Rejected (closed)', merge_requests: closed, id: 'closed') + = render 'shared/milestones/merge_requests', args.merge({ title: 'Rejected (closed)', merge_requests: closed, id: 'closed' }) .col-md-3 - .panel.panel-primary - .panel-heading Merged - %ul.well-list - - merged.each do |merge_request| - = render 'shared/milestones/merge_request', merge_request: merge_request + = render 'shared/milestones/merge_requests', args.merge({ title: 'Merged', merge_requests: merged, id: 'merged', primary: true }) From b6e5de2cfbc396ae4f59de2c2bb56432e705d695 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Tue, 23 Feb 2016 21:17:14 -0500 Subject: [PATCH 10/16] Use the same partial when rendering Issues or Merge Requests. --- app/assets/stylesheets/pages/milestone.scss | 6 +++--- app/views/shared/milestones/_issue.html.haml | 17 ----------------- app/views/shared/milestones/_issues.html.haml | 7 ------- .../shared/milestones/_issues_tab.html.haml | 6 +++--- .../shared/milestones/_merge_request.html.haml | 17 ----------------- .../shared/milestones/_merge_requests.html.haml | 8 -------- .../shared/milestones/_merge_requests_tab.haml | 8 ++++---- 7 files changed, 10 insertions(+), 59 deletions(-) delete mode 100644 app/views/shared/milestones/_issue.html.haml delete mode 100644 app/views/shared/milestones/_issues.html.haml delete mode 100644 app/views/shared/milestones/_merge_request.html.haml delete mode 100644 app/views/shared/milestones/_merge_requests.html.haml diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss index 2c2a8e34d50..300c2fa7e1b 100644 --- a/app/assets/stylesheets/pages/milestone.scss +++ b/app/assets/stylesheets/pages/milestone.scss @@ -19,7 +19,7 @@ li.milestone { width: 105px; } - .issue-row, .mr-row { + .issue-row, .merge_request-row { .color-label { border-radius: 2px; padding: 3px !important; @@ -45,10 +45,10 @@ li.milestone { } .issues-sortable-list, .merge_requests-sortable-list { - .issue-detail, .merge-request-detail { + .issue-detail, .merge_request-detail { display: block; - .issue-number, .merge-request-number { + .issue-number, .merge_request-number { color: rgba(0,0,0,0.44); margin-right: 5px; } diff --git a/app/views/shared/milestones/_issue.html.haml b/app/views/shared/milestones/_issue.html.haml deleted file mode 100644 index dbef742ebe8..00000000000 --- a/app/views/shared/milestones/_issue.html.haml +++ /dev/null @@ -1,17 +0,0 @@ --# @project is present when viewing Project's milestone -- project = @project || issue.project - -%li{ id: dom_id(issue, 'sortable'), class: 'issue-row', 'data-iid' => issue.iid, 'data-url' => issue_path(issue) } - %span - - if show_project_name - %strong #{project.name} · - - elsif show_full_project_name - %strong #{project.name_with_namespace} · - = link_to_gfm issue.title, [project.namespace.becomes(Namespace), project, issue], title: issue.title - .issue-detail - = link_to [project.namespace.becomes(Namespace), project, issue] do - %span.issue-number ##{issue.iid} - - issue.labels.each do |label| - = render_colored_label(label) - - if issue.assignee - = image_tag avatar_icon(issue.assignee, 16), class: "avatar s24", alt: '' diff --git a/app/views/shared/milestones/_issues.html.haml b/app/views/shared/milestones/_issues.html.haml deleted file mode 100644 index 04053eb854b..00000000000 --- a/app/views/shared/milestones/_issues.html.haml +++ /dev/null @@ -1,7 +0,0 @@ -.panel.panel-default - .panel-heading - = title - .pull-right= issues.size - %ul{ class: "well-list issues-sortable-list", id: "issues-list-#{id}", "data-state" => id } - - issues.sort_by(&:position).each do |issue| - = render 'shared/milestones/issue', issue: issue, show_project_name: show_project_name, show_full_project_name: show_full_project_name diff --git a/app/views/shared/milestones/_issues_tab.html.haml b/app/views/shared/milestones/_issues_tab.html.haml index 277cd81677b..44a221114bb 100644 --- a/app/views/shared/milestones/_issues_tab.html.haml +++ b/app/views/shared/milestones/_issues_tab.html.haml @@ -3,8 +3,8 @@ .row.prepend-top-default .col-md-4 - = render 'shared/milestones/issues', args.merge({ title: 'Unstarted Issues (open and unassigned)', issues: unassigned, id: 'unassigned' }) + = render 'shared/milestones/records', args.merge({ title: 'Unstarted Issues (open and unassigned)', records: unassigned, id: 'unassigned', show_counter: true }) .col-md-4 - = render 'shared/milestones/issues', args.merge({ title: 'Ongoing Issues (open and assigned)', issues: assigned, id: 'ongoing' }) + = render 'shared/milestones/records', args.merge({ title: 'Ongoing Issues (open and assigned)', records: assigned, id: 'ongoing', show_counter: true }) .col-md-4 - = render 'shared/milestones/issues', args.merge({ title: 'Completed Issues (closed)', issues: closed, id: 'closed' }) + = render 'shared/milestones/records', args.merge({ title: 'Completed Issues (closed)', records: closed, id: 'closed', show_counter: true }) diff --git a/app/views/shared/milestones/_merge_request.html.haml b/app/views/shared/milestones/_merge_request.html.haml deleted file mode 100644 index 7b6c6606460..00000000000 --- a/app/views/shared/milestones/_merge_request.html.haml +++ /dev/null @@ -1,17 +0,0 @@ --# @project is present when viewing Project's milestone -- project = @project || merge_request.project - -%li{ id: dom_id(merge_request, 'sortable'), class: 'mr-row', 'data-iid' => merge_request.iid, 'data-url' => merge_request_path(merge_request) } - %span - - if show_project_name - %strong #{project.name} · - - elsif show_full_project_name - %strong #{project.name_with_namespace} · - = link_to_gfm merge_request.title, [project.namespace.becomes(Namespace), project, merge_request], title: merge_request.title - .merge-request-detail - = link_to [project.namespace.becomes(Namespace), project, merge_request] do - %span.merge-request-number ##{merge_request.iid} - - merge_request.labels.each do |label| - = render_colored_label(label) - - if merge_request.assignee - = image_tag avatar_icon(merge_request.assignee, 16), class: "avatar s16", alt: '' diff --git a/app/views/shared/milestones/_merge_requests.html.haml b/app/views/shared/milestones/_merge_requests.html.haml deleted file mode 100644 index 71991516e90..00000000000 --- a/app/views/shared/milestones/_merge_requests.html.haml +++ /dev/null @@ -1,8 +0,0 @@ -- primary = local_assigns.fetch(:primary, false) -- panel_class = primary ? 'panel-primary' : 'panel-default' - -.panel{ class: panel_class } - .panel-heading= title - %ul{ class: "well-list merge_requests-sortable-list", id: "merge_requests-list-#{id}", "data-state" => id } - - merge_requests.sort_by(&:position).each do |merge_request| - = render 'shared/milestones/merge_request', merge_request: merge_request, show_project_name: show_project_name, show_full_project_name: show_full_project_name diff --git a/app/views/shared/milestones/_merge_requests_tab.haml b/app/views/shared/milestones/_merge_requests_tab.haml index d912e607963..15b2876c32d 100644 --- a/app/views/shared/milestones/_merge_requests_tab.haml +++ b/app/views/shared/milestones/_merge_requests_tab.haml @@ -3,10 +3,10 @@ .row.prepend-top-default .col-md-3 - = render 'shared/milestones/merge_requests', args.merge({ title: 'Work in progress (open and unassigned)', merge_requests: unassigned, id: 'unassigned' }) + = render 'shared/milestones/records', args.merge({ title: 'Work in progress (open and unassigned)', records: unassigned, id: 'unassigned' }) .col-md-3 - = render 'shared/milestones/merge_requests', args.merge({ title: 'Waiting for merge (open and assigned)', merge_requests: assigned, id: 'ongoing' }) + = render 'shared/milestones/records', args.merge({ title: 'Waiting for merge (open and assigned)', records: assigned, id: 'ongoing' }) .col-md-3 - = render 'shared/milestones/merge_requests', args.merge({ title: 'Rejected (closed)', merge_requests: closed, id: 'closed' }) + = render 'shared/milestones/records', args.merge({ title: 'Rejected (closed)', records: closed, id: 'closed' }) .col-md-3 - = render 'shared/milestones/merge_requests', args.merge({ title: 'Merged', merge_requests: merged, id: 'merged', primary: true }) + = render 'shared/milestones/records', args.merge({ title: 'Merged', records: merged, id: 'merged', primary: true }) From 3c157b6bf7fa97cba769ecacd11cdcdba43adf4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Tue, 23 Feb 2016 22:21:04 -0500 Subject: [PATCH 11/16] Show Project name on Labels tab for Group and Dashboard context. --- app/views/shared/milestones/_labels_tab.html.haml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app/views/shared/milestones/_labels_tab.html.haml b/app/views/shared/milestones/_labels_tab.html.haml index bb6ce0afb40..88e8e4f9006 100644 --- a/app/views/shared/milestones/_labels_tab.html.haml +++ b/app/views/shared/milestones/_labels_tab.html.haml @@ -1,12 +1,19 @@ +- show_project_name = local_assigns.fetch(:show_project_name, false) +- show_full_project_name = local_assigns.fetch(:show_full_project_name, false) + %ul.bordered-list.manage-labels-list - labels.each do |label| - milestone = @milestone.is_a?(Milestone) ? @milestone : label.milestone %li = render_colored_label(label) + - if show_project_name + %strong · #{milestone.project.name} + - elsif show_full_project_name + %strong · #{milestone.project.name_with_namespace} + - args = [milestone.project.namespace, milestone.project, milestone_title: milestone.title, label_name: label.title] - options = args.extract_options! - %span.issues-count = link_to namespace_project_issues_path(*args, options.merge(state: 'opened')) do = pluralize label.open_issues_count, 'open issue' From 96058605c39ed2a7f6fdbdbbc3da05fdbe170681 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Tue, 23 Feb 2016 23:02:32 -0500 Subject: [PATCH 12/16] Show some stats about Milestone according to the new UI. --- app/views/dashboard/milestones/show.html.haml | 18 +++++++++++++----- app/views/groups/milestones/show.html.haml | 18 +++++++++++++----- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/app/views/dashboard/milestones/show.html.haml b/app/views/dashboard/milestones/show.html.haml index 18cf6fcbfd3..88edc025c62 100644 --- a/app/views/dashboard/milestones/show.html.haml +++ b/app/views/dashboard/milestones/show.html.haml @@ -41,11 +41,19 @@ = milestone.expires_at .context - %p.lead - Progress: - #{@milestone.closed_items_count} closed - – - #{@milestone.open_items_count} open + .milestone-summary + %h4 Progress + %strong= @milestone.issues.size + issues: + %span.milestone-stat + %strong= @milestone.opened_issues.size + open and + %strong= @milestone.closed_issues.size + closed + %span.milestone-stat + %strong== #{@milestone.percent_complete}% + complete + = milestone_progress_bar(@milestone) %ul.nav-links.no-top.no-bottom diff --git a/app/views/groups/milestones/show.html.haml b/app/views/groups/milestones/show.html.haml index 021425cad4f..bd61e50a5bd 100644 --- a/app/views/groups/milestones/show.html.haml +++ b/app/views/groups/milestones/show.html.haml @@ -47,11 +47,19 @@ = milestone.expires_at .context - %p.lead - Progress: - #{@milestone.closed_items_count} closed - – - #{@milestone.open_items_count} open + .milestone-summary + %h4 Progress + %strong= @milestone.issues.size + issues: + %span.milestone-stat + %strong= @milestone.opened_issues.size + open and + %strong= @milestone.closed_issues.size + closed + %span.milestone-stat + %strong== #{@milestone.percent_complete}% + complete + = milestone_progress_bar(@milestone) %ul.nav-links.no-top.no-bottom From 3f2d82485ed84247d88cde6423c9df517c8962b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Wed, 24 Feb 2016 14:05:28 -0500 Subject: [PATCH 13/16] Add missing partials! --- app/views/shared/milestones/_record.html.haml | 17 +++++++++++++++++ app/views/shared/milestones/_records.html.haml | 14 ++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 app/views/shared/milestones/_record.html.haml create mode 100644 app/views/shared/milestones/_records.html.haml diff --git a/app/views/shared/milestones/_record.html.haml b/app/views/shared/milestones/_record.html.haml new file mode 100644 index 00000000000..a753dd78a6a --- /dev/null +++ b/app/views/shared/milestones/_record.html.haml @@ -0,0 +1,17 @@ +-# @project is present when viewing Project's milestone +- project = @project || record.project + +%li{ id: dom_id(record, 'sortable'), class: "#{dom_class(record)}-row", 'data-iid' => record.iid, 'data-url' => polymorphic_path(record) } + %span + - if show_project_name + %strong #{project.name} · + - elsif show_full_project_name + %strong #{project.name_with_namespace} · + = link_to_gfm record.title, [project.namespace.becomes(Namespace), project, record], title: record.title + %div{class: "#{dom_class(record)}-detail"} + = link_to [project.namespace.becomes(Namespace), project, record] do + %span{ class: "#{dom_class(record)}-number" } ##{record.iid} + - record.labels.each do |label| + = render_colored_label(label) + - if record.assignee + = image_tag avatar_icon(record.assignee, 16), class: "avatar s16", alt: '' diff --git a/app/views/shared/milestones/_records.html.haml b/app/views/shared/milestones/_records.html.haml new file mode 100644 index 00000000000..29fd6aaac9b --- /dev/null +++ b/app/views/shared/milestones/_records.html.haml @@ -0,0 +1,14 @@ +- show_counter = local_assigns.fetch(:show_counter, false) +- primary = local_assigns.fetch(:primary, false) +- panel_class = primary ? 'panel-primary' : 'panel-default' + +.panel{ class: panel_class } + .panel-heading + = title + - if show_counter + .pull-right= records.size + + - class_prefix = dom_class(records).pluralize + %ul{ class: "well-list #{class_prefix}-sortable-list", id: "#{class_prefix}-list-#{id}", "data-state" => id } + - records.sort_by(&:position).each do |record| + = render 'shared/milestones/record', record: record, show_project_name: show_project_name, show_full_project_name: show_full_project_name From baa782ac9a29ba8fe162287511cbc9e4810fc4ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Wed, 24 Feb 2016 15:37:40 -0500 Subject: [PATCH 14/16] Add some spinach specs. --- features/group/milestones.feature | 18 ++++++++++ features/steps/group/milestones.rb | 55 ++++++++++++++++++++++++++++-- 2 files changed, 71 insertions(+), 2 deletions(-) diff --git a/features/group/milestones.feature b/features/group/milestones.feature index 62ea66a783c..a097e07583a 100644 --- a/features/group/milestones.feature +++ b/features/group/milestones.feature @@ -28,3 +28,21 @@ Feature: Group Milestones And I fill milestone name When I press create mileston button Then milestone in each project should be created + + Scenario: I should see Issues listed with labels + Given Group has projects with milestones + When I visit group "Owned" page + And I click on group milestones + And I click on one group milestone + Then I should see the "bug" label + And I should see the "feature" label + And I should see the project name in the Issue row + + Scenario: I should see the Labels tab + Given Group has projects with milestones + When I visit group "Owned" page + And I click on group milestones + And I click on one group milestone + And I click on the "Labels" tab + Then I should see the list of labels + And I should see the project name in the Label row diff --git a/features/steps/group/milestones.rb b/features/steps/group/milestones.rb index 2363ad797fa..ce4e52181ce 100644 --- a/features/steps/group/milestones.rb +++ b/features/steps/group/milestones.rb @@ -24,6 +24,9 @@ class Spinach::Features::GroupMilestones < Spinach::FeatureSteps end step 'I click on one group milestone' do + milestones = Milestone.where(title: 'GL-113') + @global_milestone = GlobalMilestone.new('GL-113', milestones) + click_link 'GL-113' end @@ -33,7 +36,7 @@ class Spinach::Features::GroupMilestones < Spinach::FeatureSteps step 'I should see group milestone with all issues and MRs assigned to that milestone' do expect(page).to have_content('Milestone GL-113') - expect(page).to have_content('Progress: 0 closed – 3 open') + expect(page).to have_content('3 issues: 3 open and 0 closed') issue = Milestone.find_by(name: 'GL-113').issues.first expect(page).to have_link(issue.title, href: namespace_project_issue_path(issue.project.namespace, issue.project, issue)) end @@ -60,6 +63,47 @@ class Spinach::Features::GroupMilestones < Spinach::FeatureSteps end end + step 'I should see the "bug" label' do + page.within('#tab-issues') do + expect(page).to have_content 'bug' + end + end + + step 'I should see the "feature" label' do + page.within('#tab-issues') do + expect(page).to have_content 'bug' + end + end + + step 'I should see the project name in the Issue row' do + page.within('#tab-issues') do + @global_milestone.projects.each do |project| + expect(page).to have_content project.name + end + end + end + + step 'I click on the "Labels" tab' do + page.within('.nav-links') do + page.find(:xpath, "//a[@href='#tab-labels']").click + end + end + + step 'I should see the list of labels' do + page.within('#tab-labels') do + expect(page).to have_content 'bug' + expect(page).to have_content 'feature' + end + end + + step 'I should see the project name in the Label row' do + page.within('#tab-labels') do + @global_milestone.projects.each do |project| + expect(page).to have_content project.name + end + end + end + private def group_milestone @@ -68,6 +112,10 @@ class Spinach::Features::GroupMilestones < Spinach::FeatureSteps %w(gitlabhq gitlab-ci cookbook-gitlab).each do |path| project = create :project, path: path, group: group milestone = create :milestone, title: "Version 7.2", project: project + + create(:label, project: project, title: 'bug') + create(:label, project: project, title: 'feature') + create :issue, project: project, assignee: current_user, @@ -80,11 +128,14 @@ class Spinach::Features::GroupMilestones < Spinach::FeatureSteps due_date: '2114-08-20', description: 'Lorem Ipsum is simply dummy text' - create :issue, + issue = create :issue, project: project, assignee: current_user, author: current_user, milestone: milestone + + issue.labels << project.labels.find_by(title: 'bug') + issue.labels << project.labels.find_by(title: 'feature') end end end From c91554de09cb2b19e1403fdf50f691004e6befdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Wed, 24 Feb 2016 17:45:49 -0500 Subject: [PATCH 15/16] Add link with filter by milestone for labels and avatar. Closes #13628 --- app/views/shared/milestones/_record.html.haml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/views/shared/milestones/_record.html.haml b/app/views/shared/milestones/_record.html.haml index a753dd78a6a..f82f2132bb6 100644 --- a/app/views/shared/milestones/_record.html.haml +++ b/app/views/shared/milestones/_record.html.haml @@ -1,5 +1,7 @@ -# @project is present when viewing Project's milestone - project = @project || record.project +- record_type = record.class.table_name +- base_url_args = [project.namespace.becomes(Namespace), project, record_type] %li{ id: dom_id(record, 'sortable'), class: "#{dom_class(record)}-row", 'data-iid' => record.iid, 'data-url' => polymorphic_path(record) } %span @@ -11,7 +13,11 @@ %div{class: "#{dom_class(record)}-detail"} = link_to [project.namespace.becomes(Namespace), project, record] do %span{ class: "#{dom_class(record)}-number" } ##{record.iid} + - record.labels.each do |label| - = render_colored_label(label) + %a{ href: polymorphic_path(base_url_args, { milestone_title: @milestone.title, label_name: label.title, state: 'all' }) }< + = render_colored_label(label) + - if record.assignee - = image_tag avatar_icon(record.assignee, 16), class: "avatar s16", alt: '' + %a{ href: polymorphic_path(base_url_args, { milestone_title: @milestone.title, assignee_id: record.assignee_id, state: 'all' }) } + = image_tag(avatar_icon(record.assignee, 16), class: "avatar s16", alt: '') From 95b06a62c0db5f8c285a1d24fa1994e10c70ff27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Sun, 6 Mar 2016 23:07:19 -0500 Subject: [PATCH 16/16] Updates from last code review. --- CHANGELOG | 1 + app/assets/javascripts/dispatcher.js.coffee | 2 +- app/assets/javascripts/milestone.js.coffee | 7 +- app/assets/stylesheets/pages/milestone.scss | 14 +-- .../projects/milestones_controller.rb | 4 - app/decorators/label_with_milestone.rb | 19 ---- app/finders/issuable_finder.rb | 6 +- app/helpers/milestones_helper.rb | 26 ++++++ app/models/concerns/issuable.rb | 2 + app/models/concerns/milestoneish.rb | 25 +++++ app/models/global_label.rb | 7 +- app/models/global_milestone.rb | 53 +---------- app/models/merge_request.rb | 1 - app/models/milestone.rb | 27 +----- app/models/project.rb | 2 +- .../dashboard/milestones/_milestone.html.haml | 31 ++----- app/views/dashboard/milestones/show.html.haml | 86 +---------------- .../groups/milestones/_milestone.html.haml | 34 +------ app/views/groups/milestones/show.html.haml | 93 +------------------ .../projects/milestones/_milestone.html.haml | 36 +------ app/views/projects/milestones/show.html.haml | 59 +----------- .../shared/milestones/_issuable.html.haml | 25 +++++ ...records.html.haml => _issuables.html.haml} | 10 +- .../shared/milestones/_issues_tab.html.haml | 6 +- .../shared/milestones/_labels_tab.html.haml | 30 +++--- .../milestones/_merge_requests_tab.haml | 8 +- .../shared/milestones/_milestone.html.haml | 45 +++++++++ app/views/shared/milestones/_record.html.haml | 23 ----- .../shared/milestones/_summary.html.haml | 28 ++++++ app/views/shared/milestones/_tabs.html.haml | 30 ++++++ app/views/shared/milestones/_top.html.haml | 58 ++++++++++++ config/application.rb | 1 - features/group/milestones.feature | 1 - features/steps/group/milestones.rb | 8 -- features/steps/project/issues/milestones.rb | 2 +- spec/models/milestone_spec.rb | 3 +- 36 files changed, 317 insertions(+), 496 deletions(-) delete mode 100644 app/decorators/label_with_milestone.rb create mode 100644 app/models/concerns/milestoneish.rb create mode 100644 app/views/shared/milestones/_issuable.html.haml rename app/views/shared/milestones/{_records.html.haml => _issuables.html.haml} (52%) create mode 100644 app/views/shared/milestones/_milestone.html.haml delete mode 100644 app/views/shared/milestones/_record.html.haml create mode 100644 app/views/shared/milestones/_summary.html.haml create mode 100644 app/views/shared/milestones/_tabs.html.haml create mode 100644 app/views/shared/milestones/_top.html.haml diff --git a/CHANGELOG b/CHANGELOG index 8692f61fe36..bba032036e3 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -12,6 +12,7 @@ v 8.6.0 (unreleased) - Allow search for logged out users - Don't show Issues/MRs from archived projects in Groups view - Increase the notes polling timeout over time (Roberto Dip) + - Show labels in dashboard and group milestone views v 8.5.4 - Do not cache requests for badges (including builds badge) diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee index d7feb5d5c87..54b28f2dd8d 100644 --- a/app/assets/javascripts/dispatcher.js.coffee +++ b/app/assets/javascripts/dispatcher.js.coffee @@ -23,7 +23,7 @@ class Dispatcher new Issue() shortcut_handler = new ShortcutsIssuable() new ZenMode() - when 'projects:milestones:show' + when 'projects:milestones:show', 'groups:milestones:show', 'dashboard:milestones:show' new Milestone() when 'projects:milestones:new', 'projects:milestones:edit' new ZenMode() diff --git a/app/assets/javascripts/milestone.js.coffee b/app/assets/javascripts/milestone.js.coffee index e6d8518bec8..0037a3a21c2 100644 --- a/app/assets/javascripts/milestone.js.coffee +++ b/app/assets/javascripts/milestone.js.coffee @@ -69,7 +69,7 @@ class @Milestone @bindIssuesSorting() @bindMergeRequestSorting() - @bindTabsSwitching + @bindTabsSwitching() bindIssuesSorting: -> $("#issues-list-unassigned, #issues-list-ongoing, #issues-list-closed").sortable( @@ -104,7 +104,7 @@ class @Milestone ).disableSelection() - bindMergeRequestSorting: -> + bindTabsSwitching: -> $('a[data-toggle="tab"]').on 'show.bs.tab', (e) -> currentTabClass = $(e.target).data('show') previousTabClass = $(e.relatedTarget).data('show') @@ -112,7 +112,8 @@ class @Milestone $(previousTabClass).hide() $(currentTabClass).removeClass('hidden') $(currentTabClass).show() - + + bindMergeRequestSorting: -> $("#merge_requests-list-unassigned, #merge_requests-list-ongoing, #merge_requests-list-closed").sortable( connectWith: ".merge_requests-sortable-list", dropOnEmpty: true, diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss index 300c2fa7e1b..d0e72a4422c 100644 --- a/app/assets/stylesheets/pages/milestone.scss +++ b/app/assets/stylesheets/pages/milestone.scss @@ -19,10 +19,11 @@ li.milestone { width: 105px; } - .issue-row, .merge_request-row { + .issuable-row { .color-label { border-radius: 2px; padding: 3px !important; + margin-right: 7px; } // Issue title @@ -45,19 +46,14 @@ li.milestone { } .issues-sortable-list, .merge_requests-sortable-list { - .issue-detail, .merge_request-detail { + .issuable-detail { display: block; + margin-top: 7px; - .issue-number, .merge_request-number { + .issuable-number { color: rgba(0,0,0,0.44); margin-right: 5px; } - .color-label { - padding: 6px 10px; - margin-right: 7px; - margin-top: 10px; - } - .avatar { float: none; } diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb index 21f30f278c8..da46731d945 100644 --- a/app/controllers/projects/milestones_controller.rb +++ b/app/controllers/projects/milestones_controller.rb @@ -32,10 +32,6 @@ class Projects::MilestonesController < Projects::ApplicationController end def show - @issues = @milestone.issues - @users = @milestone.participants.uniq - @merge_requests = @milestone.merge_requests - @labels = @milestone.labels end def create diff --git a/app/decorators/label_with_milestone.rb b/app/decorators/label_with_milestone.rb deleted file mode 100644 index a70a4e2f50d..00000000000 --- a/app/decorators/label_with_milestone.rb +++ /dev/null @@ -1,19 +0,0 @@ -class LabelWithMilestone - attr_reader :milestone - - def initialize(label, milestone) - @label, @milestone = label, milestone - end - - def method_missing(meth, *args) - if @label.respond_to?(meth) - @label.send(meth, *args) - else - super - end - end - - def respond_to?(meth) - @label.respond_to?(meth) - end -end diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index f7240edd618..c88a420b412 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -263,11 +263,9 @@ class IssuableFinder def by_label(items) if labels? if filter_by_no_label? - items = items. - joins("LEFT OUTER JOIN label_links ON label_links.target_type = '#{klass.name}' AND label_links.target_id = #{klass.table_name}.id"). - where(label_links: { id: nil }) + items = items.without_label else - items = items.joins(:labels).where(labels: { title: label_names }) + items = items.with_label(label_names) if projects items = items.where(labels: { project_id: projects }) diff --git a/app/helpers/milestones_helper.rb b/app/helpers/milestones_helper.rb index 7de81d8dfdb..e3e7daa49c5 100644 --- a/app/helpers/milestones_helper.rb +++ b/app/helpers/milestones_helper.rb @@ -9,6 +9,32 @@ module MilestonesHelper end end + def milestones_label_path(opts = {}) + if @project + namespace_project_issues_path(@project.namespace, @project, opts) + elsif @group + issues_group_path(@group, opts) + else + issues_dashboard_path(opts) + end + end + + def milestones_browse_issuables_path(milestone, type:) + opts = { milestone_title: milestone.title } + + if @project + polymorphic_path([@project.namespace.becomes(Namespace), @project, type], opts) + elsif @group + polymorphic_url([type, @group], opts) + else + polymorphic_url([type, :dashboard], opts) + end + end + + def milestone_issues_by_label_count(milestone, label, state:) + milestone.issues.with_label(label.title).send(state).size + end + def milestone_progress_bar(milestone) options = { class: 'progress-bar progress-bar-success', diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index a3c4a3d2776..27b97944e38 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -36,6 +36,8 @@ module Issuable scope :closed, -> { with_state(:closed) } scope :order_milestone_due_desc, -> { joins(:milestone).reorder('milestones.due_date DESC, milestones.id DESC') } 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 :join_project, -> { joins(:project) } scope :references_project, -> { references(:project) } diff --git a/app/models/concerns/milestoneish.rb b/app/models/concerns/milestoneish.rb new file mode 100644 index 00000000000..d67df7c1d9c --- /dev/null +++ b/app/models/concerns/milestoneish.rb @@ -0,0 +1,25 @@ +module Milestoneish + def closed_items_count + issues.closed.size + merge_requests.closed_and_merged.size + end + + def total_items_count + issues.size + merge_requests.size + end + + def complete? + total_items_count == closed_items_count + end + + def percent_complete + ((closed_items_count * 100) / total_items_count).abs + rescue ZeroDivisionError + 0 + end + + def remaining_days + return 0 if !due_date || expired? + + (due_date - Date.today).to_i + end +end diff --git a/app/models/global_label.rb b/app/models/global_label.rb index 0171f7d54b7..ddd4bad5c21 100644 --- a/app/models/global_label.rb +++ b/app/models/global_label.rb @@ -2,16 +2,19 @@ class GlobalLabel attr_accessor :title, :labels alias_attribute :name, :title + delegate :color, :description, to: :@first_label + def self.build_collection(labels) labels = labels.group_by(&:title) - labels.map do |title, label| - new(title, label) + labels.map do |title, labels| + new(title, labels) end end def initialize(title, labels) @title = title @labels = labels + @first_label = labels.find { |lbl| lbl.description.present? } || labels.first end end diff --git a/app/models/global_milestone.rb b/app/models/global_milestone.rb index e13aaf16732..97bd79af083 100644 --- a/app/models/global_milestone.rb +++ b/app/models/global_milestone.rb @@ -1,4 +1,6 @@ class GlobalMilestone + include Milestoneish + attr_accessor :title, :milestones alias_attribute :name, :title @@ -31,32 +33,6 @@ class GlobalMilestone @projects ||= Project.for_milestones(milestones.map(&:id)) end - def issues_count - issues.count - end - - def merge_requests_count - merge_requests.count - end - - def open_items_count - opened_issues.count + opened_merge_requests.count - end - - def closed_items_count - closed_issues.count + closed_merge_requests.count - end - - def total_items_count - issues_count + merge_requests_count - end - - def percent_complete - ((closed_items_count * 100) / total_items_count).abs - rescue ZeroDivisionError - 0 - end - def state state = milestones.map { |milestone| milestone.state } @@ -88,29 +64,8 @@ class GlobalMilestone end def labels - @labels ||= milestones.map do |ms| - ms.labels.map { |label| LabelWithMilestone.new(label, ms) } - end.flatten.sort_by!(&:title) - end - - def opened_issues - issues.opened - end - - def closed_issues - issues.closed - end - - def opened_merge_requests - merge_requests.opened - end - - def closed_merge_requests - merge_requests.with_states(:closed, :merged, :locked) - end - - def complete? - total_items_count == closed_items_count + @labels ||= GlobalLabel.build_collection(milestones.map(&:labels).flatten) + .sort_by!(&:title) end def due_date diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index f575494e2bf..0c1a47b3f6a 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -137,7 +137,6 @@ class MergeRequest < ActiveRecord::Base scope :by_milestone, ->(milestone) { where(milestone_id: milestone) } scope :in_projects, ->(project_ids) { where("source_project_id in (:project_ids) OR target_project_id in (:project_ids)", project_ids: project_ids) } scope :of_projects, ->(ids) { where(target_project_id: ids) } - scope :opened, -> { with_states(:opened, :reopened) } scope :merged, -> { with_state(:merged) } scope :closed_and_merged, -> { with_states(:closed, :merged) } diff --git a/app/models/milestone.rb b/app/models/milestone.rb index 7dc2f909b2f..e3969f32dd6 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -24,12 +24,13 @@ class Milestone < ActiveRecord::Base include Sortable include Referable include StripAttribute + include Milestoneish belongs_to :project has_many :issues has_many :labels, -> { distinct.reorder('labels.title') }, through: :issues has_many :merge_requests - has_many :participants, through: :issues, source: :assignee + has_many :participants, -> { distinct.reorder('users.name') }, through: :issues, source: :assignee scope :active, -> { with_state(:active) } scope :closed, -> { with_state(:closed) } @@ -92,30 +93,6 @@ class Milestone < ActiveRecord::Base end end - def open_items_count - self.issues.opened.count + self.merge_requests.opened.count - end - - def closed_items_count - self.issues.closed.count + self.merge_requests.closed_and_merged.count - end - - def total_items_count - self.issues.count + self.merge_requests.count - end - - def percent_complete - ((closed_items_count * 100) / total_items_count).abs - rescue ZeroDivisionError - 0 - end - - def remaining_days - return 0 if !due_date || expired? - - (due_date - Date.today).to_i - end - def expires_at if due_date if due_date.past? diff --git a/app/models/project.rb b/app/models/project.rb index 3a28d5d7fee..3235a1cee50 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -215,7 +215,7 @@ class Project < ActiveRecord::Base scope :public_only, -> { where(visibility_level: Project::PUBLIC) } scope :public_and_internal_only, -> { where(visibility_level: Project.public_and_internal_levels) } scope :non_archived, -> { where(archived: false) } - scope :for_milestones, ->(ids) { joins(:milestones).where('milestones.id' => ids) } + scope :for_milestones, ->(ids) { joins(:milestones).where('milestones.id' => ids).distinct } state_machine :import_status, initial: :none do event :import_start do diff --git a/app/views/dashboard/milestones/_milestone.html.haml b/app/views/dashboard/milestones/_milestone.html.haml index ea6c304d7de..6173ca6ab9b 100644 --- a/app/views/dashboard/milestones/_milestone.html.haml +++ b/app/views/dashboard/milestones/_milestone.html.haml @@ -1,25 +1,6 @@ -%li{class: "milestone milestone-#{milestone.closed? ? 'closed' : 'open'}", id: dom_id(milestone.milestones.first) } - .row - .col-sm-6 - %strong - = link_to_gfm truncate(milestone.title, length: 100), dashboard_milestone_path(milestone.safe_title, title: milestone.title) - .col-sm-6 - .pull-right.light #{milestone.percent_complete}% complete - .row - .col-sm-6 - = link_to issues_dashboard_path(milestone_title: milestone.title) do - = pluralize milestone.issues_count, 'Issue' - · - = link_to merge_requests_dashboard_path(milestone_title: milestone.title) do - = pluralize milestone.merge_requests_count, 'Merge Request' - .col-sm-6 - = milestone_progress_bar(milestone) - .row - .col-sm-6 - .expiration - = render 'shared/milestone_expired', milestone: milestone - .projects - - milestone.milestones.each do |milestone| - = link_to milestone_path(milestone) do - %span.label.label-gray - = milestone.project.name_with_namespace += render 'shared/milestones/milestone', + milestone_path: dashboard_milestone_path(milestone.safe_title, title: milestone.title), + issues_path: issues_dashboard_path(milestone_title: milestone.title), + merge_requests_path: merge_requests_dashboard_path(milestone_title: milestone.title), + milestone: milestone, + dashboard: true diff --git a/app/views/dashboard/milestones/show.html.haml b/app/views/dashboard/milestones/show.html.haml index 88edc025c62..60c84a26420 100644 --- a/app/views/dashboard/milestones/show.html.haml +++ b/app/views/dashboard/milestones/show.html.haml @@ -1,85 +1,5 @@ -- page_title @milestone.title, "Milestones" - header_title "Milestones", dashboard_milestones_path -.detail-page-header - .status-box{ class: "status-box-#{@milestone.closed? ? 'closed' : 'open'}" } - - if @milestone.closed? - Closed - - else - Open - %span.identifier - Milestone #{@milestone.title} - -.detail-page-description.gray-content-block.second-block - %h2.title - = markdown escape_once(@milestone.title), pipeline: :single_line - -- if @milestone.complete? && @milestone.active? - .alert.alert-success.prepend-top-default - %span All issues for this milestone are closed. Navigate to the project to close the milestone. - -.table-holder - %table.table - %thead - %tr - %th Project - %th Open issues - %th State - %th Due date - - @milestone.milestones.each do |milestone| - %tr - %td - = link_to "#{milestone.project.name_with_namespace}", namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone) - %td - = milestone.issues.opened.count - %td - - if milestone.closed? - Closed - - else - Open - %td - = milestone.expires_at - -.context - .milestone-summary - %h4 Progress - %strong= @milestone.issues.size - issues: - %span.milestone-stat - %strong= @milestone.opened_issues.size - open and - %strong= @milestone.closed_issues.size - closed - %span.milestone-stat - %strong== #{@milestone.percent_complete}% - complete - - = milestone_progress_bar(@milestone) - -%ul.nav-links.no-top.no-bottom - %li.active - = link_to '#tab-issues', 'data-toggle' => 'tab' do - Issues - %span.badge= @milestone.issues_count - %li - = link_to '#tab-merge-requests', 'data-toggle' => 'tab' do - Merge Requests - %span.badge= @milestone.merge_requests_count - %li - = link_to '#tab-participants', 'data-toggle' => 'tab' do - Participants - %span.badge= @milestone.participants.count - %li - = link_to '#tab-labels', 'data-toggle' => 'tab', 'data-show' => '.tab-issues-buttons' do - Labels - %span.badge= @milestone.labels.count - -.tab-content.milestone-content - .tab-pane.active#tab-issues - = render 'shared/milestones/issues_tab', unassigned: @milestone.opened_issues.unassigned, assigned: @milestone.opened_issues.assigned, closed: @milestone.closed_issues, show_full_project_name: true - .tab-pane#tab-merge-requests - = render 'shared/milestones/merge_requests_tab', unassigned: @milestone.opened_merge_requests.unassigned, assigned: @milestone.opened_merge_requests.assigned, closed: @milestone.merge_requests.closed, merged: @milestone.merge_requests.merged, show_full_project_name: true - .tab-pane#tab-participants - = render 'shared/milestones/participants_tab', users: @milestone.participants - .tab-pane#tab-labels - = render 'shared/milestones/labels_tab', labels: @milestone.labels, show_full_project_name: true += render 'shared/milestones/top', milestone: @milestone += render 'shared/milestones/summary', milestone: @milestone += render 'shared/milestones/tabs', milestone: @milestone, show_full_project_name: true diff --git a/app/views/groups/milestones/_milestone.html.haml b/app/views/groups/milestones/_milestone.html.haml index 50558d7dce8..4c4e0a26728 100644 --- a/app/views/groups/milestones/_milestone.html.haml +++ b/app/views/groups/milestones/_milestone.html.haml @@ -1,29 +1,5 @@ -%li{class: "milestone milestone-#{milestone.closed? ? 'closed' : 'open'}", id: dom_id(milestone.milestones.first) } - .row - .col-sm-6 - %strong - = link_to_gfm truncate(milestone.title, length: 100), group_milestone_path(@group, milestone.safe_title, title: milestone.title) - .col-sm-6 - .pull-right.light #{milestone.percent_complete}% complete - .row - .col-sm-6 - = link_to issues_group_path(@group, milestone_title: milestone.title) do - = pluralize milestone.issues_count, 'Issue' - · - = link_to merge_requests_group_path(@group, milestone_title: milestone.title) do - = pluralize milestone.merge_requests_count, 'Merge Request' - .col-sm-6 - = milestone_progress_bar(milestone) - .row - .col-sm-6 - %div - - milestone.milestones.each do |milestone| - = link_to milestone_path(milestone) do - %span.label.label-gray - = milestone.project.name - .col-sm-6 - - if can?(current_user, :admin_milestones, @group) - - if milestone.closed? - = link_to 'Reopen Milestone', group_milestone_path(@group, milestone.safe_title, title: milestone.title, milestone: {state_event: :activate }), method: :put, class: "btn btn-xs btn-grouped btn-reopen" - - else - = link_to 'Close Milestone', group_milestone_path(@group, milestone.safe_title, title: milestone.title, milestone: {state_event: :close }), method: :put, class: "btn btn-xs btn-close" += render 'shared/milestones/milestone', + milestone_path: group_milestone_path(@group, milestone.safe_title, title: milestone.title), + issues_path: issues_group_path(@group, milestone_title: milestone.title), + merge_requests_path: merge_requests_group_path(@group, milestone_title: milestone.title), + milestone: milestone diff --git a/app/views/groups/milestones/show.html.haml b/app/views/groups/milestones/show.html.haml index bd61e50a5bd..fb6f0da28f8 100644 --- a/app/views/groups/milestones/show.html.haml +++ b/app/views/groups/milestones/show.html.haml @@ -1,91 +1,4 @@ -- page_title @milestone.title, "Milestones" = render "header_title" - -.detail-page-header - .status-box{ class: "status-box-#{@milestone.closed? ? 'closed' : 'open'}" } - - if @milestone.closed? - Closed - - else - Open - %span.identifier - Milestone #{@milestone.title} - .pull-right - - if can?(current_user, :admin_milestones, @group) - - if @milestone.active? - = link_to 'Close Milestone', group_milestone_path(@group, @milestone.safe_title, title: @milestone.title, milestone: {state_event: :close }), method: :put, class: "btn btn-grouped btn-close" - - else - = link_to 'Reopen Milestone', group_milestone_path(@group, @milestone.safe_title, title: @milestone.title, milestone: {state_event: :activate }), method: :put, class: "btn btn-grouped btn-reopen" - -.detail-page-description.gray-content-block.second-block - %h2.title - = markdown escape_once(@milestone.title), pipeline: :single_line - -- if @milestone.complete? && @milestone.active? - .alert.alert-success.prepend-top-default - %span All issues for this milestone are closed. You may close the milestone now. - -.table-holder - %table.table - %thead - %tr - %th Project - %th Open issues - %th State - %th Due date - - @milestone.milestones.each do |milestone| - %tr - %td - = link_to "#{milestone.project.name}", namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone) - %td - = milestone.issues.opened.count - %td - - if milestone.closed? - Closed - - else - Open - %td - = milestone.expires_at - -.context - .milestone-summary - %h4 Progress - %strong= @milestone.issues.size - issues: - %span.milestone-stat - %strong= @milestone.opened_issues.size - open and - %strong= @milestone.closed_issues.size - closed - %span.milestone-stat - %strong== #{@milestone.percent_complete}% - complete - - = milestone_progress_bar(@milestone) - -%ul.nav-links.no-top.no-bottom - %li.active - = link_to '#tab-issues', 'data-toggle' => 'tab' do - Issues - %span.badge= @milestone.issues_count - %li - = link_to '#tab-merge-requests', 'data-toggle' => 'tab' do - Merge Requests - %span.badge= @milestone.merge_requests_count - %li - = link_to '#tab-participants', 'data-toggle' => 'tab' do - Participants - %span.badge= @milestone.participants.count - %li - = link_to '#tab-labels', 'data-toggle' => 'tab', 'data-show' => '.tab-issues-buttons' do - Labels - %span.badge= @milestone.labels.count - -.tab-content.milestone-content - .tab-pane.active#tab-issues - = render 'shared/milestones/issues_tab', unassigned: @milestone.opened_issues.unassigned, assigned: @milestone.opened_issues.assigned, closed: @milestone.closed_issues, show_project_name: true - .tab-pane#tab-merge-requests - = render 'shared/milestones/merge_requests_tab', unassigned: @milestone.opened_merge_requests.unassigned, assigned: @milestone.opened_merge_requests.assigned, closed: @milestone.merge_requests.closed, merged: @milestone.merge_requests.merged, show_project_name: true - .tab-pane#tab-participants - = render 'shared/milestones/participants_tab', users: @milestone.participants - .tab-pane#tab-labels - = render 'shared/milestones/labels_tab', labels: @milestone.labels, show_project_name: true += render 'shared/milestones/top', milestone: @milestone, group: @group += render 'shared/milestones/summary', milestone: @milestone += render 'shared/milestones/tabs', milestone: @milestone, show_project_name: true diff --git a/app/views/projects/milestones/_milestone.html.haml b/app/views/projects/milestones/_milestone.html.haml index 67d95ab0364..77b566db6b6 100644 --- a/app/views/projects/milestones/_milestone.html.haml +++ b/app/views/projects/milestones/_milestone.html.haml @@ -1,31 +1,5 @@ -%li{class: "milestone milestone-#{milestone.closed? ? 'closed' : 'open'}", id: dom_id(milestone) } - .row - .col-sm-6 - %strong - = link_to_gfm truncate(milestone.title, length: 100), namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone) - - .col-sm-6 - .pull-right.light #{milestone.percent_complete}% complete - .row - .col-sm-6 - = link_to namespace_project_issues_path(milestone.project.namespace, milestone.project, milestone_title: milestone.title) do - = pluralize milestone.issues.count, 'Issue' - · - = link_to namespace_project_merge_requests_path(milestone.project.namespace, milestone.project, milestone_title: milestone.title) do - = pluralize milestone.merge_requests.count, 'Merge Request' - .col-sm-6 - = milestone_progress_bar(milestone) - - .row - .col-sm-6 - = render 'shared/milestone_expired', milestone: milestone - .col-sm-6 - - if can?(current_user, :admin_milestone, milestone.project) and milestone.active? - = link_to edit_namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone), class: "btn btn-xs" do - = icon('pencil-square-o') - Edit - \ - = link_to 'Close Milestone', namespace_project_milestone_path(@project.namespace, @project, milestone, milestone: {state_event: :close }), method: :put, remote: true, class: "btn btn-xs btn-close" - = link_to namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-xs btn-remove" do - = icon('trash-o') - Delete += render 'shared/milestones/milestone', + milestone_path: namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone), + issues_path: namespace_project_issues_path(milestone.project.namespace, milestone.project, milestone_title: milestone.title), + merge_requests_path: namespace_project_merge_requests_path(milestone.project.namespace, milestone.project, milestone_title: milestone.title), + milestone: milestone diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml index 4aa1a53e87e..b4597043a27 100644 --- a/app/views/projects/milestones/show.html.haml +++ b/app/views/projects/milestones/show.html.haml @@ -42,62 +42,9 @@ = preserve do = markdown @milestone.description -- if @milestone.issues.any? && @milestone.can_be_closed? +- if @milestone.complete? && @milestone.active? .alert.alert-success.prepend-top-default %span All issues for this milestone are closed. You may close milestone now. -.context.prepend-top-default - .milestone-summary - %h4 Progress - %strong= @milestone.issues.count - issues: - %span.milestone-stat - %strong= @milestone.open_items_count - open and - %strong= @milestone.closed_items_count - closed - %span.milestone-stat - %strong== #{@milestone.percent_complete}% - complete - %span.milestone-stat - %span.remaining-days= milestone_remaining_days(@milestone) - %span.pull-right.tab-issues-buttons - - if can?(current_user, :create_issue, @project) - = link_to new_namespace_project_issue_path(@project.namespace, @project, issue: { milestone_id: @milestone.id }), class: "btn btn-grouped", title: "New Issue" do - %i.fa.fa-plus - New Issue - - if can?(current_user, :read_issue, @project) - = link_to 'Browse Issues', namespace_project_issues_path(@milestone.project.namespace, @milestone.project, milestone_title: @milestone.title), class: "btn btn-grouped" - %span.pull-right.tab-merge-requests-buttons.hidden - - if can?(current_user, :read_merge_request, @project) - = link_to 'Browse Merge Requests', namespace_project_merge_requests_path(@milestone.project.namespace, @milestone.project, milestone_title: @milestone.title), class: "btn btn-grouped" - - = milestone_progress_bar(@milestone) - -%ul.nav-links.no-top.no-bottom - %li.active - = link_to '#tab-issues', 'data-toggle' => 'tab', 'data-show' => '.tab-issues-buttons' do - Issues - %span.badge= @issues.count - %li - = link_to '#tab-merge-requests', 'data-toggle' => 'tab', 'data-show' => '.tab-merge-requests-buttons' do - Merge Requests - %span.badge= @merge_requests.count - %li - = link_to '#tab-participants', 'data-toggle' => 'tab' do - Participants - %span.badge= @users.count - %li - = link_to '#tab-labels', 'data-toggle' => 'tab', 'data-show' => '.tab-issues-buttons' do - Labels - %span.badge= @labels.count - -.tab-content.milestone-content - .tab-pane.active#tab-issues - = render 'shared/milestones/issues_tab', unassigned: @issues.opened.unassigned, assigned: @issues.opened.assigned, closed: @issues.closed - .tab-pane#tab-merge-requests - = render 'shared/milestones/merge_requests_tab', unassigned: @merge_requests.opened.unassigned, assigned: @merge_requests.opened.assigned, closed: @merge_requests.closed, merged: @merge_requests.merged - .tab-pane#tab-participants - = render 'shared/milestones/participants_tab', users: @users - .tab-pane#tab-labels - = render 'shared/milestones/labels_tab', labels: @labels += render 'shared/milestones/summary', milestone: @milestone, project: @project += render 'shared/milestones/tabs', milestone: @milestone diff --git a/app/views/shared/milestones/_issuable.html.haml b/app/views/shared/milestones/_issuable.html.haml new file mode 100644 index 00000000000..f7c6fc14adf --- /dev/null +++ b/app/views/shared/milestones/_issuable.html.haml @@ -0,0 +1,25 @@ +-# @project is present when viewing Project's milestone +- project = @project || issuable.project +- assignee = issuable.assignee +- issuable_type = issuable.class.table_name +- base_url_args = [project.namespace.becomes(Namespace), project, issuable_type] + +%li{ id: dom_id(issuable, 'sortable'), class: "issuable-row", 'data-iid' => issuable.iid, 'data-url' => polymorphic_path(issuable) } + %span + - if show_project_name + %strong #{project.name} · + - elsif show_full_project_name + %strong #{project.name_with_namespace} · + = link_to_gfm issuable.title, [project.namespace.becomes(Namespace), project, issuable], title: issuable.title + %div{class: 'issuable-detail'} + = link_to [project.namespace.becomes(Namespace), project, issuable] do + %span{ class: 'issuable-number' }>= issuable.to_reference + + - issuable.labels.each do |label| + = link_to polymorphic_path(base_url_args, { milestone_title: @milestone.title, label_name: label.title, state: 'all' }) do + - render_colored_label(label) + + - if assignee + = link_to polymorphic_path(base_url_args, { milestone_title: @milestone.title, assignee_id: issuable.assignee_id, state: 'all' }), + class: 'has_tooltip', data: { 'original-title' => "Assigned to #{sanitize(assignee.name)}", container: 'body' } do + - image_tag(avatar_icon(issuable.assignee, 16), class: "avatar s16", alt: '') diff --git a/app/views/shared/milestones/_records.html.haml b/app/views/shared/milestones/_issuables.html.haml similarity index 52% rename from app/views/shared/milestones/_records.html.haml rename to app/views/shared/milestones/_issuables.html.haml index 29fd6aaac9b..8619939dde7 100644 --- a/app/views/shared/milestones/_records.html.haml +++ b/app/views/shared/milestones/_issuables.html.haml @@ -6,9 +6,11 @@ .panel-heading = title - if show_counter - .pull-right= records.size + .pull-right= issuables.size - - class_prefix = dom_class(records).pluralize + - class_prefix = dom_class(issuables).pluralize %ul{ class: "well-list #{class_prefix}-sortable-list", id: "#{class_prefix}-list-#{id}", "data-state" => id } - - records.sort_by(&:position).each do |record| - = render 'shared/milestones/record', record: record, show_project_name: show_project_name, show_full_project_name: show_full_project_name + = render partial: 'shared/milestones/issuable', + collection: issuables.sort_by(&:position), + as: :issuable, + locals: { show_project_name: show_project_name, show_full_project_name: show_full_project_name } diff --git a/app/views/shared/milestones/_issues_tab.html.haml b/app/views/shared/milestones/_issues_tab.html.haml index 44a221114bb..a8db7f8a556 100644 --- a/app/views/shared/milestones/_issues_tab.html.haml +++ b/app/views/shared/milestones/_issues_tab.html.haml @@ -3,8 +3,8 @@ .row.prepend-top-default .col-md-4 - = render 'shared/milestones/records', args.merge({ title: 'Unstarted Issues (open and unassigned)', records: unassigned, id: 'unassigned', show_counter: true }) + = render 'shared/milestones/issuables', args.merge(title: 'Unstarted Issues (open and unassigned)', issuables: issues.opened.unassigned, id: 'unassigned', show_counter: true) .col-md-4 - = render 'shared/milestones/records', args.merge({ title: 'Ongoing Issues (open and assigned)', records: assigned, id: 'ongoing', show_counter: true }) + = render 'shared/milestones/issuables', args.merge(title: 'Ongoing Issues (open and assigned)', issuables: issues.opened.assigned, id: 'ongoing', show_counter: true) .col-md-4 - = render 'shared/milestones/records', args.merge({ title: 'Completed Issues (closed)', records: closed, id: 'closed', show_counter: true }) + = render 'shared/milestones/issuables', args.merge(title: 'Completed Issues (closed)', issuables: issues.closed, id: 'closed', show_counter: true) diff --git a/app/views/shared/milestones/_labels_tab.html.haml b/app/views/shared/milestones/_labels_tab.html.haml index 88e8e4f9006..ba27bafd1bc 100644 --- a/app/views/shared/milestones/_labels_tab.html.haml +++ b/app/views/shared/milestones/_labels_tab.html.haml @@ -1,22 +1,18 @@ -- show_project_name = local_assigns.fetch(:show_project_name, false) -- show_full_project_name = local_assigns.fetch(:show_full_project_name, false) - %ul.bordered-list.manage-labels-list - labels.each do |label| - - milestone = @milestone.is_a?(Milestone) ? @milestone : label.milestone + - options = { milestone_title: @milestone.title, label_name: label.title } %li - = render_colored_label(label) - - if show_project_name - %strong · #{milestone.project.name} - - elsif show_full_project_name - %strong · #{milestone.project.name_with_namespace} + %span.label-row + = link_to milestones_label_path(options) do + - render_colored_label(label) + %span.prepend-left-10 + = markdown(label.description, pipeline: :single_line) - - args = [milestone.project.namespace, milestone.project, milestone_title: milestone.title, label_name: label.title] - - options = args.extract_options! - %span.issues-count - = link_to namespace_project_issues_path(*args, options.merge(state: 'opened')) do - = pluralize label.open_issues_count, 'open issue' - %span.issues-count - = link_to namespace_project_issues_path(*args, options.merge(state: 'closed')) do - = pluralize label.closed_issues_count, 'closed issue' + .pull-right + %strong.issues-count + = link_to milestones_label_path(options.merge(state: 'opened')) do + - pluralize milestone_issues_by_label_count(@milestone, label, state: :opened), 'open issue' + %strong.issues-count + = link_to milestones_label_path(options.merge(state: 'closed')) do + - pluralize milestone_issues_by_label_count(@milestone, label, state: :closed), 'closed issue' diff --git a/app/views/shared/milestones/_merge_requests_tab.haml b/app/views/shared/milestones/_merge_requests_tab.haml index 15b2876c32d..c29d8ee6737 100644 --- a/app/views/shared/milestones/_merge_requests_tab.haml +++ b/app/views/shared/milestones/_merge_requests_tab.haml @@ -3,10 +3,10 @@ .row.prepend-top-default .col-md-3 - = render 'shared/milestones/records', args.merge({ title: 'Work in progress (open and unassigned)', records: unassigned, id: 'unassigned' }) + = render 'shared/milestones/issuables', args.merge(title: 'Work in progress (open and unassigned)', issuables: merge_requests.opened.unassigned, id: 'unassigned') .col-md-3 - = render 'shared/milestones/records', args.merge({ title: 'Waiting for merge (open and assigned)', records: assigned, id: 'ongoing' }) + = render 'shared/milestones/issuables', args.merge(title: 'Waiting for merge (open and assigned)', issuables: merge_requests.opened.assigned, id: 'ongoing') .col-md-3 - = render 'shared/milestones/records', args.merge({ title: 'Rejected (closed)', records: closed, id: 'closed' }) + = render 'shared/milestones/issuables', args.merge(title: 'Rejected (closed)', issuables: merge_requests.closed, id: 'closed') .col-md-3 - = render 'shared/milestones/records', args.merge({ title: 'Merged', records: merged, id: 'merged', primary: true }) + = render 'shared/milestones/issuables', args.merge(title: 'Merged', issuables: merge_requests.merged, id: 'merged', primary: true) diff --git a/app/views/shared/milestones/_milestone.html.haml b/app/views/shared/milestones/_milestone.html.haml new file mode 100644 index 00000000000..f01138af3f0 --- /dev/null +++ b/app/views/shared/milestones/_milestone.html.haml @@ -0,0 +1,45 @@ +- dashboard = local_assigns[:dashboard] +- custom_dom_id = dom_id(@project ? milestone : milestone.milestones.first) + +%li{class: "milestone milestone-#{milestone.closed? ? 'closed' : 'open'}", id: custom_dom_id } + .row + .col-sm-6 + %strong= link_to_gfm truncate(milestone.title, length: 100), milestone_path + .col-sm-6 + .pull-right.light #{milestone.percent_complete}% complete + .row + .col-sm-6 + = link_to pluralize(milestone.issues.size, 'Issue'), issues_path + · + = link_to pluralize(milestone.merge_requests.size, 'Merge Request'), merge_requests_path + .col-sm-6= milestone_progress_bar(milestone) + - if milestone.is_a?(GlobalMilestone) + .row + .col-sm-6 + .expiration= render('shared/milestone_expired', milestone: milestone) + .projects + - milestone.milestones.each do |milestone| + = link_to milestone_path(milestone) do + %span.label.label-gray + = dashboard ? milestone.project.name_with_namespace : milestone.project.name + - if @group + .col-sm-6 + - if can?(current_user, :admin_milestones, @group) + - if milestone.closed? + = link_to 'Reopen Milestone', group_milestone_path(@group, milestone.safe_title, title: milestone.title, milestone: {state_event: :activate }), method: :put, class: "btn btn-xs btn-grouped btn-reopen" + - else + = link_to 'Close Milestone', group_milestone_path(@group, milestone.safe_title, title: milestone.title, milestone: {state_event: :close }), method: :put, class: "btn btn-xs btn-close" + + - if @project + .row + .col-sm-6= render('shared/milestone_expired', milestone: milestone) + .col-sm-6 + - if can?(current_user, :admin_milestone, milestone.project) and milestone.active? + = link_to edit_namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone), class: "btn btn-xs" do + = icon('pencil-square-o') + Edit + \ + = link_to 'Close Milestone', namespace_project_milestone_path(@project.namespace, @project, milestone, milestone: {state_event: :close }), method: :put, remote: true, class: "btn btn-xs btn-close" + = link_to namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-xs btn-remove" do + = icon('trash-o') + Delete diff --git a/app/views/shared/milestones/_record.html.haml b/app/views/shared/milestones/_record.html.haml deleted file mode 100644 index f82f2132bb6..00000000000 --- a/app/views/shared/milestones/_record.html.haml +++ /dev/null @@ -1,23 +0,0 @@ --# @project is present when viewing Project's milestone -- project = @project || record.project -- record_type = record.class.table_name -- base_url_args = [project.namespace.becomes(Namespace), project, record_type] - -%li{ id: dom_id(record, 'sortable'), class: "#{dom_class(record)}-row", 'data-iid' => record.iid, 'data-url' => polymorphic_path(record) } - %span - - if show_project_name - %strong #{project.name} · - - elsif show_full_project_name - %strong #{project.name_with_namespace} · - = link_to_gfm record.title, [project.namespace.becomes(Namespace), project, record], title: record.title - %div{class: "#{dom_class(record)}-detail"} - = link_to [project.namespace.becomes(Namespace), project, record] do - %span{ class: "#{dom_class(record)}-number" } ##{record.iid} - - - record.labels.each do |label| - %a{ href: polymorphic_path(base_url_args, { milestone_title: @milestone.title, label_name: label.title, state: 'all' }) }< - = render_colored_label(label) - - - if record.assignee - %a{ href: polymorphic_path(base_url_args, { milestone_title: @milestone.title, assignee_id: record.assignee_id, state: 'all' }) } - = image_tag(avatar_icon(record.assignee, 16), class: "avatar s16", alt: '') diff --git a/app/views/shared/milestones/_summary.html.haml b/app/views/shared/milestones/_summary.html.haml new file mode 100644 index 00000000000..59d4ae29f79 --- /dev/null +++ b/app/views/shared/milestones/_summary.html.haml @@ -0,0 +1,28 @@ +- project = local_assigns[:project] + +.context.prepend-top-default + .milestone-summary + %h4 Progress + %strong= milestone.issues.size + issues: + %span.milestone-stat + %strong= milestone.issues.opened.size + open and + %strong= milestone.issues.closed.size + closed + %span.milestone-stat + %strong== #{milestone.percent_complete}% + complete + + %span.milestone-stat + %span.remaining-days= milestone_remaining_days(milestone) + %span.pull-right.tab-issues-buttons + - if project && can?(current_user, :create_issue, project) + = link_to new_namespace_project_issue_path(project.namespace, project, issue: { milestone_id: milestone.id }), class: "btn btn-grouped", title: "New Issue" do + %i.fa.fa-plus + New Issue + = link_to 'Browse Issues', milestones_browse_issuables_path(milestone, type: :issues), class: "btn btn-grouped" + %span.pull-right.tab-merge-requests-buttons.hidden + = link_to 'Browse Merge Requests', milestones_browse_issuables_path(milestone, type: :merge_requests), class: "btn btn-grouped" + + = milestone_progress_bar(milestone) diff --git a/app/views/shared/milestones/_tabs.html.haml b/app/views/shared/milestones/_tabs.html.haml new file mode 100644 index 00000000000..57d7ee85a3b --- /dev/null +++ b/app/views/shared/milestones/_tabs.html.haml @@ -0,0 +1,30 @@ +%ul.nav-links.no-top.no-bottom + %li.active + = link_to '#tab-issues', 'data-toggle' => 'tab', 'data-show' => '.tab-issues-buttons' do + Issues + %span.badge= milestone.issues.size + %li + = link_to '#tab-merge-requests', 'data-toggle' => 'tab', 'data-show' => '.tab-merge-requests-buttons' do + Merge Requests + %span.badge= milestone.merge_requests.size + %li + = link_to '#tab-participants', 'data-toggle' => 'tab' do + Participants + %span.badge= milestone.participants.count + %li + = link_to '#tab-labels', 'data-toggle' => 'tab' do + Labels + %span.badge= milestone.labels.count + +- show_project_name = local_assigns.fetch(:show_project_name, false) +- show_full_project_name = local_assigns.fetch(:show_full_project_name, false) + +.tab-content.milestone-content + .tab-pane.active#tab-issues + = render 'shared/milestones/issues_tab', issues: milestone.issues, show_project_name: show_project_name, show_full_project_name: show_full_project_name + .tab-pane#tab-merge-requests + = render 'shared/milestones/merge_requests_tab', merge_requests: milestone.merge_requests, show_project_name: show_project_name, show_full_project_name: show_full_project_name + .tab-pane#tab-participants + = render 'shared/milestones/participants_tab', users: milestone.participants + .tab-pane#tab-labels + = render 'shared/milestones/labels_tab', labels: milestone.labels diff --git a/app/views/shared/milestones/_top.html.haml b/app/views/shared/milestones/_top.html.haml new file mode 100644 index 00000000000..4cf1d948b5b --- /dev/null +++ b/app/views/shared/milestones/_top.html.haml @@ -0,0 +1,58 @@ +- page_title milestone.title, "Milestones" + +- group = local_assigns[:group] + +.detail-page-header + .status-box{ class: "status-box-#{milestone.closed? ? 'closed' : 'open'}" } + - if milestone.closed? + Closed + - elsif milestone.expired? + Expired + - else + Open + %span.identifier + Milestone #{milestone.title} + - if milestone.expires_at + %span.creator + · + = milestone.expires_at + - if group + .pull-right + - if can?(current_user, :admin_milestones, group) + - if milestone.active? + = link_to 'Close Milestone', group_milestone_path(group, milestone.safe_title, title: milestone.title, milestone: {state_event: :close }), method: :put, class: "btn btn-grouped btn-close" + - else + = link_to 'Reopen Milestone', group_milestone_path(group, milestone.safe_title, title: milestone.title, milestone: {state_event: :activate }), method: :put, class: "btn btn-grouped btn-reopen" + +.detail-page-description.gray-content-block.second-block + %h2.title + = markdown escape_once(milestone.title), pipeline: :single_line + +- if milestone.complete? && milestone.active? + .alert.alert-success.prepend-top-default + - close_msg = group ? 'You may close the milestone now.' : 'Navigate to the project to close the milestone.' + %span All issues for this milestone are closed. #{close_msg} + +.table-holder + %table.table + %thead + %tr + %th Project + %th Open issues + %th State + %th Due date + - milestone.milestones.each do |ms| + %tr + %td + - project_name = group ? ms.project.name : ms.project.name_with_namespace + = link_to project_name, namespace_project_milestone_path(ms.project.namespace, ms.project, ms) + %td + = ms.issues.opened.count + %td + - if ms.closed? + Closed + - else + Open + %td + = ms.expires_at + diff --git a/config/application.rb b/config/application.rb index fee8637a4cb..28684a3e578 100644 --- a/config/application.rb +++ b/config/application.rb @@ -15,7 +15,6 @@ module Gitlab # Custom directories with classes and modules you want to be autoloadable. config.autoload_paths.push(*%W(#{config.root}/lib - #{config.root}/app/decorators #{config.root}/app/models/hooks #{config.root}/app/models/concerns #{config.root}/app/models/project_services diff --git a/features/group/milestones.feature b/features/group/milestones.feature index a097e07583a..d6c05df9840 100644 --- a/features/group/milestones.feature +++ b/features/group/milestones.feature @@ -45,4 +45,3 @@ Feature: Group Milestones And I click on one group milestone And I click on the "Labels" tab Then I should see the list of labels - And I should see the project name in the Label row diff --git a/features/steps/group/milestones.rb b/features/steps/group/milestones.rb index ce4e52181ce..a167d259837 100644 --- a/features/steps/group/milestones.rb +++ b/features/steps/group/milestones.rb @@ -96,14 +96,6 @@ class Spinach::Features::GroupMilestones < Spinach::FeatureSteps end end - step 'I should see the project name in the Label row' do - page.within('#tab-labels') do - @global_milestone.projects.each do |project| - expect(page).to have_content project.name - end - end - end - private def group_milestone diff --git a/features/steps/project/issues/milestones.rb b/features/steps/project/issues/milestones.rb index e2eda511497..4faa0f4707c 100644 --- a/features/steps/project/issues/milestones.rb +++ b/features/steps/project/issues/milestones.rb @@ -59,7 +59,7 @@ class Spinach::Features::ProjectIssuesMilestones < Spinach::FeatureSteps end step 'I should see 3 issues' do - expect(page).to have_selector('#tab-issues li.issue-row', count: 4) + expect(page).to have_selector('#tab-issues li.issuable-row', count: 4) end step 'I click link to remove milestone' do diff --git a/spec/models/milestone_spec.rb b/spec/models/milestone_spec.rb index 1b1380ce4e2..28f13100d15 100644 --- a/spec/models/milestone_spec.rb +++ b/spec/models/milestone_spec.rb @@ -60,7 +60,7 @@ describe Milestone, models: true do end it "should recover from dividing by zero" do - expect(milestone.issues).to receive(:count).and_return(0) + expect(milestone.issues).to receive(:size).and_return(0) expect(milestone.percent_complete).to eq(0) end end @@ -114,7 +114,6 @@ describe Milestone, models: true do end it { expect(milestone.closed_items_count).to eq(1) } - it { expect(milestone.open_items_count).to eq(2) } it { expect(milestone.total_items_count).to eq(3) } it { expect(milestone.is_empty?).to be_falsey } end