From 8c4d39cd254a5fa9d5b2d7e7d5c981a41358714e Mon Sep 17 00:00:00 2001 From: Andrey Lvov Date: Thu, 26 Sep 2024 14:58:09 +0600 Subject: [PATCH 1/2] added link to sprint --- app/coffee/modules/common/lightboxes.coffee | 88 +++++++++++++++++++ app/coffee/modules/userstories/detail.coffee | 3 + app/locales/taiga/locale-en.json | 12 +++ .../detail/header/detail-header.scss | 21 +++++ .../lightbox/lightbox-relate-to-sprint.jade | 66 ++++++++++++++ app/partials/us/us-detail.jade | 26 ++++++ 6 files changed, 216 insertions(+) create mode 100644 app/partials/common/lightbox/lightbox-relate-to-sprint.jade diff --git a/app/coffee/modules/common/lightboxes.coffee b/app/coffee/modules/common/lightboxes.coffee index 74bc61c82..20810f9cb 100644 --- a/app/coffee/modules/common/lightboxes.coffee +++ b/app/coffee/modules/common/lightboxes.coffee @@ -1035,4 +1035,92 @@ module.directive("tgLbRelatetoepic", [ "$rootScope", "$tgConfirm", "lightboxService", "tgCurrentUserService", "tgResources", "$tgResources", "tgEpicsService", "$tgAnalytics", RelateToEpicLightboxDirective]) +############################################################################# +## RelateToSprint Lightbox Directive +############################################################################# + +debounceLeading = @.taiga.debounceLeading + +RelateToSprintLightboxDirective = ($rootScope, $confirm, lightboxService, $tgCurrentUserService +tgResources, $rs, tgAnalytics) -> + link = ($scope, $el, $attrs) -> + us = null + + $scope.sprints = [] + $scope.loading = false + $scope.selectedProject = $scope.project.id + + params = {} + $rs.sprints.list($scope.selectedProject, params).then (result) -> + $scope.sprints = result.milestones + + existingSprintForm = $el.find(".existing-sprint-form").checksley() + + filterSprints = (selectedProjectId, filterText) -> + params = {} + $rs.sprints.list(selectedProjectId, params).then (result) -> + excludeId = $scope.us.milestone + searchText = filterText.toLowerCase() + + filteredSprints = result.milestones.filter((sprint) -> + sprint.id != excludeId and ( + sprint.slug.toLowerCase().includes(searchText) or + sprint.id.toString().includes(searchText) + ) + ) + + $scope.sprints = filteredSprints + + $el.on "click", ".close", (event) -> + event.preventDefault() + lightboxService.close($el) + + $scope.$on "relate-to-sprint:add", (ctx, item) -> + us = item + $scope.selectedSprint = null + $scope.searchSprint = "" + filterSprints($scope.selectedProject, $scope.searchSprint).then () -> + lightboxService.open($el).then -> + $el.find('input').focus + $scope.$on "$destroy", -> + $el.off() + + $scope.$on "related-sprint:changed", (ctx, userStory)-> + $rs.userstories.getByRef(userStory.project, userStory.ref, {}).then (us) -> + $scope.us = us + + $scope.onUpdateSearchSprint = debounceLeading 300, () -> + $scope.selectedSprint = null + filterSprints($scope.selectedProject, $scope.searchSprint) + + $scope.saveRelatedSprint = (selectedSprintId, onSavedRelatedSprint) -> + return if not existingSprintForm.validate() + + $scope.loading = true + + onError = (data) -> + $scope.loading = false + $confirm.notify("error") + existingSprintForm.setErrors(data) + + onSuccess = (data) -> + $scope.loading = false + $scope.$broadcast("related-sprint:changed", us) + lightboxService.close($el) + + bulk_data = [{ + us_id: us.id + order: us.sprint_order + }] + $rs.userstories.bulkUpdateMilestone($scope.selectedProject, Number(selectedSprintId), bulk_data).then( + onSuccess, onError) + + return { + templateUrl: "common/lightbox/lightbox-relate-to-sprint.html" + link:link + } + +module.directive("tgLbRelatetosprint", [ + "$rootScope", "$tgConfirm", "lightboxService", "tgCurrentUserService", "tgResources", + "$tgResources", "$tgAnalytics", RelateToSprintLightboxDirective]) diff --git a/app/coffee/modules/userstories/detail.coffee b/app/coffee/modules/userstories/detail.coffee index 92a44bfe7..0a0a0d9e1 100644 --- a/app/coffee/modules/userstories/detail.coffee +++ b/app/coffee/modules/userstories/detail.coffee @@ -100,6 +100,9 @@ class UserStoryDetailController extends mixOf(taiga.Controller, taiga.PageMixin) @scope.relateToEpic = (us) => @scope.$broadcast("relate-to-epic:add", us) + @scope.relateToSprint = (us) => + @scope.$broadcast("relate-to-sprint:add", us) + @scope.$on "related-tasks:update", => @.loadTasks() @scope.tasks = _.clone(@scope.tasks, false) diff --git a/app/locales/taiga/locale-en.json b/app/locales/taiga/locale-en.json index c2c946c57..b78570553 100644 --- a/app/locales/taiga/locale-en.json +++ b/app/locales/taiga/locale-en.json @@ -1295,6 +1295,16 @@ "CHOOSE_EPIC": "What's the epic?", "FILTER_EPICS": "Filter epics", "NO_EPICS_FOUND": "It looks like nothing was found with your search criteria" + }, + "RELATE_TO_SPRINT": { + "TITLE": "Link to sprint", + "EXISTING_SPRINT": "Existing sprint", + "CHOOSE_PROJECT_FOR_CREATION": "What's the project?", + "CHOOSE_PROJECT_FROM": "What's the project?", + "SUBJECT": "Subject", + "CHOOSE_SPRINT": "What's the sprint?", + "FILTER_SPRINTS": "Filter sprints", + "NO_SPRINTS_FOUND": "It looks like nothing was found with your search criteria" } }, "EPIC": { @@ -1336,7 +1346,9 @@ "LIGHTBOX_TITLE_BLOKING_US": "Blocking user story", "NOT_ESTIMATED": "N/E", "OWNER_US": "This user story belongs to", + "OWNER_US_SPRINT": "This user story belongs to sprint", "RELATE_TO_EPIC": "Link to epic", + "RELATE_TO_SPRINT": "Link to sprint", "REMOVE_RELATIONSHIP_WITH_EPIC": "Remove epic relationship", "TRIBE": { "PUBLISH": "Publish as Gig in Taiga Tribe", diff --git a/app/modules/components/detail/header/detail-header.scss b/app/modules/components/detail/header/detail-header.scss index a7f4b8aa7..afdcb3cba 100644 --- a/app/modules/components/detail/header/detail-header.scss +++ b/app/modules/components/detail/header/detail-header.scss @@ -60,6 +60,27 @@ } } } + .relate-to-sprint { + flex-basis: 100%; + margin: 0 1rem .5rem 0; + + .relate-to-sprint-button { + align-content: flex-end; + color: $color-link-primary; + cursor: pointer; + display: flex; + .icon-sprints { + fill: currentColor; + margin-right: .25rem; + } + .relate-to-sprint-text { + font-size: .9375rem; + } + } + } + .belong-to-sprint-title { + color: $color-black600; + } & > .color-selector { display: block; margin-bottom: -2rem; diff --git a/app/partials/common/lightbox/lightbox-relate-to-sprint.jade b/app/partials/common/lightbox/lightbox-relate-to-sprint.jade new file mode 100644 index 000000000..aa95eb991 --- /dev/null +++ b/app/partials/common/lightbox/lightbox-relate-to-sprint.jade @@ -0,0 +1,66 @@ + +//- This source code is licensed under the terms of the +//- GNU Affero General Public License found in the LICENSE file in +//- the root directory of this source tree. +//- +//- Copyright (c) 2021-present Kaleidos INC + +tg-lightbox-close + +.lightbox-create-related-sprint-wrapper + h2.title(translate="LIGHTBOX.RELATE_TO_SPRINT.TITLE") + + .button-group(tg-check-permission="modify_us") + .button-group-single + input( + type="radio" + name="related-with-selector" + id="existing-sprint" + value="existing-sprint" + ng-model="relatedWithSelector" + ng-init="relatedWithSelector='existing-sprint'" + ) + label.e2e-existing-sprint-label(for="existing-sprint") + span.name {{ 'LIGHTBOX.RELATE_TO_SPRINT.EXISTING_SPRINT' | translate}} + + + fieldset.existing-sprint(ng-show="relatedWithSelector=='existing-sprint'") + label( + translate="LIGHTBOX.RELATE_TO_SPRINT.CHOOSE_SPRINT" + for="sprint-filter" + ) + input.sprint-filter.e2e-filter-userstories-input( + id="sprint-filter" + type="text" + placeholder="{{'LIGHTBOX.RELATE_TO_SPRINT.FILTER_SPRINTS' | translate}}" + ng-model="searchSprint" + ng-change="onUpdateSearchSprint()" + ) + + form.existing-sprint-form(ng-show="relatedWithSelector=='existing-sprint' && sprints.length") + select.sprint.e2e-userstories-select( + size="5" + ng-model="selectedSprint" + data-required="true" + ) + - var hash = "#"; + option.hidden( + value="" + ) + option( + ng-repeat="sprint in sprints track by sprint.id" + value="{{ ::sprint.id }}" + ) #{hash}{{::sprint.id}} {{::sprint.name}} + + p.no-stories-found( + ng-show="relatedWithSelector=='existing-sprint' && !sprints.length" + translate="LIGHTBOX.RELATE_TO_SPRINT.NO_SPRINTS_FOUND" + ) + + button.btn-big.e2e-select-related-sprint-button( + href="" + ng-click="saveRelatedSprint(selectedSprint, closeLightbox)" + tg-loading="loading" + translate="COMMON.SAVE" + ) + diff --git a/app/partials/us/us-detail.jade b/app/partials/us/us-detail.jade index dd3253414..2641effd5 100644 --- a/app/partials/us/us-detail.jade +++ b/app/partials/us/us-detail.jade @@ -25,6 +25,31 @@ div.wrapper( type="text" ) + .detail-header-line.belong-to-sprint-wrapper + .related-to-sprint + span.belong-to-sprint-title(ng-if="us.milestone", translate="US.OWNER_US_SPRINT") + a.belong-to-sprint-text( + href="" + tg-nav="project-taskboard:project=project.slug,sprint=sprint.slug" + ng-bind-html="'#'+us.milestone+' '+us.milestone_name | emojify" + ) + + + //- User Story relate to Sprint + .relate-to-sprint( + tg-check-permission="modify_us" + ) + a.relate-to-sprint-button.ng-animate-disabled( + href="" + title="{{'US.RELATE_TO_SPRINT' | translate}}" + ng-click="relateToSprint(us)" + ) + //- tg-svg( + //- svg-icon="icon-add-to-sprint" + //- svg-title-translate="US.RELATE_TO_SPRINT" + //- ) + span.relate-to-sprint-text(translate="US.RELATE_TO_SPRINT") + //- User Story belongs to epic .detail-header-line.belong-to-epics-wrapper(ng-if="!!project.is_epics_activated") .related-to-epics @@ -215,3 +240,4 @@ div.wrapper( ng-model="us" ) div.lightbox.lightbox-relate-to-epic(tg-lb-relatetoepic) + div.lightbox.lightbox-relate-to-story(tg-lb-relatetosprint) From ff0959d41eecfa06ab95d89f0af7c3164ef70dca Mon Sep 17 00:00:00 2001 From: Andrey Lvov Date: Thu, 26 Sep 2024 15:11:38 +0600 Subject: [PATCH 2/2] fixed sprint link update --- app/coffee/modules/common/lightboxes.coffee | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/coffee/modules/common/lightboxes.coffee b/app/coffee/modules/common/lightboxes.coffee index 20810f9cb..d4981df4f 100644 --- a/app/coffee/modules/common/lightboxes.coffee +++ b/app/coffee/modules/common/lightboxes.coffee @@ -1087,8 +1087,12 @@ tgResources, $rs, tgAnalytics) -> $el.off() $scope.$on "related-sprint:changed", (ctx, userStory)-> - $rs.userstories.getByRef(userStory.project, userStory.ref, {}).then (us) -> + $rs.userstories.getByRef(userStory.project, userStory.ref, {}).then((us) -> $scope.us = us + return us + ).then (us)-> + $rs.sprints.get(us.project, us.milestone).then (sprint) => + $scope.sprint = sprint $scope.onUpdateSearchSprint = debounceLeading 300, () -> $scope.selectedSprint = null