summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/controllers/groups/milestones_controller.rb6
-rw-r--r--app/models/group_milestone.rb69
-rw-r--r--app/services/milestones/group_service.rb10
-rw-r--r--app/views/groups/milestones/_issue.html.haml9
-rw-r--r--app/views/groups/milestones/_issues.html.haml6
-rw-r--r--app/views/groups/milestones/_merge_request.html.haml6
-rw-r--r--app/views/groups/milestones/_merge_requests.html.haml5
-rw-r--r--app/views/groups/milestones/index.html.haml15
-rw-r--r--app/views/groups/milestones/show.html.haml77
9 files changed, 190 insertions, 13 deletions
diff --git a/app/controllers/groups/milestones_controller.rb b/app/controllers/groups/milestones_controller.rb
index a86cc58c021..235b27f53a9 100644
--- a/app/controllers/groups/milestones_controller.rb
+++ b/app/controllers/groups/milestones_controller.rb
@@ -13,14 +13,16 @@ class Groups::MilestonesController < ApplicationController
def show
project_milestones = Milestone.where(project_id: group.projects)
- @group_milestones = Milestones::GroupService.new(project_milestones).milestone(title)
+ @group_milestone = Milestones::GroupService.new(project_milestones).milestone(title)
+ @project_issues = @group_milestone.filter_by(params[:status], "issues")
+ @project_merge_requests = @group_milestone.filter_by(params[:status], "merge_requests")
end
def update
project_milestones = Milestone.where(project_id: group.projects)
@group_milestones = Milestones::GroupService.new(project_milestones).milestone(title)
- @group_milestones.each do |milestone|
+ @group_milestones.milestones.each do |milestone|
Milestones::UpdateService.new(milestone.project, current_user, params[:milestone]).execute(milestone)
end
diff --git a/app/models/group_milestone.rb b/app/models/group_milestone.rb
index 567e0c2b9fb..0faff39ae7a 100644
--- a/app/models/group_milestone.rb
+++ b/app/models/group_milestone.rb
@@ -9,6 +9,10 @@ class GroupMilestone
@title
end
+ def safe_title
+ @title.gsub(".", "-")
+ end
+
def milestones
@milestones
end
@@ -25,6 +29,10 @@ class GroupMilestone
milestones.map{ |milestone| milestone.merge_requests.count }.sum
end
+ def open_items_count
+ milestones.map{ |milestone| milestone.open_items_count }.sum
+ end
+
def closed_items_count
milestones.map{ |milestone| milestone.closed_items_count }.sum
end
@@ -42,10 +50,69 @@ class GroupMilestone
def state
state = milestones.map{ |milestone| milestone.state }
- if state.count("active") == state.size
+ if state.count('active') == state.size
'active'
else
'closed'
end
end
+
+ def active?
+ state == 'active'
+ end
+
+ def closed?
+ state == 'closed'
+ end
+
+ def opened_unassigned_issues
+ milestones.map{ |milestone| milestone.issues.opened.unassigned }
+ end
+
+ def opened_assigned_issues
+ milestones.map{ |milestone| milestone.issues.opened.assigned }
+ end
+
+ def closed_issues
+ milestones.map{ |milestone| milestone.issues.closed }
+ end
+
+ def participants
+ milestones.map{ |milestone| milestone.participants.uniq }.reject(&:empty?).flatten
+ end
+
+ def filter_by(filter, entity)
+ if entity
+ milestones = self.milestones.sort_by(&:project_id)
+ entities = {}
+ milestones.each do |project_milestone|
+ next unless project_milestone.send(entity).any?
+ project_name = project_milestone.project.name
+ entities_by_state = state_filter(filter, project_milestone.send(entity))
+ entities.store(project_name, entities_by_state)
+ end
+ entities
+ else
+ {}
+ end
+ end
+
+ def state_filter(filter, entities)
+ if entities.present?
+ sorted_entities = entities.sort_by(&:position)
+ entities_by_state = case filter
+ when 'active'; sorted_entities.group_by(&:state)['opened']
+ when 'closed'; sorted_entities.group_by(&:state)['closed']
+ else sorted_entities
+ end
+ if entities_by_state.blank?
+ []
+ else
+ entities_by_state
+ end
+ else
+ []
+ end
+ end
+
end
diff --git a/app/services/milestones/group_service.rb b/app/services/milestones/group_service.rb
index 39ae913a72a..2d1aa878c24 100644
--- a/app/services/milestones/group_service.rb
+++ b/app/services/milestones/group_service.rb
@@ -5,16 +5,22 @@ module Milestones
end
def execute
- @project_milestones.map{ |title, milestone| GroupMilestone.new(title, milestone) }
+ build(@project_milestones)
end
def milestone(title)
if title
- @project_milestones[title]
+ group_milestone = @project_milestones[title].group_by(&:title)
+ build(group_milestone).first
else
nil
end
end
+ private
+
+ def build(milestone)
+ milestone.map{ |title, milestones| GroupMilestone.new(title, milestones) }
+ end
end
end
diff --git a/app/views/groups/milestones/_issue.html.haml b/app/views/groups/milestones/_issue.html.haml
new file mode 100644
index 00000000000..7009400a46c
--- /dev/null
+++ b/app/views/groups/milestones/_issue.html.haml
@@ -0,0 +1,9 @@
+%li{ id: dom_id(issue, 'sortable'), class: 'issue-row', 'data-iid' => issue.iid }
+ %span.str-truncated
+ - project = issue.project
+ = link_to [project, issue] do
+ %span.cgray ##{issue.iid}
+ = link_to_gfm issue.title, [project, issue]
+ .pull-right.assignee-icon
+ - if issue.assignee
+ = image_tag avatar_icon(issue.assignee.email, 16), class: "avatar s16"
diff --git a/app/views/groups/milestones/_issues.html.haml b/app/views/groups/milestones/_issues.html.haml
new file mode 100644
index 00000000000..3fefb2f0107
--- /dev/null
+++ b/app/views/groups/milestones/_issues.html.haml
@@ -0,0 +1,6 @@
+.panel.panel-default
+ .panel-heading= name
+ %ul{ class: "well-list issues-sortable-list" }
+ - 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
new file mode 100644
index 00000000000..4f11aaa74b7
--- /dev/null
+++ b/app/views/groups/milestones/_merge_request.html.haml
@@ -0,0 +1,6 @@
+%li{ id: dom_id(merge_request, 'sortable'), class: 'mr-row', 'data-iid' => merge_request.iid }
+ %span.str-truncated
+ - project = merge_request.project
+ = link_to [project, merge_request] do
+ %span.cgray ##{merge_request.iid}
+ = link_to_gfm truncate(merge_request.title, length: 60), [project, merge_request]
diff --git a/app/views/groups/milestones/_merge_requests.html.haml b/app/views/groups/milestones/_merge_requests.html.haml
new file mode 100644
index 00000000000..9bb213ffe32
--- /dev/null
+++ b/app/views/groups/milestones/_merge_requests.html.haml
@@ -0,0 +1,5 @@
+.panel.panel-default
+ .panel-heading= name
+ %ul{ class: "well-list merge_requests-sortable-list" }
+ - merge_requests.sort_by(&:position).each do |merge_request|
+ = render 'merge_request', merge_request: merge_request
diff --git a/app/views/groups/milestones/index.html.haml b/app/views/groups/milestones/index.html.haml
index b93ff09d25c..e3dd1ae5ae0 100644
--- a/app/views/groups/milestones/index.html.haml
+++ b/app/views/groups/milestones/index.html.haml
@@ -22,21 +22,20 @@
.nothing-here-block No milestones to show
- else
- @group_milestones.each do |milestone|
- %li{class: "milestone milestone-#{milestone.state == 'closed' ? 'closed' : 'open'}", id: dom_id(milestone.milestones.first) }
+ %li{class: "milestone milestone-#{milestone.closed? ? 'closed' : 'open'}", id: dom_id(milestone.milestones.first) }
.pull-right
- - safe_title = milestone.title.gsub(".", "-")
- - if milestone.state == 'closed'
- = link_to 'Reopen Milestone', group_milestone_path(@group, safe_title, milestone: {state_event: :activate }), method: :put, class: "btn btn-small btn-grouped"
+ - if milestone.closed?
+ = link_to 'Reopen Milestone', group_milestone_path(@group, milestone.safe_title, milestone: {state_event: :activate }), method: :put, class: "btn btn-small btn-grouped"
- else
- = link_to 'Close Milestone', group_milestone_path(@group, safe_title, milestone: {state_event: :close }), method: :put, class: "btn btn-small btn-remove"
+ = link_to 'Close Milestone', group_milestone_path(@group, milestone.safe_title, milestone: {state_event: :close }), method: :put, class: "btn btn-small btn-remove"
%h4
- = link_to_gfm truncate(milestone.title, length: 100), group_milestone_path(@group, safe_title)
+ = link_to_gfm truncate(milestone.title, length: 100), group_milestone_path(@group, milestone.safe_title)
%div
%div
- = link_to root_path do
+ = link_to group_milestone_path(@group, milestone.safe_title) do
= pluralize milestone.issue_count, 'Issue'
&nbsp;
- = link_to root_path do
+ = link_to group_milestone_path(@group, milestone.safe_title) do
= pluralize milestone.merge_requests_count, 'Merge Request'
&nbsp;
%span.light #{milestone.percent_complete}% complete
diff --git a/app/views/groups/milestones/show.html.haml b/app/views/groups/milestones/show.html.haml
new file mode 100644
index 00000000000..a450e7d09a5
--- /dev/null
+++ b/app/views/groups/milestones/show.html.haml
@@ -0,0 +1,77 @@
+%h3.page-title
+ Milestone #{@group_milestone.title}
+ .pull-right
+ - if @group_milestone.active?
+ = link_to 'Close Milestone', group_milestone_path(@group, @group_milestone.safe_title, milestone: {state_event: :close }), method: :put, class: "btn btn-small btn-remove"
+ - else
+ = link_to 'Reopen Milestone', group_milestone_path(@group, @group_milestone.safe_title, milestone: {state_event: :activate }), method: :put, class: "btn btn-small btn-grouped"
+
+- if (@group_milestone.total_items_count == @group_milestone.closed_items_count) && @group_milestone.active?
+ .alert.alert-success
+ %span All issues for this milestone are closed. You may close the milestone now.
+
+.back-link
+ = link_to group_milestones_path(@group) do
+ &larr; To milestones list
+
+.issue-box{ class: "issue-box-#{@group_milestone.closed? ? 'closed' : 'open'}" }
+ .state.clearfix
+ .state-label
+ - if @group_milestone.closed?
+ Closed
+ - else
+ Open
+
+ %h4.title
+ = gfm escape_once(@group_milestone.title)
+
+ .context
+ %p
+ Progress:
+ #{@group_milestone.closed_items_count} closed
+ &ndash;
+ #{@group_milestone.open_items_count} open
+
+ .progress.progress-info
+ .progress-bar{style: "width: #{@group_milestone.percent_complete}%;"}
+
+%ul.nav.nav-tabs
+ %li.active
+ = link_to '#tab-issues', 'data-toggle' => 'tab' do
+ Issues
+ %span.badge= @group_milestone.issue_count
+ %li
+ = link_to '#tab-merge-requests', 'data-toggle' => 'tab' do
+ Merge Requests
+ %span.badge= @group_milestone.merge_requests_count
+ %li
+ = link_to '#tab-participants', 'data-toggle' => 'tab' do
+ Participants
+ %span.badge= @group_milestone.participants.count
+
+.tab-content
+ .tab-pane.active#tab-issues
+ .row
+ .col-md-4.responsive-side
+ = render 'groups/filter', entity: 'milestone'
+ .col-md-8
+ - @project_issues.each do |name, issues|
+ = render 'issues', name: name, issues: issues
+
+ .tab-pane#tab-merge-requests
+ .row
+ .col-md-4.responsive-side
+ = render 'groups/filter', entity: 'milestone'
+ .col-md-8
+ - @project_merge_requests.each do |name, merge_requests|
+ = render 'merge_requests', name: name, merge_requests: merge_requests
+
+ .tab-pane#tab-participants
+ %ul.bordered-list
+ - @group_milestone.participants.each do |user|
+ %li
+ = link_to user, title: user.name, class: "darken" do
+ = image_tag avatar_icon(user.email, 32), class: "avatar s32"
+ %strong= truncate(user.name, lenght: 40)
+ %br
+ %small.cgray= user.username