提交 95b06a62 编写于 作者: R Rubén Dávila

Updates from last code review.

上级 c91554de
......@@ -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)
......
......@@ -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()
......
......@@ -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,
......
......@@ -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;
}
......
......@@ -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
......
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
......@@ -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 })
......
......@@ -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',
......
......@@ -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) }
......
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
......@@ -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
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
......
......@@ -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) }
......
......@@ -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?
......
......@@ -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
......
%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'
&middot;
= 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
- 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
%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'
&middot;
= 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
- 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
%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'
&middot;
= 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
......@@ -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
-# @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} &middot;
- elsif show_full_project_name
%strong #{project.name_with_namespace} &middot;
= 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: '')
......@@ -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 }
......@@ -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)
- 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 &middot; #{milestone.project.name}
- elsif show_full_project_name
%strong &middot; #{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'
......@@ -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)
- 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
&middot;
= 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
-# @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} &middot;
- elsif show_full_project_name
%strong #{project.name_with_namespace} &middot;
= 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: '')
- 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)
%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
- 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
&middot;
= 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
......@@ -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
......
......@@ -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
......@@ -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
......
......@@ -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
......
......@@ -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
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册