Commit f4fed44f authored by Sean McGivern's avatar Sean McGivern

Merge branch '22392-add-x-of-y-tasks-completed-on-issuable' into 'master'

add "x of y tasks completed" on issuable

Closes #22392

See merge request !6527
parents f60e6d55 32913b74
...@@ -22,6 +22,7 @@ Please view this file on the master branch, on stable branches it's out of date. ...@@ -22,6 +22,7 @@ Please view this file on the master branch, on stable branches it's out of date.
- Fix filtering of milestones with quotes in title (airatshigapov) - Fix filtering of milestones with quotes in title (airatshigapov)
- Refactor less readable existance checking code from CoffeeScript !6289 (jlogandavison) - Refactor less readable existance checking code from CoffeeScript !6289 (jlogandavison)
- Update mail_room and enable sentinel support to Reply By Email (!7101) - Update mail_room and enable sentinel support to Reply By Email (!7101)
- Add task completion status in Issues and Merge Requests tabs: "X of Y tasks completed" (!6527, @gmesalazar)
- Simpler arguments passed to named_route on toggle_award_url helper method - Simpler arguments passed to named_route on toggle_award_url helper method
- Fix typo in framework css class. !7086 (Daniel Voogsgerd) - Fix typo in framework css class. !7086 (Daniel Voogsgerd)
- New issue board list dropdown stays open after adding a new list - New issue board list dropdown stays open after adding a new list
......
...@@ -95,7 +95,11 @@ ...@@ -95,7 +95,11 @@
return $.ajax({ return $.ajax({
type: 'PATCH', type: 'PATCH',
url: $('form.js-issuable-update').attr('action'), url: $('form.js-issuable-update').attr('action'),
data: patchData data: patchData,
success: function(issue) {
document.querySelector('#task_status').innerText = issue.task_status;
document.querySelector('#task_status_short').innerText = issue.task_status_short;
}
}); });
// TODO (rspeicher): Make the issue description inline-editable like a note so // TODO (rspeicher): Make the issue description inline-editable like a note so
// that we can re-use its form here // that we can re-use its form here
......
...@@ -97,7 +97,11 @@ ...@@ -97,7 +97,11 @@
return $.ajax({ return $.ajax({
type: 'PATCH', type: 'PATCH',
url: $('form.js-issuable-update').attr('action'), url: $('form.js-issuable-update').attr('action'),
data: patchData data: patchData,
success: function(mergeRequest) {
document.querySelector('#task_status').innerText = mergeRequest.task_status;
document.querySelector('#task_status_short').innerText = mergeRequest.task_status_short;
}
}); });
// TODO (rspeicher): Make the merge request description inline-editable like a // TODO (rspeicher): Make the merge request description inline-editable like a
// note so that we can re-use its form here // note so that we can re-use its form here
......
...@@ -112,7 +112,7 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -112,7 +112,7 @@ class Projects::IssuesController < Projects::ApplicationController
end end
format.json do format.json do
render json: @issue.to_json(include: { milestone: {}, assignee: { methods: :avatar_url }, labels: { methods: :text_color } }) render json: @issue.to_json(include: { milestone: {}, assignee: { methods: :avatar_url }, labels: { methods: :text_color } }, methods: [:task_status, :task_status_short])
end end
end end
......
...@@ -278,7 +278,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -278,7 +278,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request.target_project, @merge_request]) @merge_request.target_project, @merge_request])
end end
format.json do format.json do
render json: @merge_request.to_json(include: { milestone: {}, assignee: { methods: :avatar_url }, labels: { methods: :text_color } }) render json: @merge_request.to_json(include: { milestone: {}, assignee: { methods: :avatar_url }, labels: { methods: :text_color } }, methods: [:task_status, :task_status_short])
end end
end end
else else
......
...@@ -71,6 +71,14 @@ module IssuablesHelper ...@@ -71,6 +71,14 @@ module IssuablesHelper
author_output = link_to_member(project, issuable.author, size: 24, mobile_classes: "hidden-xs", tooltip: true) author_output = link_to_member(project, issuable.author, size: 24, mobile_classes: "hidden-xs", tooltip: true)
author_output << link_to_member(project, issuable.author, size: 24, by_username: true, avatar: false, mobile_classes: "hidden-sm hidden-md hidden-lg") author_output << link_to_member(project, issuable.author, size: 24, by_username: true, avatar: false, mobile_classes: "hidden-sm hidden-md hidden-lg")
end end
if issuable.tasks?
output << "&ensp;".html_safe
output << content_tag(:span, issuable.task_status, id: "task_status", class: "hidden-xs")
output << content_tag(:span, issuable.task_status_short, id: "task_status_short", class: "hidden-sm hidden-md hidden-lg")
end
output
end end
def issuable_todo(issuable) def issuable_todo(issuable)
......
...@@ -12,6 +12,7 @@ module Issuable ...@@ -12,6 +12,7 @@ module Issuable
include Subscribable include Subscribable
include StripAttribute include StripAttribute
include Awardable include Awardable
include Taskable
included do included do
cache_markdown_field :title, pipeline: :single_line cache_markdown_field :title, pipeline: :single_line
......
...@@ -53,10 +53,22 @@ module Taskable ...@@ -53,10 +53,22 @@ module Taskable
# Return a string that describes the current state of this Taskable's task # Return a string that describes the current state of this Taskable's task
# list items, e.g. "12 of 20 tasks completed" # list items, e.g. "12 of 20 tasks completed"
def task_status def task_status(short: false)
return '' if description.blank? return '' if description.blank?
prep, completed = if short
['/', '']
else
[' of ', ' completed']
end
sum = tasks.summary sum = tasks.summary
"#{sum.complete_count} of #{sum.item_count} #{'task'.pluralize(sum.item_count)} completed" "#{sum.complete_count}#{prep}#{sum.item_count} #{'task'.pluralize(sum.item_count)}#{completed}"
end
# Return a short string that describes the current state of this Taskable's
# task list items -- for small screens
def task_status_short
task_status(short: true)
end end
end end
...@@ -5,7 +5,6 @@ class Issue < ActiveRecord::Base ...@@ -5,7 +5,6 @@ class Issue < ActiveRecord::Base
include Issuable include Issuable
include Referable include Referable
include Sortable include Sortable
include Taskable
include Spammable include Spammable
include FasterCacheKeys include FasterCacheKeys
......
...@@ -3,7 +3,6 @@ class MergeRequest < ActiveRecord::Base ...@@ -3,7 +3,6 @@ class MergeRequest < ActiveRecord::Base
include Issuable include Issuable
include Referable include Referable
include Sortable include Sortable
include Taskable
include Importable include Importable
belongs_to :target_project, class_name: "Project" belongs_to :target_project, class_name: "Project"
......
...@@ -17,6 +17,8 @@ shared_examples 'a Taskable' do ...@@ -17,6 +17,8 @@ shared_examples 'a Taskable' do
it 'returns the correct task status' do it 'returns the correct task status' do
expect(subject.task_status).to match('2 of') expect(subject.task_status).to match('2 of')
expect(subject.task_status).to match('5 tasks completed') expect(subject.task_status).to match('5 tasks completed')
expect(subject.task_status_short).to match('2/')
expect(subject.task_status_short).to match('5 tasks')
end end
describe '#tasks?' do describe '#tasks?' do
...@@ -41,6 +43,8 @@ shared_examples 'a Taskable' do ...@@ -41,6 +43,8 @@ shared_examples 'a Taskable' do
it 'returns the correct task status' do it 'returns the correct task status' do
expect(subject.task_status).to match('0 of') expect(subject.task_status).to match('0 of')
expect(subject.task_status).to match('1 task completed') expect(subject.task_status).to match('1 task completed')
expect(subject.task_status_short).to match('0/')
expect(subject.task_status_short).to match('1 task')
end end
end end
...@@ -54,6 +58,8 @@ shared_examples 'a Taskable' do ...@@ -54,6 +58,8 @@ shared_examples 'a Taskable' do
it 'returns the correct task status' do it 'returns the correct task status' do
expect(subject.task_status).to match('1 of') expect(subject.task_status).to match('1 of')
expect(subject.task_status).to match('1 task completed') expect(subject.task_status).to match('1 task completed')
expect(subject.task_status_short).to match('1/')
expect(subject.task_status_short).to match('1 task')
end end
end 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