summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacob Schatz <jschatz1@gmail.com>2016-02-19 19:44:13 +0000
committerRémy Coutable <remy@rymai.me>2016-02-19 20:56:00 +0100
commit610a4f918c64f12c60759c8a2c482f88ff38b261 (patch)
treea3374fabca0dabdabb40e72f87b893cd3a0ae7a5
parent3d9943394e571753ad743c90882c653108794d8b (diff)
downloadgitlab-ce-610a4f918c64f12c60759c8a2c482f88ff38b261.tar.gz
Merge branch 'issue_3276' into 'master'
Labels should be visible in milestone view Closes #3276 See merge request !2599
-rw-r--r--app/assets/javascripts/milestone.js.coffee10
-rw-r--r--app/assets/stylesheets/pages/milestone.scss57
-rw-r--r--app/controllers/projects/milestones_controller.rb1
-rw-r--r--app/models/label.rb4
-rw-r--r--app/models/milestone.rb14
-rw-r--r--app/views/projects/milestones/_issue.html.haml13
-rw-r--r--app/views/projects/milestones/_issues.html.haml5
-rw-r--r--app/views/projects/milestones/_merge_requests.html.haml1
-rw-r--r--app/views/projects/milestones/show.html.haml87
-rw-r--r--features/project/milestone.feature23
-rw-r--r--features/steps/project/project_milestone.rb53
11 files changed, 223 insertions, 45 deletions
diff --git a/app/assets/javascripts/milestone.js.coffee b/app/assets/javascripts/milestone.js.coffee
index d644d50b669..31f6c6d3d47 100644
--- a/app/assets/javascripts/milestone.js.coffee
+++ b/app/assets/javascripts/milestone.js.coffee
@@ -64,6 +64,7 @@ class @Milestone
constructor: ->
@bindIssuesSorting()
@bindMergeRequestSorting()
+ @bindTabsSwitching
bindIssuesSorting: ->
$("#issues-list-unassigned, #issues-list-ongoing, #issues-list-closed").sortable(
@@ -122,3 +123,12 @@ class @Milestone
Milestone.updateMergeRequest(ui.item, merge_request_url, data)
).disableSelection()
+
+ bindMergeRequestSorting: ->
+ $('a[data-toggle="tab"]').on 'show.bs.tab', (e) ->
+ currentTabClass = $(e.target).data('show')
+ previousTabClass = $(e.relatedTarget).data('show')
+
+ $(previousTabClass).hide()
+ $(currentTabClass).removeClass('hidden')
+ $(currentTabClass).show()
diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss
index e80dc9e84a1..9144a83647d 100644
--- a/app/assets/stylesheets/pages/milestone.scss
+++ b/app/assets/stylesheets/pages/milestone.scss
@@ -11,3 +11,60 @@ li.milestone {
height: 6px;
}
}
+
+.milestone-content {
+ .issues-count {
+ margin-right: 17px;
+ float: right;
+ width: 105px;
+ }
+
+ .issue-row {
+ .color-label {
+ border-radius: 2px;
+ padding: 3px !important;
+ }
+
+ // Issue title
+ span a {
+ color: rgba(0,0,0,0.64);
+ }
+ }
+}
+
+.milestone-summary {
+ margin-bottom: 25px;
+
+ .milestone-stat {
+ margin-right: 10px;
+ }
+
+ .time-elapsed {
+ color: $orange-light;
+ }
+}
+
+.issues-sortable-list {
+ .issue-detail {
+ display: block;
+
+ .issue-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;
+ }
+ }
+}
+
+.milestone-detail {
+ border-bottom: 1px solid $border-color;
+ padding: 20px 0;
+}
diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb
index a5c4ef1c7c7..21f30f278c8 100644
--- a/app/controllers/projects/milestones_controller.rb
+++ b/app/controllers/projects/milestones_controller.rb
@@ -35,6 +35,7 @@ class Projects::MilestonesController < Projects::ApplicationController
@issues = @milestone.issues
@users = @milestone.participants.uniq
@merge_requests = @milestone.merge_requests
+ @labels = @milestone.labels
end
def create
diff --git a/app/models/label.rb b/app/models/label.rb
index 220da10a6ab..f93ce7e1c89 100644
--- a/app/models/label.rb
+++ b/app/models/label.rb
@@ -85,6 +85,10 @@ class Label < ActiveRecord::Base
issues.opened.count
end
+ def closed_issues_count
+ issues.closed.count
+ end
+
def template?
template
end
diff --git a/app/models/milestone.rb b/app/models/milestone.rb
index 9c4476c768e..cbe65d70997 100644
--- a/app/models/milestone.rb
+++ b/app/models/milestone.rb
@@ -27,6 +27,7 @@ class Milestone < ActiveRecord::Base
belongs_to :project
has_many :issues
+ has_many :labels, through: :issues
has_many :merge_requests
has_many :participants, through: :issues, source: :assignee
@@ -109,6 +110,19 @@ class Milestone < ActiveRecord::Base
0
end
+ # Returns the elapsed time (in percent) since the Milestone creation date until today.
+ # If the Milestone doesn't have a due_date then returns 0 since we can't calculate the elapsed time.
+ # If the Milestone is overdue then it returns 100%.
+ def percent_time_used
+ return 0 unless due_date
+ return 100 if expired?
+
+ duration = ((created_at - due_date.to_datetime) / 1.day)
+ days_elapsed = ((created_at - Time.now) / 1.day)
+
+ ((days_elapsed.to_f / duration) * 100).floor
+ end
+
def expires_at
if due_date
if due_date.past?
diff --git a/app/views/projects/milestones/_issue.html.haml b/app/views/projects/milestones/_issue.html.haml
index 133d802aaca..ca51b8c745d 100644
--- a/app/views/projects/milestones/_issue.html.haml
+++ b/app/views/projects/milestones/_issue.html.haml
@@ -1,9 +1,10 @@
%li{ id: dom_id(issue, 'sortable'), class: 'issue-row', 'data-iid' => issue.iid, 'data-url' => issue_path(issue) }
- .pull-right.assignee-icon
- - if issue.assignee
- = image_tag avatar_icon(issue.assignee, 16), class: "avatar s16", alt: ''
%span
- = 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
-
+ .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/projects/milestones/_issues.html.haml b/app/views/projects/milestones/_issues.html.haml
index 6e4df75a3df..6f8a341e478 100644
--- a/app/views/projects/milestones/_issues.html.haml
+++ b/app/views/projects/milestones/_issues.html.haml
@@ -1,6 +1,7 @@
.panel.panel-default
- .panel-heading= title
+ .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 'issue', issue: issue
- %li.light.ui-sort-disabled Drag and drop available
diff --git a/app/views/projects/milestones/_merge_requests.html.haml b/app/views/projects/milestones/_merge_requests.html.haml
index 00889a5eb24..9a5a02af215 100644
--- a/app/views/projects/milestones/_merge_requests.html.haml
+++ b/app/views/projects/milestones/_merge_requests.html.haml
@@ -3,4 +3,3 @@
%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
- %li.light.ui-sort-disabled Drag and drop available
diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml
index 528a4f9552f..631bc8c3e9d 100644
--- a/app/views/projects/milestones/show.html.haml
+++ b/app/views/projects/milestones/show.html.haml
@@ -24,7 +24,7 @@
- else
= link_to 'Reopen Milestone', namespace_project_milestone_path(@project.namespace, @project, @milestone, milestone: {state_event: :activate }), method: :put, class: "btn btn-reopen btn-nr btn-grouped"
- = link_to namespace_project_milestone_path(@project.namespace, @project, @milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-grouped btn-nr btn-remove" do
+ = link_to namespace_project_milestone_path(@project.namespace, @project, @milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-grouped btn-nr" do
= icon('trash-o')
Delete
@@ -32,7 +32,7 @@
= icon('pencil-square-o')
Edit
-.detail-page-description.content-block
+.detail-page-description.milestone-detail.second-block
%h2.title
= markdown escape_once(@milestone.title), pipeline: :single_line
%div
@@ -47,44 +47,55 @@
%span All issues for this milestone are closed. You may close milestone now.
.context.prepend-top-default
- %p.lead
- Progress:
- #{@milestone.closed_items_count} closed
- &ndash;
- #{@milestone.open_items_count} open
- &nbsp;
- %span.light #{@milestone.percent_complete}% complete
- %span.pull-right= @milestone.expires_at
+ .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.time-elapsed
+ %strong== #{@milestone.percent_time_used}%
+ time elapsed
+ %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' do
+ = 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' do
+ = 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
+.tab-content.milestone-content
.tab-pane.active#tab-issues
- .content-block.oneline-block
- .controls
- - 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"
-
- .oneline
- All issues in this milestone
-
.row.prepend-top-default
.col-md-4
= render('issues', title: 'Unstarted Issues (open and unassigned)', issues: @issues.opened.unassigned, id: 'unassigned')
@@ -94,14 +105,6 @@
= render('issues', title: 'Completed Issues (closed)', issues: @issues.closed, id: 'closed')
.tab-pane#tab-merge-requests
- .content-block.oneline-block
- .controls
- - 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"
-
- .oneline
- All merge requests in this milestone
-
.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')
@@ -117,9 +120,6 @@
= render 'merge_request', merge_request: merge_request
.tab-pane#tab-participants
- .content-block.oneline-block
- All participants to this milestone
-
%ul.bordered-list
- @users.each do |user|
%li
@@ -128,3 +128,18 @@
%strong= truncate(user.name, lenght: 40)
%br
%small.cgray= user.username
+
+ .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'
diff --git a/features/project/milestone.feature b/features/project/milestone.feature
new file mode 100644
index 00000000000..e0f4c0e9d7c
--- /dev/null
+++ b/features/project/milestone.feature
@@ -0,0 +1,23 @@
+Feature: Project Milestone
+ Background:
+ Given I sign in as a user
+ And I own project "Shop"
+ And project "Shop" has labels: "bug", "feature", "enhancement"
+ And project "Shop" has milestone "v2.2"
+ And milestone has issue "Bugfix1" with labels: "bug", "feature"
+ And milestone has issue "Bugfix2" with labels: "bug", "enhancement"
+
+
+ @javascript
+ Scenario: Listing issues from issues tab
+ Given I visit project "Shop" milestones page
+ And I click link "v2.2"
+ Then I should see the labels "bug", "enhancement" and "feature"
+
+ @javascript
+ Scenario: Listing labels from labels tab
+ Given I visit project "Shop" milestones page
+ And I click link "v2.2"
+ And I click link "Labels"
+ Then I should see the list of labels
+ And I should see the labels "bug", "enhancement" and "feature"
diff --git a/features/steps/project/project_milestone.rb b/features/steps/project/project_milestone.rb
new file mode 100644
index 00000000000..ec881c0d8fc
--- /dev/null
+++ b/features/steps/project/project_milestone.rb
@@ -0,0 +1,53 @@
+class Spinach::Features::ProjectMilestone < Spinach::FeatureSteps
+ include SharedAuthentication
+ include SharedProject
+ include SharedPaths
+
+ step 'milestone has issue "Bugfix1" with labels: "bug", "feature"' do
+ project = Project.find_by(name: "Shop")
+ milestone = project.milestones.find_by(title: 'v2.2')
+ issue = create(:issue, title: "Bugfix1", project: project, milestone: milestone)
+ issue.labels << project.labels.find_by(title: 'bug')
+ issue.labels << project.labels.find_by(title: 'feature')
+ end
+
+ step 'milestone has issue "Bugfix2" with labels: "bug", "enhancement"' do
+ project = Project.find_by(name: "Shop")
+ milestone = project.milestones.find_by(title: 'v2.2')
+ issue = create(:issue, title: "Bugfix2", project: project, milestone: milestone)
+ issue.labels << project.labels.find_by(title: 'bug')
+ issue.labels << project.labels.find_by(title: 'enhancement')
+ end
+
+ step 'project "Shop" has milestone "v2.2"' do
+ project = Project.find_by(name: "Shop")
+ milestone = create(:milestone,
+ title: "v2.2",
+ project: project,
+ description: "# Description header"
+ )
+ 3.times { create(:issue, project: project, milestone: milestone) }
+ end
+
+ step 'I should see the list of labels' do
+ expect(page).to have_selector('ul.manage-labels-list')
+ end
+
+ step 'I should see the labels "bug", "enhancement" and "feature"' do
+ page.within('#tab-issues') do
+ expect(page).to have_content 'bug'
+ expect(page).to have_content 'enhancement'
+ expect(page).to have_content 'feature'
+ end
+ end
+
+ step 'I click link "v2.2"' do
+ click_link "v2.2"
+ end
+
+ step 'I click link "Labels"' do
+ page.within('.nav-links') do
+ page.find(:xpath, "//a[@href='#tab-labels']").click
+ end
+ end
+end