Commit 610a4f91 authored by Jacob Schatz's avatar Jacob Schatz Committed by Rémy Coutable

Merge branch 'issue_3276' into 'master'

Labels should be visible in milestone view

Closes #3276 

See merge request !2599
parent 3d994339
...@@ -64,6 +64,7 @@ class @Milestone ...@@ -64,6 +64,7 @@ class @Milestone
constructor: -> constructor: ->
@bindIssuesSorting() @bindIssuesSorting()
@bindMergeRequestSorting() @bindMergeRequestSorting()
@bindTabsSwitching
bindIssuesSorting: -> bindIssuesSorting: ->
$("#issues-list-unassigned, #issues-list-ongoing, #issues-list-closed").sortable( $("#issues-list-unassigned, #issues-list-ongoing, #issues-list-closed").sortable(
...@@ -122,3 +123,12 @@ class @Milestone ...@@ -122,3 +123,12 @@ class @Milestone
Milestone.updateMergeRequest(ui.item, merge_request_url, data) Milestone.updateMergeRequest(ui.item, merge_request_url, data)
).disableSelection() ).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()
...@@ -11,3 +11,60 @@ li.milestone { ...@@ -11,3 +11,60 @@ li.milestone {
height: 6px; 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;
}
...@@ -35,6 +35,7 @@ class Projects::MilestonesController < Projects::ApplicationController ...@@ -35,6 +35,7 @@ class Projects::MilestonesController < Projects::ApplicationController
@issues = @milestone.issues @issues = @milestone.issues
@users = @milestone.participants.uniq @users = @milestone.participants.uniq
@merge_requests = @milestone.merge_requests @merge_requests = @milestone.merge_requests
@labels = @milestone.labels
end end
def create def create
......
...@@ -85,6 +85,10 @@ class Label < ActiveRecord::Base ...@@ -85,6 +85,10 @@ class Label < ActiveRecord::Base
issues.opened.count issues.opened.count
end end
def closed_issues_count
issues.closed.count
end
def template? def template?
template template
end end
......
...@@ -27,6 +27,7 @@ class Milestone < ActiveRecord::Base ...@@ -27,6 +27,7 @@ class Milestone < ActiveRecord::Base
belongs_to :project belongs_to :project
has_many :issues has_many :issues
has_many :labels, through: :issues
has_many :merge_requests has_many :merge_requests
has_many :participants, through: :issues, source: :assignee has_many :participants, through: :issues, source: :assignee
...@@ -109,6 +110,19 @@ class Milestone < ActiveRecord::Base ...@@ -109,6 +110,19 @@ class Milestone < ActiveRecord::Base
0 0
end 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 def expires_at
if due_date if due_date
if due_date.past? if due_date.past?
......
%li{ id: dom_id(issue, 'sortable'), class: 'issue-row', 'data-iid' => issue.iid, 'data-url' => issue_path(issue) } %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 %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 = 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: ''
.panel.panel-default .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 } %ul{ class: "well-list issues-sortable-list", id: "issues-list-#{id}", "data-state" => id }
- issues.sort_by(&:position).each do |issue| - issues.sort_by(&:position).each do |issue|
= render 'issue', issue: issue = render 'issue', issue: issue
%li.light.ui-sort-disabled Drag and drop available
...@@ -3,4 +3,3 @@ ...@@ -3,4 +3,3 @@
%ul{ class: "well-list merge_requests-sortable-list", id: "merge_requests-list-#{id}", "data-state" => id } %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| - merge_requests.sort_by(&:position).each do |merge_request|
= render 'merge_request', merge_request: merge_request = render 'merge_request', merge_request: merge_request
%li.light.ui-sort-disabled Drag and drop available
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
- else - 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 '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') = icon('trash-o')
Delete Delete
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
= icon('pencil-square-o') = icon('pencil-square-o')
Edit Edit
.detail-page-description.content-block .detail-page-description.milestone-detail.second-block
%h2.title %h2.title
= markdown escape_once(@milestone.title), pipeline: :single_line = markdown escape_once(@milestone.title), pipeline: :single_line
%div %div
...@@ -47,44 +47,55 @@ ...@@ -47,44 +47,55 @@
%span All issues for this milestone are closed. You may close milestone now. %span All issues for this milestone are closed. You may close milestone now.
.context.prepend-top-default .context.prepend-top-default
%p.lead .milestone-summary
Progress: %h4 Progress
#{@milestone.closed_items_count} closed %strong= @milestone.issues.count
&ndash; issues:
#{@milestone.open_items_count} open %span.milestone-stat
&nbsp; %strong= @milestone.open_items_count
%span.light #{@milestone.percent_complete}% complete open and
%span.pull-right= @milestone.expires_at %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) = milestone_progress_bar(@milestone)
%ul.nav-links.no-top.no-bottom %ul.nav-links.no-top.no-bottom
%li.active %li.active
= link_to '#tab-issues', 'data-toggle' => 'tab' do = link_to '#tab-issues', 'data-toggle' => 'tab', 'data-show' => '.tab-issues-buttons' do
Issues Issues
%span.badge= @issues.count %span.badge= @issues.count
%li %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 Merge Requests
%span.badge= @merge_requests.count %span.badge= @merge_requests.count
%li %li
= link_to '#tab-participants', 'data-toggle' => 'tab' do = link_to '#tab-participants', 'data-toggle' => 'tab' do
Participants Participants
%span.badge= @users.count %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 .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 .row.prepend-top-default
.col-md-4 .col-md-4
= render('issues', title: 'Unstarted Issues (open and unassigned)', issues: @issues.opened.unassigned, id: 'unassigned') = render('issues', title: 'Unstarted Issues (open and unassigned)', issues: @issues.opened.unassigned, id: 'unassigned')
...@@ -94,14 +105,6 @@ ...@@ -94,14 +105,6 @@
= render('issues', title: 'Completed Issues (closed)', issues: @issues.closed, id: 'closed') = render('issues', title: 'Completed Issues (closed)', issues: @issues.closed, id: 'closed')
.tab-pane#tab-merge-requests .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 .row.prepend-top-default
.col-md-3 .col-md-3
= render('merge_requests', title: 'Work in progress (open and unassigned)', merge_requests: @merge_requests.opened.unassigned, id: 'unassigned') = render('merge_requests', title: 'Work in progress (open and unassigned)', merge_requests: @merge_requests.opened.unassigned, id: 'unassigned')
...@@ -117,9 +120,6 @@ ...@@ -117,9 +120,6 @@
= render 'merge_request', merge_request: merge_request = render 'merge_request', merge_request: merge_request
.tab-pane#tab-participants .tab-pane#tab-participants
.content-block.oneline-block
All participants to this milestone
%ul.bordered-list %ul.bordered-list
- @users.each do |user| - @users.each do |user|
%li %li
...@@ -128,3 +128,18 @@ ...@@ -128,3 +128,18 @@
%strong= truncate(user.name, lenght: 40) %strong= truncate(user.name, lenght: 40)
%br %br
%small.cgray= user.username %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'
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"
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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment