Create vote and watch buttons in story, task and issue detail pages
parent
2a4b3ab81a
commit
d1349c4272
|
@ -26,6 +26,7 @@ toString = @.taiga.toString
|
||||||
joinStr = @.taiga.joinStr
|
joinStr = @.taiga.joinStr
|
||||||
groupBy = @.taiga.groupBy
|
groupBy = @.taiga.groupBy
|
||||||
bindOnce = @.taiga.bindOnce
|
bindOnce = @.taiga.bindOnce
|
||||||
|
bindMethods = @.taiga.bindMethods
|
||||||
|
|
||||||
module = angular.module("taigaIssues")
|
module = angular.module("taigaIssues")
|
||||||
|
|
||||||
|
@ -52,6 +53,8 @@ class IssueDetailController extends mixOf(taiga.Controller, taiga.PageMixin)
|
||||||
|
|
||||||
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location,
|
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location,
|
||||||
@log, @appMetaService, @analytics, @navUrls, @translate) ->
|
@log, @appMetaService, @analytics, @navUrls, @translate) ->
|
||||||
|
bindMethods(@)
|
||||||
|
|
||||||
@scope.issueRef = @params.issueref
|
@scope.issueRef = @params.issueref
|
||||||
@scope.sectionName = @translate.instant("ISSUES.SECTION_NAME")
|
@scope.sectionName = @translate.instant("ISSUES.SECTION_NAME")
|
||||||
@.initializeEventHandlers()
|
@.initializeEventHandlers()
|
||||||
|
@ -144,6 +147,49 @@ class IssueDetailController extends mixOf(taiga.Controller, taiga.PageMixin)
|
||||||
@.fillUsersAndRoles(project.members, project.roles)
|
@.fillUsersAndRoles(project.members, project.roles)
|
||||||
@.loadIssue()
|
@.loadIssue()
|
||||||
|
|
||||||
|
###
|
||||||
|
# Note: This methods (onUpvote() and onDownvote()) are related to tg-vote-button.
|
||||||
|
# See app/modules/components/vote-button for more info
|
||||||
|
###
|
||||||
|
onUpvote: ->
|
||||||
|
onSuccess = =>
|
||||||
|
@.loadIssue()
|
||||||
|
@rootscope.$broadcast("object:updated")
|
||||||
|
onError = =>
|
||||||
|
@confirm.notify("error")
|
||||||
|
|
||||||
|
return @rs.issues.upvote(@scope.issueId).then(onSuccess, onError)
|
||||||
|
|
||||||
|
onDownvote: ->
|
||||||
|
onSuccess = =>
|
||||||
|
@.loadIssue()
|
||||||
|
@rootscope.$broadcast("object:updated")
|
||||||
|
onError = =>
|
||||||
|
@confirm.notify("error")
|
||||||
|
|
||||||
|
return @rs.issues.downvote(@scope.issueId).then(onSuccess, onError)
|
||||||
|
|
||||||
|
###
|
||||||
|
# Note: This methods (onWatch() and onUnwatch()) are related to tg-watch-button.
|
||||||
|
# See app/modules/components/watch-button for more info
|
||||||
|
###
|
||||||
|
onWatch: ->
|
||||||
|
onSuccess = =>
|
||||||
|
@.loadIssue()
|
||||||
|
@rootscope.$broadcast("object:updated")
|
||||||
|
onError = =>
|
||||||
|
@confirm.notify("error")
|
||||||
|
|
||||||
|
return @rs.issues.watch(@scope.issueId).then(onSuccess, onError)
|
||||||
|
|
||||||
|
onUnwatch: ->
|
||||||
|
onSuccess = =>
|
||||||
|
@.loadIssue()
|
||||||
|
@rootscope.$broadcast("object:updated")
|
||||||
|
onError = =>
|
||||||
|
@confirm.notify("error")
|
||||||
|
|
||||||
|
return @rs.issues.unwatch(@scope.issueId).then(onSuccess, onError)
|
||||||
|
|
||||||
module.controller("IssueDetailController", IssueDetailController)
|
module.controller("IssueDetailController", IssueDetailController)
|
||||||
|
|
||||||
|
|
|
@ -37,8 +37,11 @@ urls = {
|
||||||
"users-change-password": "/users/change_password"
|
"users-change-password": "/users/change_password"
|
||||||
"users-change-email": "/users/change_email"
|
"users-change-email": "/users/change_email"
|
||||||
"users-cancel-account": "/users/cancel"
|
"users-cancel-account": "/users/cancel"
|
||||||
"contacts": "/users/%s/contacts"
|
"user-stats": "/users/%s/stats"
|
||||||
"stats": "/users/%s/stats"
|
"user-liked": "/users/%s/liked"
|
||||||
|
"user-voted": "/users/%s/voted"
|
||||||
|
"user-watched": "/users/%s/watched"
|
||||||
|
"user-contacts": "/users/%s/contacts"
|
||||||
|
|
||||||
# User - Notification
|
# User - Notification
|
||||||
"permissions": "/permissions"
|
"permissions": "/permissions"
|
||||||
|
@ -63,6 +66,10 @@ urls = {
|
||||||
"project-templates": "/project-templates"
|
"project-templates": "/project-templates"
|
||||||
"project-modules": "/projects/%s/modules"
|
"project-modules": "/projects/%s/modules"
|
||||||
"bulk-update-projects-order": "/projects/bulk_update_order"
|
"bulk-update-projects-order": "/projects/bulk_update_order"
|
||||||
|
"project-like": "/projects/%s/like"
|
||||||
|
"project-unlike": "/projects/%s/unlike"
|
||||||
|
"project-watch": "/projects/%s/watch"
|
||||||
|
"project-unwatch": "/projects/%s/unwatch"
|
||||||
|
|
||||||
# Project Values - Choises
|
# Project Values - Choises
|
||||||
"userstory-statuses": "/userstory-statuses"
|
"userstory-statuses": "/userstory-statuses"
|
||||||
|
@ -83,16 +90,28 @@ urls = {
|
||||||
"bulk-update-us-sprint-order": "/userstories/bulk_update_sprint_order"
|
"bulk-update-us-sprint-order": "/userstories/bulk_update_sprint_order"
|
||||||
"bulk-update-us-kanban-order": "/userstories/bulk_update_kanban_order"
|
"bulk-update-us-kanban-order": "/userstories/bulk_update_kanban_order"
|
||||||
"userstories-filters": "/userstories/filters_data"
|
"userstories-filters": "/userstories/filters_data"
|
||||||
|
"userstory-upvote": "/userstories/%s/upvote"
|
||||||
|
"userstory-downvote": "/userstories/%s/downvote"
|
||||||
|
"userstory-watch": "/userstories/%s/watch"
|
||||||
|
"userstory-unwatch": "/userstories/%s/unwatch"
|
||||||
|
|
||||||
# Tasks
|
# Tasks
|
||||||
"tasks": "/tasks"
|
"tasks": "/tasks"
|
||||||
"bulk-create-tasks": "/tasks/bulk_create"
|
"bulk-create-tasks": "/tasks/bulk_create"
|
||||||
"bulk-update-task-taskboard-order": "/tasks/bulk_update_taskboard_order"
|
"bulk-update-task-taskboard-order": "/tasks/bulk_update_taskboard_order"
|
||||||
|
"task-upvote": "/tasks/%s/upvote"
|
||||||
|
"task-downvote": "/tasks/%s/downvote"
|
||||||
|
"task-watch": "/tasks/%s/watch"
|
||||||
|
"task-unwatch": "/tasks/%s/unwatch"
|
||||||
|
|
||||||
# Issues
|
# Issues
|
||||||
"issues": "/issues"
|
"issues": "/issues"
|
||||||
"bulk-create-issues": "/issues/bulk_create"
|
"bulk-create-issues": "/issues/bulk_create"
|
||||||
"issues-filters": "/issues/filters_data"
|
"issues-filters": "/issues/filters_data"
|
||||||
|
"issue-upvote": "/issues/%s/upvote"
|
||||||
|
"issue-downvote": "/issues/%s/downvote"
|
||||||
|
"issue-watch": "/issues/%s/watch"
|
||||||
|
"issue-unwatch": "/issues/%s/unwatch"
|
||||||
|
|
||||||
# Wiki pages
|
# Wiki pages
|
||||||
"wiki": "/wiki"
|
"wiki": "/wiki"
|
||||||
|
|
|
@ -55,6 +55,22 @@ resourceProvider = ($repo, $http, $urls, $storage, $q) ->
|
||||||
params = {project_id: projectId, bulk_issues: data}
|
params = {project_id: projectId, bulk_issues: data}
|
||||||
return $http.post(url, params)
|
return $http.post(url, params)
|
||||||
|
|
||||||
|
service.upvote = (issueId) ->
|
||||||
|
url = $urls.resolve("issue-upvote", issueId)
|
||||||
|
return $http.post(url)
|
||||||
|
|
||||||
|
service.downvote = (issueId) ->
|
||||||
|
url = $urls.resolve("issue-downvote", issueId)
|
||||||
|
return $http.post(url)
|
||||||
|
|
||||||
|
service.watch = (issueId) ->
|
||||||
|
url = $urls.resolve("issue-watch", issueId)
|
||||||
|
return $http.post(url)
|
||||||
|
|
||||||
|
service.unwatch = (issueId) ->
|
||||||
|
url = $urls.resolve("issue-unwatch", issueId)
|
||||||
|
return $http.post(url)
|
||||||
|
|
||||||
service.stats = (projectId) ->
|
service.stats = (projectId) ->
|
||||||
return $repo.queryOneRaw("projects", "#{projectId}/issues_stats")
|
return $repo.queryOneRaw("projects", "#{projectId}/issues_stats")
|
||||||
|
|
||||||
|
|
|
@ -57,6 +57,22 @@ resourceProvider = ($repo, $http, $urls, $storage) ->
|
||||||
return $http.post(url, params).then (result) ->
|
return $http.post(url, params).then (result) ->
|
||||||
return result.data
|
return result.data
|
||||||
|
|
||||||
|
service.upvote = (taskId) ->
|
||||||
|
url = $urls.resolve("task-upvote", taskId)
|
||||||
|
return $http.post(url)
|
||||||
|
|
||||||
|
service.downvote = (taskId) ->
|
||||||
|
url = $urls.resolve("task-downvote", taskId)
|
||||||
|
return $http.post(url)
|
||||||
|
|
||||||
|
service.watch = (taskId) ->
|
||||||
|
url = $urls.resolve("task-watch", taskId)
|
||||||
|
return $http.post(url)
|
||||||
|
|
||||||
|
service.unwatch = (taskId) ->
|
||||||
|
url = $urls.resolve("task-unwatch", taskId)
|
||||||
|
return $http.post(url)
|
||||||
|
|
||||||
service.bulkUpdateTaskTaskboardOrder = (projectId, data) ->
|
service.bulkUpdateTaskTaskboardOrder = (projectId, data) ->
|
||||||
url = $urls.resolve("bulk-update-task-taskboard-order")
|
url = $urls.resolve("bulk-update-task-taskboard-order")
|
||||||
params = {project_id: projectId, bulk_tasks: data}
|
params = {project_id: projectId, bulk_tasks: data}
|
||||||
|
|
|
@ -67,6 +67,22 @@ resourceProvider = ($repo, $http, $urls, $storage) ->
|
||||||
|
|
||||||
return $http.post(url, data)
|
return $http.post(url, data)
|
||||||
|
|
||||||
|
service.upvote = (userStoryId) ->
|
||||||
|
url = $urls.resolve("userstory-upvote", userStoryId)
|
||||||
|
return $http.post(url)
|
||||||
|
|
||||||
|
service.downvote = (userStoryId) ->
|
||||||
|
url = $urls.resolve("userstory-downvote", userStoryId)
|
||||||
|
return $http.post(url)
|
||||||
|
|
||||||
|
service.watch = (userStoryId) ->
|
||||||
|
url = $urls.resolve("userstory-watch", userStoryId)
|
||||||
|
return $http.post(url)
|
||||||
|
|
||||||
|
service.unwatch = (userStoryId) ->
|
||||||
|
url = $urls.resolve("userstory-unwatch", userStoryId)
|
||||||
|
return $http.post(url)
|
||||||
|
|
||||||
service.bulkUpdateBacklogOrder = (projectId, data) ->
|
service.bulkUpdateBacklogOrder = (projectId, data) ->
|
||||||
url = $urls.resolve("bulk-update-us-backlog-order")
|
url = $urls.resolve("bulk-update-us-backlog-order")
|
||||||
params = {project_id: projectId, bulk_stories: data}
|
params = {project_id: projectId, bulk_stories: data}
|
||||||
|
|
|
@ -23,6 +23,7 @@ taiga = @.taiga
|
||||||
|
|
||||||
mixOf = @.taiga.mixOf
|
mixOf = @.taiga.mixOf
|
||||||
groupBy = @.taiga.groupBy
|
groupBy = @.taiga.groupBy
|
||||||
|
bindMethods = @.taiga.bindMethods
|
||||||
|
|
||||||
module = angular.module("taigaTasks")
|
module = angular.module("taigaTasks")
|
||||||
|
|
||||||
|
@ -50,6 +51,8 @@ class TaskDetailController extends mixOf(taiga.Controller, taiga.PageMixin)
|
||||||
|
|
||||||
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location,
|
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location,
|
||||||
@log, @appMetaService, @navUrls, @analytics, @translate) ->
|
@log, @appMetaService, @navUrls, @analytics, @translate) ->
|
||||||
|
bindMethods(@)
|
||||||
|
|
||||||
@scope.taskRef = @params.taskref
|
@scope.taskRef = @params.taskref
|
||||||
@scope.sectionName = @translate.instant("TASK.SECTION_NAME")
|
@scope.sectionName = @translate.instant("TASK.SECTION_NAME")
|
||||||
@.initializeEventHandlers()
|
@.initializeEventHandlers()
|
||||||
|
@ -145,6 +148,50 @@ class TaskDetailController extends mixOf(taiga.Controller, taiga.PageMixin)
|
||||||
@.fillUsersAndRoles(project.members, project.roles)
|
@.fillUsersAndRoles(project.members, project.roles)
|
||||||
@.loadTask().then(=> @q.all([@.loadSprint(), @.loadUserStory()]))
|
@.loadTask().then(=> @q.all([@.loadSprint(), @.loadUserStory()]))
|
||||||
|
|
||||||
|
###
|
||||||
|
# Note: This methods (onUpvote() and onDownvote()) are related to tg-vote-button.
|
||||||
|
# See app/modules/components/vote-button for more info
|
||||||
|
###
|
||||||
|
onUpvote: ->
|
||||||
|
onSuccess = =>
|
||||||
|
@.loadTask()
|
||||||
|
@rootscope.$broadcast("object:updated")
|
||||||
|
onError = =>
|
||||||
|
@confirm.notify("error")
|
||||||
|
|
||||||
|
return @rs.tasks.upvote(@scope.taskId).then(onSuccess, onError)
|
||||||
|
|
||||||
|
onDownvote: ->
|
||||||
|
onSuccess = =>
|
||||||
|
@.loadTask()
|
||||||
|
@rootscope.$broadcast("object:updated")
|
||||||
|
onError = =>
|
||||||
|
@confirm.notify("error")
|
||||||
|
|
||||||
|
return @rs.tasks.downvote(@scope.taskId).then(onSuccess, onError)
|
||||||
|
|
||||||
|
###
|
||||||
|
# Note: This methods (onWatch() and onUnwatch()) are related to tg-watch-button.
|
||||||
|
# See app/modules/components/watch-button for more info
|
||||||
|
###
|
||||||
|
onWatch: ->
|
||||||
|
onSuccess = =>
|
||||||
|
@.loadTask()
|
||||||
|
@rootscope.$broadcast("object:updated")
|
||||||
|
onError = =>
|
||||||
|
@confirm.notify("error")
|
||||||
|
|
||||||
|
return @rs.tasks.watch(@scope.taskId).then(onSuccess, onError)
|
||||||
|
|
||||||
|
onUnwatch: ->
|
||||||
|
onSuccess = =>
|
||||||
|
@.loadTask()
|
||||||
|
@rootscope.$broadcast("object:updated")
|
||||||
|
onError = =>
|
||||||
|
@confirm.notify("error")
|
||||||
|
|
||||||
|
return @rs.tasks.unwatch(@scope.taskId).then(onSuccess, onError)
|
||||||
|
|
||||||
module.controller("TaskDetailController", TaskDetailController)
|
module.controller("TaskDetailController", TaskDetailController)
|
||||||
|
|
||||||
|
|
||||||
|
@ -195,7 +242,7 @@ module.directive("tgTaskStatusDisplay", ["$tgTemplate", "$compile", TaskStatusDi
|
||||||
## Task status button directive
|
## Task status button directive
|
||||||
#############################################################################
|
#############################################################################
|
||||||
|
|
||||||
TaskStatusButtonDirective = ($rootScope, $repo, $confirm, $loading, $qqueue, $compile, $translate) ->
|
TaskStatusButtonDirective = ($rootScope, $repo, $confirm, $loading, $qqueue, $compile, $translate, $template) ->
|
||||||
# Display the status of Task and you can edit it.
|
# Display the status of Task and you can edit it.
|
||||||
#
|
#
|
||||||
# Example:
|
# Example:
|
||||||
|
@ -206,21 +253,7 @@ TaskStatusButtonDirective = ($rootScope, $repo, $confirm, $loading, $qqueue, $co
|
||||||
# - scope.statusById object
|
# - scope.statusById object
|
||||||
# - $scope.project.my_permissions
|
# - $scope.project.my_permissions
|
||||||
|
|
||||||
template = _.template("""
|
template = $template.get("us/us-status-button.html", true)
|
||||||
<div class="status-data <% if(editable){ %>clickable<% }%>">
|
|
||||||
<span class="level" style="background-color:<%- status.color %>"></span>
|
|
||||||
<span class="status-status"><%- status.name %></span>
|
|
||||||
<% if(editable){ %><span class="icon icon-arrow-bottom"></span><% }%>
|
|
||||||
<span class="level-name" translate="COMMON.FIELDS.STATUS"></span>
|
|
||||||
|
|
||||||
<ul class="popover pop-status">
|
|
||||||
<% _.each(statuses, function(st) { %>
|
|
||||||
<li><a href="" class="status" title="<%- st.name %>"
|
|
||||||
data-status-id="<%- st.id %>"><%- st.name %></a></li>
|
|
||||||
<% }); %>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
""")
|
|
||||||
|
|
||||||
link = ($scope, $el, $attrs, $model) ->
|
link = ($scope, $el, $attrs, $model) ->
|
||||||
isEditable = ->
|
isEditable = ->
|
||||||
|
@ -288,7 +321,7 @@ TaskStatusButtonDirective = ($rootScope, $repo, $confirm, $loading, $qqueue, $co
|
||||||
}
|
}
|
||||||
|
|
||||||
module.directive("tgTaskStatusButton", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgLoading", "$tgQqueue",
|
module.directive("tgTaskStatusButton", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgLoading", "$tgQqueue",
|
||||||
"$compile", "$translate", TaskStatusButtonDirective])
|
"$compile", "$translate", "$tgTemplate", TaskStatusButtonDirective])
|
||||||
|
|
||||||
|
|
||||||
TaskIsIocaineButtonDirective = ($rootscope, $tgrepo, $confirm, $loading, $qqueue, $compile) ->
|
TaskIsIocaineButtonDirective = ($rootscope, $tgrepo, $confirm, $loading, $qqueue, $compile) ->
|
||||||
|
|
|
@ -24,6 +24,7 @@ taiga = @.taiga
|
||||||
mixOf = @.taiga.mixOf
|
mixOf = @.taiga.mixOf
|
||||||
groupBy = @.taiga.groupBy
|
groupBy = @.taiga.groupBy
|
||||||
bindOnce = @.taiga.bindOnce
|
bindOnce = @.taiga.bindOnce
|
||||||
|
bindMethods = @.taiga.bindMethods
|
||||||
|
|
||||||
module = angular.module("taigaUserStories")
|
module = angular.module("taigaUserStories")
|
||||||
|
|
||||||
|
@ -50,6 +51,8 @@ class UserStoryDetailController extends mixOf(taiga.Controller, taiga.PageMixin)
|
||||||
|
|
||||||
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location,
|
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location,
|
||||||
@log, @appMetaService, @navUrls, @analytics, @translate) ->
|
@log, @appMetaService, @navUrls, @analytics, @translate) ->
|
||||||
|
bindMethods(@)
|
||||||
|
|
||||||
@scope.usRef = @params.usref
|
@scope.usRef = @params.usref
|
||||||
@scope.sectionName = @translate.instant("US.SECTION_NAME")
|
@scope.sectionName = @translate.instant("US.SECTION_NAME")
|
||||||
@.initializeEventHandlers()
|
@.initializeEventHandlers()
|
||||||
|
@ -182,6 +185,50 @@ class UserStoryDetailController extends mixOf(taiga.Controller, taiga.PageMixin)
|
||||||
@.fillUsersAndRoles(project.members, project.roles)
|
@.fillUsersAndRoles(project.members, project.roles)
|
||||||
@.loadUs().then(=> @q.all([@.loadSprint(), @.loadTasks()]))
|
@.loadUs().then(=> @q.all([@.loadSprint(), @.loadTasks()]))
|
||||||
|
|
||||||
|
###
|
||||||
|
# Note: This methods (onUpvote() and onDownvote()) are related to tg-vote-button.
|
||||||
|
# See app/modules/components/vote-button for more info
|
||||||
|
###
|
||||||
|
onUpvote: ->
|
||||||
|
onSuccess = =>
|
||||||
|
@.loadUs()
|
||||||
|
@rootscope.$broadcast("object:updated")
|
||||||
|
onError = =>
|
||||||
|
@confirm.notify("error")
|
||||||
|
|
||||||
|
return @rs.userstories.upvote(@scope.usId).then(onSuccess, onError)
|
||||||
|
|
||||||
|
onDownvote: ->
|
||||||
|
onSuccess = =>
|
||||||
|
@.loadUs()
|
||||||
|
@rootscope.$broadcast("object:updated")
|
||||||
|
onError = =>
|
||||||
|
@confirm.notify("error")
|
||||||
|
|
||||||
|
return @rs.userstories.downvote(@scope.usId).then(onSuccess, onError)
|
||||||
|
|
||||||
|
###
|
||||||
|
# Note: This methods (onWatch() and onUnwatch()) are related to tg-watch-button.
|
||||||
|
# See app/modules/components/watch-button for more info
|
||||||
|
###
|
||||||
|
onWatch: ->
|
||||||
|
onSuccess = =>
|
||||||
|
@.loadUs()
|
||||||
|
@rootscope.$broadcast("object:updated")
|
||||||
|
onError = =>
|
||||||
|
@confirm.notify("error")
|
||||||
|
|
||||||
|
return @rs.userstories.watch(@scope.usId).then(onSuccess, onError)
|
||||||
|
|
||||||
|
onUnwatch: ->
|
||||||
|
onSuccess = =>
|
||||||
|
@.loadUs()
|
||||||
|
@rootscope.$broadcast("object:updated")
|
||||||
|
onError = =>
|
||||||
|
@confirm.notify("error")
|
||||||
|
|
||||||
|
return @rs.userstories.unwatch(@scope.usId).then(onSuccess, onError)
|
||||||
|
|
||||||
module.controller("UserStoryDetailController", UserStoryDetailController)
|
module.controller("UserStoryDetailController", UserStoryDetailController)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
class VoteButtonController
|
||||||
|
@.$inject = [
|
||||||
|
"tgCurrentUserService",
|
||||||
|
]
|
||||||
|
|
||||||
|
constructor: (@currentUserService) ->
|
||||||
|
@.user = @currentUserService.getUser()
|
||||||
|
@.isMouseOver = false
|
||||||
|
@.loading = false
|
||||||
|
|
||||||
|
showTextWhenMouseIsOver: ->
|
||||||
|
@.isMouseOver = true
|
||||||
|
|
||||||
|
showTextWhenMouseIsLeave: ->
|
||||||
|
@.isMouseOver = false
|
||||||
|
|
||||||
|
toggleVote: ->
|
||||||
|
@.loading = true
|
||||||
|
|
||||||
|
if not @.item.is_voter
|
||||||
|
promise = @._upvote()
|
||||||
|
else
|
||||||
|
promise = @._downvote()
|
||||||
|
|
||||||
|
promise.finally () => @.loading = false
|
||||||
|
|
||||||
|
return promise
|
||||||
|
|
||||||
|
_upvote: ->
|
||||||
|
@.onUpvote().then =>
|
||||||
|
@.showTextWhenMouseIsLeave()
|
||||||
|
|
||||||
|
_downvote: ->
|
||||||
|
@.onDownvote()
|
||||||
|
|
||||||
|
angular.module("taigaComponents").controller("VoteButton", VoteButtonController)
|
|
@ -0,0 +1,82 @@
|
||||||
|
describe "VoteButton", ->
|
||||||
|
provide = null
|
||||||
|
$controller = null
|
||||||
|
$rootScope = null
|
||||||
|
mocks = {}
|
||||||
|
|
||||||
|
_mockCurrentUser = () ->
|
||||||
|
mocks.currentUser = {
|
||||||
|
getUser: sinon.stub()
|
||||||
|
}
|
||||||
|
|
||||||
|
provide.value "tgCurrentUserService", mocks.currentUser
|
||||||
|
|
||||||
|
_mocks = ->
|
||||||
|
mocks = {
|
||||||
|
onUpvote: sinon.stub(),
|
||||||
|
onDownvote: sinon.stub()
|
||||||
|
}
|
||||||
|
|
||||||
|
module ($provide) ->
|
||||||
|
provide = $provide
|
||||||
|
_mockCurrentUser()
|
||||||
|
return null
|
||||||
|
|
||||||
|
_inject = (callback) ->
|
||||||
|
inject (_$controller_, _$rootScope_) ->
|
||||||
|
$rootScope = _$rootScope_
|
||||||
|
$controller = _$controller_
|
||||||
|
|
||||||
|
_setup = ->
|
||||||
|
_mocks()
|
||||||
|
_inject()
|
||||||
|
|
||||||
|
beforeEach ->
|
||||||
|
module "taigaComponents"
|
||||||
|
_setup()
|
||||||
|
|
||||||
|
it "upvote", (done) ->
|
||||||
|
$scope = $rootScope.$new()
|
||||||
|
|
||||||
|
mocks.onUpvote = sinon.stub().promise()
|
||||||
|
|
||||||
|
ctrl = $controller("VoteButton", $scope, {
|
||||||
|
item: {is_voter: false}
|
||||||
|
onUpvote: mocks.onUpvote
|
||||||
|
onDownvote: mocks.onDownvote
|
||||||
|
})
|
||||||
|
|
||||||
|
promise = ctrl.toggleVote()
|
||||||
|
|
||||||
|
expect(ctrl.loading).to.be.true;
|
||||||
|
|
||||||
|
mocks.onUpvote.resolve()
|
||||||
|
|
||||||
|
promise.finally () ->
|
||||||
|
expect(mocks.onUpvote).to.be.calledOnce
|
||||||
|
expect(ctrl.loading).to.be.false;
|
||||||
|
|
||||||
|
done()
|
||||||
|
|
||||||
|
it "downvote", (done) ->
|
||||||
|
$scope = $rootScope.$new()
|
||||||
|
|
||||||
|
mocks.onDownvote = sinon.stub().promise()
|
||||||
|
|
||||||
|
ctrl = $controller("VoteButton", $scope, {
|
||||||
|
item: {is_voter: true}
|
||||||
|
onUpvote: mocks.onUpvote
|
||||||
|
onDownvote: mocks.onDownvote
|
||||||
|
})
|
||||||
|
|
||||||
|
promise = ctrl.toggleVote()
|
||||||
|
|
||||||
|
expect(ctrl.loading).to.be.true;
|
||||||
|
|
||||||
|
mocks.onDownvote.resolve()
|
||||||
|
|
||||||
|
promise.finally () ->
|
||||||
|
expect(mocks.onDownvote).to.be.calledOnce
|
||||||
|
expect(ctrl.loading).to.be.false;
|
||||||
|
|
||||||
|
done()
|
|
@ -0,0 +1,14 @@
|
||||||
|
VoteButtonDirective = ->
|
||||||
|
return {
|
||||||
|
scope: {}
|
||||||
|
controller: "VoteButton",
|
||||||
|
bindToController: {
|
||||||
|
item: "=",
|
||||||
|
onUpvote: "=",
|
||||||
|
onDownvote: "="
|
||||||
|
}
|
||||||
|
controllerAs: "vm",
|
||||||
|
templateUrl: "components/vote-button/vote-button.html",
|
||||||
|
}
|
||||||
|
|
||||||
|
angular.module("taigaComponents").directive("tgVoteButton", VoteButtonDirective)
|
|
@ -0,0 +1,15 @@
|
||||||
|
a(
|
||||||
|
href=""
|
||||||
|
title="{{ 'COMMON.VOTE_BUTTON.BUTTON_TITLE' | translate }}"
|
||||||
|
ng-if="::vm.user"
|
||||||
|
ng-click="vm.toggleVote()"
|
||||||
|
ng-class="{'active': vm.item.is_voter, 'is-hover': vm.item.is_voter && vm.isMouseOver, 'disable': !vm.user}"
|
||||||
|
ng-mouseover="vm.showTextWhenMouseIsOver()"
|
||||||
|
ng-mouseleave="vm.showTextWhenMouseIsLeave()"
|
||||||
|
)
|
||||||
|
span.track-icon
|
||||||
|
include ../../../svg/upvote.svg
|
||||||
|
span.track-button-counter(
|
||||||
|
title="{{ 'COMMON.VOTE_BUTTON.COUNTER_TITLE'|translate:{total:vm.item.total_voters||0}:'messageformat' }}",
|
||||||
|
tg-loading="vm.loading"
|
||||||
|
) {{ vm.item.total_voters }}
|
|
@ -0,0 +1,36 @@
|
||||||
|
class WatchButtonController
|
||||||
|
@.$inject = [
|
||||||
|
"tgCurrentUserService",
|
||||||
|
]
|
||||||
|
|
||||||
|
constructor: (@currentUserService) ->
|
||||||
|
@.user = @currentUserService.getUser()
|
||||||
|
@.isMouseOver = false
|
||||||
|
@.loading = false
|
||||||
|
|
||||||
|
showTextWhenMouseIsOver: ->
|
||||||
|
@.isMouseOver = true
|
||||||
|
|
||||||
|
showTextWhenMouseIsLeave: ->
|
||||||
|
@.isMouseOver = false
|
||||||
|
|
||||||
|
toggleWatch: ->
|
||||||
|
@.loading = true
|
||||||
|
|
||||||
|
if not @.item.is_watcher
|
||||||
|
promise = @._watch()
|
||||||
|
else
|
||||||
|
promise = @._unwatch()
|
||||||
|
|
||||||
|
promise.finally () => @.loading = false
|
||||||
|
|
||||||
|
return promise
|
||||||
|
|
||||||
|
_watch: ->
|
||||||
|
@.onWatch().then =>
|
||||||
|
@.showTextWhenMouseIsLeave()
|
||||||
|
|
||||||
|
_unwatch: ->
|
||||||
|
@.onUnwatch()
|
||||||
|
|
||||||
|
angular.module("taigaComponents").controller("WatchButton", WatchButtonController)
|
|
@ -0,0 +1,83 @@
|
||||||
|
describe "WatchButton", ->
|
||||||
|
provide = null
|
||||||
|
$controller = null
|
||||||
|
$rootScope = null
|
||||||
|
mocks = {}
|
||||||
|
|
||||||
|
_mockCurrentUser = () ->
|
||||||
|
mocks.currentUser = {
|
||||||
|
getUser: sinon.stub()
|
||||||
|
}
|
||||||
|
|
||||||
|
provide.value "tgCurrentUserService", mocks.currentUser
|
||||||
|
|
||||||
|
_mocks = ->
|
||||||
|
mocks = {
|
||||||
|
onWatch: sinon.stub(),
|
||||||
|
onUnwatch: sinon.stub()
|
||||||
|
}
|
||||||
|
|
||||||
|
module ($provide) ->
|
||||||
|
provide = $provide
|
||||||
|
_mockCurrentUser()
|
||||||
|
return null
|
||||||
|
|
||||||
|
_inject = (callback) ->
|
||||||
|
inject (_$controller_, _$rootScope_) ->
|
||||||
|
$rootScope = _$rootScope_
|
||||||
|
$controller = _$controller_
|
||||||
|
|
||||||
|
_setup = ->
|
||||||
|
_mocks()
|
||||||
|
_inject()
|
||||||
|
|
||||||
|
beforeEach ->
|
||||||
|
module "taigaComponents"
|
||||||
|
_setup()
|
||||||
|
|
||||||
|
it "watch", (done) ->
|
||||||
|
$scope = $rootScope.$new()
|
||||||
|
|
||||||
|
mocks.onWatch = sinon.stub().promise()
|
||||||
|
|
||||||
|
ctrl = $controller("WatchButton", $scope, {
|
||||||
|
item: {is_watcher: false}
|
||||||
|
onWatch: mocks.onWatch
|
||||||
|
onUnwatch: mocks.onUnwatch
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
promise = ctrl.toggleWatch()
|
||||||
|
|
||||||
|
expect(ctrl.loading).to.be.true;
|
||||||
|
|
||||||
|
mocks.onWatch.resolve()
|
||||||
|
|
||||||
|
promise.finally () ->
|
||||||
|
expect(mocks.onWatch).to.be.calledOnce
|
||||||
|
expect(ctrl.loading).to.be.false;
|
||||||
|
|
||||||
|
done()
|
||||||
|
|
||||||
|
it "unwatch", (done) ->
|
||||||
|
$scope = $rootScope.$new()
|
||||||
|
|
||||||
|
mocks.onUnwatch = sinon.stub().promise()
|
||||||
|
|
||||||
|
ctrl = $controller("WatchButton", $scope, {
|
||||||
|
item: {is_watcher: true}
|
||||||
|
onWatch: mocks.onWatch
|
||||||
|
onUnwatch: mocks.onUnwatch
|
||||||
|
})
|
||||||
|
|
||||||
|
promise = ctrl.toggleWatch()
|
||||||
|
|
||||||
|
expect(ctrl.loading).to.be.true;
|
||||||
|
|
||||||
|
mocks.onUnwatch.resolve()
|
||||||
|
|
||||||
|
promise.finally () ->
|
||||||
|
expect(mocks.onUnwatch).to.be.calledOnce
|
||||||
|
expect(ctrl.loading).to.be.false;
|
||||||
|
|
||||||
|
done()
|
|
@ -0,0 +1,14 @@
|
||||||
|
WatchButtonDirective = ->
|
||||||
|
return {
|
||||||
|
scope: {}
|
||||||
|
controller: "WatchButton",
|
||||||
|
bindToController: {
|
||||||
|
item: "=",
|
||||||
|
onWatch: "=",
|
||||||
|
onUnwatch: "="
|
||||||
|
}
|
||||||
|
controllerAs: "vm",
|
||||||
|
templateUrl: "components/watch-button/watch-button.html",
|
||||||
|
}
|
||||||
|
|
||||||
|
angular.module("taigaComponents").directive("tgWatchButton", WatchButtonDirective)
|
|
@ -0,0 +1,44 @@
|
||||||
|
mixin counter
|
||||||
|
span.track-button-counter(
|
||||||
|
title="{{ 'COMMON.WATCH_BUTTON.COUNTER_TITLE'|translate:{total:vm.item.watchers.length||0}:'messageformat' }}",
|
||||||
|
tg-loading="vm.loading"
|
||||||
|
)
|
||||||
|
| {{ vm.item.watchers.length }}
|
||||||
|
|
||||||
|
|
||||||
|
//- Registered user button
|
||||||
|
a.track-button.watch-button.watch-container(
|
||||||
|
href=""
|
||||||
|
title="{{ 'COMMON.WATCH_BUTTON.BUTTON_TITLE' | translate }}"
|
||||||
|
ng-if="::vm.user"
|
||||||
|
ng-click="vm.toggleWatch()"
|
||||||
|
ng-class="{'active': vm.item.is_watcher, 'is-hover': vm.item.is_watcher && vm.isMouseOver}"
|
||||||
|
ng-mouseover="vm.showTextWhenMouseIsOver()"
|
||||||
|
ng-mouseleave="vm.showTextWhenMouseIsLeave()"
|
||||||
|
)
|
||||||
|
span.track-inner
|
||||||
|
span.track-icon
|
||||||
|
include ../../../svg/watch.svg
|
||||||
|
span(
|
||||||
|
ng-if="!vm.item.is_watcher",
|
||||||
|
translate="COMMON.WATCH_BUTTON.WATCH"
|
||||||
|
)
|
||||||
|
span(
|
||||||
|
ng-if="vm.item.is_watcher && !vm.isMouseOver",
|
||||||
|
translate="COMMON.WATCH_BUTTON.WATCHING"
|
||||||
|
)
|
||||||
|
span(
|
||||||
|
ng-if="vm.item.is_watcher && vm.isMouseOver",
|
||||||
|
translate="COMMON.WATCH_BUTTON.UNWATCH"
|
||||||
|
)
|
||||||
|
+counter
|
||||||
|
|
||||||
|
//- Anonymous user button
|
||||||
|
span.track-button.watch-button.watch-container(
|
||||||
|
ng-if="::!vm.user"
|
||||||
|
)
|
||||||
|
span.track-inner
|
||||||
|
span.track-icon
|
||||||
|
include ../../../svg/watch.svg
|
||||||
|
span(translate="COMMON.WATCH_BUTTON.WATCHERS")
|
||||||
|
+counter
|
|
@ -1,30 +1,45 @@
|
||||||
doctype html
|
doctype html
|
||||||
|
|
||||||
div.wrapper(ng-controller="IssueDetailController as ctrl",
|
div.wrapper(
|
||||||
ng-init="section='issues'")
|
ng-controller="IssueDetailController as ctrl",
|
||||||
|
ng-init="section='issues'"
|
||||||
|
)
|
||||||
tg-project-menu
|
tg-project-menu
|
||||||
|
|
||||||
div.main.us-detail
|
div.main.us-detail
|
||||||
div.us-detail-header.header-with-actions
|
div.us-detail-header.header-with-actions
|
||||||
include ../includes/components/mainTitle
|
include ../includes/components/mainTitle
|
||||||
|
|
||||||
section.us-story-main-data
|
section.us-story-main-data
|
||||||
div.us-title(ng-class="{blocked: issue.is_blocked}")
|
header
|
||||||
|
tg-vote-button.upvote-btn(
|
||||||
|
item="issue"
|
||||||
|
on-upvote="ctrl.onUpvote"
|
||||||
|
on-downvote="ctrl.onDownvote"
|
||||||
|
)
|
||||||
|
.us-title(ng-class="{blocked: issue.is_blocked}")
|
||||||
h2.us-title-text
|
h2.us-title-text
|
||||||
span.us-number(tg-bo-ref="issue.ref")
|
span.us-number(tg-bo-ref="issue.ref")
|
||||||
span.us-name(tg-editable-subject, ng-model="issue", required-perm="modify_issue")
|
span.us-name(tg-editable-subject, ng-model="issue", required-perm="modify_issue")
|
||||||
|
|
||||||
p.us-related-task(ng-if="issue.generated_user_stories.length")
|
p.us-related-task(ng-if="issue.generated_user_stories.length")
|
||||||
| {{ 'ISSUES.PROMOTED'|translate }}
|
| {{ 'ISSUES.PROMOTED'|translate }}
|
||||||
a(ng-repeat="us in issue.generated_user_stories",
|
a(
|
||||||
tg-check-permission="view_us", href="",
|
href=""
|
||||||
tg-bo-title="'#' + us.ref + ' ' + us.subject",
|
ng-repeat="us in issue.generated_user_stories"
|
||||||
tg-nav="project-userstories-detail:project=project.slug,ref=us.ref")
|
tg-check-permission="view_us"
|
||||||
|
tg-bo-title="'#' + us.ref + ' ' + us.subject"
|
||||||
|
tg-nav="project-userstories-detail:project=project.slug,ref=us.ref"
|
||||||
|
)
|
||||||
span(tg-bo-ref="us.ref")
|
span(tg-bo-ref="us.ref")
|
||||||
|
|
||||||
p.external-reference(ng-if="issue.external_reference")
|
p.external-reference(ng-if="issue.external_reference")
|
||||||
| {{ 'ISSUES.EXTERNAL_REFERENCE'|translate }}
|
| {{ 'ISSUES.EXTERNAL_REFERENCE'|translate }}
|
||||||
a(target="_blank", tg-bo-href="issue.external_reference[1]",
|
a(
|
||||||
title="{{'ISSUES.GO_TO_EXTERNAL_REFERENCE' | translate}}")
|
target="_blank"
|
||||||
|
tg-bo-href="issue.external_reference[1]"
|
||||||
|
title="{{'ISSUES.GO_TO_EXTERNAL_REFERENCE' | translate}}"
|
||||||
|
)
|
||||||
span {{ issue.external_reference[1] }}
|
span {{ issue.external_reference[1] }}
|
||||||
|
|
||||||
p.block-desc-container(ng-show="issue.is_blocked")
|
p.block-desc-container(ng-show="issue.is_blocked")
|
||||||
|
@ -32,43 +47,69 @@ div.wrapper(ng-controller="IssueDetailController as ctrl",
|
||||||
span.block-description(ng-bind="issue.blocked_note || ('ISSUES.BLOCKED' | translate)")
|
span.block-description(ng-bind="issue.blocked_note || ('ISSUES.BLOCKED' | translate)")
|
||||||
|
|
||||||
div.issue-nav
|
div.issue-nav
|
||||||
a.icon.icon-arrow-left(ng-show="previousUrl", tg-bo-href="previousUrl",
|
a.icon.icon-arrow-left(
|
||||||
title="{{'ISSUES.TITLE_PREVIOUS_ISSUE' | translate}}")
|
ng-show="previousUrl"
|
||||||
a.icon.icon-arrow-right(ng-show="nextUrl", tg-bo-href="nextUrl",
|
tg-bo-href="previousUrl"
|
||||||
title="{{'ISSUES.TITLE_NEXT_ISSUE' | translate}}")
|
title="{{'ISSUES.TITLE_PREVIOUS_ISSUE' | translate}}"
|
||||||
|
)
|
||||||
|
a.icon.icon-arrow-right(
|
||||||
|
ng-show="nextUrl"
|
||||||
|
tg-bo-href="nextUrl"
|
||||||
|
title="{{'ISSUES.TITLE_NEXT_ISSUE' | translate}}"
|
||||||
|
)
|
||||||
|
|
||||||
div.tags-block(tg-tag-line, ng-model="issue", required-perm="modify_issue")
|
div.tags-block(tg-tag-line, ng-model="issue", required-perm="modify_issue")
|
||||||
|
|
||||||
section.duty-content(tg-editable-description, ng-model="issue", required-perm="modify_issue")
|
section.duty-content(tg-editable-description, ng-model="issue", required-perm="modify_issue")
|
||||||
|
|
||||||
// Custom Fields
|
// Custom Fields
|
||||||
tg-custom-attributes-values(ng-model="issue", type="issue", project="project", required-edition-perm="modify_issue")
|
tg-custom-attributes-values(
|
||||||
|
ng-model="issue"
|
||||||
|
type="issue"
|
||||||
|
project="project"
|
||||||
|
required-edition-perm="modify_issue"
|
||||||
|
)
|
||||||
|
|
||||||
tg-attachments(ng-model="issue", type="issue")
|
tg-attachments(ng-model="issue", type="issue")
|
||||||
tg-history(ng-model="issue", type="issue")
|
tg-history(ng-model="issue", type="issue")
|
||||||
|
|
||||||
sidebar.menu-secondary.sidebar
|
sidebar.menu-secondary.sidebar.ticket-data
|
||||||
section.us-status
|
section.status
|
||||||
h1(tg-issue-status-display, ng-model="issue")
|
.ticket-title(tg-issue-status-display, ng-model="issue")
|
||||||
tg-created-by-display.us-created-by(ng-model="issue")
|
tg-created-by-display.ticket-created-by(ng-model="issue")
|
||||||
div.duty-data-container
|
div.ticket-data-container
|
||||||
div.duty-data(tg-issue-type-button, ng-model="issue")
|
div.ticket-status(tg-issue-type-button, ng-model="issue")
|
||||||
div.duty-data(tg-issue-severity-button, ng-model="issue")
|
div.ticket-status(tg-issue-severity-button, ng-model="issue")
|
||||||
div.duty-data(tg-issue-priority-button, ng-model="issue")
|
div.ticket-status(tg-issue-priority-button, ng-model="issue")
|
||||||
div.duty-data(tg-issue-status-button, ng-model="issue")
|
div.ticket-status(tg-issue-status-button, ng-model="issue")
|
||||||
|
|
||||||
section.duty-assigned-to(tg-assigned-to, ng-model="issue", required-perm="modify_issue")
|
section.ticket-assigned-to(tg-assigned-to, ng-model="issue", required-perm="modify_issue")
|
||||||
|
|
||||||
section.watchers(tg-watchers, ng-model="issue", required-perm="modify_issue")
|
section.track-buttons-container.ticket-track-buttons
|
||||||
|
|
||||||
section.us-detail-settings
|
div.watch-button
|
||||||
|
tg-watch-button(
|
||||||
|
item="issue"
|
||||||
|
on-watch="ctrl.onWatch"
|
||||||
|
on-unwatch="ctrl.onUnwatch"
|
||||||
|
)
|
||||||
|
|
||||||
|
div.ticket-watchers(
|
||||||
|
tg-watchers
|
||||||
|
ng-model="issue"
|
||||||
|
required-perm="modify_issue"
|
||||||
|
)
|
||||||
|
|
||||||
|
section.ticket-detail-settings
|
||||||
tg-promote-issue-to-us-button(tg-check-permission="add_us", ng-model="issue")
|
tg-promote-issue-to-us-button(tg-check-permission="add_us", ng-model="issue")
|
||||||
tg-block-button(tg-check-permission="modify_issue", ng-model="issue")
|
tg-block-button(tg-check-permission="modify_issue", ng-model="issue")
|
||||||
tg-delete-button(tg-check-permission="delete_issue",
|
tg-delete-button(
|
||||||
|
tg-check-permission="delete_issue",
|
||||||
on-delete-title="{{'ISSUES.ACTION_DELETE' | translate}}",
|
on-delete-title="{{'ISSUES.ACTION_DELETE' | translate}}",
|
||||||
on-delete-go-to-url="onDeleteGoToUrl",
|
on-delete-go-to-url="onDeleteGoToUrl",
|
||||||
ng-model="issue")
|
ng-model="issue"
|
||||||
|
)
|
||||||
|
|
||||||
div.lightbox.lightbox-block(tg-lb-block, title="ISSUES.LIGHTBOX_TITLE_BLOKING_ISSUE", ng-model="issue")
|
div.lightbox.lightbox-block(tg-lb-block, ng-model="issue", title="ISSUES.LIGHTBOX_TITLE_BLOKING_ISSUE")
|
||||||
div.lightbox.lightbox-select-user(tg-lb-assignedto)
|
div.lightbox.lightbox-select-user(tg-lb-assignedto)
|
||||||
div.lightbox.lightbox-select-user(tg-lb-watchers)
|
div.lightbox.lightbox-select-user(tg-lb-watchers)
|
||||||
|
|
|
@ -1,76 +1,130 @@
|
||||||
doctype html
|
doctype html
|
||||||
|
|
||||||
div.wrapper(ng-controller="TaskDetailController as ctrl",
|
div.wrapper(
|
||||||
ng-init="section='backlog-kanban'")
|
ng-controller="TaskDetailController as ctrl"
|
||||||
|
ng-init="section='backlog-kanban'"
|
||||||
|
)
|
||||||
tg-project-menu
|
tg-project-menu
|
||||||
|
|
||||||
div.main.us-detail
|
div.main.us-detail
|
||||||
div.us-detail-header.header-with-actions
|
div.us-detail-header.header-with-actions
|
||||||
include ../includes/components/mainTitle
|
include ../includes/components/mainTitle
|
||||||
.action-buttons
|
.action-buttons
|
||||||
a.button-gray(
|
a.button-gray(
|
||||||
tg-check-permission="view_milestones",
|
href=""
|
||||||
href="", title="{{'TASK.TITLE_LINK_TASKBOARD' | translate}}",
|
title="{{'TASK.TITLE_LINK_TASKBOARD' | translate}}"
|
||||||
tg-nav="project-taskboard:project=project.slug,sprint=sprint.slug",
|
tg-check-permission="view_milestones"
|
||||||
ng-if="sprint && project.is_backlog_activated", translate="TASK.LINK_TASKBOARD")
|
tg-nav="project-taskboard:project=project.slug,sprint=sprint.slug"
|
||||||
|
ng-if="sprint && project.is_backlog_activated"
|
||||||
|
translate="TASK.LINK_TASKBOARD"
|
||||||
|
)
|
||||||
|
|
||||||
section.us-story-main-data
|
section.us-story-main-data
|
||||||
|
header
|
||||||
|
tg-vote-button.upvote-btn(
|
||||||
|
item="task",
|
||||||
|
on-upvote="ctrl.onUpvote",
|
||||||
|
on-downvote="ctrl.onDownvote"
|
||||||
|
)
|
||||||
div.us-title(ng-class="{blocked: task.is_blocked}")
|
div.us-title(ng-class="{blocked: task.is_blocked}")
|
||||||
h2.us-title-text
|
h2.us-title-text
|
||||||
span.us-number(tg-bo-ref="task.ref")
|
span.us-number(tg-bo-ref="task.ref")
|
||||||
span.us-name(tg-editable-subject, ng-model="task", required-perm="modify_task")
|
span.us-name(
|
||||||
|
tg-editable-subject
|
||||||
|
ng-model="task"
|
||||||
|
required-perm="modify_task"
|
||||||
|
)
|
||||||
|
|
||||||
h3.us-related-task(ng-if="us")
|
h3.us-related-task(ng-if="us")
|
||||||
| {{ 'TASK.OWNER_US'|translate }}
|
| {{ 'TASK.OWNER_US'|translate }}
|
||||||
a(tg-check-permission="view_us", href="", title="{{'TASK.TITLE_LINK_GO_OWNER' | translate}}",
|
a(
|
||||||
tg-nav="project-userstories-detail:project=project.slug,ref=us.ref")
|
href=""
|
||||||
|
tg-check-permission="view_us"
|
||||||
|
tg-nav="project-userstories-detail:project=project.slug,ref=us.ref"
|
||||||
|
title="{{'TASK.TITLE_LINK_GO_OWNER' | translate}}"
|
||||||
|
)
|
||||||
span(tg-bo-ref="us.ref")
|
span(tg-bo-ref="us.ref")
|
||||||
span(tg-bo-bind="us.subject")
|
span(tg-bo-bind="us.subject")
|
||||||
|
|
||||||
p.external-reference(ng-if="task.external_reference")
|
p.external-reference(ng-if="task.external_reference")
|
||||||
a(target="_blank", tg-bo-href="task.external_reference[1]",
|
a(
|
||||||
title="{{'TASK.TITLE_LINK_GO_ORIGIN' | translate}}")
|
tg-bo-href="task.external_reference[1]",
|
||||||
|
target="_blank"
|
||||||
|
title="{{'TASK.TITLE_LINK_GO_ORIGIN' | translate}}"
|
||||||
|
)
|
||||||
| {{ "TASK.ORIGIN_US"| translate }}
|
| {{ "TASK.ORIGIN_US"| translate }}
|
||||||
span {{ task.external_reference[1] }}
|
span {{ task.external_reference[1] }}
|
||||||
|
|
||||||
p.block-desc-container(ng-show="task.is_blocked")
|
p.block-desc-container(ng-show="task.is_blocked")
|
||||||
span.block-description-title(translate="COMMON.BLOCKED")
|
span.block-description-title(translate="COMMON.BLOCKED")
|
||||||
span.block-description(ng-bind="task.blocked_note || ('TASK.BLOCKED_DESCRIPTION' | translate)")
|
span.block-description(
|
||||||
|
ng-bind="task.blocked_note || ('TASK.BLOCKED_DESCRIPTION' | translate)"
|
||||||
|
)
|
||||||
|
|
||||||
div.issue-nav
|
div.issue-nav
|
||||||
a.icon.icon-arrow-left(ng-show="previousUrl", tg-bo-href="previousUrl",
|
a.icon.icon-arrow-left(
|
||||||
title="{{'TASK.PREVIOUS' | translate}}")
|
ng-show="previousUrl"
|
||||||
a.icon.icon-arrow-right(ng-show="nextUrl", tg-bo-href="nextUrl",
|
tg-bo-href="previousUrl"
|
||||||
title="{{'TASK.NEXT' | translate}}")
|
title="{{'TASK.PREVIOUS' | translate}}"
|
||||||
|
)
|
||||||
|
a.icon.icon-arrow-right(
|
||||||
|
ng-show="nextUrl"
|
||||||
|
tg-bo-href="nextUrl"
|
||||||
|
title="{{'TASK.NEXT' | translate}}"
|
||||||
|
)
|
||||||
|
|
||||||
div.tags-block(tg-tag-line, ng-model="task", required-perm="modify_task")
|
div.tags-block(tg-tag-line, ng-model="task", required-perm="modify_task")
|
||||||
|
|
||||||
section.duty-content(tg-editable-description, ng-model="task", required-perm="modify_task")
|
section.duty-content(tg-editable-description, ng-model="task", required-perm="modify_task")
|
||||||
|
|
||||||
// Custom Fields
|
// Custom Fields
|
||||||
tg-custom-attributes-values(ng-model="task", type="task", project="project", required-edition-perm="modify_task")
|
tg-custom-attributes-values(
|
||||||
|
ng-model="task"
|
||||||
|
type="task"
|
||||||
|
project="project"
|
||||||
|
required-edition-perm="modify_task"
|
||||||
|
)
|
||||||
|
|
||||||
tg-attachments(ng-model="task", type="task")
|
tg-attachments(ng-model="task", type="task")
|
||||||
tg-history(ng-model="task", type="task")
|
tg-history(ng-model="task", type="task")
|
||||||
|
|
||||||
sidebar.menu-secondary.sidebar
|
sidebar.menu-secondary.sidebar.ticket-data
|
||||||
section.us-status
|
|
||||||
h1(tg-task-status-display, ng-model="task")
|
|
||||||
div.us-created-by(tg-created-by-display, ng-model="task")
|
|
||||||
div.duty-data-container
|
|
||||||
div.duty-data(tg-task-status-button, ng-model="task")
|
|
||||||
|
|
||||||
section.duty-assigned-to(tg-assigned-to, ng-model="task", required-perm="modify_task")
|
section.status
|
||||||
|
|
||||||
section.watchers(tg-watchers, ng-model="task", required-perm="modify_task")
|
.ticket-title(tg-task-status-display, ng-model="task")
|
||||||
|
|
||||||
section.us-detail-settings
|
.ticket-created-by(tg-created-by-display, ng-model="task")
|
||||||
|
|
||||||
|
.ticket-data-container
|
||||||
|
.ticket-status(tg-task-status-button, ng-model="task")
|
||||||
|
|
||||||
|
section.ticket-assigned-to(tg-assigned-to, ng-model="task", required-perm="modify_task")
|
||||||
|
|
||||||
|
section.track-buttons-container.ticket-track-buttons
|
||||||
|
div.watch-button
|
||||||
|
tg-watch-button(
|
||||||
|
item="task"
|
||||||
|
on-watch="ctrl.onWatch"
|
||||||
|
on-unwatch="ctrl.onUnwatch"
|
||||||
|
)
|
||||||
|
|
||||||
|
div.ticket-watchers(
|
||||||
|
tg-watchers,
|
||||||
|
ng-model="task",
|
||||||
|
required-perm="modify_task"
|
||||||
|
)
|
||||||
|
|
||||||
|
section.ticket-detail-settings
|
||||||
tg-task-is-iocaine-button(ng-model="task")
|
tg-task-is-iocaine-button(ng-model="task")
|
||||||
tg-block-button(tg-check-permission="modify_task", ng-model="task")
|
tg-block-button(tg-check-permission="modify_task", ng-model="task")
|
||||||
tg-delete-button(tg-check-permission="delete_task",
|
tg-delete-button(
|
||||||
on-delete-title="{{'TASK.TITLE_DELETE_ACTION' | translate}}",
|
tg-check-permission="delete_task"
|
||||||
on-delete-go-to-url="onDeleteGoToUrl",
|
on-delete-title="{{'TASK.TITLE_DELETE_ACTION' | translate}}"
|
||||||
ng-model="task")
|
on-delete-go-to-url="onDeleteGoToUrl"
|
||||||
|
ng-model="task"
|
||||||
|
)
|
||||||
|
|
||||||
div.lightbox.lightbox-block(tg-lb-block, title="TASK.LIGHTBOX_TITLE_BLOKING_TASK", ng-model="task")
|
div.lightbox.lightbox-block(tg-lb-block, ng-model="task", title="TASK.LIGHTBOX_TITLE_BLOKING_TASK")
|
||||||
div.lightbox.lightbox-select-user(tg-lb-assignedto)
|
div.lightbox.lightbox-select-user(tg-lb-assignedto)
|
||||||
div.lightbox.lightbox-select-user(tg-lb-watchers)
|
div.lightbox.lightbox-select-user(tg-lb-watchers)
|
||||||
|
|
|
@ -1,19 +1,31 @@
|
||||||
doctype html
|
doctype html
|
||||||
|
|
||||||
div.wrapper(ng-controller="UserStoryDetailController as ctrl",
|
div.wrapper(
|
||||||
ng-init="section='backlog-kanban'")
|
ng-controller="UserStoryDetailController as ctrl",
|
||||||
|
ng-init="section='backlog-kanban'"
|
||||||
|
)
|
||||||
tg-project-menu
|
tg-project-menu
|
||||||
|
|
||||||
div.main.us-detail
|
div.main.us-detail
|
||||||
div.us-detail-header.header-with-actions
|
div.us-detail-header.header-with-actions
|
||||||
include ../includes/components/mainTitle
|
include ../includes/components/mainTitle
|
||||||
.action-buttons
|
.action-buttons
|
||||||
a.button-gray(
|
a.button-gray(
|
||||||
tg-check-permission="view_milestones",
|
href=""
|
||||||
href="", title="{{'US.TITLE_LINK_TASKBOARD' | translate}}",
|
tg-check-permission="view_milestones"
|
||||||
tg-nav="project-taskboard:project=project.slug,sprint=sprint.slug",
|
tg-nav="project-taskboard:project=project.slug,sprint=sprint.slug"
|
||||||
ng-if="sprint && project.is_backlog_activated", translate="US.LINK_TASKBOARD")
|
ng-if="sprint && project.is_backlog_activated"
|
||||||
|
title="{{'US.TITLE_LINK_TASKBOARD' | translate}}"
|
||||||
|
translate="US.LINK_TASKBOARD"
|
||||||
|
)
|
||||||
|
|
||||||
section.us-story-main-data
|
section.us-story-main-data
|
||||||
|
header
|
||||||
|
tg-vote-button.upvote-btn(
|
||||||
|
item="us"
|
||||||
|
on-upvote="ctrl.onUpvote"
|
||||||
|
on-downvote="ctrl.onDownvote"
|
||||||
|
)
|
||||||
div.us-title(ng-class="{blocked: us.is_blocked}")
|
div.us-title(ng-class="{blocked: us.is_blocked}")
|
||||||
h2.us-title-text
|
h2.us-title-text
|
||||||
span.us-number(tg-bo-ref="us.ref")
|
span.us-number(tg-bo-ref="us.ref")
|
||||||
|
@ -21,60 +33,120 @@ div.wrapper(ng-controller="UserStoryDetailController as ctrl",
|
||||||
|
|
||||||
p.us-related-task(ng-if="us.origin_issue")
|
p.us-related-task(ng-if="us.origin_issue")
|
||||||
| {{ 'US.PROMOTED'|translate }}
|
| {{ 'US.PROMOTED'|translate }}
|
||||||
a(tg-check-permission="view_us", href="", title="{{'US.TITLE_LINK_GO_TO_ISSUE' | translate}}",
|
a(
|
||||||
|
href=""
|
||||||
|
tg-check-permission="view_us"
|
||||||
tg-nav="project-issues-detail:project=project.slug,ref=us.origin_issue.ref"
|
tg-nav="project-issues-detail:project=project.slug,ref=us.origin_issue.ref"
|
||||||
tg-bo-title="'#' + us.origin_issue.ref + ' ' + us.origin_issue.subject")
|
tg-bo-title="'#' + us.origin_issue.ref + ' ' + us.origin_issue.subject"
|
||||||
|
title="{{'US.TITLE_LINK_GO_TO_ISSUE' | translate}}"
|
||||||
|
)
|
||||||
span(tg-bo-ref="us.origin_issue.ref")
|
span(tg-bo-ref="us.origin_issue.ref")
|
||||||
|
|
||||||
p.external-reference(ng-if="us.external_reference")
|
p.external-reference(ng-if="us.external_reference")
|
||||||
| {{ 'US.EXTERNAL_REFERENCE'|translate }}
|
| {{ 'US.EXTERNAL_REFERENCE'|translate }}
|
||||||
a(target="_blank", tg-bo-href="us.external_reference[1]",
|
a(
|
||||||
title="{{'US.GO_TO_EXTERNAL_REFERENCE' | translate}}")
|
tg-bo-href="us.external_reference[1]",
|
||||||
|
title="{{'US.GO_TO_EXTERNAL_REFERENCE' | translate}}"
|
||||||
|
target="_blank"
|
||||||
|
)
|
||||||
span {{ us.external_reference[1] }}
|
span {{ us.external_reference[1] }}
|
||||||
|
|
||||||
p.block-desc-container(ng-show="us.is_blocked")
|
p.block-desc-container(ng-show="us.is_blocked")
|
||||||
span.block-description-title(translate="COMMON.BLOCKED")
|
span.block-description-title(translate="COMMON.BLOCKED")
|
||||||
span.block-description(ng-bind="us.blocked_note || ('US.BLOCKED' | translate)")
|
span.block-description(ng-bind="us.blocked_note || ('US.BLOCKED' | translate)")
|
||||||
div.issue-nav
|
div.issue-nav
|
||||||
a.icon.icon-arrow-left(ng-show="previousUrl", tg-bo-href="previousUrl",
|
a.icon.icon-arrow-left(
|
||||||
title="{{'US.PREVIOUS' | translate}}")
|
ng-show="previousUrl"
|
||||||
a.icon.icon-arrow-right(ng-show="nextUrl", tg-bo-href="nextUrl",
|
tg-bo-href="previousUrl"
|
||||||
title="{{'US.NEXT' | translate}}")
|
title="{{'US.PREVIOUS' | translate}}"
|
||||||
|
)
|
||||||
|
a.icon.icon-arrow-right(
|
||||||
|
ng-show="nextUrl"
|
||||||
|
tg-bo-href="nextUrl"
|
||||||
|
title="{{'US.NEXT' | translate}}"
|
||||||
|
)
|
||||||
|
|
||||||
div.tags-block(tg-tag-line, ng-model="us", required-perm="modify_us")
|
div.tags-block(tg-tag-line, ng-model="us", required-perm="modify_us")
|
||||||
|
|
||||||
section.duty-content(tg-editable-description, ng-model="us", required-perm="modify_us")
|
section.duty-content(tg-editable-description, ng-model="us", required-perm="modify_us")
|
||||||
|
|
||||||
// Custom Fields
|
// Custom Fields
|
||||||
tg-custom-attributes-values(ng-model="us", type="userstory", project="project", required-edition-perm="modify_us")
|
tg-custom-attributes-values(
|
||||||
|
ng-model="us"
|
||||||
|
type="userstory"
|
||||||
|
project="project"
|
||||||
|
required-edition-perm="modify_us"
|
||||||
|
)
|
||||||
|
|
||||||
include ../includes/modules/related-tasks
|
include ../includes/modules/related-tasks
|
||||||
|
|
||||||
tg-attachments(ng-model="us", type="us")
|
tg-attachments(
|
||||||
tg-history(ng-model="us", type="us")
|
ng-model="us"
|
||||||
|
type="us"
|
||||||
|
)
|
||||||
|
tg-history(
|
||||||
|
ng-model="us"
|
||||||
|
type="us"
|
||||||
|
)
|
||||||
|
|
||||||
sidebar.menu-secondary.sidebar
|
sidebar.menu-secondary.sidebar.ticket-data
|
||||||
section.us-status
|
section
|
||||||
h1(tg-us-status-display, ng-model="us")
|
div.ticket-title(
|
||||||
div.us-detail-progress-bar(tg-us-tasks-progress-display, ng-model="tasks")
|
tg-us-status-display
|
||||||
tg-created-by-display.us-created-by(ng-model="us")
|
ng-model="us"
|
||||||
|
)
|
||||||
|
|
||||||
|
tg-created-by-display.ticket-created-by(ng-model="us")
|
||||||
|
|
||||||
|
//div.ticket-detail-progress-bar(tg-us-tasks-progress-display, ng-model="tasks")
|
||||||
|
|
||||||
|
div.ticket-data-container
|
||||||
|
div.ticket-status(
|
||||||
|
tg-us-status-button
|
||||||
|
ng-model="us"
|
||||||
|
)
|
||||||
|
|
||||||
|
section.ticket-estimation
|
||||||
tg-us-estimation(ng-model="us")
|
tg-us-estimation(ng-model="us")
|
||||||
div.duty-data-container
|
|
||||||
div.duty-data(tg-us-status-button, ng-model="us")
|
|
||||||
|
|
||||||
section.duty-assigned-to(tg-assigned-to, ng-model="us", required-perm="modify_us")
|
section.ticket-assigned-to(
|
||||||
|
tg-assigned-to
|
||||||
|
ng-model="us"
|
||||||
|
required-perm="modify_us"
|
||||||
|
)
|
||||||
|
|
||||||
section.watchers(tg-watchers, ng-model="us", required-perm="modify_us")
|
section.track-buttons-container.ticket-track-buttons
|
||||||
|
div.watch-button
|
||||||
|
tg-watch-button(
|
||||||
|
item="us"
|
||||||
|
on-watch="ctrl.onWatch"
|
||||||
|
on-unwatch="ctrl.onUnwatch"
|
||||||
|
)
|
||||||
|
|
||||||
section.us-detail-settings
|
div.ticket-watchers(
|
||||||
|
tg-watchers
|
||||||
|
ng-model="us"
|
||||||
|
required-perm="modify_us"
|
||||||
|
)
|
||||||
|
|
||||||
|
section.ticket-detail-settings
|
||||||
tg-us-team-requirement-button(ng-model="us")
|
tg-us-team-requirement-button(ng-model="us")
|
||||||
tg-us-client-requirement-button(ng-model="us")
|
tg-us-client-requirement-button(ng-model="us")
|
||||||
tg-block-button(tg-check-permission="modify_us", ng-model="us")
|
tg-block-button(
|
||||||
tg-delete-button(tg-check-permission="delete_us",
|
tg-check-permission="modify_us"
|
||||||
on-delete-title="{{'Delete User Story' | translate}}",
|
ng-model="us"
|
||||||
on-delete-go-to-url="onDeleteGoToUrl",
|
)
|
||||||
ng-model="us")
|
tg-delete-button(
|
||||||
|
tg-check-permission="delete_us"
|
||||||
|
on-delete-title="{{'Delete User Story' | translate}}"
|
||||||
|
on-delete-go-to-url="onDeleteGoToUrl"
|
||||||
|
ng-model="us"
|
||||||
|
)
|
||||||
|
|
||||||
div.lightbox.lightbox-block(tg-lb-block, title="{{ 'US.LIGHTBOX_TITLE_BLOKING_US' | translate }}", ng-model="us")
|
div.lightbox.lightbox-block(
|
||||||
|
tg-lb-block
|
||||||
|
title="{{ 'US.LIGHTBOX_TITLE_BLOKING_US' | translate }}"
|
||||||
|
ng-model="us"
|
||||||
|
)
|
||||||
div.lightbox.lightbox-select-user(tg-lb-assignedto)
|
div.lightbox.lightbox-select-user(tg-lb-assignedto)
|
||||||
div.lightbox.lightbox-select-user(tg-lb-watchers)
|
div.lightbox.lightbox-select-user(tg-lb-watchers)
|
||||||
|
|
Loading…
Reference in New Issue