diff --git a/app/coffee/modules/resources/issues.coffee b/app/coffee/modules/resources/issues.coffee index b487c822..2271c652 100644 --- a/app/coffee/modules/resources/issues.coffee +++ b/app/coffee/modules/resources/issues.coffee @@ -51,6 +51,12 @@ resourceProvider = ($repo, $http, $urls, $storage, $q) -> service.storeQueryParams(projectId, params) return $repo.queryPaginated("issues", params, options) + service.listInProject = (projectId, sprintId=null, params) -> + params = _.merge(params, {project: projectId}) + params.milestone = sprintId if sprintId + service.storeQueryParams(projectId, params) + return $repo.queryMany("issues", params) + service.bulkCreate = (projectId, data) -> url = $urls.resolve("bulk-create-issues") params = {project_id: projectId, bulk_issues: data} diff --git a/app/coffee/modules/taskboard/main.coffee b/app/coffee/modules/taskboard/main.coffee index e677ada6..363345b5 100644 --- a/app/coffee/modules/taskboard/main.coffee +++ b/app/coffee/modules/taskboard/main.coffee @@ -57,12 +57,14 @@ class TaskboardController extends mixOf(taiga.Controller, taiga.PageMixin, taiga "$translate", "tgErrorHandlingService", "tgTaskboardTasks", + "tgTaskboardIssues", "$tgStorage", "tgFilterRemoteStorageService" ] constructor: (@scope, @rootscope, @repo, @confirm, @rs, @rs2, @params, @q, @appMetaService, @location, @navUrls, - @events, @analytics, @translate, @errorHandlingService, @taskboardTasksService, @storage, @filterRemoteStorageService) -> + @events, @analytics, @translate, @errorHandlingService, @taskboardTasksService, + @taskboardIssuesService, @storage, @filterRemoteStorageService) -> bindMethods(@) @taskboardTasksService.reset() @scope.userstories = [] @@ -76,6 +78,9 @@ class TaskboardController extends mixOf(taiga.Controller, taiga.PageMixin, taiga taiga.defineImmutableProperty @.scope, "usTasks", () => return @taskboardTasksService.usTasks + taiga.defineImmutableProperty @.scope, "milestoneIssues", () => + return @taskboardIssuesService.milestoneIssues + firstLoad: () -> promise = @.loadInitialData() @@ -388,6 +393,14 @@ class TaskboardController extends mixOf(taiga.Controller, taiga.PageMixin, taiga return sprint + loadIssues: -> + params = {} + params = _.merge params, @location.search() + + return @rs.issues.listInProject(@scope.projectId, @scope.sprintId, params).then (issues) => + @taskboardIssuesService.init(@scope.project, @scope.usersById) + @taskboardIssuesService.set(issues) + loadTasks: -> params = {} @@ -395,7 +408,7 @@ class TaskboardController extends mixOf(taiga.Controller, taiga.PageMixin, taiga params.include_attachments = 1 params = _.merge params, @location.search() - + console.log '@scope.sprintId tasks', @scope.sprintId return @rs.tasks.list(@scope.projectId, @scope.sprintId, null, params).then (tasks) => @taskboardTasksService.init(@scope.project, @scope.usersById) @taskboardTasksService.set(tasks) @@ -404,7 +417,10 @@ class TaskboardController extends mixOf(taiga.Controller, taiga.PageMixin, taiga return @q.all([ @.refreshTagsColors(), @.loadSprintStats(), - @.loadSprint().then(=> @.loadTasks()) + @.loadSprint().then(=> + @.loadTasks() + @.loadIssues() + ) ]) loadInitialData: -> @@ -515,6 +531,11 @@ class TaskboardController extends mixOf(taiga.Controller, taiga.PageMixin, taiga }) when "bulk" then @rootscope.$broadcast("taskform:bulk", @scope.sprintId, us?.id) + addNewIssue: (type, us) -> + switch type + when "standard" then @rootscope.$broadcast("taskform:new", @scope.sprintId, us?.id) + when "bulk" then @rootscope.$broadcast("taskform:bulk", @scope.sprintId, us?.id) + toggleFold: (id) -> @taskboardTasksService.toggleFold(id) @@ -608,12 +629,8 @@ TaskboardSquishColumnDirective = (rs) -> recalculateTaskboardWidth() - $scope.foldUs = (us) -> - if !us - $scope.usFolded[null] = !!!$scope.usFolded[null] - else - $scope.usFolded[us.id] = !!!$scope.usFolded[us.id] - + $scope.foldUs = (rowId) -> + $scope.usFolded[rowId] = !!!$scope.usFolded[rowId] rs.tasks.storeUsRowModes($scope.projectId, $scope.sprintId, $scope.usFolded) recalculateTaskboardWidth() @@ -659,6 +676,13 @@ TaskboardSquishColumnDirective = (rs) -> $el.find('.taskboard-table-inner').css("width", totalWidth) + columnWidths.pop() + issuesRowWidth = _.reduce columnWidths, (total, width) -> + return total + width + + issuesBoxWidth = $el.find('.issues-row .taskboard-issues-box').outerWidth(true) + $el.find('.issues-row').css("width", issuesRowWidth) + recalculateStatusColumnWidth = (statusId) => #unassigned ceil statusFoldedWidth = getCeilWidth(null, statusId) diff --git a/app/coffee/modules/taskboard/taskboard-issues.coffee b/app/coffee/modules/taskboard/taskboard-issues.coffee new file mode 100644 index 00000000..de5c8ed3 --- /dev/null +++ b/app/coffee/modules/taskboard/taskboard-issues.coffee @@ -0,0 +1,78 @@ +### +# Copyright (C) 2014-2017 Taiga Agile LLC +# +# 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 . +# +# File: taskboard-issues.coffee +### + +groupBy = @.taiga.groupBy + +class TaskboardIssuesService extends taiga.Service + @.$inject = [] + constructor: () -> + @.reset() + + reset: () -> + @.issuesRaw = [] + + init: (project, usersById) -> + @.project = project + @.usersById = usersById + + add: (issue) -> + @.issuesRaw = @.issuesRaw.concat(issue) + @.refresh() + + set: (issues) -> + @.issuesRaw = issues + @.refresh() + + getIssue: (id) -> + return @.milestoneIssues.find (issue) -> return issue.get('id') == id + + getIssueModel: (id) -> + return _.find @.issuesRaw, (issue) -> return issue.id == id + + replaceModel: (issue) -> + console.log 'replacesModel' + @.issuesRaw = _.map @.issuesRaw, (item) -> + if issue.id == item.id + return issue + else + return item + + @.refresh() + + refresh: -> + issues = [] + for issueModel in @.issuesRaw + console.log issueModel + issue = {} + + model = issueModel.getAttrs() + + issue.model = model + issue.images = _.filter model.attachments, (it) -> return !!it.thumbnail_card_url + issue.id = issueModel.id + issue.assigned_to = @.usersById[issueModel.assigned_to] + issue.colorized_tags = _.map issue.model.tags, (tag) => + return {name: tag[0], color: tag[1]} + + issues.push(issue) + + @.milestoneIssues = Immutable.fromJS(issues) + console.log @.milestoneIssues, 'milestoneIssues' + +angular.module("taigaKanban").service("tgTaskboardIssues", TaskboardIssuesService) diff --git a/app/locales/taiga/locale-en.json b/app/locales/taiga/locale-en.json index bed74bed..af1697e8 100644 --- a/app/locales/taiga/locale-en.json +++ b/app/locales/taiga/locale-en.json @@ -1346,6 +1346,8 @@ "SECTION_NAME": "Taskboard", "TITLE_ACTION_ADD": "Add a new Task", "TITLE_ACTION_ADD_BULK": "Add some new Tasks in bulk", + "TITLE_ACTION_ADD_ISSUE": "Add a new Issue", + "TITLE_ACTION_ADD_ISSUE_BULK": "Add some new Issues in bulk", "TITLE_ACTION_ASSIGN": "Assign task", "PLACEHOLDER_CARD_TITLE": "This could be a task", "PLACEHOLDER_CARD_TEXT": "Split Stories into tasks to track them separately", @@ -1356,7 +1358,8 @@ "TITLE_ACTION_FOLD_ROW": "Fold Row", "TITLE_ACTION_UNFOLD_ROW": "Unfold Row", "FIELD_POINTS": "points", - "ROW_UNASSIGED_TASKS_TITLE": "Unassigned tasks" + "ROW_UNASSIGED_TASKS_TITLE": "Unassigned tasks", + "ROW_ISSUES_TITLE": "Sprint Issues" }, "CHARTS": { "XAXIS_LABEL": "Days", diff --git a/app/modules/resources/issues-resource.service.coffee b/app/modules/resources/issues-resource.service.coffee index 31f51822..075f88a4 100644 --- a/app/modules/resources/issues-resource.service.coffee +++ b/app/modules/resources/issues-resource.service.coffee @@ -22,7 +22,7 @@ Resource = (urlsService, http) -> service.listInAllProjects = (params) -> url = urlsService.resolve("issues") - + console.log 'resource issues url', url httpOptions = { headers: { "x-disable-pagination": "1" diff --git a/app/modules/resources/tasks-resource.service.coffee b/app/modules/resources/tasks-resource.service.coffee index e4cc845f..bdb48255 100644 --- a/app/modules/resources/tasks-resource.service.coffee +++ b/app/modules/resources/tasks-resource.service.coffee @@ -22,7 +22,7 @@ Resource = (urlsService, http) -> service.listInAllProjects = (params) -> url = urlsService.resolve("tasks") - + console.log 'resoruce tasks url', url httpOptions = { headers: { "x-disable-pagination": "1" diff --git a/app/partials/includes/components/addnewissue.jade b/app/partials/includes/components/addnewissue.jade new file mode 100644 index 00000000..ee0e0b94 --- /dev/null +++ b/app/partials/includes/components/addnewissue.jade @@ -0,0 +1,13 @@ +tg-svg.add-action( + tg-check-permission="add_task", + title="{{'TASKBOARD.TITLE_ACTION_ADD_ISSUE' | translate}}", + ng-click="ctrl.addNewIssue('standard', issue)", + svg-icon="icon-add" +) + +tg-svg.bulk-action( + title="{{'TASKBOARD.TITLE_ACTION_ADD_ISSUE_BULK' | translate}}", + ng-click="ctrl.addNewIssue('bulk', issue)", + tg-check-permission="add_issue", + svg-icon="icon-bulk" +) diff --git a/app/partials/includes/modules/taskboard-table.jade b/app/partials/includes/modules/taskboard-table.jade index 5c567ff5..944500dd 100644 --- a/app/partials/includes/modules/taskboard-table.jade +++ b/app/partials/includes/modules/taskboard-table.jade @@ -35,14 +35,14 @@ div.taskboard-table( div.taskboard-userstory-box.task-column(tg-bo-title="us.blocked_note") tg-svg.vfold.fold-action( svg-icon="icon-fold-row", - ng-click='foldUs(us)' + ng-click='foldUs(us.id)' title="{{'TASKBOARD.TABLE.TITLE_ACTION_FOLD_ROW' | translate}}" ng-class='{hidden:usFolded[us.id]}' ) tg-svg.vunfold.fold-action( svg-icon="icon-unfold-row", - ng-click='foldUs(us)' + ng-click='foldUs(us.id)' title="{{'TASKBOARD.TABLE.TITLE_ACTION_UNFOLD_ROW' | translate}}" ng-class='{hidden:!usFolded[us.id]}' ) @@ -95,14 +95,14 @@ div.taskboard-table( a.vfold( href="" title="{{'TASKBOARD.TABLE.TITLE_ACTION_FOLD_ROW' | translate}}" - ng-click='foldUs()' + ng-click='foldUs(null)' ng-class="{hidden:usFolded[null]}" ) tg-svg.fold-action(svg-icon="icon-fold-row") a.vunfold( href="" title="{{'TASKBOARD.TABLE.TITLE_ACTION_UNFOLD_ROW' | translate}}" - ng-click='foldUs()' + ng-click='foldUs(null)' ng-class="{hidden:!usFolded[null]}" ) tg-svg.fold-action(svg-icon="icon-unfold-row") @@ -136,3 +136,39 @@ div.taskboard-table( zoom-level="ctrl.zoomLevel" type="task" ) + div.issues-row + div.taskboard-issues-box.task-column + a.vfold( + href="" + title="{{'TASKBOARD.TABLE.TITLE_ACTION_FOLD_ROW' | translate}}" + ng-click='foldUs(0)' + ng-class="{hidden:usFolded[0]}" + ) + tg-svg.fold-action(svg-icon="icon-fold-row") + a.vunfold( + href="" + title="{{'TASKBOARD.TABLE.TITLE_ACTION_UNFOLD_ROW' | translate}}" + ng-click='foldUs(0)' + ng-class="{hidden:!usFolded[0]}" + ) + tg-svg.fold-action(svg-icon="icon-unfold-row") + h3.task-colum-name(translate="TASKBOARD.TABLE.ROW_ISSUES_TITLE") + include ../components/addnewissue.jade + + div.taskboard-tasks-box.issues-cell + div + tg-card.card.ng-animate-disabled( + tg-repeat="issue in milestoneIssues" + ng-class="{'kanban-task-maximized': ctrl.isMaximized(s.id), 'kanban-task-minimized': ctrl.isMinimized(s.id)}" + tg-class-permission="{'readonly': '!modify_task'}" + tg-bind-scope, + on-toggle-fold="ctrl.toggleFold(id)" + on-click-edit="ctrl.editTask(id)" + on-click-delete="ctrl.deleteTask(id)" + on-click-assigned-to="ctrl.changeTaskAssignedTo(id)" + project="project" + item="issue" + zoom="ctrl.zoom" + zoom-level="ctrl.zoomLevel" + type="issue" + ) \ No newline at end of file diff --git a/app/styles/modules/backlog/taskboard-table.scss b/app/styles/modules/backlog/taskboard-table.scss index a09e3d4e..c9b99985 100644 --- a/app/styles/modules/backlog/taskboard-table.scss +++ b/app/styles/modules/backlog/taskboard-table.scss @@ -1,11 +1,13 @@ //Table basic shared vars - +$margin: 5px; $column-width: 300px; $column-flex: 1; $column-shrink: 0; -$column-margin: 0 5px 0 0; +$column-margin: 0 $margin 0 0; $column-padding: .5rem 1rem; +$issues-column-width: $column-width - $margin * 4; + @mixin fold { .card { align-self: flex-start; @@ -133,7 +135,20 @@ $column-padding: .5rem 1rem; &:last-child { margin-right: 0; } + } + .issues-cell { + display: flex; + flex-direction: row; + max-width: inherit; + width: 100%; + .card { + flex-basis: $issues-column-width; + flex-grow: $column-flex; + flex-shrink: 1; + } + } + .row-fold { @include fold; } @@ -174,6 +189,15 @@ $column-padding: .5rem 1rem; } } } + .issues-row { + display: flex; + margin-bottom: .25rem; + // margin-top: 1rem; + // border-top: 1rem solid; + // border-top-color: black; + min-height: 10rem; + width: auto; + } .taskboard-userstory-box { padding: .5rem .5rem .5rem 1.5rem; }