diff --git a/app/coffee/modules/issues/detail.coffee b/app/coffee/modules/issues/detail.coffee index 781e558b..c871e0fc 100644 --- a/app/coffee/modules/issues/detail.coffee +++ b/app/coffee/modules/issues/detail.coffee @@ -30,6 +30,7 @@ joinStr = @.taiga.joinStr groupBy = @.taiga.groupBy bindOnce = @.taiga.bindOnce bindMethods = @.taiga.bindMethods +normalizeString = @.taiga.normalizeString module = angular.module("taigaIssues") @@ -58,7 +59,8 @@ class IssueDetailController extends mixOf(taiga.Controller, taiga.PageMixin) ] constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location, - @log, @appMetaService, @analytics, @navUrls, @translate, @modelTransform, @errorHandlingService, @projectService) -> + @log, @appMetaService, @analytics, @navUrls, @translate, @modelTransform, + @errorHandlingService, @projectService) -> bindMethods(@) @scope.issueRef = @params.issueref @@ -105,6 +107,11 @@ class IssueDetailController extends mixOf(taiga.Controller, taiga.PageMixin) @scope.$on "custom-attributes-values:edit", => @rootscope.$broadcast("object:updated") + @scope.$on "assign-sprint-to-issue:success", (ctx, milestoneId) => + @rootscope.$broadcast("object:updated") + @scope.issue.milestone = milestoneId + @.loadSprint() + initializeOnDeleteGoToUrl: -> ctx = {project: @scope.project.slug} if @scope.project.is_issues_activated @@ -694,39 +701,44 @@ module.directive("tgPromoteIssueToUsButton", ["$rootScope", "$tgRepo", "$tgConfi ## Add Issue to Sprint button directive ############################################################################# -AssignSprintToIssueButtonDirective = ($rootScope, $repo, $translate, lightboxService) -> +AssignSprintToIssueButtonDirective = ($rootScope, $rs, $repo, $loading, $translate, lightboxService) -> link = ($scope, $el, $attrs, $model) -> - - $scope.$watch $attrs.ngModel, (item) -> - return if not item - if item.milestone - $el.find('.assign-issue-button').addClass('button-set') - else - $el.find('.assign-issue-button').removeClass('button-set') + avaliableMilestones = [] + issue = null $el.on "click", "a", (event) -> event.preventDefault() event.stopPropagation() - issue = $model.$modelValue - title = $translate.instant("ISSUES.ACTION_ASSIGN_SPRINT") - - # scope.selectProject(selectedProjectId).then () => - # lightboxService.open(el.find(".lightbox-create-related-user-stories")) - # if ctrl.disabled() - # return - - # $el.find(".lightbox-assign-sprint-to-issue").popover().open() - - lightboxService.open($el.find(".lightbox-assign-sprint-to-issue")) - # $scope.new = { - # projectId: projectId, - # milestoneId: milestoneId - # } + issue = $model.$modelValue + $rs.sprints.list($scope.projectId, null).then (data) -> + $scope.milestones = data.milestones + avaliableMilestones = angular.copy($scope.milestones) + lightboxService.open($el.find(".lightbox-assign-sprint-to-issue")) $scope.$on "$destroy", -> $el.off() + existsMilestone = (needle, haystack) -> + haystack = normalizeString(haystack.toUpperCase()) + needle = normalizeString(needle.toUpperCase()) + return _.includes(haystack, needle) + + $scope.filterMilestones = (filterText) -> + $scope.milestones = avaliableMilestones.filter((milestone) -> + existsMilestone(filterText, milestone.name) + ) + + $scope.saveIssueToSprint = (selectedSprintId) -> + currentLoading = $loading().target($el.find(".e2e-select-related-sprint-button")).start() + issue.setAttr('milestone', selectedSprintId) + $repo.save(issue, true).then (data) -> + currentLoading.finish() + lightboxService.close($el.find(".lightbox-assign-sprint-to-issue")) + $scope.$broadcast("assign-sprint-to-issue:success", selectedSprintId) + + + return { link: link restrict: "EA" @@ -735,5 +747,6 @@ AssignSprintToIssueButtonDirective = ($rootScope, $repo, $translate, lightboxSer } -module.directive("tgAssignSprintToIssueButton", ["$rootScope", "$tgRepo", "$translate" - "lightboxService", AssignSprintToIssueButtonDirective] ) +module.directive("tgAssignSprintToIssueButton", ["$rootScope", "$tgResources", "$tgRepo", + "$tgLoading", "$translate", "lightboxService", + AssignSprintToIssueButtonDirective] ) diff --git a/app/partials/issue/assign-sprint-to-issue-button.jade b/app/partials/issue/assign-sprint-to-issue-button.jade new file mode 100644 index 00000000..f2845557 --- /dev/null +++ b/app/partials/issue/assign-sprint-to-issue-button.jade @@ -0,0 +1,55 @@ +a.assign-issue-button.button-gray.is-editable( + href="" + tg-check-permission="add_us" + title="{{ 'ISSUES.ACTION_ASSIGN_SPRINT' | translate }}" + ng-class="{'button-set': issue.milestone}" +) + tg-svg(svg-icon="icon-promote") + + +.lightbox.lightbox-assign-sprint-to-issue + tg-lightbox-close + + div.lightbox-assign-related-sprint + h2 "{{ 'ISSUES.ACTION_ASSIGN_SPRINT' | translate }}" + + fieldset.existing-sprint + label( + translate="ISSUES.CHOOSE_SPRINT" + for="sprint-filter" + ) + input.sprint-filter.e2e-filter-sprints-input( + id="sprint-filter" + type="text" + placeholder="{{'ISSUES.FILTER_SPRINTS' | translate}}" + ng-model="filterText" + ng-change="filterMilestones(filterText)" + ) + + form.existing-user-story-form + select.userstory.e2e-userstories-select( + size="5" + data-required="true" + ng-model="selectedSprint" + ) + - var hash = "#"; + option.hidden( + value="" + ) + option( + ng-repeat="milestone in milestones | toMutable track by milestone.id" + value="{{ ::milestone.id }}" + ) #{hash}{{::milestone.name}} + + p.no-stories-found( + ng-show="!milestones.length" + translate="EPIC.NO_USERSTORIES_FOUND" + ) + + button.button-green.e2e-select-related-sprint-button( + href="" + ng-click="saveIssueToSprint(selectedSprint)" + ng-disabled="!selectedSprint" + tg-loading="vm.loading" + translate="COMMON.SAVE" + ) diff --git a/app/styles/modules/common/lightbox.scss b/app/styles/modules/common/lightbox.scss index 102bb447..1b6a8696 100644 --- a/app/styles/modules/common/lightbox.scss +++ b/app/styles/modules/common/lightbox.scss @@ -618,7 +618,6 @@ } } - .lightbox-create-edit { z-index: 9998; $control-height: 30px; @@ -854,3 +853,32 @@ margin-top: .5rem; } } + + +.ticket-detail-settings .lightbox-assign-sprint-to-issue { + svg { + fill: initial; + max-height: initial; + max-width: initial; + } + + fieldset { + label { + background: none; + display: inline-block; + margin-bottom: .5rem; + margin-right: 0; + padding: 0; + + +input { + display: initial; + } + } + } + + button { + width: 100%; + } + + +}