Generic lightbox form to create/edit us, tasks and issues
parent
c2f69429df
commit
90fbaaee0e
|
@ -541,7 +541,12 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
|
|||
|
||||
return @rs.userstories.getByRef(projectId, ref).then (us) =>
|
||||
@rs2.attachments.list("us", us.id, projectId).then (attachments) =>
|
||||
@rootscope.$broadcast("usform:edit", us, attachments.toJS())
|
||||
@rootscope.$broadcast("genericform:edit", {
|
||||
'objType': 'us',
|
||||
'statusList': @scope.usStatusList,
|
||||
'obj': us,
|
||||
'attachments': attachments.toJS()
|
||||
})
|
||||
currentLoading.finish()
|
||||
|
||||
deleteUserStory: (us) ->
|
||||
|
@ -566,8 +571,12 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
|
|||
|
||||
addNewUs: (type) ->
|
||||
switch type
|
||||
when "standard" then @rootscope.$broadcast("usform:new", @scope.projectId,
|
||||
@scope.project.default_us_status, @scope.usStatusList)
|
||||
when "standard" then @rootscope.$broadcast("genericform:new",
|
||||
{
|
||||
'objType': 'us',
|
||||
'project': @scope.project,
|
||||
'statusList': @scope.usStatusList
|
||||
})
|
||||
when "bulk" then @rootscope.$broadcast("usform:bulk", @scope.projectId,
|
||||
@scope.project.default_us_status)
|
||||
|
||||
|
@ -575,13 +584,13 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
|
|||
@rootscope.$broadcast("sprintform:create", @scope.projectId)
|
||||
|
||||
findCurrentSprint: () ->
|
||||
currentDate = new Date().getTime()
|
||||
currentDate = new Date().getTime()
|
||||
|
||||
return _.find @scope.sprints, (sprint) ->
|
||||
start = moment(sprint.estimated_start, 'YYYY-MM-DD').format('x')
|
||||
end = moment(sprint.estimated_finish, 'YYYY-MM-DD').format('x')
|
||||
return _.find @scope.sprints, (sprint) ->
|
||||
start = moment(sprint.estimated_start, 'YYYY-MM-DD').format('x')
|
||||
end = moment(sprint.estimated_finish, 'YYYY-MM-DD').format('x')
|
||||
|
||||
return currentDate >= start && currentDate <= end
|
||||
return currentDate >= start && currentDate <= end
|
||||
|
||||
module.controller("BacklogController", BacklogController)
|
||||
|
||||
|
|
|
@ -525,6 +525,266 @@ module.directive("tgAssignedTo", ["$rootScope", "$tgConfirm", "$tgRepo", "$tgLoa
|
|||
AssignedToDirective])
|
||||
|
||||
|
||||
|
||||
#############################################################################
|
||||
## Assigned to (inline) directive
|
||||
#############################################################################
|
||||
|
||||
AssignedToInlineDirective = ($rootscope, $confirm, $repo, $loading, $modelTransform, $template
|
||||
$translate, $compile, $currentUserService, avatarService) ->
|
||||
link = ($scope, $el, $attrs, $model) ->
|
||||
isEditable = ->
|
||||
return $scope.project?.my_permissions?.indexOf($attrs.requiredPerm) != -1
|
||||
|
||||
normalizeString = (string) ->
|
||||
normalizedString = string
|
||||
normalizedString = normalizedString.replace("Á", "A").replace("Ä", "A").replace("À", "A")
|
||||
normalizedString = normalizedString.replace("É", "E").replace("Ë", "E").replace("È", "E")
|
||||
normalizedString = normalizedString.replace("Í", "I").replace("Ï", "I").replace("Ì", "I")
|
||||
normalizedString = normalizedString.replace("Ó", "O").replace("Ö", "O").replace("Ò", "O")
|
||||
normalizedString = normalizedString.replace("Ú", "U").replace("Ü", "U").replace("Ù", "U")
|
||||
return normalizedString
|
||||
|
||||
filterUsers = (text, user) ->
|
||||
username = user.full_name_display.toUpperCase()
|
||||
username = normalizeString(username)
|
||||
text = text.toUpperCase()
|
||||
text = normalizeString(text)
|
||||
return _.includes(username, text)
|
||||
|
||||
renderUserlist = (text) ->
|
||||
users = _.clone($scope.activeUsers, true)
|
||||
users = _.reject(users, {"id": $scope.selected.id}) if $scope.selected?
|
||||
users = _.sortBy(users, (o) -> if o.id is $scope.user.id then 0 else o.id)
|
||||
users = _.filter(users, _.partial(filterUsers, text)) if text?
|
||||
|
||||
visibleUsers = _.slice(users, 0, 5)
|
||||
visibleUsers = _.map visibleUsers, (user) -> user.avatar = avatarService.getAvatar(user)
|
||||
|
||||
$scope.users = _.slice(users, 0, 5)
|
||||
$scope.showMore = users.length > 5
|
||||
|
||||
renderUser = (assignedObject) ->
|
||||
if assignedObject?.assigned_to
|
||||
$scope.selected = assignedObject.assigned_to
|
||||
assignedObject.assigned_to_extra_info = $scope.usersById[$scope.selected]
|
||||
$scope.fullName = assignedObject.assigned_to_extra_info?.full_name_display
|
||||
$scope.isUnassigned = false
|
||||
$scope.avatar = avatarService.getAvatar(assignedObject.assigned_to_extra_info)
|
||||
$scope.bg = $scope.avatar.bg
|
||||
$scope.isIocaine = assignedObject?.is_iocaine
|
||||
else
|
||||
$scope.fullName = $translate.instant("COMMON.ASSIGNED_TO.ASSIGN")
|
||||
$scope.isUnassigned = true
|
||||
$scope.avatar = avatarService.getAvatar(null)
|
||||
$scope.bg = null
|
||||
$scope.isIocaine = false
|
||||
|
||||
$scope.fullNameVisible = !($scope.isUnassigned && !$currentUserService.isAuthenticated())
|
||||
$scope.isEditable = isEditable()
|
||||
|
||||
$el.on "click", ".users-dropdown", (event) ->
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
renderUserlist()
|
||||
$scope.$apply()
|
||||
$el.find(".pop-users").popover().open()
|
||||
|
||||
$el.on "click", ".users-search", (event) ->
|
||||
event.stopPropagation()
|
||||
|
||||
$el.on "click", ".assign-to-me", (event) ->
|
||||
event.preventDefault()
|
||||
return if not isEditable()
|
||||
$model.$modelValue.assigned_to = $currentUserService.getUser().get('id')
|
||||
renderUser($model.$modelValue)
|
||||
$scope.$apply()
|
||||
|
||||
$el.on "click", ".remove-user", (event) ->
|
||||
event.preventDefault()
|
||||
return if not isEditable()
|
||||
$model.$modelValue.assigned_to = null
|
||||
renderUser()
|
||||
$scope.$apply()
|
||||
|
||||
$scope.$watch "usersSearch", (searchingText) ->
|
||||
if searchingText?
|
||||
renderUserlist(searchingText)
|
||||
$el.find('input').focus()
|
||||
|
||||
$el.on "click", ".user-list-single", (event) ->
|
||||
event.preventDefault()
|
||||
target = angular.element(event.currentTarget)
|
||||
$model.$modelValue.assigned_to = target.data("user-id")
|
||||
renderUser($model.$modelValue)
|
||||
$scope.$apply()
|
||||
|
||||
$scope.$watch $attrs.ngModel, (instance) ->
|
||||
renderUser(instance)
|
||||
|
||||
$scope.$on "isiocaine:changed", (ctx, instance) ->
|
||||
renderUser(instance)
|
||||
|
||||
$scope.$on "$destroy", ->
|
||||
$el.off()
|
||||
|
||||
return {
|
||||
link:link,
|
||||
require:"ngModel",
|
||||
templateUrl: "common/components/assigned-to-inline.html"
|
||||
}
|
||||
|
||||
module.directive("tgAssignedToInline", ["$rootScope", "$tgConfirm", "$tgRepo", "$tgLoading"
|
||||
"$tgQueueModelTransformation", "$tgTemplate", "$translate", "$compile","tgCurrentUserService"
|
||||
"tgAvatarService", AssignedToInlineDirective])
|
||||
|
||||
|
||||
#############################################################################
|
||||
## Assigned users (inline) directive
|
||||
#############################################################################
|
||||
|
||||
AssignedUsersInlineDirective = ($rootscope, $confirm, $repo, $loading, $modelTransform, $template
|
||||
$translate, $compile, $currentUserService, avatarService) ->
|
||||
link = ($scope, $el, $attrs, $model) ->
|
||||
currentAssignedIds = []
|
||||
currentAssignedTo = null
|
||||
|
||||
isAssigned = ->
|
||||
return currentAssignedIds.length > 0
|
||||
|
||||
normalizeString = (string) ->
|
||||
normalizedString = string
|
||||
normalizedString = normalizedString.replace("Á", "A").replace("Ä", "A").replace("À", "A")
|
||||
normalizedString = normalizedString.replace("É", "E").replace("Ë", "E").replace("È", "E")
|
||||
normalizedString = normalizedString.replace("Í", "I").replace("Ï", "I").replace("Ì", "I")
|
||||
normalizedString = normalizedString.replace("Ó", "O").replace("Ö", "O").replace("Ò", "O")
|
||||
normalizedString = normalizedString.replace("Ú", "U").replace("Ü", "U").replace("Ù", "U")
|
||||
return normalizedString
|
||||
|
||||
filterUsers = (text, user) ->
|
||||
username = user.full_name_display.toUpperCase()
|
||||
username = normalizeString(username)
|
||||
text = text.toUpperCase()
|
||||
text = normalizeString(text)
|
||||
return _.includes(username, text)
|
||||
|
||||
renderUsersList = (text) ->
|
||||
users = _.clone($scope.activeUsers, true)
|
||||
users = _.sortBy(users, (o) -> if o.id is $scope.user.id then 0 else o.id)
|
||||
users = _.filter(users, _.partial(filterUsers, text)) if text?
|
||||
|
||||
# Add selected users
|
||||
selected = []
|
||||
_.map users, (user) ->
|
||||
if user.id in currentAssignedIds
|
||||
user.avatar = avatarService.getAvatar(user)
|
||||
selected.push(user)
|
||||
|
||||
# Filter users in searchs
|
||||
visible = []
|
||||
_.map users, (user) ->
|
||||
if user.id not in currentAssignedIds
|
||||
user.avatar = avatarService.getAvatar(user)
|
||||
visible.push(user)
|
||||
|
||||
$scope.selected = _.slice(selected, 0, 5)
|
||||
if $scope.selected.length < 5
|
||||
$scope.users = _.slice(visible, 0, 5 - $scope.selected.length)
|
||||
else
|
||||
$scope.users = []
|
||||
$scope.showMore = users.length > 5
|
||||
|
||||
renderUsers = () ->
|
||||
assignedUsers = _.map(currentAssignedIds, (assignedUserId) -> $scope.usersById[assignedUserId])
|
||||
assignedUsers = _.filter assignedUsers, (it) -> return !!it
|
||||
|
||||
$scope.hiddenUsers = if currentAssignedIds.length > 3 then currentAssignedIds.length - 3 else 0
|
||||
$scope.assignedUsers = _.slice(assignedUsers, 0, 3)
|
||||
|
||||
$scope.isAssigned = isAssigned()
|
||||
|
||||
applyToModel = () ->
|
||||
_.map currentAssignedIds, (userId) ->
|
||||
if !$scope.usersById[userId]
|
||||
currentAssignedIds.splice(currentAssignedIds.indexOf(userId), 1)
|
||||
if currentAssignedIds.length == 0
|
||||
currentAssignedTo = null
|
||||
else if currentAssignedIds.indexOf(currentAssignedTo) == -1 || !currentAssignedTo
|
||||
currentAssignedTo = currentAssignedIds[0]
|
||||
$model.$modelValue.setAttr('assigned_users', currentAssignedIds)
|
||||
$model.$modelValue.assigned_to = currentAssignedTo
|
||||
|
||||
$el.on "click", ".users-dropdown", (event) ->
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
renderUsersList()
|
||||
$scope.$apply()
|
||||
$el.find(".pop-users").popover().open()
|
||||
|
||||
$el.on "click", ".users-search", (event) ->
|
||||
event.stopPropagation()
|
||||
|
||||
$el.on "click", ".assign-to-me", (event) ->
|
||||
event.preventDefault()
|
||||
currentAssignedIds.push($currentUserService.getUser().get('id'))
|
||||
renderUsers()
|
||||
applyToModel()
|
||||
$scope.usersSearch = null
|
||||
$scope.$apply()
|
||||
|
||||
$scope.$watch "usersSearch", (searchingText) ->
|
||||
if searchingText?
|
||||
renderUsersList(searchingText)
|
||||
$el.find('input').focus()
|
||||
|
||||
$el.on "click", ".user-list-single", (event) ->
|
||||
event.preventDefault()
|
||||
target = angular.element(event.currentTarget)
|
||||
index = currentAssignedIds.indexOf(target.data("user-id"))
|
||||
if index == -1
|
||||
currentAssignedIds.push(target.data("user-id"))
|
||||
else
|
||||
currentAssignedIds.splice(index, 1)
|
||||
renderUsers()
|
||||
applyToModel()
|
||||
$el.find(".pop-users").popover().close()
|
||||
$scope.usersSearch = null
|
||||
$scope.$apply()
|
||||
|
||||
$el.on "click", ".remove-user", (event) ->
|
||||
event.preventDefault()
|
||||
target = angular.element(event.currentTarget)
|
||||
index = currentAssignedIds.indexOf(target.data("user-id"))
|
||||
if index > -1
|
||||
currentAssignedIds.splice(index, 1)
|
||||
renderUsers()
|
||||
applyToModel()
|
||||
$scope.$apply()
|
||||
|
||||
$scope.$watch $attrs.ngModel, (item) ->
|
||||
return if not item?
|
||||
currentAssignedIds = []
|
||||
assigned_to = null
|
||||
|
||||
if item.assigned_users?
|
||||
currentAssignedIds = item.assigned_users
|
||||
assigned_to = item.assigned_to
|
||||
renderUsers()
|
||||
|
||||
$scope.$on "$destroy", ->
|
||||
$el.off()
|
||||
|
||||
return {
|
||||
link:link,
|
||||
require: "ngModel",
|
||||
templateUrl: "common/components/assigned-users-inline.html"
|
||||
}
|
||||
|
||||
module.directive("tgAssignedUsersInline", ["$rootScope", "$tgConfirm", "$tgRepo",
|
||||
"$tgLoading", "$tgQueueModelTransformation", "$tgTemplate", "$translate", "$compile",
|
||||
"tgCurrentUserService", "tgAvatarService", AssignedUsersInlineDirective])
|
||||
|
||||
|
||||
#############################################################################
|
||||
## Block Button directive
|
||||
#############################################################################
|
||||
|
|
|
@ -294,237 +294,6 @@ BlockingMessageInputDirective = ($log, $template, $compile) ->
|
|||
module.directive("tgBlockingMessageInput", ["$log", "$tgTemplate", "$compile", BlockingMessageInputDirective])
|
||||
|
||||
|
||||
#############################################################################
|
||||
## Create/Edit Userstory Lightbox Directive
|
||||
#############################################################################
|
||||
|
||||
CreateEditUserstoryDirective = ($repo, $model, $rs, $rootScope, lightboxService, $loading, $translate, $confirm, $q, attachmentsService) ->
|
||||
link = ($scope, $el, attrs) ->
|
||||
form = null
|
||||
$scope.createEditUs = {}
|
||||
$scope.isNew = true
|
||||
|
||||
attachmentsToAdd = Immutable.List()
|
||||
attachmentsToDelete = Immutable.List()
|
||||
|
||||
resetAttachments = () ->
|
||||
attachmentsToAdd = Immutable.List()
|
||||
attachmentsToDelete = Immutable.List()
|
||||
|
||||
$scope.addAttachment = (attachment) ->
|
||||
attachmentsToAdd = attachmentsToAdd.push(attachment)
|
||||
|
||||
$scope.deleteAttachment = (attachment) ->
|
||||
attachmentsToAdd = attachmentsToAdd.filter (it) ->
|
||||
return it.get('name') != attachment.get('name')
|
||||
|
||||
if attachment.get("id")
|
||||
attachmentsToDelete = attachmentsToDelete.push(attachment)
|
||||
|
||||
$scope.addTag = (tag, color) ->
|
||||
value = trim(tag.toLowerCase())
|
||||
|
||||
tags = $scope.project.tags
|
||||
projectTags = $scope.project.tags_colors
|
||||
|
||||
tags = [] if not tags?
|
||||
projectTags = {} if not projectTags?
|
||||
|
||||
if value not in tags
|
||||
tags.push(value)
|
||||
|
||||
projectTags[tag] = color || null
|
||||
|
||||
$scope.project.tags = tags
|
||||
|
||||
itemtags = _.clone($scope.us.tags)
|
||||
|
||||
inserted = _.find itemtags, (it) -> it[0] == value
|
||||
|
||||
if !inserted
|
||||
itemtags.push([value , color])
|
||||
$scope.us.tags = itemtags
|
||||
|
||||
$scope.deleteTag = (tag) ->
|
||||
value = trim(tag[0].toLowerCase())
|
||||
|
||||
tags = $scope.project.tags
|
||||
itemtags = _.clone($scope.us.tags)
|
||||
|
||||
_.remove itemtags, (tag) -> tag[0] == value
|
||||
|
||||
$scope.us.tags = itemtags
|
||||
|
||||
_.pull($scope.us.tags, value)
|
||||
|
||||
$scope.$on "usform:new", (ctx, projectId, status, statusList) ->
|
||||
form.reset() if form
|
||||
$scope.isNew = true
|
||||
$scope.usStatusList = statusList
|
||||
$scope.attachments = Immutable.List()
|
||||
|
||||
resetAttachments()
|
||||
|
||||
$scope.us = $model.make_model("userstories", {
|
||||
project: projectId
|
||||
points : {}
|
||||
status: status
|
||||
is_archived: false
|
||||
tags: []
|
||||
subject: ""
|
||||
description: ""
|
||||
})
|
||||
|
||||
# Update texts for creation
|
||||
$el.find(".button-green").html($translate.instant("COMMON.CREATE"))
|
||||
$el.find(".title").html($translate.instant("LIGHTBOX.CREATE_EDIT_US.NEW_US"))
|
||||
$el.find(".tag-input").val("")
|
||||
|
||||
$el.find(".blocked-note").addClass("hidden")
|
||||
$el.find("label.blocked").removeClass("selected")
|
||||
$el.find("label.team-requirement").removeClass("selected")
|
||||
$el.find("label.client-requirement").removeClass("selected")
|
||||
|
||||
$scope.createEditUsOpen = true
|
||||
|
||||
lightboxService.open $el, () ->
|
||||
$scope.createEditUsOpen = false
|
||||
|
||||
$scope.$on "usform:edit", (ctx, us, attachments) ->
|
||||
form.reset() if form
|
||||
|
||||
$scope.us = us
|
||||
$scope.attachments = Immutable.fromJS(attachments)
|
||||
$scope.isNew = false
|
||||
|
||||
resetAttachments()
|
||||
|
||||
# Update texts for edition
|
||||
$el.find(".button-green").html($translate.instant("COMMON.SAVE"))
|
||||
$el.find(".title").html($translate.instant("LIGHTBOX.CREATE_EDIT_US.EDIT_US"))
|
||||
$el.find(".tag-input").val("")
|
||||
|
||||
# Update requirement info (team, client or blocked)
|
||||
if us.is_blocked
|
||||
$el.find(".blocked-note").removeClass("hidden")
|
||||
$el.find("label.blocked").addClass("selected")
|
||||
else
|
||||
$el.find(".blocked-note").addClass("hidden")
|
||||
$el.find("label.blocked").removeClass("selected")
|
||||
|
||||
if us.team_requirement
|
||||
$el.find("label.team-requirement").addClass("selected")
|
||||
else
|
||||
$el.find("label.team-requirement").removeClass("selected")
|
||||
if us.client_requirement
|
||||
$el.find("label.client-requirement").addClass("selected")
|
||||
else
|
||||
$el.find("label.client-requirement").removeClass("selected")
|
||||
|
||||
$scope.createEditUsOpen = true
|
||||
|
||||
lightboxService.open $el, () ->
|
||||
$scope.createEditUsOpen = false
|
||||
|
||||
createAttachments = (obj) ->
|
||||
promises = _.map attachmentsToAdd.toJS(), (attachment) ->
|
||||
attachmentsService.upload(attachment.file, obj.id, $scope.us.project, 'us')
|
||||
|
||||
return $q.all(promises)
|
||||
|
||||
deleteAttachments = (obj) ->
|
||||
promises = _.map attachmentsToDelete.toJS(), (attachment) ->
|
||||
return attachmentsService.delete("us", attachment.id)
|
||||
|
||||
return $q.all(promises)
|
||||
|
||||
submit = debounce 2000, (event) =>
|
||||
event.preventDefault()
|
||||
|
||||
form = $el.find("form").checksley()
|
||||
if not form.validate()
|
||||
return
|
||||
|
||||
currentLoading = $loading()
|
||||
.target(submitButton)
|
||||
.start()
|
||||
|
||||
params = {
|
||||
include_attachments: true,
|
||||
include_tasks: true
|
||||
}
|
||||
|
||||
if $scope.isNew
|
||||
promise = $repo.create("userstories", $scope.us)
|
||||
broadcastEvent = "usform:new:success"
|
||||
else
|
||||
promise = $repo.save($scope.us, true)
|
||||
broadcastEvent = "usform:edit:success"
|
||||
|
||||
promise.then (data) ->
|
||||
deleteAttachments(data)
|
||||
.then () => createAttachments(data)
|
||||
.then () =>
|
||||
currentLoading.finish()
|
||||
lightboxService.close($el)
|
||||
|
||||
$rs.userstories.getByRef(data.project, data.ref, params).then (us) ->
|
||||
$rootScope.$broadcast(broadcastEvent, us)
|
||||
|
||||
|
||||
promise.then null, (data) ->
|
||||
currentLoading.finish()
|
||||
form.setErrors(data)
|
||||
if data._error_message
|
||||
$confirm.notify("error", data._error_message)
|
||||
|
||||
submitButton = $el.find(".submit-button")
|
||||
|
||||
close = () =>
|
||||
if !$scope.us.isModified()
|
||||
lightboxService.close($el)
|
||||
$scope.$apply ->
|
||||
$scope.us.revert()
|
||||
else
|
||||
$confirm.ask($translate.instant("LIGHTBOX.CREATE_EDIT_US.CONFIRM_CLOSE")).then (result) ->
|
||||
lightboxService.close($el)
|
||||
$scope.us.revert()
|
||||
result.finish()
|
||||
|
||||
$el.on "submit", "form", submit
|
||||
|
||||
$el.find('.close').on "click", (event) ->
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
close()
|
||||
|
||||
$el.keydown (event) ->
|
||||
event.stopPropagation()
|
||||
code = if event.keyCode then event.keyCode else event.which
|
||||
if code == 27
|
||||
close()
|
||||
|
||||
$scope.$on "$destroy", ->
|
||||
$el.find('.close').off()
|
||||
$el.off()
|
||||
|
||||
return {link: link}
|
||||
|
||||
module.directive("tgLbCreateEditUserstory", [
|
||||
"$tgRepo",
|
||||
"$tgModel",
|
||||
"$tgResources",
|
||||
"$rootScope",
|
||||
"lightboxService",
|
||||
"$tgLoading",
|
||||
"$translate",
|
||||
"$tgConfirm",
|
||||
"$q",
|
||||
"tgAttachmentsService"
|
||||
CreateEditUserstoryDirective
|
||||
])
|
||||
|
||||
|
||||
#############################################################################
|
||||
## Creare Bulk Userstories Lightbox Directive
|
||||
#############################################################################
|
||||
|
@ -938,6 +707,17 @@ SetDueDateDirective = (lightboxService, $loading, $translate, $confirm, $modelTr
|
|||
.target($el.find(".submit-button"))
|
||||
.start()
|
||||
|
||||
if $scope.notAutoSave
|
||||
new_due_date = $('.due-date').val()
|
||||
$scope.object.due_date = if (new_due_date) \
|
||||
then moment(new_due_date, prettyDate).format("YYYY-MM-DD") \
|
||||
else null
|
||||
|
||||
$scope.$apply()
|
||||
currentLoading.finish()
|
||||
lightboxService.close($el)
|
||||
return
|
||||
|
||||
transform = $modelTransform.save (object) ->
|
||||
new_due_date = $('.due-date').val()
|
||||
object.due_date = if (new_due_date) \
|
||||
|
@ -968,7 +748,11 @@ SetDueDateDirective = (lightboxService, $loading, $translate, $confirm, $modelTr
|
|||
askResponse.finish()
|
||||
$('.due-date').val(null)
|
||||
$scope.object.due_date_reason = null
|
||||
save()
|
||||
if $scope.notAutoSave
|
||||
$scope.object.due_date = null
|
||||
lightboxService.close($el)
|
||||
else
|
||||
save()
|
||||
|
||||
$el.on "click", ".delete-due-date", (event) ->
|
||||
event.preventDefault()
|
||||
|
@ -982,3 +766,328 @@ SetDueDateDirective = (lightboxService, $loading, $translate, $confirm, $modelTr
|
|||
|
||||
module.directive("tgLbSetDueDate", ["lightboxService", "$tgLoading", "$translate", "$tgConfirm"
|
||||
"$tgQueueModelTransformation", SetDueDateDirective])
|
||||
|
||||
|
||||
|
||||
#############################################################################
|
||||
## Create/Edit Lightbox Directive
|
||||
#############################################################################
|
||||
|
||||
CreateEditDirective = (
|
||||
$log, $repo, $model, $rs, $rootScope, lightboxService, $loading, $translate,
|
||||
$confirm, $q, attachmentsService) ->
|
||||
link = ($scope, $el, attrs) ->
|
||||
form = null
|
||||
schemas = {
|
||||
us: {
|
||||
objName: 'User Story',
|
||||
model: 'userstories',
|
||||
params: { include_attachments: true, include_tasks: true },
|
||||
requiredAttrs: ['project'],
|
||||
initialData: (data) ->
|
||||
return {
|
||||
project: data.project.id
|
||||
points : {}
|
||||
status: data.project.default_us_status
|
||||
is_archived: false
|
||||
tags: []
|
||||
subject: ""
|
||||
description: ""
|
||||
}
|
||||
}
|
||||
task: {
|
||||
objName: 'Task',
|
||||
model: 'tasks',
|
||||
params: { include_attachments: true },
|
||||
requiredAttrs: ['project', 'sprintId', 'usId'],
|
||||
initialData: (data) ->
|
||||
return {
|
||||
project: data.project.id
|
||||
milestone: data.sprintId
|
||||
user_story: data.usId
|
||||
is_archived: false
|
||||
status: data.project.default_task_status
|
||||
assigned_to: null
|
||||
tags: [],
|
||||
subject: "",
|
||||
description: "",
|
||||
}
|
||||
},
|
||||
issue: {
|
||||
objName: 'Issue',
|
||||
model: 'issues',
|
||||
params: { include_attachments: true },
|
||||
requiredAttrs: ['project', 'typeList', 'typeById', 'severityList', 'priorityList'],
|
||||
initialData: (data) ->
|
||||
return {
|
||||
project: data.project.id
|
||||
subject: ""
|
||||
status: data.project.default_issue_status
|
||||
type: data.project.default_issue_type
|
||||
priority: data.project.default_priority
|
||||
severity: data.project.default_severity
|
||||
tags: []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
attachmentsToAdd = Immutable.List()
|
||||
attachmentsToDelete = Immutable.List()
|
||||
|
||||
$scope.$on "genericform:new", (ctx, data) ->
|
||||
if beforeMount('new', data, ['objType', 'statusList', ])
|
||||
mountCreateForm(data)
|
||||
afterMount()
|
||||
|
||||
$scope.$on "genericform:edit", (ctx, data) ->
|
||||
if beforeMount('edit', data, ['objType', 'statusList', 'obj', 'attachments'])
|
||||
mountUpdateForm(data)
|
||||
afterMount()
|
||||
|
||||
beforeMount = (mode, data, requiredAttrs) ->
|
||||
form.reset() if form
|
||||
$el.find(".tag-input").val("")
|
||||
|
||||
# Get form schema
|
||||
if !data.objType || !schemas[data.objType]
|
||||
return $log.error(
|
||||
"Invalid objType `#{data.objType}` for `genericform:#{mode}` event")
|
||||
$scope.schema = schemas[data.objType]
|
||||
|
||||
# Get required attrs for creation from objType schema
|
||||
if mode == 'new'
|
||||
requiredAttrs = $scope.schema.requiredAttrs.concat(requiredAttrs)
|
||||
|
||||
# Check if required attrs for creating are present
|
||||
getAttrs(mode, data, requiredAttrs)
|
||||
|
||||
return true
|
||||
|
||||
mountCreateForm = (data) ->
|
||||
$scope.obj = $model.make_model($scope.schema.model, $scope.schema.initialData(data))
|
||||
$scope.isNew = true
|
||||
$scope.attachments = Immutable.List()
|
||||
|
||||
# Update texts for creation
|
||||
$el.find(".button-green").html($translate.instant("COMMON.CREATE"))
|
||||
$el.find(".title").html($translate.instant(
|
||||
"LIGHTBOX.CREATE_EDIT.NEW", { objName: $scope.schema.objName }))
|
||||
$el.find(".blocked-note").addClass("hidden")
|
||||
|
||||
mountUpdateForm = (data) ->
|
||||
$scope.isNew = false
|
||||
$scope.attachments = Immutable.fromJS($scope.attachments)
|
||||
|
||||
# Update texts for edition
|
||||
$el.find(".button-green").html($translate.instant("COMMON.SAVE"))
|
||||
$el.find(".title").html($translate.instant(
|
||||
"LIGHTBOX.CREATE_EDIT.EDIT", { objName: $scope.schema.objName }))
|
||||
|
||||
afterMount = () ->
|
||||
resetAttachments()
|
||||
setStatus($scope.obj.status)
|
||||
$scope.createEditOpen = true
|
||||
lightboxService.open $el, () ->
|
||||
$scope.createEditOpen = false
|
||||
|
||||
getAttrs = (mode, data, attrs) ->
|
||||
for attr in attrs
|
||||
if !data[attr]
|
||||
return $log.error "`#{attr}` attribute required in `genericform:#{mode}` event"
|
||||
$scope[attr] = data[attr]
|
||||
|
||||
resetAttachments = () ->
|
||||
attachmentsToAdd = Immutable.List()
|
||||
attachmentsToDelete = Immutable.List()
|
||||
|
||||
$scope.addAttachment = (attachment) ->
|
||||
attachmentsToAdd = attachmentsToAdd.push(attachment)
|
||||
|
||||
$scope.deleteAttachment = (attachment) ->
|
||||
attachmentsToAdd = attachmentsToAdd.filter (it) ->
|
||||
return it.get('name') != attachment.get('name')
|
||||
|
||||
if attachment.get("id")
|
||||
attachmentsToDelete = attachmentsToDelete.push(attachment)
|
||||
|
||||
$scope.addTag = (tag, color) ->
|
||||
value = trim(tag.toLowerCase())
|
||||
|
||||
tags = $scope.project.tags
|
||||
projectTags = $scope.project.tags_colors
|
||||
|
||||
tags = [] if not tags?
|
||||
projectTags = {} if not projectTags?
|
||||
|
||||
if value not in tags
|
||||
tags.push(value)
|
||||
|
||||
projectTags[tag] = color || null
|
||||
|
||||
$scope.project.tags = tags
|
||||
|
||||
itemtags = _.clone($scope.obj.tags)
|
||||
|
||||
inserted = _.find itemtags, (it) -> it[0] == value
|
||||
|
||||
if !inserted
|
||||
itemtags.push([value , color])
|
||||
$scope.obj.tags = itemtags
|
||||
|
||||
$scope.deleteTag = (tag) ->
|
||||
value = trim(tag[0].toLowerCase())
|
||||
|
||||
tags = $scope.project.tags
|
||||
itemtags = _.clone($scope.obj.tags)
|
||||
|
||||
_.remove itemtags, (tag) -> tag[0] == value
|
||||
|
||||
$scope.obj.tags = itemtags
|
||||
|
||||
_.pull($scope.obj.tags, value)
|
||||
|
||||
|
||||
createAttachments = (obj) ->
|
||||
promises = _.map attachmentsToAdd.toJS(), (attachment) ->
|
||||
attachmentsService.upload(
|
||||
attachment.file, obj.id, $scope.obj.project, $scope.objType)
|
||||
|
||||
return $q.all(promises)
|
||||
|
||||
deleteAttachments = (obj) ->
|
||||
promises = _.map attachmentsToDelete.toJS(), (attachment) ->
|
||||
return attachmentsService.delete($scope.objType, attachment.id)
|
||||
|
||||
return $q.all(promises)
|
||||
|
||||
submit = debounce 2000, (event) ->
|
||||
event.preventDefault()
|
||||
|
||||
form = $el.find("form").checksley()
|
||||
if not form.validate()
|
||||
return
|
||||
|
||||
currentLoading = $loading().target(submitButton).start()
|
||||
|
||||
if $scope.isNew
|
||||
promise = $repo.create($scope.schema.model, $scope.obj)
|
||||
broadcastEvent = "#{$scope.objType}form:new:success"
|
||||
else
|
||||
promise = $repo.save($scope.obj, true)
|
||||
broadcastEvent = "#{$scope.objType}form:edit:success"
|
||||
|
||||
promise.then (data) ->
|
||||
deleteAttachments(data)
|
||||
.then () -> createAttachments(data)
|
||||
.then () ->
|
||||
currentLoading.finish()
|
||||
lightboxService.close($el)
|
||||
|
||||
$rs[$scope.schema.model].getByRef(
|
||||
data.project, data.ref, $scope.schema.params).then (obj) ->
|
||||
$rootScope.$broadcast(broadcastEvent, obj)
|
||||
|
||||
promise.then null, (data) ->
|
||||
currentLoading.finish()
|
||||
form.setErrors(data)
|
||||
if data._error_message
|
||||
$confirm.notify("error", data._error_message)
|
||||
|
||||
submitButton = $el.find(".submit-button")
|
||||
|
||||
close = () ->
|
||||
if !$scope.obj.isModified()
|
||||
lightboxService.close($el)
|
||||
$scope.$apply ->
|
||||
$scope.obj.revert()
|
||||
else
|
||||
$confirm.ask(
|
||||
$translate.instant("LIGHTBOX.CREATE_EDIT.CONFIRM_CLOSE")).then (result) ->
|
||||
lightboxService.close($el)
|
||||
$scope.obj.revert()
|
||||
result.finish()
|
||||
|
||||
$el.on "submit", "form", submit
|
||||
|
||||
$el.find('.close').on "click", (event) ->
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
close()
|
||||
|
||||
$el.keydown (event) ->
|
||||
event.stopPropagation()
|
||||
code = if event.keyCode then event.keyCode else event.which
|
||||
if code == 27
|
||||
close()
|
||||
|
||||
$scope.$on "$destroy", ->
|
||||
$el.find('.close').off()
|
||||
$el.off()
|
||||
|
||||
$scope.$watch "obj", ->
|
||||
if !$scope.obj
|
||||
return
|
||||
setStatus($scope.obj.status)
|
||||
|
||||
$el.on "click", ".status-dropdown", (event) ->
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
$el.find(".pop-status").popover().open()
|
||||
|
||||
$el.on "click", ".status", (event) ->
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
setStatus(angular.element(event.currentTarget).data("status-id"))
|
||||
$scope.$apply()
|
||||
$scope.$broadcast("status:changed", $scope.obj.status)
|
||||
$el.find(".pop-status").popover().close()
|
||||
|
||||
$el.on "click", ".users-dropdown", (event) ->
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
$el.find(".pop-users").popover().open()
|
||||
|
||||
$el.on "click", ".team-requirement", (event) ->
|
||||
$scope.obj.team_requirement = not $scope.obj.team_requirement
|
||||
$scope.$apply()
|
||||
|
||||
$el.on "click", ".client-requirement", (event) ->
|
||||
$scope.obj.client_requirement = not $scope.obj.client_requirement
|
||||
$scope.$apply()
|
||||
|
||||
$el.on "click", ".is-blocked", (event) ->
|
||||
$scope.obj.is_blocked = not $scope.obj.is_blocked
|
||||
$scope.$apply()
|
||||
|
||||
$el.on "click", ".iocaine", (event) ->
|
||||
$scope.obj.is_iocaine = not $scope.obj.is_iocaine
|
||||
$scope.$broadcast("isiocaine:changed", $scope.obj)
|
||||
|
||||
setStatus = (id) ->
|
||||
$scope.obj.status = id
|
||||
$scope.selectedStatus = _.find $scope.statusList, (item) -> item.id == id
|
||||
|
||||
$scope.isTeamRequirement = () ->
|
||||
return $scope.obj?.team_requirement
|
||||
|
||||
$scope.isClientRequirement = () ->
|
||||
return $scope.obj?.client_requirement
|
||||
|
||||
return {
|
||||
link: link
|
||||
}
|
||||
|
||||
module.directive("tgLbCreateEdit", [
|
||||
"$log",
|
||||
"$tgRepo",
|
||||
"$tgModel",
|
||||
"$tgResources",
|
||||
"$rootScope",
|
||||
"lightboxService",
|
||||
"$tgLoading",
|
||||
"$translate",
|
||||
"$tgConfirm",
|
||||
"$q",
|
||||
"tgAttachmentsService"
|
||||
CreateEditDirective
|
||||
])
|
|
@ -355,10 +355,12 @@ IssueTypeButtonDirective = ($rootScope, $repo, $confirm, $loading, $modelTransfo
|
|||
template = $template.get("issue/issue-type-button.html", true)
|
||||
|
||||
link = ($scope, $el, $attrs, $model) ->
|
||||
notAutoSave = $scope.$eval($attrs.notAutoSave)
|
||||
|
||||
isEditable = ->
|
||||
return $scope.project.my_permissions.indexOf("modify_issue") != -1
|
||||
|
||||
render = (issue) =>
|
||||
render = (issue) ->
|
||||
type = $scope.typeById[issue.type]
|
||||
|
||||
html = template({
|
||||
|
@ -374,6 +376,11 @@ IssueTypeButtonDirective = ($rootScope, $repo, $confirm, $loading, $modelTransfo
|
|||
save = (type) ->
|
||||
$.fn.popover().closeAll()
|
||||
|
||||
if notAutoSave
|
||||
$model.$modelValue.type = type
|
||||
$scope.$apply()
|
||||
return
|
||||
|
||||
currentLoading = $loading()
|
||||
.target($el.find(".level-name"))
|
||||
.start()
|
||||
|
@ -445,10 +452,12 @@ IssueSeverityButtonDirective = ($rootScope, $repo, $confirm, $loading, $modelTra
|
|||
template = $template.get("issue/issue-severity-button.html", true)
|
||||
|
||||
link = ($scope, $el, $attrs, $model) ->
|
||||
notAutoSave = $scope.$eval($attrs.notAutoSave)
|
||||
|
||||
isEditable = ->
|
||||
return $scope.project.my_permissions.indexOf("modify_issue") != -1
|
||||
|
||||
render = (issue) =>
|
||||
render = (issue) ->
|
||||
severity = $scope.severityById[issue.severity]
|
||||
|
||||
html = template({
|
||||
|
@ -464,6 +473,11 @@ IssueSeverityButtonDirective = ($rootScope, $repo, $confirm, $loading, $modelTra
|
|||
save = (severity) ->
|
||||
$.fn.popover().closeAll()
|
||||
|
||||
if notAutoSave
|
||||
$model.$modelValue.severity = severity
|
||||
$scope.$apply()
|
||||
return
|
||||
|
||||
currentLoading = $loading()
|
||||
.target($el.find(".level-name"))
|
||||
.start()
|
||||
|
@ -536,10 +550,12 @@ IssuePriorityButtonDirective = ($rootScope, $repo, $confirm, $loading, $modelTra
|
|||
template = $template.get("issue/issue-priority-button.html", true)
|
||||
|
||||
link = ($scope, $el, $attrs, $model) ->
|
||||
notAutoSave = $scope.$eval($attrs.notAutoSave)
|
||||
|
||||
isEditable = ->
|
||||
return $scope.project.my_permissions.indexOf("modify_issue") != -1
|
||||
|
||||
render = (issue) =>
|
||||
render = (issue) ->
|
||||
priority = $scope.priorityById[issue.priority]
|
||||
|
||||
html = template({
|
||||
|
@ -555,6 +571,11 @@ IssuePriorityButtonDirective = ($rootScope, $repo, $confirm, $loading, $modelTra
|
|||
save = (priority) ->
|
||||
$.fn.popover().closeAll()
|
||||
|
||||
if notAutoSave
|
||||
$model.$modelValue.priority = priority
|
||||
$scope.$apply()
|
||||
return
|
||||
|
||||
currentLoading = $loading()
|
||||
.target($el.find(".level-name"))
|
||||
.start()
|
||||
|
|
|
@ -29,131 +29,6 @@ trim = @.taiga.trim
|
|||
|
||||
module = angular.module("taigaIssues")
|
||||
|
||||
#############################################################################
|
||||
## Issue Create Lightbox Directive
|
||||
#############################################################################
|
||||
|
||||
CreateIssueDirective = ($repo, $confirm, $rootscope, lightboxService, $loading, $q, attachmentsService) ->
|
||||
link = ($scope, $el, $attrs) ->
|
||||
form = $el.find("form").checksley()
|
||||
$scope.issue = {}
|
||||
$scope.attachments = Immutable.List()
|
||||
|
||||
$scope.$on "issueform:new", (ctx, project)->
|
||||
form.reset()
|
||||
|
||||
resetAttachments()
|
||||
|
||||
$el.find(".tag-input").val("")
|
||||
lightboxService.open $el, () ->
|
||||
$scope.createIssueOpen = false
|
||||
|
||||
$scope.issue = {
|
||||
project: project.id
|
||||
subject: ""
|
||||
status: project.default_issue_status
|
||||
type: project.default_issue_type
|
||||
priority: project.default_priority
|
||||
severity: project.default_severity
|
||||
tags: []
|
||||
}
|
||||
|
||||
$scope.createIssueOpen = true
|
||||
|
||||
$scope.$on "$destroy", ->
|
||||
$el.off()
|
||||
|
||||
createAttachments = (obj) ->
|
||||
promises = _.map attachmentsToAdd.toJS(), (attachment) ->
|
||||
return attachmentsService.upload(attachment.file, obj.id, $scope.issue.project, 'issue')
|
||||
|
||||
return $q.all(promises)
|
||||
|
||||
attachmentsToAdd = Immutable.List()
|
||||
|
||||
resetAttachments = () ->
|
||||
attachmentsToAdd = Immutable.List()
|
||||
$scope.attachments = Immutable.List()
|
||||
|
||||
$scope.addAttachment = (attachment) ->
|
||||
attachmentsToAdd = attachmentsToAdd.push(attachment)
|
||||
|
||||
$scope.deleteAttachment = (attachment) ->
|
||||
attachmentsToAdd = attachmentsToAdd.filter (it) ->
|
||||
return it.get('name') != attachment.get('name')
|
||||
|
||||
$scope.addTag = (tag, color) ->
|
||||
value = trim(tag.toLowerCase())
|
||||
|
||||
tags = $scope.project.tags
|
||||
projectTags = $scope.project.tags_colors
|
||||
|
||||
tags = [] if not tags?
|
||||
projectTags = {} if not projectTags?
|
||||
|
||||
if value not in tags
|
||||
tags.push(value)
|
||||
|
||||
projectTags[tag] = color || null
|
||||
|
||||
$scope.project.tags = tags
|
||||
|
||||
itemtags = _.clone($scope.issue.tags)
|
||||
|
||||
inserted = _.find itemtags, (it) -> it[0] == value
|
||||
|
||||
if !inserted
|
||||
itemtags.push([tag , color])
|
||||
$scope.issue.tags = itemtags
|
||||
|
||||
$scope.deleteTag = (tag) ->
|
||||
value = trim(tag[0].toLowerCase())
|
||||
|
||||
tags = $scope.project.tags
|
||||
itemtags = _.clone($scope.issue.tags)
|
||||
|
||||
_.remove itemtags, (tag) -> tag[0] == value
|
||||
|
||||
$scope.issue.tags = itemtags
|
||||
|
||||
_.pull($scope.issue.tags, value)
|
||||
|
||||
submit = debounce 2000, (event) =>
|
||||
event.preventDefault()
|
||||
|
||||
if not form.validate()
|
||||
return
|
||||
|
||||
currentLoading = $loading()
|
||||
.target(submitButton)
|
||||
.start()
|
||||
|
||||
promise = $repo.create("issues", $scope.issue)
|
||||
|
||||
promise.then (data) ->
|
||||
return createAttachments(data)
|
||||
|
||||
promise.then (data) ->
|
||||
currentLoading.finish()
|
||||
$rootscope.$broadcast("issueform:new:success", data)
|
||||
lightboxService.close($el)
|
||||
$confirm.notify("success")
|
||||
|
||||
promise.then null, ->
|
||||
currentLoading.finish()
|
||||
$confirm.notify("error")
|
||||
|
||||
submitButton = $el.find(".submit-button")
|
||||
|
||||
$el.on "submit", "form", submit
|
||||
|
||||
|
||||
return {link:link}
|
||||
|
||||
module.directive("tgLbCreateIssue", ["$tgRepo", "$tgConfirm", "$rootScope", "lightboxService", "$tgLoading",
|
||||
"$q", "tgAttachmentsService", CreateIssueDirective])
|
||||
|
||||
|
||||
#############################################################################
|
||||
## Issue Bulk Create Lightbox Directive
|
||||
#############################################################################
|
||||
|
|
|
@ -367,7 +367,16 @@ class IssuesController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
|
|||
|
||||
# Functions used from templates
|
||||
addNewIssue: ->
|
||||
@rootscope.$broadcast("issueform:new", @scope.project)
|
||||
project = @projectService.project.toJS()
|
||||
@rootscope.$broadcast("genericform:new", {
|
||||
'objType': 'issue',
|
||||
'statusList': @scope.issueStatusList,
|
||||
'project': project,
|
||||
'severityList': @scope.severityList,
|
||||
'priorityList': @scope.priorityList,
|
||||
'typeById': groupBy(project.issue_types, (x) -> x.id),
|
||||
'typeList': _.sortBy(project.issue_types, "order")
|
||||
})
|
||||
|
||||
addIssuesInBulk: ->
|
||||
@rootscope.$broadcast("issueform:bulk", @scope.projectId)
|
||||
|
|
|
@ -164,8 +164,12 @@ class KanbanController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
|
|||
|
||||
addNewUs: (type, statusId) ->
|
||||
switch type
|
||||
when "standard" then @rootscope.$broadcast("usform:new",
|
||||
@scope.projectId, statusId, @scope.usStatusList)
|
||||
when "standard" then @rootscope.$broadcast("genericform:new",
|
||||
{
|
||||
'objType': 'us',
|
||||
'project': @scope.project,
|
||||
'statusList': @scope.usStatusList
|
||||
})
|
||||
when "bulk" then @rootscope.$broadcast("usform:bulk",
|
||||
@scope.projectId, statusId)
|
||||
|
||||
|
@ -175,9 +179,15 @@ class KanbanController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
|
|||
@kanbanUserstoriesService.replace(us)
|
||||
|
||||
@rs.userstories.getByRef(us.getIn(['model', 'project']), us.getIn(['model', 'ref']))
|
||||
.then (editingUserStory) =>
|
||||
@rs2.attachments.list("us", us.get('id'), us.getIn(['model', 'project'])).then (attachments) =>
|
||||
@rootscope.$broadcast("usform:edit", editingUserStory, attachments.toJS())
|
||||
.then (editingUserStory) =>
|
||||
@rs2.attachments.list(
|
||||
"us", us.get('id'), us.getIn(['model', 'project'])).then (attachments) =>
|
||||
@rootscope.$broadcast("genericform:edit", {
|
||||
'objType': 'us',
|
||||
'obj': editingUserStory,
|
||||
'statusList': @scope.usStatusList,
|
||||
'attachments': attachments.toJS()
|
||||
})
|
||||
|
||||
us = us.set('loading-edit', false)
|
||||
@kanbanUserstoriesService.replace(us)
|
||||
|
|
|
@ -27,196 +27,6 @@ bindOnce = @.taiga.bindOnce
|
|||
debounce = @.taiga.debounce
|
||||
trim = @.taiga.trim
|
||||
|
||||
CreateEditTaskDirective = ($repo, $model, $rs, $rootscope, $loading, lightboxService, $translate, $q, $confirm, attachmentsService) ->
|
||||
link = ($scope, $el, attrs) ->
|
||||
$scope.isNew = true
|
||||
|
||||
attachmentsToAdd = Immutable.List()
|
||||
attachmentsToDelete = Immutable.List()
|
||||
|
||||
resetAttachments = () ->
|
||||
attachmentsToAdd = Immutable.List()
|
||||
attachmentsToDelete = Immutable.List()
|
||||
|
||||
$scope.addAttachment = (attachment) ->
|
||||
attachmentsToAdd = attachmentsToAdd.push(attachment)
|
||||
|
||||
$scope.deleteAttachment = (attachment) ->
|
||||
attachmentsToAdd = attachmentsToAdd.filter (it) ->
|
||||
return it.get('name') != attachment.get('name')
|
||||
|
||||
if attachment.get("id")
|
||||
attachmentsToDelete = attachmentsToDelete.push(attachment)
|
||||
|
||||
createAttachments = (obj) ->
|
||||
promises = _.map attachmentsToAdd.toJS(), (attachment) ->
|
||||
attachmentsService.upload(attachment.file, obj.id, $scope.task.project, 'task')
|
||||
|
||||
return $q.all(promises)
|
||||
|
||||
deleteAttachments = (obj) ->
|
||||
promises = _.map attachmentsToDelete.toJS(), (attachment) ->
|
||||
return attachmentsService.delete("task", attachment.id)
|
||||
|
||||
return $q.all(promises)
|
||||
|
||||
tagsToAdd = []
|
||||
|
||||
$scope.addTag = (tag, color) ->
|
||||
value = trim(tag.toLowerCase())
|
||||
|
||||
tags = $scope.project.tags
|
||||
projectTags = $scope.project.tags_colors
|
||||
|
||||
tags = [] if not tags?
|
||||
projectTags = {} if not projectTags?
|
||||
|
||||
if value not in tags
|
||||
tags.push(value)
|
||||
|
||||
projectTags[tag] = color || null
|
||||
|
||||
$scope.project.tags = tags
|
||||
|
||||
itemtags = _.clone($scope.task.tags)
|
||||
|
||||
inserted = _.find itemtags, (it) -> it[0] == value
|
||||
|
||||
if !inserted
|
||||
itemtags.push([tag , color])
|
||||
$scope.task.tags = itemtags
|
||||
|
||||
|
||||
$scope.deleteTag = (tag) ->
|
||||
value = trim(tag[0].toLowerCase())
|
||||
|
||||
tags = $scope.project.tags
|
||||
itemtags = _.clone($scope.task.tags)
|
||||
|
||||
_.remove itemtags, (tag) -> tag[0] == value
|
||||
|
||||
$scope.task.tags = itemtags
|
||||
|
||||
_.pull($scope.task.tags, value)
|
||||
|
||||
$scope.$on "taskform:new", (ctx, sprintId, usId) ->
|
||||
$scope.task = $model.make_model('tasks', {
|
||||
project: $scope.projectId
|
||||
milestone: sprintId
|
||||
user_story: usId
|
||||
is_archived: false
|
||||
status: $scope.project.default_task_status
|
||||
assigned_to: null
|
||||
tags: [],
|
||||
subject: "",
|
||||
description: "",
|
||||
})
|
||||
$scope.isNew = true
|
||||
$scope.attachments = Immutable.List()
|
||||
|
||||
resetAttachments()
|
||||
|
||||
# Update texts for creation
|
||||
create = $translate.instant("COMMON.CREATE")
|
||||
$el.find(".button-green").html(create)
|
||||
|
||||
newTask = $translate.instant("LIGHTBOX.CREATE_EDIT_TASK.TITLE")
|
||||
$el.find(".title").html(newTask + " ")
|
||||
|
||||
$el.find(".tag-input").val("")
|
||||
lightboxService.open $el, () ->
|
||||
$scope.createEditTaskOpen = false
|
||||
|
||||
$scope.createEditTaskOpen = true
|
||||
|
||||
$scope.$on "taskform:edit", (ctx, task, attachments) ->
|
||||
$scope.task = task
|
||||
$scope.isNew = false
|
||||
|
||||
$scope.attachments = Immutable.fromJS(attachments)
|
||||
|
||||
resetAttachments()
|
||||
|
||||
# Update texts for edition
|
||||
save = $translate.instant("COMMON.SAVE")
|
||||
edit = $translate.instant("LIGHTBOX.CREATE_EDIT_TASK.ACTION_EDIT")
|
||||
|
||||
$el.find(".button-green").html(save)
|
||||
$el.find(".title").html(edit + " ")
|
||||
|
||||
$el.find(".tag-input").val("")
|
||||
lightboxService.open $el, () ->
|
||||
$scope.createEditTaskOpen = false
|
||||
|
||||
$scope.createEditTaskOpen = true
|
||||
|
||||
|
||||
submitButton = $el.find(".submit-button")
|
||||
|
||||
submit = debounce 2000, (event) =>
|
||||
event.preventDefault()
|
||||
|
||||
form = $el.find("form").checksley()
|
||||
if not form.validate()
|
||||
return
|
||||
|
||||
params = {
|
||||
include_attachments: true,
|
||||
include_tasks: true
|
||||
}
|
||||
|
||||
if $scope.isNew
|
||||
promise = $repo.create("tasks", $scope.task)
|
||||
broadcastEvent = "taskform:new:success"
|
||||
else
|
||||
promise = $repo.save($scope.task)
|
||||
broadcastEvent = "taskform:edit:success"
|
||||
|
||||
promise.then (data) ->
|
||||
deleteAttachments(data)
|
||||
.then () => createAttachments(data)
|
||||
.then () =>
|
||||
$rs.tasks.getByRef(data.project, data.ref, params).then (task) ->
|
||||
$rootscope.$broadcast(broadcastEvent, task)
|
||||
|
||||
currentLoading = $loading()
|
||||
.target(submitButton)
|
||||
.start()
|
||||
|
||||
promise.then (data) ->
|
||||
currentLoading.finish()
|
||||
lightboxService.close($el)
|
||||
|
||||
$el.on "submit", "form", submit
|
||||
|
||||
close = () =>
|
||||
if !$scope.task.isModified()
|
||||
lightboxService.close($el)
|
||||
$scope.$apply ->
|
||||
$scope.task.revert()
|
||||
else
|
||||
$confirm.ask($translate.instant("LIGHTBOX.CREATE_EDIT_TASK.CONFIRM_CLOSE")).then (result) ->
|
||||
lightboxService.close($el)
|
||||
$scope.task.revert()
|
||||
result.finish()
|
||||
|
||||
$el.find('.close').on "click", (event) ->
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
close()
|
||||
|
||||
$el.keydown (event) ->
|
||||
event.stopPropagation()
|
||||
code = if event.keyCode then event.keyCode else event.which
|
||||
if code == 27
|
||||
close()
|
||||
|
||||
$scope.$on "$destroy", ->
|
||||
$el.find('.close').off()
|
||||
$el.off()
|
||||
|
||||
return {link: link}
|
||||
|
||||
|
||||
CreateBulkTasksDirective = ($repo, $rs, $rootscope, $loading, lightboxService, $model) ->
|
||||
link = ($scope, $el, attrs) ->
|
||||
|
@ -248,7 +58,6 @@ CreateBulkTasksDirective = ($repo, $rs, $rootscope, $loading, lightboxService, $
|
|||
# TODO: error handling
|
||||
promise.then null, ->
|
||||
currentLoading.finish()
|
||||
console.log "FAIL"
|
||||
|
||||
$scope.$on "taskform:bulk", (ctx, sprintId, usId)->
|
||||
lightboxService.open($el)
|
||||
|
@ -266,20 +75,6 @@ CreateBulkTasksDirective = ($repo, $rs, $rootscope, $loading, lightboxService, $
|
|||
|
||||
module = angular.module("taigaTaskboard")
|
||||
|
||||
module.directive("tgLbCreateEditTask", [
|
||||
"$tgRepo",
|
||||
"$tgModel",
|
||||
"$tgResources",
|
||||
"$rootScope",
|
||||
"$tgLoading",
|
||||
"lightboxService",
|
||||
"$translate",
|
||||
"$q",
|
||||
"$tgConfirm",
|
||||
"tgAttachmentsService",
|
||||
CreateEditTaskDirective
|
||||
])
|
||||
|
||||
module.directive("tgLbCreateBulkTasks", [
|
||||
"$tgRepo",
|
||||
"$tgResources",
|
||||
|
|
|
@ -439,9 +439,17 @@ class TaskboardController extends mixOf(taiga.Controller, taiga.PageMixin, taiga
|
|||
task = task.set('loading-edit', true)
|
||||
@taskboardTasksService.replace(task)
|
||||
|
||||
@rs.tasks.getByRef(task.getIn(['model', 'project']), task.getIn(['model', 'ref'])).then (editingTask) =>
|
||||
@rs2.attachments.list("task", task.get('id'), task.getIn(['model', 'project'])).then (attachments) =>
|
||||
@rootscope.$broadcast("taskform:edit", editingTask, attachments.toJS())
|
||||
@rs.tasks.getByRef(task.getIn(['model', 'project']), task.getIn(['model', 'ref']))
|
||||
.then (editingTask) =>
|
||||
@rs2.attachments.list("task", task.get('id'), task.getIn(['model', 'project']))
|
||||
.then (attachments) =>
|
||||
@rootscope.$broadcast("genericform:edit", {
|
||||
'objType': 'task',
|
||||
'obj': editingTask,
|
||||
'statusList': @scope.taskStatusList,
|
||||
'attachments': attachments.toJS()
|
||||
})
|
||||
|
||||
task = task.set('loading', false)
|
||||
@taskboardTasksService.replace(task)
|
||||
|
||||
|
@ -496,7 +504,15 @@ class TaskboardController extends mixOf(taiga.Controller, taiga.PageMixin, taiga
|
|||
## Template actions
|
||||
addNewTask: (type, us) ->
|
||||
switch type
|
||||
when "standard" then @rootscope.$broadcast("taskform:new", @scope.sprintId, us?.id)
|
||||
when "standard" then @rootscope.$broadcast("genericform:new",
|
||||
{
|
||||
'objType': 'task',
|
||||
'project': @scope.project,
|
||||
'sprintId': @scope.sprintId,
|
||||
'usId': us?.id,
|
||||
'status': @scope.project.default_task_status,
|
||||
'statusList': @scope.taskStatusList
|
||||
})
|
||||
when "bulk" then @rootscope.$broadcast("taskform:bulk", @scope.sprintId, us?.id)
|
||||
|
||||
toggleFold: (id) ->
|
||||
|
|
|
@ -150,7 +150,9 @@
|
|||
"VOTES": "Votes",
|
||||
"SPRINT": "Sprint",
|
||||
"DUE_DATE": "Due date",
|
||||
"DUE_DATE_REASON": "Due date reason"
|
||||
"DUE_DATE_REASON": "Due date reason",
|
||||
"CLIENT_REQUIREMENT": "Client requirement",
|
||||
"TEAM_REQUIREMENT": "Team requirement"
|
||||
},
|
||||
"ROLES": {
|
||||
"ALL": "All"
|
||||
|
@ -1091,6 +1093,12 @@
|
|||
"EDIT_US": "Edit user story",
|
||||
"CONFIRM_CLOSE": "You have not saved changes.\nAre you sure you want to close the form?"
|
||||
},
|
||||
"CREATE_EDIT": {
|
||||
"PLACEHOLDER_DESCRIPTION": "Please add descriptive text to help others better understand this {{ objName }}",
|
||||
"NEW": "New {{ objName }}",
|
||||
"EDIT": "Edit {{ objName }}",
|
||||
"CONFIRM_CLOSE": "You have not saved changes.\nAre you sure you want to close the form?"
|
||||
},
|
||||
"DELETE_DUE_DATE": {
|
||||
"TITLE": "Delete due date",
|
||||
"SUBTITLE": "Are you sure you want to delete this due date?"
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
.card-statistics
|
||||
tg-due-date.statistic.card-due-date(
|
||||
due-date="vm.item.getIn(['model', 'due_date'])"
|
||||
due-date-status="vm.item.getIn(['model', 'due_date_status'])"
|
||||
is-closed="vm.item.getIn(['model', 'is_closed'])"
|
||||
)
|
||||
.statistic.card-iocaine(
|
||||
|
|
|
@ -21,9 +21,8 @@
|
|||
tg-svg.detail-edit.e2e-detail-edit(svg-icon="icon-edit")
|
||||
tg-due-date(
|
||||
due-date="vm.item.due_date"
|
||||
due-date-status="vm.item.due_date_status"
|
||||
ng-if="vm.item.due_date"
|
||||
is-closed="vm.item.is_closed"
|
||||
ng-if="vm.item.due_date"
|
||||
)
|
||||
.edit-title-wrapper(ng-if="vm.editMode")
|
||||
input.edit-title-input.e2e-title-input(
|
||||
|
|
|
@ -21,9 +21,10 @@ class DueDateController
|
|||
@.$inject = [
|
||||
"$translate"
|
||||
"tgLightboxFactory"
|
||||
"tgProjectService"
|
||||
]
|
||||
|
||||
constructor: (@translate, @tgLightboxFactory) ->
|
||||
constructor: (@translate, @tgLightboxFactory, @projectService) ->
|
||||
|
||||
visible: () ->
|
||||
return @.format == 'button' or @.dueDate?
|
||||
|
@ -37,17 +38,35 @@ class DueDateController
|
|||
'due_soon': 'due-soon',
|
||||
'past_due': 'past-due',
|
||||
'set': 'due-set',
|
||||
'not_set': 'not-set',
|
||||
}
|
||||
return colors[@.dueDateStatus] or ''
|
||||
return colors[@status()] or ''
|
||||
|
||||
title: () ->
|
||||
if @.format == 'button'
|
||||
return if @.dueDate then @._formatTitle() else 'Edit due date'
|
||||
if @.dueDate
|
||||
return @._formatTitle()
|
||||
else if @.format == 'button'
|
||||
return @translate.instant('COMMON.DUE_DATE.TITLE_ACTION_SET_DUE_DATE')
|
||||
return ''
|
||||
|
||||
return @._formatTitle()
|
||||
status: () ->
|
||||
if !@.dueDate
|
||||
return 'not_set'
|
||||
|
||||
project = @projectService.project.toJS()
|
||||
project.due_soon_threshold = 14 # TODO get value from taiga-back
|
||||
dueDate = moment(@.dueDate)
|
||||
now = moment()
|
||||
|
||||
if @.isClosed
|
||||
return 'no_longer_applicable'
|
||||
else if now > dueDate
|
||||
return 'past_due'
|
||||
else if now.add(moment.duration(project.due_soon_threshold, "days")) >= dueDate
|
||||
return 'due_soon'
|
||||
return 'set'
|
||||
|
||||
_formatTitle: () ->
|
||||
dueDateStatus = 'closed'
|
||||
titles = {
|
||||
'no_longer_applicable': 'COMMON.DUE_DATE.NO_LONGER_APPLICABLE',
|
||||
'due_soon': 'COMMON.DUE_DATE.DUE_SOON',
|
||||
|
@ -56,16 +75,17 @@ class DueDateController
|
|||
prettyDate = @translate.instant("COMMON.PICKERDATE.FORMAT")
|
||||
formatedDate = moment(@.dueDate).format(prettyDate)
|
||||
|
||||
if not titles[@.dueDateStatus]
|
||||
status = @status()
|
||||
if not titles[status]
|
||||
return formatedDate
|
||||
return "#{formatedDate} (#{@translate.instant(titles[@.dueDateStatus])})"
|
||||
return "#{formatedDate} (#{@translate.instant(titles[status])})"
|
||||
|
||||
setDueDate: () ->
|
||||
return if @.disabled()
|
||||
@tgLightboxFactory.create(
|
||||
"tg-lb-set-due-date",
|
||||
{"class": "lightbox lightbox-set-due-date"},
|
||||
{"object": @.item}
|
||||
{"object": @.item, "notAutoSave": @.notAutoSave}
|
||||
)
|
||||
|
||||
angular.module('taigaComponents').controller('DueDate', DueDateController)
|
||||
angular.module('taigaComponents').controller('DueDateCtrl', DueDateController)
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
###
|
||||
# Copyright (C) 2014-2018 Taiga Agile LLC <taiga@taiga.io>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# File: due-date.directive.coffee
|
||||
###
|
||||
|
||||
module = angular.module("taigaComponents")
|
||||
|
||||
dueDatePopoverDirective = ($translate, datePickerConfigService) ->
|
||||
return {
|
||||
link: (scope, el, attrs, ctrl) ->
|
||||
prettyDate = $translate.instant("COMMON.PICKERDATE.FORMAT")
|
||||
if ctrl.dueDate
|
||||
ctrl.dueDate = moment(ctrl.dueDate, prettyDate)
|
||||
|
||||
el.on "click", ".date-picker-popover-trigger", (event) ->
|
||||
if ctrl.disabled()
|
||||
return
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
el.find(".date-picker-popover").popover().open()
|
||||
|
||||
el.on "click", ".date-picker-clean", (event) ->
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
ctrl.dueDate = null
|
||||
scope.$apply()
|
||||
el.find(".date-picker-popover").popover().close()
|
||||
|
||||
datePickerConfig = datePickerConfigService.get()
|
||||
_.merge(datePickerConfig, {
|
||||
field: el.find('input.due-date')[0]
|
||||
container: el.find('.date-picker-container')[0]
|
||||
bound: false
|
||||
onSelect: () ->
|
||||
ctrl.dueDate = this.getMoment().format('YYYY-MM-DD')
|
||||
el.find(".date-picker-popover").popover().close()
|
||||
scope.$apply()
|
||||
})
|
||||
|
||||
el.picker = new Pikaday(datePickerConfig)
|
||||
|
||||
controller: "DueDateCtrl",
|
||||
controllerAs: "vm",
|
||||
bindToController: true,
|
||||
templateUrl: "components/due-date/due-date-popover.html",
|
||||
scope: {
|
||||
dueDate: '=',
|
||||
isClosed: '=',
|
||||
item: '=',
|
||||
format: '@',
|
||||
notAutoSave: '='
|
||||
}
|
||||
}
|
||||
|
||||
module.directive('tgDueDatePopover', ['$translate', 'tgDatePickerConfigService', dueDatePopoverDirective])
|
|
@ -0,0 +1,16 @@
|
|||
.due-date-button-wrapper
|
||||
label.due-date-button.button-gray.is-editable.date-picker-popover-trigger(
|
||||
ng-disabled="vm.disabled()"
|
||||
ng-class="vm.color()"
|
||||
ng-attr-title="{{ vm.title() }}"
|
||||
)
|
||||
tg-svg(svg-icon="icon-clock")
|
||||
input.due-date.no-focus(
|
||||
type="hidden"
|
||||
picker-value="{{ vm.dueDate }}"
|
||||
)
|
||||
div.date-picker-popover
|
||||
div.date-picker-container
|
||||
div.date-picker-popover-footer(ng-if="vm.dueDate")
|
||||
a.date-picker-clean(href="", title="{{'LIGHTBOX.SET_DUE_DATE.TITLE_ACTION_DELETE_DUE_DATE' | translate}}")
|
||||
tg-svg(svg-icon="icon-trash")
|
|
@ -19,25 +19,60 @@
|
|||
|
||||
module = angular.module("taigaComponents")
|
||||
|
||||
dueDateDirective = () ->
|
||||
dueDateDirective = ($translate, datePickerConfigService) ->
|
||||
templateUrl = (el, attrs) ->
|
||||
if attrs.format
|
||||
return "components/due-date/due-date-" + attrs.format + ".html"
|
||||
return "components/due-date/due-date-icon.html"
|
||||
|
||||
return {
|
||||
link: (scope) ->
|
||||
controller: "DueDate",
|
||||
link: (scope, el, attrs, ctrl) ->
|
||||
renderDatePicker = () ->
|
||||
prettyDate = $translate.instant("COMMON.PICKERDATE.FORMAT")
|
||||
if ctrl.dueDate
|
||||
ctrl.dueDate = moment(ctrl.dueDate, prettyDate)
|
||||
|
||||
el.on "click", ".date-picker-popover-trigger", (event) ->
|
||||
if ctrl.disabled()
|
||||
return
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
el.find(".date-picker-popover").popover().open()
|
||||
|
||||
el.on "click", ".date-picker-clean", (event) ->
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
ctrl.dueDate = null
|
||||
scope.$apply()
|
||||
el.find(".date-picker-popover").popover().close()
|
||||
|
||||
datePickerConfig = datePickerConfigService.get()
|
||||
_.merge(datePickerConfig, {
|
||||
field: el.find('input.due-date')[0]
|
||||
container: el.find('.date-picker-container')[0]
|
||||
bound: false
|
||||
onSelect: () ->
|
||||
ctrl.dueDate = this.getMoment().format('YYYY-MM-DD')
|
||||
el.find(".date-picker-popover").popover().close()
|
||||
scope.$apply()
|
||||
})
|
||||
|
||||
el.picker = new Pikaday(datePickerConfig)
|
||||
|
||||
if attrs.format == 'button-popover'
|
||||
renderDatePicker()
|
||||
|
||||
controller: "DueDateCtrl",
|
||||
controllerAs: "vm",
|
||||
bindToController: true,
|
||||
templateUrl: templateUrl,
|
||||
scope: {
|
||||
dueDate: '=',
|
||||
dueDateStatus: '=',
|
||||
isClosed: '=',
|
||||
item: '=',
|
||||
format: '@'
|
||||
format: '@',
|
||||
notAutoSave: '='
|
||||
}
|
||||
}
|
||||
|
||||
module.directive('tgDueDate', dueDateDirective)
|
||||
module.directive('tgDueDate', ['$translate', 'tgDatePickerConfigService', dueDateDirective])
|
|
@ -6,17 +6,25 @@
|
|||
padding: 1rem;
|
||||
transition: background .2s linear;
|
||||
transition-delay: .1s;
|
||||
&.closed {
|
||||
&.closed,
|
||||
&.text-button.closed:hover {
|
||||
background: $gray-lighter;
|
||||
border-color: $gray-lighter;
|
||||
}
|
||||
&.due-set {
|
||||
&.due-set,
|
||||
&.text-button.due-set:hover {
|
||||
background: $yellow-green;
|
||||
border-color: $yellow-green;
|
||||
}
|
||||
&.due-soon {
|
||||
&.due-soon,
|
||||
&.text-button.due-soon:hover {
|
||||
background: $my-sin;
|
||||
border-color: $my-sin;
|
||||
}
|
||||
&.past-due {
|
||||
&.past-due,
|
||||
&.text-button.past-due:hover {
|
||||
background: $red-light;
|
||||
border-color: $red-light;
|
||||
}
|
||||
&:hover {
|
||||
background: $gray;
|
||||
|
@ -24,6 +32,17 @@
|
|||
&.editable {
|
||||
cursor: pointer;
|
||||
}
|
||||
&.text-button {
|
||||
color: $white;
|
||||
margin: 0;
|
||||
padding: .5rem;
|
||||
&.not-set:hover {
|
||||
color: $white;
|
||||
}
|
||||
&.not-set {
|
||||
color: $gray;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.due-date-icon {
|
||||
|
@ -66,3 +85,39 @@
|
|||
width: .9rem;
|
||||
}
|
||||
}
|
||||
|
||||
.due-date-button-wrapper {
|
||||
display: flex;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.date-picker-container {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.date-picker-popover {
|
||||
background: $white;
|
||||
border: 1px solid $gray;
|
||||
display: none;
|
||||
left: 0;
|
||||
overflow: visible;
|
||||
position: absolute;
|
||||
top: 56px;
|
||||
width: auto;
|
||||
.pika-single {
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.date-picker-popover-footer {
|
||||
padding: .2rem .5rem;
|
||||
text-align: right;
|
||||
svg {
|
||||
fill: $gray;
|
||||
height: 1rem;
|
||||
width: 1rem;
|
||||
}
|
||||
a:hover svg {
|
||||
fill: $red;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -123,8 +123,8 @@ div.wrapper(tg-backlog, ng-controller="BacklogController as ctrl",
|
|||
sidebar.menu-secondary.sidebar
|
||||
include ../includes/modules/sprints
|
||||
|
||||
div.lightbox.lightbox-generic-form.lb-create-edit-userstory(tg-lb-create-edit-userstory)
|
||||
include ../includes/modules/lightbox-us-create-edit
|
||||
div.lightbox.lightbox-generic-form.lightbox-create-edit(tg-lb-create-edit)
|
||||
include ../includes/modules/lightbox-create-edit/lb-create-edit-us
|
||||
|
||||
div.lightbox.lightbox-generic-bulk(tg-lb-create-bulk-userstories)
|
||||
include ../includes/modules/lightbox-us-bulk
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
.user-avatar(ng-class!="{ 'is-iocaine': isIocaine }")
|
||||
img(
|
||||
style="background-color: {{ bg }}"
|
||||
src="{{ avatar.url }}"
|
||||
alt="{{ fullName }}"
|
||||
)
|
||||
.iocaine-symbol(
|
||||
ng-if="isIocaine"
|
||||
title="{{ 'TASK.TITLE_ACTION_IOCAINE' | translate }}"
|
||||
)
|
||||
tg-svg(svg-icon="icon-iocaine")
|
||||
|
||||
|
||||
.assigned-to
|
||||
.assigned-to-options
|
||||
span.assigned-name(
|
||||
ng-if="!isEditable && fullNameVisible"
|
||||
) {{ fullName }}
|
||||
|
||||
span.users-dropdown.user-assigned(
|
||||
title="{{ 'COMMON.ASSIGNED_TO.TITLE_ACTION_EDIT_ASSIGNMENT'|translate }}"
|
||||
ng-class="{ 'editable': isEditable }"
|
||||
ng-if="isEditable"
|
||||
)
|
||||
span.assigned-name
|
||||
span(ng-if="fullNameVisible") {{ fullName }}
|
||||
|
||||
div.pop-users.popover
|
||||
input.users-search(
|
||||
type="text"
|
||||
data-maxlength="500"
|
||||
placeholder="{{'LIGHTBOX.ASSIGNED_TO.SEARCH' | translate}}"
|
||||
ng-model="usersSearch"
|
||||
)
|
||||
a.user-list-single(
|
||||
href=""
|
||||
data-user-id="{{ user.id }}"
|
||||
title="{{ user.full_name_display }}"
|
||||
ng-repeat="user in users"
|
||||
)
|
||||
img.user-list-avatar(
|
||||
style="background: {{user.avatar.bg }}"
|
||||
src="{{ user.avatar.url }}"
|
||||
)
|
||||
span.user-list-name(
|
||||
href=""
|
||||
title="{{ user.full_name_display }}"
|
||||
) {{ user.full_name_display }}
|
||||
.show-more(ng-if="showMore")
|
||||
span(translate="COMMON.ASSIGNED_TO.TOO_MANY")
|
||||
|
||||
span(translate="COMMON.OR", ng-if="isUnassigned")
|
||||
div(ng-if="isUnassigned")
|
||||
a.assign-to-me(
|
||||
href="#"
|
||||
title="{{'COMMON.ASSIGNED_TO.SELF' | translate}}"
|
||||
)
|
||||
span {{ "COMMON.ASSIGNED_TO.SELF" | translate }}
|
||||
|
||||
tg-svg.remove-user(
|
||||
ng-if="isEditable && !isUnassigned"
|
||||
svg-icon="icon-close",
|
||||
title="{{'COMMON.ASSIGNED_TO.DELETE_ASSIGNMENT' | translate}}"
|
||||
)
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
div(ng-if="isAssigned")
|
||||
.user-list(ng-if="assignedUsers.length > 1")
|
||||
.user-list-item(ng-repeat="assignedUser in assignedUsers")
|
||||
img(
|
||||
tg-avatar="assignedUser"
|
||||
title="{{assignedUser.full_name_display}}"
|
||||
alt="{{assignedUser.full_name_display}}"
|
||||
)
|
||||
.user-list-item.counter(ng-if="hiddenUsers")
|
||||
span +{{ hiddenUsers }}
|
||||
|
||||
.ticket-assigned-to.single-assign(ng-if="assignedUsers.length == 1")
|
||||
.user-avatar
|
||||
img(
|
||||
tg-avatar="assignedUsers[0]"
|
||||
title="{{assignedUsers[0].full_name_display}}"
|
||||
alt="{{assignedUsers[0].full_name_display}}"
|
||||
)
|
||||
.assigned-to
|
||||
.assigned-users-options
|
||||
span.user-assigned {{ assignedUsers[0].full_name_display }}
|
||||
tg-svg.remove-user(
|
||||
svg-icon="icon-close",
|
||||
title="{{'COMMON.ASSIGNED_TO.DELETE_ASSIGNMENT' | translate}}"
|
||||
data-user-id="{{ assignedUsers[0].id }}"
|
||||
)
|
||||
|
||||
div.add-assigned
|
||||
a.users-dropdown.tg-add-assigned(
|
||||
href=""
|
||||
ng-click="openAssignedUsers()"
|
||||
)
|
||||
tg-svg.add-assigned(
|
||||
data-assigned-user-id="{{assignedUser.id}}",
|
||||
svg-icon="icon-add",
|
||||
title="{{'COMMON.ASSIGNED_USERS.ADD_ASSIGNED' | translate}}"
|
||||
)
|
||||
span {{ "COMMON.ASSIGNED_USERS.ADD_ASSIGNED" | translate }}
|
||||
|
||||
|
||||
.ticket-assigned-to(ng-if="!isAssigned")
|
||||
.user-avatar
|
||||
img(
|
||||
tg-avatar=""
|
||||
alt="{{ 'COMMON.ASSIGNED_TO.ASSIGN' | translate }}"
|
||||
)
|
||||
.assigned-to
|
||||
.assigned-users-options
|
||||
a.users-dropdown.user-assigned(
|
||||
href=""
|
||||
title="{{ 'COMMON.ASSIGNED_TO.TITLE_ACTION_EDIT_ASSIGNMENT'|translate }}"
|
||||
class="user-assigned"
|
||||
)
|
||||
span.assigned-name {{ "COMMON.ASSIGNED_TO.ASSIGN" | translate }}
|
||||
|
|
||||
span(translate="COMMON.OR")
|
||||
|
|
||||
a.assign-to-me(
|
||||
href="#"
|
||||
title="{{'COMMON.ASSIGNED_TO.SELF' | translate}}"
|
||||
)
|
||||
span {{ "COMMON.ASSIGNED_TO.SELF" | translate }}
|
||||
|
||||
div.pop-users.popover(ng-class="{'multiple': assignedUsers.length > 0}")
|
||||
input.users-search(
|
||||
type="text"
|
||||
data-maxlength="500"
|
||||
placeholder="{{'LIGHTBOX.ASSIGNED_TO.SEARCH' | translate}}"
|
||||
ng-model="usersSearch"
|
||||
)
|
||||
a.user-list-single.selected(
|
||||
href=""
|
||||
data-user-id="{{ user.id }}"
|
||||
title="{{ user.full_name_display }}"
|
||||
ng-repeat="user in selected"
|
||||
ng-class="{'selected': selected.indexOf(user) == -1 }"
|
||||
)
|
||||
img.user-list-avatar(
|
||||
style="background: {{user.avatar.bg }}"
|
||||
src="{{ user.avatar.url }}"
|
||||
)
|
||||
span.user-list-name(
|
||||
href=""
|
||||
title="{{ user.full_name_display }}"
|
||||
) {{ user.full_name_display }}
|
||||
tg-svg.remove(
|
||||
href=""
|
||||
data-user-id="{{ user.id }}"
|
||||
ng-if="selected.indexOf(user) > -1"
|
||||
svg-icon="icon-close",
|
||||
title="{{'COMMON.ASSIGNED_TO.DELETE_ASSIGNMENT' | translate}}"
|
||||
)
|
||||
|
||||
a.user-list-single(
|
||||
href=""
|
||||
data-user-id="{{ user.id }}"
|
||||
title="{{ user.full_name_display }}"
|
||||
ng-repeat="user in users"
|
||||
)
|
||||
img.user-list-avatar(
|
||||
style="background: {{user.avatar.bg }}"
|
||||
src="{{ user.avatar.url }}"
|
||||
)
|
||||
span.user-list-name(
|
||||
href=""
|
||||
title="{{ user.full_name_display }}"
|
||||
) {{ user.full_name_display }}
|
||||
tg-svg.remove(
|
||||
href=""
|
||||
data-user-id="{{ user.id }}"
|
||||
ng-if="selected.indexOf(user) > -1"
|
||||
svg-icon="icon-close",
|
||||
title="{{'COMMON.ASSIGNED_TO.DELETE_ASSIGNMENT' | translate}}"
|
||||
)
|
||||
|
||||
.show-more(ng-if="showMore")
|
||||
span(translate="COMMON.ASSIGNED_TO.TOO_MANY")
|
|
@ -40,5 +40,6 @@ form
|
|||
a.delete-due-date(
|
||||
href=""
|
||||
title="{{'LIGHTBOX.SET_DUE_DATE.TITLE_ACTION_DELETE_DUE_DATE' | translate}}"
|
||||
v-if="new_due_date"
|
||||
)
|
||||
tg-svg(svg-icon="icon-trash")
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
span(ng-bind-html="us.subject | emojify")
|
||||
tg-due-date(
|
||||
due-date="us.due_date"
|
||||
due-date-status="us.due_date_status"
|
||||
is-closed="us.is_closed"
|
||||
ng-if="us.due_date"
|
||||
)
|
||||
tg-belong-to-epics(
|
||||
|
|
|
@ -56,7 +56,7 @@ section.issues-table.basic-table(ng-class="{empty: !issues.length}")
|
|||
span(ng-bind-html="issue.subject | emojify")
|
||||
tg-due-date(
|
||||
due-date="issue.due_date"
|
||||
due-date-status="issue.due_date_status"
|
||||
is-closed="us.is_closed"
|
||||
ng-if="issue.due_date"
|
||||
)
|
||||
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
extends lb-create-edit
|
||||
|
||||
block options
|
||||
section.ticket-assigned-to(
|
||||
tg-assigned-to-inline
|
||||
ng-model="obj"
|
||||
required-perm="modify_{{ objType }}"
|
||||
)
|
||||
div.ticket-data-container
|
||||
tg-issue-type-button.ticket-status(
|
||||
autosave="false"
|
||||
ng-model="obj"
|
||||
)
|
||||
tg-issue-severity-button.ticket-status(
|
||||
autosave="false"
|
||||
ng-model="obj"
|
||||
)
|
||||
tg-issue-priority-button.ticket-status(
|
||||
autosave="false"
|
||||
ng-model="obj"
|
||||
)
|
||||
|
||||
div.ticket-detail-settings
|
||||
tg-due-date-popover(
|
||||
due-date="obj.due_date"
|
||||
is-closed="obj.is_closed"
|
||||
item="obj"
|
||||
not-auto-save="true"
|
||||
)
|
||||
div
|
||||
label.button-gray.is-blocked(
|
||||
title="{{ 'COMMON.BLOCK_TITLE' | translate }}"
|
||||
ng-class="{ 'button-red item-unblock': obj.is_blocked, 'item-block': !obj.is_blocked }"
|
||||
)
|
||||
tg-svg(svg-icon="icon-lock")
|
||||
|
||||
tg-blocking-message-input(
|
||||
watch="obj.is_blocked"
|
||||
ng-model="obj.blocked_note"
|
||||
)
|
|
@ -0,0 +1,40 @@
|
|||
extends lb-create-edit
|
||||
|
||||
block options
|
||||
section.ticket-assigned-to(
|
||||
tg-assigned-to-inline
|
||||
ng-model="obj"
|
||||
required-perm="modify_{{ objType }}"
|
||||
)
|
||||
div.ticket-detail-settings
|
||||
tg-due-date-popover(
|
||||
due-date="obj.due_date"
|
||||
is-closed="obj.is_closed"
|
||||
item="obj"
|
||||
not-auto-save="true"
|
||||
)
|
||||
div
|
||||
fieldset.iocaine-flag(title="{{ 'TASK.TITLE_ACTION_IOCAINE' | translate }}")
|
||||
label.button-gray.iocaine(
|
||||
for="is-iocaine"
|
||||
ng-class="{'active': obj.is_iocaine}"
|
||||
)
|
||||
tg-svg(svg-icon="icon-iocaine")
|
||||
input(
|
||||
type="checkbox"
|
||||
id="is-iocaine"
|
||||
name="is-iocaine"
|
||||
ng-model="obj.is_iocaine"
|
||||
ng-value="true"
|
||||
)
|
||||
div
|
||||
label.button-gray.is-blocked(
|
||||
title="{{ 'COMMON.BLOCK_TITLE' | translate }}"
|
||||
ng-class="{ 'button-red item-unblock': obj.is_blocked, 'item-block': !obj.is_blocked }"
|
||||
)
|
||||
tg-svg(svg-icon="icon-lock")
|
||||
|
||||
tg-blocking-message-input(
|
||||
watch="obj.is_blocked"
|
||||
ng-model="obj.blocked_note"
|
||||
)
|
|
@ -0,0 +1,44 @@
|
|||
extends lb-create-edit
|
||||
|
||||
block options
|
||||
section.ticket-assigned-to.multiple-assign(
|
||||
tg-assigned-users-inline
|
||||
ng-model="obj"
|
||||
required-perm="modify_{{ objType }}"
|
||||
)
|
||||
div.ticket-estimation
|
||||
tg-lb-us-estimation(ng-model="obj")
|
||||
|
||||
div.ticket-detail-settings
|
||||
tg-due-date-popover(
|
||||
due-date="obj.due_date"
|
||||
is-closed="obj.is_closed"
|
||||
item="obj"
|
||||
not-auto-save="true"
|
||||
)
|
||||
div
|
||||
label.button-gray.team-requirement(
|
||||
for="team-requirement"
|
||||
title="{{ 'COMMON.TEAM_REQUIREMENT' | translate }}"
|
||||
ng-class="{ 'active': isTeamRequirement() }"
|
||||
)
|
||||
tg-svg(svg-icon="icon-team-requirement")
|
||||
div
|
||||
label.button-gray.client-requirement(
|
||||
for="client-requirement"
|
||||
title="{{ 'COMMON.CLIENT_REQUIREMENT' | translate }}"
|
||||
ng-class="{ 'active': isClientRequirement() }"
|
||||
)
|
||||
tg-svg(svg-icon="icon-client-requirement")
|
||||
|
||||
div
|
||||
label.button-gray.is-blocked(
|
||||
title="{{ 'COMMON.BLOCK_TITLE' | translate }}"
|
||||
ng-class="{ 'button-red item-unblock': obj.is_blocked, 'item-block': !obj.is_blocked }"
|
||||
)
|
||||
tg-svg(svg-icon="icon-lock")
|
||||
|
||||
tg-blocking-message-input(
|
||||
watch="obj.is_blocked"
|
||||
ng-model="obj.blocked_note"
|
||||
)
|
|
@ -0,0 +1,66 @@
|
|||
tg-lightbox-close
|
||||
|
||||
form
|
||||
h2.title(translate="LIGHTBOX.CREATE_EDIT.TITLE_NEW")
|
||||
div.form-wrapper
|
||||
main
|
||||
fieldset
|
||||
input(
|
||||
type="text"
|
||||
name="subject"
|
||||
ng-model-options="{ debounce: 200 }"
|
||||
ng-model="obj.subject"
|
||||
placeholder="{{'COMMON.FIELDS.SUBJECT' | translate}}"
|
||||
data-required="true"
|
||||
data-maxlength="500"
|
||||
)
|
||||
|
||||
fieldset
|
||||
tg-tag-line-common.tags-block(
|
||||
ng-if="project && createEditOpen"
|
||||
project="project"
|
||||
tags="obj.tags"
|
||||
permissions="add_{{objType}}"
|
||||
on-add-tag="addTag(name, color)"
|
||||
on-delete-tag="deleteTag(tag)"
|
||||
)
|
||||
|
||||
fieldset
|
||||
textarea.description(
|
||||
rows=7
|
||||
name="description"
|
||||
ng-model="obj.description"
|
||||
ng-model-options="{ debounce: 200 }"
|
||||
ng-attr-placeholder="{{'LIGHTBOX.CREATE_EDIT.PLACEHOLDER_DESCRIPTION' | translate}}"
|
||||
)
|
||||
fieldset
|
||||
section
|
||||
tg-attachments-simple(
|
||||
attachments="attachments",
|
||||
on-add="addAttachment(attachment)"
|
||||
on-delete="deleteAttachment(attachment)"
|
||||
)
|
||||
|
||||
sidebar.sidebar.ticket-data
|
||||
fieldset.status-button
|
||||
div.status-dropdown.editable(style="background-color:{{ selectedStatus.color }}")
|
||||
span.status-text {{ selectedStatus.name }}
|
||||
tg-svg(svg-icon="icon-arrow-down")
|
||||
|
||||
ul.pop-status.popover
|
||||
li(ng-repeat="s in statusList")
|
||||
a.status(
|
||||
href=""
|
||||
title="{{ s.name }}"
|
||||
data-status-id="{{ s.id }}"
|
||||
) {{ s.name }}
|
||||
|
||||
block options
|
||||
|
||||
button.button-green.submit-button(
|
||||
type="submit"
|
||||
title="{{'COMMON.CREATE' | translate}}"
|
||||
translate="COMMON.CREATE"
|
||||
)
|
||||
|
||||
div.lightbox.lightbox-select-user(tg-lb-assignedto)
|
|
@ -1,60 +0,0 @@
|
|||
tg-lightbox-close
|
||||
|
||||
form
|
||||
h2.title(translate="LIGHTBOX.CREATE_ISSUE.TITLE")
|
||||
fieldset
|
||||
input(
|
||||
type="text"
|
||||
ng-model="issue.subject"
|
||||
ng-attr-placeholder="{{'COMMON.FIELDS.SUBJECT' | translate}}"
|
||||
ng-model-options="{ debounce: 200 }"
|
||||
data-required="true"
|
||||
data-maxlength="500"
|
||||
)
|
||||
.fieldset-row
|
||||
fieldset
|
||||
select.type(
|
||||
ng-model="issue.type"
|
||||
ng-options="t.id as t.name for t in issueTypes"
|
||||
)
|
||||
fieldset
|
||||
select.priority(
|
||||
ng-model="issue.priority"
|
||||
ng-options="p.id as p.name for p in priorityList"
|
||||
)
|
||||
fieldset
|
||||
select.severity(
|
||||
ng-model="issue.severity"
|
||||
ng-options="s.id as s.name for s in severityList"
|
||||
)
|
||||
|
||||
fieldset
|
||||
tg-tag-line-common.tags-block(
|
||||
ng-if="project && createIssueOpen"
|
||||
project="project"
|
||||
tags="issue.tags"
|
||||
permissions="add_issue"
|
||||
on-add-tag="addTag(name, color)"
|
||||
on-delete-tag="deleteTag(tag)"
|
||||
)
|
||||
|
||||
fieldset
|
||||
section
|
||||
tg-attachments-simple(
|
||||
attachments="attachments",
|
||||
on-add="addAttachment(attachment)"
|
||||
on-delete="deleteAttachment(attachment)"
|
||||
)
|
||||
|
||||
fieldset
|
||||
textarea.description(
|
||||
ng-attr-placeholder="{{'COMMON.FIELDS.DESCRIPTION' | translate}}"
|
||||
ng-model="issue.description"
|
||||
)
|
||||
|
||||
// include lightbox-attachments
|
||||
button.button-green.submit-button(
|
||||
type="submit"
|
||||
title="{{'COMMON.CREATE' | translate}}"
|
||||
translate="COMMON.CREATE"
|
||||
)
|
|
@ -1,92 +0,0 @@
|
|||
tg-lightbox-close
|
||||
form
|
||||
h2.title(translate="LIGHTBOX.CREATE_EDIT_TASK.TITLE")
|
||||
fieldset
|
||||
input(
|
||||
type="text"
|
||||
ng-model="task.subject"
|
||||
ng-attr-placeholder="{{'LIGHTBOX.CREATE_EDIT_TASK.PLACEHOLDER_SUBJECT' | translate}}"
|
||||
ng-model-options="{ debounce: 200 }"
|
||||
data-required="true"
|
||||
data-maxlength="500"
|
||||
)
|
||||
|
||||
fieldset
|
||||
select(
|
||||
ng-model="task.status"
|
||||
ng-options="s.id as s.name for s in taskStatusList"
|
||||
placeholder="{{'LIGHTBOX.CREATE_EDIT_TASK.PLACEHOLDER_STATUS' | translate}}"
|
||||
)
|
||||
|
||||
fieldset
|
||||
select(
|
||||
ng-model="task.assigned_to"
|
||||
ng-options="s.id as s.full_name_display for s in users"
|
||||
placeholder="{{'Assigned to'}}"
|
||||
)
|
||||
option(
|
||||
value=""
|
||||
translate="LIGHTBOX.CREATE_EDIT_TASK.OPTION_UNASSIGNED"
|
||||
)
|
||||
|
||||
fieldset
|
||||
tg-tag-line-common.tags-block(
|
||||
ng-if="project && createEditTaskOpen"
|
||||
project="project"
|
||||
tags="task.tags"
|
||||
permissions="add_task"
|
||||
on-add-tag="addTag(name, color)"
|
||||
on-delete-tag="deleteTag(tag)"
|
||||
)
|
||||
|
||||
fieldset
|
||||
section
|
||||
tg-attachments-simple(
|
||||
attachments="attachments",
|
||||
on-add="addAttachment(attachment)"
|
||||
on-delete="deleteAttachment(attachment)"
|
||||
)
|
||||
|
||||
fieldset
|
||||
textarea.description(
|
||||
ng-attr-placeholder="{{'LIGHTBOX.CREATE_EDIT_TASK.PLACEHOLDER_SHORT_DESCRIPTION' | translate}}"
|
||||
ng-model="task.description"
|
||||
ng-model-options="{ debounce: 200 }"
|
||||
)
|
||||
|
||||
div.settings
|
||||
fieldset.iocaine-flag(title="{{'COMMON.IOCAINE_TEXT' | translate}}")
|
||||
input(
|
||||
type="checkbox"
|
||||
ng-model="task.is_iocaine"
|
||||
name="iocaine-task"
|
||||
id="iocaine-task"
|
||||
ng-value="true"
|
||||
)
|
||||
label.iocaine.trans-button(for="iocaine-task")
|
||||
tg-svg(svg-icon="icon-iocaine")
|
||||
span Iocaine
|
||||
|
||||
fieldset.blocking-flag
|
||||
input(
|
||||
type="checkbox"
|
||||
ng-model="task.is_blocked"
|
||||
name="blocked-task"
|
||||
id="blocked-task"
|
||||
ng-value="true"
|
||||
)
|
||||
label.blocked.trans-button(
|
||||
for="blocked-task"
|
||||
translate="COMMON.BLOCKED"
|
||||
)
|
||||
|
||||
tg-blocking-message-input(
|
||||
watch="task.is_blocked"
|
||||
ng-model="task.blocked_note"
|
||||
)
|
||||
|
||||
button.button-green.submit-button(
|
||||
type="submit"
|
||||
title="{{'COMMON.CREATE' | translate}}"
|
||||
translate="COMMON.CREATE"
|
||||
)
|
|
@ -1,100 +0,0 @@
|
|||
tg-lightbox-close
|
||||
|
||||
form
|
||||
h2.title(translate="LIGHTBOX.CREATE_EDIT_US.TITLE")
|
||||
fieldset
|
||||
input(
|
||||
type="text"
|
||||
name="subject"
|
||||
ng-model-options="{ debounce: 200 }"
|
||||
ng-model="us.subject"
|
||||
placeholder="{{'COMMON.FIELDS.SUBJECT' | translate}}"
|
||||
data-required="true"
|
||||
data-maxlength="500"
|
||||
)
|
||||
|
||||
fieldset.ticket-estimation
|
||||
tg-lb-us-estimation(ng-model="us")
|
||||
|
||||
fieldset
|
||||
select(
|
||||
name="status"
|
||||
ng-model="us.status"
|
||||
ng-options="s.id as s.name for s in usStatusList"
|
||||
)
|
||||
|
||||
fieldset
|
||||
tg-tag-line-common.tags-block(
|
||||
ng-if="project && createEditUsOpen"
|
||||
project="project"
|
||||
tags="us.tags"
|
||||
permissions="add_us"
|
||||
on-add-tag="addTag(name, color)"
|
||||
on-delete-tag="deleteTag(tag)"
|
||||
)
|
||||
|
||||
fieldset
|
||||
textarea.description(
|
||||
name="description"
|
||||
ng-model="us.description"
|
||||
ng-model-options="{ debounce: 200 }"
|
||||
ng-attr-placeholder="{{'LIGHTBOX.CREATE_EDIT_US.PLACEHOLDER_DESCRIPTION' | translate}}"
|
||||
)
|
||||
fieldset
|
||||
section
|
||||
tg-attachments-simple(
|
||||
attachments="attachments",
|
||||
on-add="addAttachment(attachment)"
|
||||
on-delete="deleteAttachment(attachment)"
|
||||
)
|
||||
|
||||
div.settings
|
||||
fieldset.team-requirement
|
||||
input(
|
||||
type="checkbox"
|
||||
name="team_requirement"
|
||||
ng-model="us.team_requirement"
|
||||
id="team-requirement"
|
||||
ng-value="true"
|
||||
)
|
||||
label.requirement.trans-button(
|
||||
for="team-requirement"
|
||||
translate="US.FIELDS.TEAM_REQUIREMENT"
|
||||
)
|
||||
|
||||
fieldset.client-requirement
|
||||
input(
|
||||
type="checkbox"
|
||||
name="client_requirement"
|
||||
ng-model="us.client_requirement",
|
||||
id="client-requirement"
|
||||
ng-value="true"
|
||||
)
|
||||
label.requirement.trans-button(
|
||||
for="client-requirement"
|
||||
translate="US.FIELDS.CLIENT_REQUIREMENT"
|
||||
)
|
||||
|
||||
fieldset.blocking-flag
|
||||
input(
|
||||
type="checkbox"
|
||||
name="is_blocked"
|
||||
ng-model="us.is_blocked"
|
||||
id="blocked-us"
|
||||
ng-value="true"
|
||||
)
|
||||
label.blocked.trans-button(
|
||||
for="blocked-us"
|
||||
translate="COMMON.BLOCKED"
|
||||
)
|
||||
|
||||
tg-blocking-message-input(
|
||||
watch="us.is_blocked"
|
||||
ng-model="us.blocked_note"
|
||||
)
|
||||
|
||||
button.button-green.submit-button(
|
||||
type="submit"
|
||||
title="{{'COMMON.CREATE' | translate}}"
|
||||
translate="COMMON.CREATE"
|
||||
)
|
|
@ -114,12 +114,11 @@ div.wrapper(
|
|||
|
||||
section.ticket-detail-settings
|
||||
tg-due-date(
|
||||
tg-check-permission="modify_issue"
|
||||
due-date="issue.due_date"
|
||||
due-date-status="issue.due_date_status"
|
||||
format="button"
|
||||
is-closed="issue.is_closed"
|
||||
item="issue"
|
||||
format="button"
|
||||
tg-check-permission="modify_issue"
|
||||
)
|
||||
tg-promote-issue-to-us-button(
|
||||
tg-check-permission="add_us",
|
||||
|
|
|
@ -33,8 +33,8 @@ div.wrapper.issues.lightbox-generic-form(
|
|||
|
||||
div.lightbox.lightbox-select-user(tg-lb-assignedto)
|
||||
|
||||
div.lightbox.lightbox-create-issue(tg-lb-create-issue)
|
||||
include ../includes/modules/lightbox-create-issue
|
||||
div.lightbox.lightbox-generic-form.lightbox-create-edit(tg-lb-create-edit)
|
||||
include ../includes/modules/lightbox-create-edit/lb-create-edit-issue
|
||||
|
||||
div.lightbox.lightbox-generic-bulk(tg-lb-create-bulk-issues)
|
||||
include ../includes/modules/lightbox-issue-bulk
|
||||
|
|
|
@ -40,8 +40,8 @@ div.wrapper(
|
|||
|
||||
include ../includes/modules/kanban-table
|
||||
|
||||
div.lightbox.lightbox-generic-form.lb-create-edit-userstory(tg-lb-create-edit-userstory)
|
||||
include ../includes/modules/lightbox-us-create-edit
|
||||
div.lightbox.lightbox-generic-form.lightbox-create-edit(tg-lb-create-edit)
|
||||
include ../includes/modules/lightbox-create-edit/lb-create-edit-us
|
||||
|
||||
div.lightbox.lightbox-generic-bulk(tg-lb-create-bulk-userstories)
|
||||
include ../includes/modules/lightbox-us-bulk
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
span(ng-non-bindable) <%= emojify(task.subject) %>
|
||||
tg-due-date(
|
||||
due-date="task.due_date"
|
||||
due-date-status="task.due_date_status"
|
||||
ng-if="task.due_date"
|
||||
)
|
||||
.task-settings
|
||||
|
|
|
@ -103,12 +103,11 @@ div.wrapper(
|
|||
|
||||
section.ticket-detail-settings
|
||||
tg-due-date(
|
||||
tg-check-permission="modify_task"
|
||||
due-date="task.due_date"
|
||||
due-date-status="task.due_date_status"
|
||||
format="button"
|
||||
is-closed="task.is_closed"
|
||||
item="task"
|
||||
format="button"
|
||||
tg-check-permission="modify_task"
|
||||
)
|
||||
tg-task-is-iocaine-button(ng-model="task")
|
||||
tg-block-button(tg-check-permission="modify_task", ng-model="task")
|
||||
|
|
|
@ -47,8 +47,8 @@ div.wrapper(
|
|||
|
||||
include ../includes/modules/taskboard-table
|
||||
|
||||
div.lightbox.lightbox-generic-form(tg-lb-create-edit-task)
|
||||
include ../includes/modules/lightbox-task-create-edit
|
||||
div.lightbox.lightbox-generic-form.lightbox-create-edit(tg-lb-create-edit)
|
||||
include ../includes/modules/lightbox-create-edit/lb-create-edit-task
|
||||
|
||||
div.lightbox.lightbox-generic-bulk.lightbox-task-bulk(tg-lb-create-bulk-tasks)
|
||||
include ../includes/modules/lightbox-task-bulk
|
||||
|
|
|
@ -128,12 +128,11 @@ div.wrapper(
|
|||
|
||||
section.ticket-detail-settings
|
||||
tg-due-date(
|
||||
tg-check-permission="modify_us"
|
||||
due-date="us.due_date"
|
||||
due-date-status="us.due_date_status"
|
||||
format="button"
|
||||
is-closed="us.is_closed"
|
||||
item="us"
|
||||
format="button"
|
||||
tg-check-permission="modify_us"
|
||||
)
|
||||
tg-us-team-requirement-button(ng-model="us")
|
||||
tg-us-client-requirement-button(ng-model="us")
|
||||
|
|
|
@ -600,3 +600,229 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.lightbox-create-edit {
|
||||
z-index: 9998;
|
||||
$control-height: 30px;
|
||||
$spacing: 15px;
|
||||
$width: 700px;
|
||||
$pop-width: 203px;
|
||||
$sidebar-width: 220px;
|
||||
form {
|
||||
flex-basis: $width;
|
||||
max-width: $width;
|
||||
width: $width;
|
||||
}
|
||||
.form-wrapper {
|
||||
display: flex;
|
||||
flex-basis: $width;
|
||||
flex-direction: row;
|
||||
flex-grow: 0;
|
||||
margin-bottom: $spacing*2;
|
||||
main {
|
||||
flex-grow: 1;
|
||||
margin-right: $spacing;
|
||||
}
|
||||
.sidebar {
|
||||
border-left: 2px solid $whitish;
|
||||
padding-left: $spacing;
|
||||
width: $sidebar-width;
|
||||
}
|
||||
}
|
||||
.status-button {
|
||||
display: flex;
|
||||
position: relative;
|
||||
}
|
||||
.status-dropdown {
|
||||
align-content: center;
|
||||
color: $white;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-basis: 100%;
|
||||
height: $control-height;
|
||||
padding: .25rem .5rem;
|
||||
.status-text {
|
||||
flex-grow: 1;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
svg {
|
||||
fill: $white;
|
||||
height: .9rem;
|
||||
width: .9rem;
|
||||
}
|
||||
}
|
||||
|
||||
.popover {
|
||||
a {
|
||||
color: $white;
|
||||
display: block;
|
||||
padding: .5rem 1rem;
|
||||
text-align: left;
|
||||
&:hover,
|
||||
&.active {
|
||||
color: $primary-light;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ticket-assigned-to {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
&.single-assign {
|
||||
margin: 0;
|
||||
}
|
||||
&.multiple-assign {
|
||||
align-items: start;
|
||||
flex-direction: column;
|
||||
margin: 0;
|
||||
}
|
||||
.assigned-to-options {
|
||||
display: block;
|
||||
}
|
||||
.remove-user {
|
||||
top: 1.2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.user-list {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
$item-size: 44.75px;
|
||||
.user-list-item {
|
||||
margin-right: .5rem;
|
||||
width: $item-size;
|
||||
img {
|
||||
height: $item-size;
|
||||
width: $item-size;
|
||||
}
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
&.counter {
|
||||
background: $gray-lighter;
|
||||
color: $gray-light;
|
||||
font-weight: 400;
|
||||
height: $item-size;
|
||||
line-height: $item-size;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tg-add-assigned {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
margin: .25rem 0 .75rem;
|
||||
.add-assigned {
|
||||
fill: $gray;
|
||||
opacity: 1;
|
||||
right: .5rem;
|
||||
top: 2rem;
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
fill: $red;
|
||||
transition: fill .2s;
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
@include font-size(small);
|
||||
@include font-type(light);
|
||||
color: $gray;
|
||||
margin: .2rem .5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.users-dropdown {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.pop-status {
|
||||
@include popover($pop-width, $control-height - 2px);
|
||||
}
|
||||
|
||||
.pop-users {
|
||||
@include popover($pop-width, 60px, 0, '', '', 16px, -7px, ($pop-width / 3));
|
||||
&.multiple {
|
||||
top: 84px;
|
||||
&:after {
|
||||
left: 30px;
|
||||
}
|
||||
}
|
||||
ul {
|
||||
margin-bottom: .5rem;
|
||||
}
|
||||
li {
|
||||
border-bottom: 1px solid $gray-light;
|
||||
&:last-child {
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
.user-list-single {
|
||||
align-content: center;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-grow: 0;
|
||||
padding: .5rem 0;
|
||||
&.selected {
|
||||
color: $primary;
|
||||
}
|
||||
}
|
||||
.user-list-avatar {
|
||||
height: 32px;
|
||||
margin-right: .5rem;
|
||||
width: 32px;
|
||||
}
|
||||
.user-list-name {
|
||||
@include font-type(text);
|
||||
flex-grow: 1;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.remove svg {
|
||||
fill: $whitish;
|
||||
height: .8rem;
|
||||
width: .8rem;
|
||||
}
|
||||
|
||||
.show-more {
|
||||
border-top: 1px solid $gray-light;
|
||||
padding-top: .5rem;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.ticket-data-container {
|
||||
margin: 0;
|
||||
padding: 0 0 .1rem;
|
||||
.ticket-status .priority-data {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.ticket-estimation .points-per-role {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.ticket-detail-settings {
|
||||
height: 52px;
|
||||
justify-content: left;
|
||||
margin: 1rem 0 0;
|
||||
label {
|
||||
border: 0;
|
||||
padding: 12px 13px;
|
||||
width: auto;
|
||||
}
|
||||
.item-block,
|
||||
.item-unblock {
|
||||
display: block;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.blocked-note {
|
||||
margin-top: .5rem;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue