Initial drag and drop support for backlog.
parent
42acdbb01b
commit
c3da3a21dd
|
@ -169,6 +169,7 @@ LoginDirective = ($auth, $confirm, $location) ->
|
||||||
|
|
||||||
$el.on "submit", (event) ->
|
$el.on "submit", (event) ->
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
console.log "kaka"
|
||||||
submit()
|
submit()
|
||||||
|
|
||||||
$el.on "click", "a.button-login", (event) ->
|
$el.on "click", "a.button-login", (event) ->
|
||||||
|
|
|
@ -58,7 +58,7 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
|
||||||
@scope.$on("sprintform:create:success", @.loadSprints)
|
@scope.$on("sprintform:create:success", @.loadSprints)
|
||||||
@scope.$on("sprintform:create:success", @.loadProjectStats)
|
@scope.$on("sprintform:create:success", @.loadProjectStats)
|
||||||
@scope.$on("sprintform:remove:success", @.loadSprints)
|
@scope.$on("sprintform:remove:success", @.loadSprints)
|
||||||
@scope.$on("sprintform:remove:success", @.loadProjectStats)
|
@scope.$on("sprintform:remove:success", @.loadProjectStats)
|
||||||
@scope.$on("usform:new:success", @.loadUserstories)
|
@scope.$on("usform:new:success", @.loadUserstories)
|
||||||
@scope.$on("usform:edit:success", @.loadUserstories)
|
@scope.$on("usform:edit:success", @.loadUserstories)
|
||||||
|
|
||||||
|
@ -171,7 +171,6 @@ class BacklogController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.F
|
||||||
console.log "statuses", obj
|
console.log "statuses", obj
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
console.log @scope.filters.statuses
|
|
||||||
return @scope.filters
|
return @scope.filters
|
||||||
|
|
||||||
## Template actions
|
## Template actions
|
||||||
|
@ -249,76 +248,6 @@ BacklogDirective = ($repo, $rootscope) ->
|
||||||
$scope.$on("userstories:loaded", reloadDoomlineLocation)
|
$scope.$on("userstories:loaded", reloadDoomlineLocation)
|
||||||
$scope.$on("doomline:redraw", reloadDoomlineLocation)
|
$scope.$on("doomline:redraw", reloadDoomlineLocation)
|
||||||
|
|
||||||
#########################
|
|
||||||
## Drag & Drop Link
|
|
||||||
#########################
|
|
||||||
|
|
||||||
linkSortable = ($scope, $el, $attrs, $ctrl) ->
|
|
||||||
resortAndSave = ->
|
|
||||||
toSave = []
|
|
||||||
for item, i in $scope.userstories
|
|
||||||
if item.order == i
|
|
||||||
continue
|
|
||||||
item.order = i
|
|
||||||
|
|
||||||
toSave = _.filter($scope.userstories, (x) -> x.isModified())
|
|
||||||
$repo.saveAll(toSave).then ->
|
|
||||||
console.log "FINISHED", arguments
|
|
||||||
|
|
||||||
onUpdateItem = (event) ->
|
|
||||||
console.log "onUpdate", event
|
|
||||||
|
|
||||||
item = angular.element(event.item)
|
|
||||||
itemScope = item.scope()
|
|
||||||
|
|
||||||
ids = _.map($scope.userstories, "id")
|
|
||||||
index = ids.indexOf(itemScope.us.id)
|
|
||||||
|
|
||||||
$scope.userstories.splice(index, 1)
|
|
||||||
$scope.userstories.splice(item.index(), 0, itemScope.us)
|
|
||||||
|
|
||||||
resortAndSave()
|
|
||||||
|
|
||||||
onAddItem = (event) ->
|
|
||||||
console.log "onAddItem", event
|
|
||||||
item = angular.element(event.item)
|
|
||||||
itemScope = item.scope()
|
|
||||||
itemIndex = item.index()
|
|
||||||
|
|
||||||
itemScope.us.milestone = null
|
|
||||||
userstories = $scope.userstories
|
|
||||||
userstories.splice(itemIndex, 0, itemScope.us)
|
|
||||||
|
|
||||||
item.remove()
|
|
||||||
item.off()
|
|
||||||
|
|
||||||
$scope.$apply()
|
|
||||||
resortAndSave()
|
|
||||||
|
|
||||||
onRemoveItem = (event) ->
|
|
||||||
console.log "onRemoveItem", event
|
|
||||||
item = angular.element(event.item)
|
|
||||||
itemScope = item.scope()
|
|
||||||
|
|
||||||
ids = _.map($scope.userstories, "id")
|
|
||||||
index = ids.indexOf(itemScope.us.id)
|
|
||||||
|
|
||||||
if index != -1
|
|
||||||
userstories = $scope.userstories
|
|
||||||
userstories.splice(index, 1)
|
|
||||||
|
|
||||||
item.off()
|
|
||||||
itemScope.$destroy()
|
|
||||||
|
|
||||||
dom = $el.find(".backlog-table-body")
|
|
||||||
sortable = new Sortable(dom[0], {
|
|
||||||
group: "backlog",
|
|
||||||
selector: ".us-item-row",
|
|
||||||
onUpdate: onUpdateItem
|
|
||||||
onAdd: onAddItem
|
|
||||||
onRemove: onRemoveItem
|
|
||||||
})
|
|
||||||
|
|
||||||
##############################
|
##############################
|
||||||
## Move to current sprint link
|
## Move to current sprint link
|
||||||
##############################
|
##############################
|
||||||
|
@ -335,6 +264,7 @@ BacklogDirective = ($repo, $rootscope) ->
|
||||||
|
|
||||||
# Add them to current sprint
|
# Add them to current sprint
|
||||||
$scope.sprints[0].user_stories = _.union(selectedUss, $scope.sprints[0].user_stories)
|
$scope.sprints[0].user_stories = _.union(selectedUss, $scope.sprints[0].user_stories)
|
||||||
|
|
||||||
# Update the total of points
|
# Update the total of points
|
||||||
$scope.sprints[0].total_points += totalExtraPoints
|
$scope.sprints[0].total_points += totalExtraPoints
|
||||||
|
|
||||||
|
@ -391,127 +321,16 @@ BacklogDirective = ($repo, $rootscope) ->
|
||||||
$ctrl = $el.controller()
|
$ctrl = $el.controller()
|
||||||
|
|
||||||
linkToolbar($scope, $el, $attrs, $ctrl)
|
linkToolbar($scope, $el, $attrs, $ctrl)
|
||||||
linkSortable($scope, $el, $attrs, $ctrl)
|
|
||||||
linkFilters($scope, $el, $attrs, $ctrl)
|
linkFilters($scope, $el, $attrs, $ctrl)
|
||||||
linkDoomLine($scope, $el, $attrs, $ctrl)
|
linkDoomLine($scope, $el, $attrs, $ctrl)
|
||||||
|
|
||||||
$scope.$on "$destroy", ->
|
$el.find(".backlog-table-body").disableSelection()
|
||||||
$el.off()
|
|
||||||
|
|
||||||
return {link: link}
|
|
||||||
|
|
||||||
#############################################################################
|
|
||||||
## Sprint Directive
|
|
||||||
#############################################################################
|
|
||||||
|
|
||||||
BacklogSprintDirective = ($repo, $rootscope) ->
|
|
||||||
|
|
||||||
#########################
|
|
||||||
## Common parts
|
|
||||||
#########################
|
|
||||||
|
|
||||||
linkCommon = ($scope, $el, $attrs, $ctrl) ->
|
|
||||||
sprint = $scope.$eval($attrs.tgBacklogSprint)
|
|
||||||
if $scope.$first
|
|
||||||
$el.addClass("sprint-current")
|
|
||||||
$el.find(".sprint-table").addClass('open')
|
|
||||||
|
|
||||||
else if sprint.closed
|
|
||||||
$el.addClass("sprint-closed")
|
|
||||||
|
|
||||||
else if not $scope.$first and not sprint.closed
|
|
||||||
$el.addClass("sprint-old-open")
|
|
||||||
|
|
||||||
# Update progress bars
|
|
||||||
progressPercentage = Math.round(100 * (sprint.closed_points / sprint.total_points))
|
|
||||||
$el.find(".current-progress").css("width", "#{progressPercentage}%")
|
|
||||||
|
|
||||||
# Event Handlers
|
|
||||||
$el.on "click", ".sprint-name > .icon-arrow-up", (event) ->
|
|
||||||
target = $(event.currentTarget)
|
|
||||||
target.toggleClass('active')
|
|
||||||
$el.find(".sprint-table").toggleClass('open')
|
|
||||||
|
|
||||||
$el.on "click", ".sprint-name > .icon-edit", (event) ->
|
|
||||||
$rootscope.$broadcast("sprintform:edit", sprint)
|
|
||||||
|
|
||||||
#########################
|
|
||||||
## Drag & Drop Link
|
|
||||||
#########################
|
|
||||||
|
|
||||||
linkSortable = ($scope, $el, $attrs, $ctrl) ->
|
|
||||||
resortAndSave = ->
|
|
||||||
toSave = []
|
|
||||||
for item, i in $scope.sprint.user_stories
|
|
||||||
if item.order == i
|
|
||||||
continue
|
|
||||||
item.order = i
|
|
||||||
|
|
||||||
toSave = _.filter($scope.sprint.user_stories, (x) -> x.isModified())
|
|
||||||
$repo.saveAll(toSave).then ->
|
|
||||||
console.log "FINISHED", arguments
|
|
||||||
|
|
||||||
onUpdateItem = (event) ->
|
|
||||||
item = angular.element(event.item)
|
|
||||||
itemScope = item.scope()
|
|
||||||
|
|
||||||
ids = _.map($scope.sprint.user_stories, {"id": itemScope.us.id})
|
|
||||||
index = ids.indexOf(itemScope.us.id)
|
|
||||||
|
|
||||||
$scope.sprint.user_stories.splice(index, 1)
|
|
||||||
$scope.sprint.user_stories.splice(item.index(), 0, itemScope.us)
|
|
||||||
resortAndSave()
|
|
||||||
|
|
||||||
onAddItem = (event) ->
|
|
||||||
item = angular.element(event.item)
|
|
||||||
itemScope = item.scope()
|
|
||||||
itemIndex = item.index()
|
|
||||||
|
|
||||||
itemScope.us.milestone = $scope.sprint.id
|
|
||||||
userstories = $scope.sprint.user_stories
|
|
||||||
userstories.splice(itemIndex, 0, itemScope.us)
|
|
||||||
|
|
||||||
item.remove()
|
|
||||||
item.off()
|
|
||||||
|
|
||||||
$scope.$apply()
|
|
||||||
resortAndSave()
|
|
||||||
|
|
||||||
onRemoveItem = (event) ->
|
|
||||||
item = angular.element(event.item)
|
|
||||||
itemScope = item.scope()
|
|
||||||
|
|
||||||
ids = _.map($scope.sprint.user_stories, "id")
|
|
||||||
index = ids.indexOf(itemScope.us.id)
|
|
||||||
|
|
||||||
if index != -1
|
|
||||||
userstories = $scope.sprint.user_stories
|
|
||||||
userstories.splice(index, 1)
|
|
||||||
|
|
||||||
item.off()
|
|
||||||
itemScope.$destroy()
|
|
||||||
|
|
||||||
dom = $el.find(".sprint-table")
|
|
||||||
|
|
||||||
sortable = new Sortable(dom[0], {
|
|
||||||
group: "backlog",
|
|
||||||
selector: ".milestone-us-item-row",
|
|
||||||
onUpdate: onUpdateItem,
|
|
||||||
onAdd: onAddItem,
|
|
||||||
onRemove: onRemoveItem,
|
|
||||||
})
|
|
||||||
|
|
||||||
link = ($scope, $el, $attrs) ->
|
|
||||||
$ctrl = $el.closest("div.wrapper").controller()
|
|
||||||
linkSortable($scope, $el, $attrs, $ctrl)
|
|
||||||
linkCommon($scope, $el, $attrs, $ctrl)
|
|
||||||
|
|
||||||
$scope.$on "$destroy", ->
|
$scope.$on "$destroy", ->
|
||||||
$el.off()
|
$el.off()
|
||||||
|
|
||||||
return {link: link}
|
return {link: link}
|
||||||
|
|
||||||
|
|
||||||
#############################################################################
|
#############################################################################
|
||||||
## User story points directive
|
## User story points directive
|
||||||
#############################################################################
|
#############################################################################
|
||||||
|
@ -577,6 +396,7 @@ UsRolePointsSelectorDirective = ($rootscope) ->
|
||||||
|
|
||||||
return {link: link}
|
return {link: link}
|
||||||
|
|
||||||
|
|
||||||
UsPointsDirective = ($repo) ->
|
UsPointsDirective = ($repo) ->
|
||||||
selectionTemplate = _.template("""
|
selectionTemplate = _.template("""
|
||||||
<ul class="popover pop-role">
|
<ul class="popover pop-role">
|
||||||
|
@ -822,9 +642,7 @@ GmBacklogGraphDirective = ->
|
||||||
|
|
||||||
return {link: link}
|
return {link: link}
|
||||||
|
|
||||||
|
|
||||||
module.directive("tgBacklog", ["$tgRepo", "$rootScope", BacklogDirective])
|
module.directive("tgBacklog", ["$tgRepo", "$rootScope", BacklogDirective])
|
||||||
module.directive("tgBacklogSprint", ["$tgRepo", "$rootScope", BacklogSprintDirective])
|
|
||||||
module.directive("tgUsPoints", ["$tgRepo", UsPointsDirective])
|
module.directive("tgUsPoints", ["$tgRepo", UsPointsDirective])
|
||||||
module.directive("tgUsRolePointsSelector", ["$rootScope", UsRolePointsSelectorDirective])
|
module.directive("tgUsRolePointsSelector", ["$rootScope", UsRolePointsSelectorDirective])
|
||||||
module.directive("tgGmBacklogGraph", GmBacklogGraphDirective)
|
module.directive("tgGmBacklogGraph", GmBacklogGraphDirective)
|
||||||
|
|
|
@ -0,0 +1,228 @@
|
||||||
|
###
|
||||||
|
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||||
|
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||||
|
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||||
|
#
|
||||||
|
# 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: modules/backlog/sortable.coffee
|
||||||
|
###
|
||||||
|
|
||||||
|
taiga = @.taiga
|
||||||
|
|
||||||
|
mixOf = @.taiga.mixOf
|
||||||
|
toggleText = @.taiga.toggleText
|
||||||
|
scopeDefer = @.taiga.scopeDefer
|
||||||
|
bindOnce = @.taiga.bindOnce
|
||||||
|
groupBy = @.taiga.groupBy
|
||||||
|
|
||||||
|
module = angular.module("taigaBacklog")
|
||||||
|
|
||||||
|
|
||||||
|
#############################################################################
|
||||||
|
## Sortable Directive
|
||||||
|
#############################################################################
|
||||||
|
|
||||||
|
BacklogSortableDirective = ($repo, $rs, $rootscope) ->
|
||||||
|
|
||||||
|
#########################
|
||||||
|
## Drag & Drop Link
|
||||||
|
#########################
|
||||||
|
# http://stackoverflow.com/questions/5791886/jquery-draggable-shows-
|
||||||
|
# helper-in-wrong-place-when-scrolled-down-page
|
||||||
|
|
||||||
|
resort = (uses) ->
|
||||||
|
items = []
|
||||||
|
for item, index in uses
|
||||||
|
item.order = index
|
||||||
|
if item.isModified()
|
||||||
|
items.push(item)
|
||||||
|
|
||||||
|
return items
|
||||||
|
|
||||||
|
prepareBulkUpdateData = (uses) ->
|
||||||
|
return _.map(uses, (x) -> [x.id, x.order])
|
||||||
|
|
||||||
|
linkSortable = ($scope, $el, $attrs, $ctrl) ->
|
||||||
|
# State
|
||||||
|
oldParentScope = null
|
||||||
|
newParentScope = null
|
||||||
|
itemEl = null
|
||||||
|
tdom = $el
|
||||||
|
|
||||||
|
tdom.sortable({
|
||||||
|
# handle: ".icon-drag-v",
|
||||||
|
items: "div.sprint-table > div.row, .backlog-table-body > div.row"
|
||||||
|
})
|
||||||
|
|
||||||
|
tdom.on "sortstop", (event, ui) ->
|
||||||
|
# Common state for stop event handler
|
||||||
|
parentEl = ui.item.parent()
|
||||||
|
itemEl = ui.item
|
||||||
|
itemUs = itemEl.scope().us
|
||||||
|
itemIndex = itemEl.index()
|
||||||
|
newParentScope = parentEl.scope()
|
||||||
|
|
||||||
|
if itemEl.is(".milestone-us-item-row") and parentEl.is(".backlog-table-body")
|
||||||
|
itemUs.milestone = null
|
||||||
|
|
||||||
|
# Completelly remove item and its scope from dom
|
||||||
|
itemEl.scope().$destroy()
|
||||||
|
itemEl.off()
|
||||||
|
itemEl.remove()
|
||||||
|
|
||||||
|
$scope.$apply ->
|
||||||
|
# Add new us to backlog userstories list
|
||||||
|
newParentScope.userstories.splice(itemIndex, 0, itemUs)
|
||||||
|
newParentScope.visibleUserstories.splice(itemIndex, 0, itemUs)
|
||||||
|
|
||||||
|
# Execute the prefiltering of user stories
|
||||||
|
$ctrl.filterVisibleUserstories()
|
||||||
|
|
||||||
|
# Remove the us from the sprint list.
|
||||||
|
r = oldParentScope.sprint.user_stories.indexOf(itemUs)
|
||||||
|
oldParentScope.sprint.user_stories.splice(r, 1)
|
||||||
|
|
||||||
|
# Persist the milestone change of userstory
|
||||||
|
promise = $repo.save(itemUs)
|
||||||
|
|
||||||
|
# Rehash userstories order field
|
||||||
|
# and persist in bulk all changes.
|
||||||
|
promise = promise.then ->
|
||||||
|
projectId = $scope.projectId
|
||||||
|
|
||||||
|
items = resort(newParentScope.userstories)
|
||||||
|
data = prepareBulkUpdateData(items)
|
||||||
|
|
||||||
|
return $rs.userstories.bulkUpdateOrder(projectId, data)
|
||||||
|
|
||||||
|
promise.then null, ->
|
||||||
|
# TODO
|
||||||
|
console.log "FAIL"
|
||||||
|
|
||||||
|
else if itemEl.is(".us-item-row") and parentEl.is(".sprint-table")
|
||||||
|
|
||||||
|
# Completelly remove item and its scope from dom
|
||||||
|
itemEl.scope().$destroy()
|
||||||
|
itemEl.off()
|
||||||
|
itemEl.remove()
|
||||||
|
|
||||||
|
itemUs.milestone = newParentScope.sprint.id
|
||||||
|
|
||||||
|
$scope.$apply ->
|
||||||
|
# Add moving us to sprint user stories list
|
||||||
|
newParentScope.sprint.user_stories.splice(itemIndex, 0, itemUs)
|
||||||
|
|
||||||
|
# Remove moving us from backlog userstories lists.
|
||||||
|
r = oldParentScope.visibleUserstories.indexOf(itemUs)
|
||||||
|
oldParentScope.visibleUserstories.splice(r, 1)
|
||||||
|
r = oldParentScope.userstories.indexOf(itemUs)
|
||||||
|
oldParentScope.userstories.splice(r, 1)
|
||||||
|
|
||||||
|
# Persist the milestone change of userstory
|
||||||
|
promise = $repo.save(itemUs)
|
||||||
|
|
||||||
|
# Rehash userstories order field
|
||||||
|
# and persist in bulk all changes.
|
||||||
|
promise = promise.then ->
|
||||||
|
projectId = $scope.projectId
|
||||||
|
items = resort(newParentScope.sprint.user_stories)
|
||||||
|
data = prepareBulkUpdateData(items)
|
||||||
|
return $rs.userstories.bulkUpdateOrder(projectId, data)
|
||||||
|
|
||||||
|
# TODO: handle properly the error
|
||||||
|
promise.then null, ->
|
||||||
|
console.log "FAIL"
|
||||||
|
|
||||||
|
else if parentEl.is(".sprint-table") and newParentScope.sprint.id != oldParentScope.sprint.id
|
||||||
|
itemUs.milestone = newParentScope.sprint.id
|
||||||
|
|
||||||
|
# Completelly remove item and its scope from dom
|
||||||
|
itemEl.scope().$destroy()
|
||||||
|
itemEl.off()
|
||||||
|
itemEl.remove()
|
||||||
|
|
||||||
|
$scope.$apply ->
|
||||||
|
# Add new us to backlog userstories list
|
||||||
|
newParentScope.sprint.user_stories.splice(itemIndex, 0, itemUs)
|
||||||
|
|
||||||
|
# Remove the us from the sprint list.
|
||||||
|
r = oldParentScope.sprint.user_stories.indexOf(itemUs)
|
||||||
|
oldParentScope.sprint.user_stories.splice(r, 1)
|
||||||
|
|
||||||
|
# Persist the milestone change of userstory
|
||||||
|
promise = $repo.save(itemUs)
|
||||||
|
|
||||||
|
# Rehash userstories order field
|
||||||
|
# and persist in bulk all changes.
|
||||||
|
promise = promise.then ->
|
||||||
|
projectId = $scope.projectId
|
||||||
|
|
||||||
|
items = resort(newParentScope.sprint.user_stories)
|
||||||
|
data = prepareBulkUpdateData(items)
|
||||||
|
return $rs.userstories.bulkUpdateOrder(projectId, data)
|
||||||
|
|
||||||
|
promise.then null, ->
|
||||||
|
# TODO
|
||||||
|
console.log "FAIL"
|
||||||
|
|
||||||
|
else
|
||||||
|
items = null
|
||||||
|
userstories = null
|
||||||
|
|
||||||
|
if parentEl.is(".backlog-table-body")
|
||||||
|
userstories = newParentScope.userstories
|
||||||
|
else
|
||||||
|
userstories = newParentScope.sprint.user_stories
|
||||||
|
|
||||||
|
$scope.$apply ->
|
||||||
|
r = userstories.indexOf(itemUs)
|
||||||
|
userstories.splice(r, 1)
|
||||||
|
userstories.splice(itemIndex, 0, itemUs)
|
||||||
|
|
||||||
|
# Rehash userstories order field
|
||||||
|
items = resort(userstories)
|
||||||
|
data = prepareBulkUpdateData(items)
|
||||||
|
|
||||||
|
# Persist in bulk all affected
|
||||||
|
# userstories with order change
|
||||||
|
promise = $rs.userstories.bulkUpdateOrder($scope.projectId, data)
|
||||||
|
promise.then null, ->
|
||||||
|
console.log "FAIL"
|
||||||
|
|
||||||
|
tdom.on "sortstart", (event, ui) ->
|
||||||
|
oldParentScope = ui.item.parent().scope()
|
||||||
|
|
||||||
|
tdom.on "sort", (event, ui) ->
|
||||||
|
ui.helper.css("background-color", "#ddd")
|
||||||
|
|
||||||
|
tdom.on "sortbeforestop", (event, ui) ->
|
||||||
|
ui.helper.css("background-color", "transparent")
|
||||||
|
|
||||||
|
link = ($scope, $el, $attrs) ->
|
||||||
|
$ctrl = $el.controller()
|
||||||
|
linkSortable($scope, $el, $attrs, $ctrl)
|
||||||
|
|
||||||
|
$scope.$on "$destroy", ->
|
||||||
|
$el.off()
|
||||||
|
|
||||||
|
return {link: link}
|
||||||
|
|
||||||
|
|
||||||
|
module.directive("tgBacklogSortable", [
|
||||||
|
"$tgRepo",
|
||||||
|
"$tgResources",
|
||||||
|
"$rootScope",
|
||||||
|
BacklogSortableDirective
|
||||||
|
])
|
|
@ -0,0 +1,144 @@
|
||||||
|
###
|
||||||
|
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||||
|
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
|
||||||
|
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
|
||||||
|
#
|
||||||
|
# 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: modules/backlog/sprints.coffee
|
||||||
|
###
|
||||||
|
|
||||||
|
taiga = @.taiga
|
||||||
|
|
||||||
|
mixOf = @.taiga.mixOf
|
||||||
|
toggleText = @.taiga.toggleText
|
||||||
|
scopeDefer = @.taiga.scopeDefer
|
||||||
|
bindOnce = @.taiga.bindOnce
|
||||||
|
groupBy = @.taiga.groupBy
|
||||||
|
|
||||||
|
module = angular.module("taigaBacklog")
|
||||||
|
|
||||||
|
#############################################################################
|
||||||
|
## Sprint Directive
|
||||||
|
#############################################################################
|
||||||
|
|
||||||
|
BacklogSprintDirective = ($repo, $rootscope) ->
|
||||||
|
|
||||||
|
#########################
|
||||||
|
## Common parts
|
||||||
|
#########################
|
||||||
|
|
||||||
|
linkCommon = ($scope, $el, $attrs, $ctrl) ->
|
||||||
|
sprint = $scope.$eval($attrs.tgBacklogSprint)
|
||||||
|
if $scope.$first
|
||||||
|
$el.addClass("sprint-current")
|
||||||
|
$el.find(".sprint-table").addClass('open')
|
||||||
|
|
||||||
|
else if sprint.closed
|
||||||
|
$el.addClass("sprint-closed")
|
||||||
|
|
||||||
|
else if not $scope.$first and not sprint.closed
|
||||||
|
$el.addClass("sprint-old-open")
|
||||||
|
|
||||||
|
# Update progress bars
|
||||||
|
progressPercentage = Math.round(100 * (sprint.closed_points / sprint.total_points))
|
||||||
|
$el.find(".current-progress").css("width", "#{progressPercentage}%")
|
||||||
|
|
||||||
|
$el.find(".sprint-table").disableSelection()
|
||||||
|
|
||||||
|
# Event Handlers
|
||||||
|
$el.on "click", ".sprint-name > .icon-arrow-up", (event) ->
|
||||||
|
target = $(event.currentTarget)
|
||||||
|
target.toggleClass('active')
|
||||||
|
$el.find(".sprint-table").toggleClass('open')
|
||||||
|
|
||||||
|
$el.on "click", ".sprint-name > .icon-edit", (event) ->
|
||||||
|
$rootscope.$broadcast("sprintform:edit", sprint)
|
||||||
|
|
||||||
|
#########################
|
||||||
|
## Drag & Drop Link
|
||||||
|
#########################
|
||||||
|
|
||||||
|
# linkSortable = ($scope, $el, $attrs, $ctrl) ->
|
||||||
|
# resortAndSave = ->
|
||||||
|
# toSave = []
|
||||||
|
# for item, i in $scope.sprint.user_stories
|
||||||
|
# if item.order == i
|
||||||
|
# continue
|
||||||
|
# item.order = i
|
||||||
|
|
||||||
|
# toSave = _.filter($scope.sprint.user_stories, (x) -> x.isModified())
|
||||||
|
# $repo.saveAll(toSave).then ->
|
||||||
|
# console.log "FINISHED", arguments
|
||||||
|
|
||||||
|
# onUpdateItem = (event) ->
|
||||||
|
# item = angular.element(event.item)
|
||||||
|
# itemScope = item.scope()
|
||||||
|
|
||||||
|
# ids = _.map($scope.sprint.user_stories, {"id": itemScope.us.id})
|
||||||
|
# index = ids.indexOf(itemScope.us.id)
|
||||||
|
|
||||||
|
# $scope.sprint.user_stories.splice(index, 1)
|
||||||
|
# $scope.sprint.user_stories.splice(item.index(), 0, itemScope.us)
|
||||||
|
# resortAndSave()
|
||||||
|
|
||||||
|
# onAddItem = (event) ->
|
||||||
|
# item = angular.element(event.item)
|
||||||
|
# itemScope = item.scope()
|
||||||
|
# itemIndex = item.index()
|
||||||
|
|
||||||
|
# itemScope.us.milestone = $scope.sprint.id
|
||||||
|
# userstories = $scope.sprint.user_stories
|
||||||
|
# userstories.splice(itemIndex, 0, itemScope.us)
|
||||||
|
|
||||||
|
# item.remove()
|
||||||
|
# item.off()
|
||||||
|
|
||||||
|
# $scope.$apply()
|
||||||
|
# resortAndSave()
|
||||||
|
|
||||||
|
# onRemoveItem = (event) ->
|
||||||
|
# item = angular.element(event.item)
|
||||||
|
# itemScope = item.scope()
|
||||||
|
|
||||||
|
# ids = _.map($scope.sprint.user_stories, "id")
|
||||||
|
# index = ids.indexOf(itemScope.us.id)
|
||||||
|
|
||||||
|
# if index != -1
|
||||||
|
# userstories = $scope.sprint.user_stories
|
||||||
|
# userstories.splice(index, 1)
|
||||||
|
|
||||||
|
# item.off()
|
||||||
|
# itemScope.$destroy()
|
||||||
|
|
||||||
|
# dom = $el.find(".sprint-table")
|
||||||
|
|
||||||
|
# sortable = new Sortable(dom[0], {
|
||||||
|
# group: "backlog",
|
||||||
|
# selector: ".milestone-us-item-row",
|
||||||
|
# onUpdate: onUpdateItem,
|
||||||
|
# onAdd: onAddItem,
|
||||||
|
# onRemove: onRemoveItem,
|
||||||
|
# })
|
||||||
|
|
||||||
|
link = ($scope, $el, $attrs) ->
|
||||||
|
$ctrl = $el.closest("div.wrapper").controller()
|
||||||
|
linkCommon($scope, $el, $attrs, $ctrl)
|
||||||
|
|
||||||
|
$scope.$on "$destroy", ->
|
||||||
|
$el.off()
|
||||||
|
|
||||||
|
return {link: link}
|
||||||
|
|
||||||
|
module.directive("tgBacklogSprint", ["$tgRepo", "$rootScope", BacklogSprintDirective])
|
|
@ -33,6 +33,11 @@ resourceProvider = ($repo, $http, $urls) ->
|
||||||
params = {projectId: projectId, bulkStories: data}
|
params = {projectId: projectId, bulkStories: data}
|
||||||
return $http.post(url, params)
|
return $http.post(url, params)
|
||||||
|
|
||||||
|
service.bulkUpdateOrder = (projectId, data) ->
|
||||||
|
url = $urls.resolve("bulk-update-us-order")
|
||||||
|
params = {projectId: projectId, bulkStories: data}
|
||||||
|
return $http.post(url, params)
|
||||||
|
|
||||||
return (instance) ->
|
return (instance) ->
|
||||||
instance.userstories = service
|
instance.userstories = service
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -5,13 +5,13 @@ block head
|
||||||
|
|
||||||
block content
|
block content
|
||||||
div.wrapper(tg-backlog, ng-controller="BacklogController as ctrl",
|
div.wrapper(tg-backlog, ng-controller="BacklogController as ctrl",
|
||||||
ng-init="section='backlog'")
|
ng-init="section='backlog'", tg-backlog-sortable)
|
||||||
sidebar.menu-secondary.extrabar.filters-bar(tg-backlog-filters)
|
sidebar.menu-secondary.extrabar.filters-bar(tg-backlog-filters)
|
||||||
include views/modules/backlog-filters
|
include views/modules/backlog-filters
|
||||||
section.main.backlog
|
section.main.backlog
|
||||||
include views/components/mainTitle
|
include views/components/mainTitle
|
||||||
include views/components/summary
|
include views/components/summary
|
||||||
div.graphics-container
|
div.graphics-container.burndown-container
|
||||||
div.burndown(tg-gm-backlog-graph)
|
div.burndown(tg-gm-backlog-graph)
|
||||||
include views/modules/burndown
|
include views/modules/burndown
|
||||||
div.backlog-menu
|
div.backlog-menu
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
div.row.us-item-row(ng-repeat="us in visibleUserstories track by us.id")
|
div.row.us-item-row(ng-repeat="us in visibleUserstories|orderBy:order track by us.id", tg-draggable)
|
||||||
div.user-stories
|
div.user-stories
|
||||||
div.user-story-tags
|
div.user-story-tags
|
||||||
span.tag(ng-repeat="tag in us.tags") {{ tag }}
|
span.tag(ng-repeat="tag in us.tags") {{ tag }}
|
||||||
|
|
|
@ -11,6 +11,7 @@ div.login-form-container(tg-login)
|
||||||
|
|
||||||
fieldset
|
fieldset
|
||||||
a.button.button-login.button-gray(href="", ng-click="ctrl.submit()", title="Sign in") Sign in
|
a.button.button-login.button-gray(href="", ng-click="ctrl.submit()", title="Sign in") Sign in
|
||||||
|
input(type="submit", style="display:none")
|
||||||
|
|
||||||
p.login-text
|
p.login-text
|
||||||
span Not registered yet?
|
span Not registered yet?
|
||||||
|
|
|
@ -28,7 +28,7 @@ section.sprints
|
||||||
div.sprint-progress-bar
|
div.sprint-progress-bar
|
||||||
div.current-progress(tg-sprint-progressbar="sprint")
|
div.current-progress(tg-sprint-progressbar="sprint")
|
||||||
div.sprint-table
|
div.sprint-table
|
||||||
div.row.milestone-us-item-row(ng-repeat="us in sprint.user_stories track by us.id")
|
div.row.milestone-us-item-row(ng-repeat="us in sprint.user_stories|orderBy:order track by us.id")
|
||||||
div.column-us.width-8
|
div.column-us.width-8
|
||||||
a.us-name(href="", title="")
|
a.us-name(href="", title="")
|
||||||
span(tg-bo-ref="us.ref")
|
span(tg-bo-ref="us.ref")
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
.backlog-table-body {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
.backlog-table-header,
|
.backlog-table-header,
|
||||||
.backlog-table-body {
|
.backlog-table-body {
|
||||||
@include table-flex();
|
@include table-flex();
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
.burndown {
|
.burndown {
|
||||||
height: 200px;
|
|
||||||
margin-bottom: 2rem;
|
margin-bottom: 2rem;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
.burndown-container {
|
||||||
|
height: 240px
|
||||||
|
}
|
||||||
|
|
|
@ -23,7 +23,11 @@
|
||||||
}
|
}
|
||||||
.sprint {
|
.sprint {
|
||||||
margin-bottom: 2rem;
|
margin-bottom: 2rem;
|
||||||
position: relative;
|
|
||||||
|
header {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
.icon-edit {
|
.icon-edit {
|
||||||
@include transition (opacity .2s ease-in);
|
@include transition (opacity .2s ease-in);
|
||||||
|
|
|
@ -70,15 +70,14 @@
|
||||||
"moment": "~2.6.0",
|
"moment": "~2.6.0",
|
||||||
"isMobile": "~0.3.1",
|
"isMobile": "~0.3.1",
|
||||||
"favico.js": "0.3.4",
|
"favico.js": "0.3.4",
|
||||||
"Sortable": "~0.1.8"
|
"Sortable": "~0.1.8",
|
||||||
|
"pikaday": "~1.2.0",
|
||||||
|
"jquery-ui": "~1.11.0"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"lodash": "~2.4.1",
|
"lodash": "~2.4.1",
|
||||||
"moment": "~2.6.0",
|
"moment": "~2.6.0",
|
||||||
"jquery": "~2.1.1"
|
"jquery": "~2.1.1"
|
||||||
},
|
},
|
||||||
"private": true,
|
"private": true
|
||||||
"devDependencies": {
|
|
||||||
"pikaday": "~1.2.0"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,6 +66,7 @@ paths = {
|
||||||
"app/vendor/jquery-flot/jquery.flot.time.js",
|
"app/vendor/jquery-flot/jquery.flot.time.js",
|
||||||
"app/vendor/jquery-textcomplete/jquery.textcomplete.js",
|
"app/vendor/jquery-textcomplete/jquery.textcomplete.js",
|
||||||
"app/vendor/markitup/markitup/jquery.markitup.js"
|
"app/vendor/markitup/markitup/jquery.markitup.js"
|
||||||
|
"app/js/jquery.ui.git.js",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue