Merge branch 'kanban-cards'
commit
4c354e1399
|
@ -593,6 +593,9 @@ BacklogDirective = ($repo, $rootscope) ->
|
||||||
|
|
||||||
return {link: link}
|
return {link: link}
|
||||||
|
|
||||||
|
|
||||||
|
module.directive("tgBacklog", ["$tgRepo", "$rootScope", BacklogDirective])
|
||||||
|
|
||||||
#############################################################################
|
#############################################################################
|
||||||
## User story points directive
|
## User story points directive
|
||||||
#############################################################################
|
#############################################################################
|
||||||
|
@ -600,13 +603,15 @@ BacklogDirective = ($repo, $rootscope) ->
|
||||||
UsRolePointsSelectorDirective = ($rootscope) ->
|
UsRolePointsSelectorDirective = ($rootscope) ->
|
||||||
#TODO: i18n
|
#TODO: i18n
|
||||||
selectionTemplate = _.template("""
|
selectionTemplate = _.template("""
|
||||||
<ul class="popover pop-role">
|
<ul class="popover pop-role">
|
||||||
<li><a class="clear-selection" href="" title="All">All</a></li>
|
<li><a class="clear-selection" href="" title="All">All</a></li>
|
||||||
<% _.each(roles, function(role) { %>
|
<% _.each(roles, function(role) { %>
|
||||||
<li><a href="" class="role" title="<%- role.name %>"
|
<li>
|
||||||
data-role-id="<%- role.id %>"><%- role.name %></a></li>
|
<a href="" class="role" title="<%- role.name %>"
|
||||||
<% }); %>
|
data-role-id="<%- role.id %>"><%- role.name %></a>
|
||||||
</ul>
|
</li>
|
||||||
|
<% }); %>
|
||||||
|
</ul>
|
||||||
""")
|
""")
|
||||||
|
|
||||||
link = ($scope, $el, $attrs) ->
|
link = ($scope, $el, $attrs) ->
|
||||||
|
@ -616,7 +621,7 @@ UsRolePointsSelectorDirective = ($rootscope) ->
|
||||||
numberOfRoles = _.size(roles)
|
numberOfRoles = _.size(roles)
|
||||||
|
|
||||||
if numberOfRoles > 1
|
if numberOfRoles > 1
|
||||||
$el.append(selectionTemplate({ 'roles': roles }))
|
$el.append(selectionTemplate({"roles":roles}))
|
||||||
else
|
else
|
||||||
$el.find(".icon-arrow-bottom").remove()
|
$el.find(".icon-arrow-bottom").remove()
|
||||||
|
|
||||||
|
@ -655,15 +660,18 @@ UsRolePointsSelectorDirective = ($rootscope) ->
|
||||||
|
|
||||||
return {link: link}
|
return {link: link}
|
||||||
|
|
||||||
|
module.directive("tgUsRolePointsSelector", ["$rootScope", UsRolePointsSelectorDirective])
|
||||||
|
|
||||||
|
|
||||||
UsPointsDirective = ($repo) ->
|
UsPointsDirective = ($repo) ->
|
||||||
selectionTemplate = _.template("""
|
rolesTemplate = _.template("""
|
||||||
<ul class="popover pop-role">
|
<ul class="popover pop-role">
|
||||||
<% _.each(rolePoints, function(rolePointsElement) { %>
|
<% _.each(roles, function(role) { %>
|
||||||
<li><a href="" class="role" title="<%- rolePointsElement.name %>"
|
<li>
|
||||||
data-role-id="<%- rolePointsElement.id %>">
|
<a href="" class="role" title="<%- role.name %>"
|
||||||
<%- rolePointsElement.name %>
|
data-role-id="<%- role.id %>">
|
||||||
(<%- rolePointsElement.points %>)
|
<%- role.name %>
|
||||||
|
(<%- role.points %>)
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<% }); %>
|
<% }); %>
|
||||||
|
@ -673,8 +681,14 @@ UsPointsDirective = ($repo) ->
|
||||||
pointsTemplate = _.template("""
|
pointsTemplate = _.template("""
|
||||||
<ul class="popover pop-points-open">
|
<ul class="popover pop-points-open">
|
||||||
<% _.each(points, function(point) { %>
|
<% _.each(points, function(point) { %>
|
||||||
<li><a href="" class="point" title="<%- point.name %>"
|
<li>
|
||||||
|
<% if (point.selected) { %>
|
||||||
|
<a href="" class="point" title="<%- point.name %>"
|
||||||
data-point-id="<%- point.id %>"><%- point.name %></a>
|
data-point-id="<%- point.id %>"><%- point.name %></a>
|
||||||
|
<% } else { %>
|
||||||
|
<a href="" class="point active" title="<%- point.name %>"
|
||||||
|
data-point-id="<%- point.id %>"><%- point.name %></a>
|
||||||
|
<% } %>
|
||||||
</li>
|
</li>
|
||||||
<% }); %>
|
<% }); %>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -682,95 +696,116 @@ UsPointsDirective = ($repo) ->
|
||||||
|
|
||||||
link = ($scope, $el, $attrs) ->
|
link = ($scope, $el, $attrs) ->
|
||||||
$ctrl = $el.controller()
|
$ctrl = $el.controller()
|
||||||
us = $scope.$eval($attrs.tgUsPoints)
|
|
||||||
|
us = $scope.$eval($attrs.tgBacklogUsPoints)
|
||||||
|
|
||||||
updatingSelectedRoleId = null
|
updatingSelectedRoleId = null
|
||||||
selectedRoleId = null
|
selectedRoleId = null
|
||||||
numberOfRoles = _.size(us.points)
|
numberOfRoles = _.size(us.points)
|
||||||
|
|
||||||
# Preselect the rol if we have only one
|
# Preselect the role if we have only one
|
||||||
if numberOfRoles == 1
|
if numberOfRoles == 1
|
||||||
selectedRoleId = _.keys(us.points)[0]
|
selectedRoleId = _.keys(us.points)[0]
|
||||||
|
|
||||||
showPopPoints = () ->
|
renderPointsSelector = (us, roleId) ->
|
||||||
$(".popover").popover().close()
|
# Prepare data for rendering
|
||||||
|
points = _.map $scope.project.points, (point) ->
|
||||||
|
point = _.clone(point, true)
|
||||||
|
point.selected = if us.points[roleId] == point.id then false else true
|
||||||
|
return point
|
||||||
|
|
||||||
|
html = pointsTemplate({"points": points})
|
||||||
|
|
||||||
|
# Remove any prevous state
|
||||||
|
$el.find(".popover").popover().close()
|
||||||
$el.find(".pop-points-open").remove()
|
$el.find(".pop-points-open").remove()
|
||||||
$el.append(pointsTemplate({ "points": $scope.project.points }))
|
|
||||||
dataPointId = us.points[updatingSelectedRoleId]
|
# Render into DOM and show the new created element
|
||||||
$el.find(".pop-points-open a[data-point-id='#{dataPointId}']").addClass("active")
|
$el.append(html)
|
||||||
|
|
||||||
# If not showing role selection let's move to the left
|
# If not showing role selection let's move to the left
|
||||||
if not $el.find(".pop-role:visible").css('left')?
|
if not $el.find(".pop-role:visible").css("left")?
|
||||||
$el.find(".pop-points-open").css('left', '110px')
|
$el.find(".pop-points-open").css("left", "110px")
|
||||||
|
|
||||||
$el.find(".pop-points-open").show()
|
$el.find(".pop-points-open").show()
|
||||||
|
|
||||||
showPopRoles = () ->
|
renderRolesSelector = (us) ->
|
||||||
$el.find(".pop-role").remove()
|
# Prepare data for rendering
|
||||||
rolePoints = _.clone(_.filter($scope.project.roles, "computable"), true)
|
computableRoles = _.filter($scope.project.roles, "computable")
|
||||||
|
|
||||||
undefinedToQuestion = (val) ->
|
roles = _.map computableRoles, (role) ->
|
||||||
return "?" if not val?
|
pointId = us.points[role.id]
|
||||||
return val
|
pointObj = $scope.pointsById[pointId]
|
||||||
|
|
||||||
_.map rolePoints, (v, k) ->
|
role = _.clone(role, true)
|
||||||
v.points = undefinedToQuestion($scope.pointsById[us.points[v.id]]?.value)
|
role.points = if pointObj.value? then pointObj.value else "?"
|
||||||
$el.append(selectionTemplate({ "rolePoints": rolePoints }))
|
return role
|
||||||
|
|
||||||
|
html = rolesTemplate({"roles": roles})
|
||||||
|
|
||||||
|
# Render into DOM and show the new created element
|
||||||
|
$el.append(html)
|
||||||
$el.find(".pop-role").popover().open(() -> $(this).remove())
|
$el.find(".pop-role").popover().open(() -> $(this).remove())
|
||||||
|
|
||||||
updatePoints = (roleId) ->
|
renderPoints = (us, roleId) ->
|
||||||
# Update the dom with the points
|
dom = $el.find("a > span.points-value")
|
||||||
pointsDom = $el.find("a > span.points-value")
|
|
||||||
usTotalPoints = calculateTotalPoints(us)
|
totalPoints = calculateTotalPoints(us)
|
||||||
us.total_points = usTotalPoints
|
if roleId == null or numberOfRoles == 1
|
||||||
if not roleId? or numberOfRoles == 1
|
dom.text(us.total_points)
|
||||||
pointsDom.text(us.total_points)
|
|
||||||
else
|
else
|
||||||
pointId = us.points[roleId]
|
pointId = us.points[roleId]
|
||||||
points = $scope.pointsById[pointId]
|
pointObj = $scope.pointsById[pointId]
|
||||||
pointsDom.html("#{points.name} / <span>#{us.total_points}</span>")
|
dom.html("#{pointObj.name} / <span>#{us.total_points}</span>")
|
||||||
|
|
||||||
calculateTotalPoints = ->
|
calculateTotalPoints = ->
|
||||||
values = _.map(us.points, (v, k) -> $scope.pointsById[v].value)
|
values = _.map(us.points, (v, k) -> $scope.pointsById[v].value)
|
||||||
values = _.filter(values, (num) -> num?)
|
values = _.filter(values, (num) -> num?)
|
||||||
|
|
||||||
if values.length == 0
|
if values.length == 0
|
||||||
return "?"
|
return "?"
|
||||||
|
|
||||||
return _.reduce(values, (acc, num) -> acc + num)
|
return _.reduce(values, (acc, num) -> acc + num)
|
||||||
|
|
||||||
updatePoints(null)
|
$scope.$watch $attrs.tgBacklogUsPoints, (us) ->
|
||||||
|
renderPoints(us, selectedRoleId) if us
|
||||||
|
|
||||||
$scope.$on "uspoints:select", (ctx, roleId, roleName) ->
|
$scope.$on "uspoints:select", (ctx, roleId, roleName) ->
|
||||||
updatePoints(roleId)
|
us = $scope.$eval($attrs.tgBacklogUsPoints)
|
||||||
|
renderPoints(us, roleId)
|
||||||
selectedRoleId = roleId
|
selectedRoleId = roleId
|
||||||
|
|
||||||
$scope.$on "uspoints:clear-selection", (ctx) ->
|
$scope.$on "uspoints:clear-selection", (ctx) ->
|
||||||
updatePoints(null)
|
us = $scope.$eval($attrs.tgBacklogUsPoints)
|
||||||
|
renderPoints(us, null)
|
||||||
selectedRoleId = null
|
selectedRoleId = null
|
||||||
|
|
||||||
$el.on "click", "a.us-points span", (event) ->
|
$el.on "click", "a.us-points span", (event) ->
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
target = angular.element(event.target)
|
|
||||||
|
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
|
|
||||||
|
us = $scope.$eval($attrs.tgBacklogUsPoints)
|
||||||
|
updatingSelectedRoleId = selectedRoleId
|
||||||
|
|
||||||
if selectedRoleId?
|
if selectedRoleId?
|
||||||
updatingSelectedRoleId = selectedRoleId
|
renderPointsSelector(us, selectedRoleId)
|
||||||
showPopPoints()
|
|
||||||
else
|
else
|
||||||
showPopRoles()
|
renderRolesSelector(us)
|
||||||
|
|
||||||
$el.on "click", ".role", (event) ->
|
$el.on "click", ".role", (event) ->
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
|
|
||||||
target = angular.element(event.currentTarget)
|
target = angular.element(event.currentTarget)
|
||||||
|
|
||||||
|
us = $scope.$eval($attrs.tgBacklogUsPoints)
|
||||||
|
|
||||||
updatingSelectedRoleId = target.data("role-id")
|
updatingSelectedRoleId = target.data("role-id")
|
||||||
|
|
||||||
popRolesDom = $el.find(".pop-role")
|
popRolesDom = $el.find(".pop-role")
|
||||||
popRolesDom.find("a").removeClass("active")
|
popRolesDom.find("a").removeClass("active")
|
||||||
popRolesDom.find("a[data-role-id='#{updatingSelectedRoleId}']").addClass("active")
|
popRolesDom.find("a[data-role-id='#{updatingSelectedRoleId}']").addClass("active")
|
||||||
showPopPoints()
|
|
||||||
|
renderPointsSelector(us, updatingSelectedRoleId)
|
||||||
|
|
||||||
$el.on "click", ".point", (event) ->
|
$el.on "click", ".point", (event) ->
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
@ -780,15 +815,16 @@ UsPointsDirective = ($repo) ->
|
||||||
$el.find(".pop-points-open").hide()
|
$el.find(".pop-points-open").hide()
|
||||||
$el.find(".pop-role").hide()
|
$el.find(".pop-role").hide()
|
||||||
|
|
||||||
$scope.$apply () ->
|
us = $scope.$eval($attrs.tgBacklogUsPoints)
|
||||||
usPoints = _.clone(us.points, true)
|
|
||||||
usPoints[updatingSelectedRoleId] = target.data("point-id")
|
|
||||||
us.points = usPoints
|
|
||||||
|
|
||||||
usTotalPoints = calculateTotalPoints(us)
|
points = _.clone(us.points, true)
|
||||||
us.total_points = usTotalPoints
|
points[updatingSelectedRoleId] = target.data("point-id")
|
||||||
|
|
||||||
updatePoints(selectedRoleId)
|
$scope.$apply ->
|
||||||
|
us.points = points
|
||||||
|
us.total_points = calculateTotalPoints(us)
|
||||||
|
|
||||||
|
renderPoints(us, selectedRoleId)
|
||||||
|
|
||||||
$repo.save(us).then ->
|
$repo.save(us).then ->
|
||||||
# Little Hack for refresh.
|
# Little Hack for refresh.
|
||||||
|
@ -806,6 +842,7 @@ UsPointsDirective = ($repo) ->
|
||||||
|
|
||||||
return {link: link}
|
return {link: link}
|
||||||
|
|
||||||
|
module.directive("tgBacklogUsPoints", ["$tgRepo", UsPointsDirective])
|
||||||
|
|
||||||
#############################################################################
|
#############################################################################
|
||||||
## Burndown graph directive
|
## Burndown graph directive
|
||||||
|
@ -837,14 +874,14 @@ tgBacklogGraphDirective = ->
|
||||||
lines:
|
lines:
|
||||||
fillColor : "rgba(102,153,51,0.3)"
|
fillColor : "rgba(102,153,51,0.3)"
|
||||||
})
|
})
|
||||||
team_increment_line = _.map(dataToDraw.milestones, (ml) -> -ml['team-increment'])
|
team_increment_line = _.map(dataToDraw.milestones, (ml) -> -ml["team-increment"])
|
||||||
data.push({
|
data.push({
|
||||||
data: _.zip(milestonesRange, team_increment_line)
|
data: _.zip(milestonesRange, team_increment_line)
|
||||||
lines:
|
lines:
|
||||||
fillColor : "rgba(153,51,51,0.3)"
|
fillColor : "rgba(153,51,51,0.3)"
|
||||||
})
|
})
|
||||||
client_increment_line = _.map dataToDraw.milestones, (ml) ->
|
client_increment_line = _.map dataToDraw.milestones, (ml) ->
|
||||||
-ml['team-increment'] - ml['client-increment']
|
-ml["team-increment"] - ml["client-increment"]
|
||||||
data.push({
|
data.push({
|
||||||
data: _.zip(milestonesRange, client_increment_line)
|
data: _.zip(milestonesRange, client_increment_line)
|
||||||
lines:
|
lines:
|
||||||
|
@ -862,14 +899,14 @@ tgBacklogGraphDirective = ->
|
||||||
options = {
|
options = {
|
||||||
grid: {
|
grid: {
|
||||||
borderWidth: { top: 0, right: 1, left:0, bottom: 0 }
|
borderWidth: { top: 0, right: 1, left:0, bottom: 0 }
|
||||||
borderColor: '#ccc'
|
borderColor: "#ccc"
|
||||||
}
|
}
|
||||||
xaxis: {
|
xaxis: {
|
||||||
ticks: dataToDraw.milestones.length
|
ticks: dataToDraw.milestones.length
|
||||||
axisLabel: "Sprints"
|
axisLabel: "Sprints"
|
||||||
axisLabelUseCanvas: true
|
axisLabelUseCanvas: true
|
||||||
axisLabelFontSizePixels: 14
|
axisLabelFontSizePixels: 14
|
||||||
axisLabelFontFamily: 'Verdana, Arial, Helvetica, Tahoma, sans-serif'
|
axisLabelFontFamily: "Verdana, Arial, Helvetica, Tahoma, sans-serif"
|
||||||
axisLabelPadding: 15
|
axisLabelPadding: 15
|
||||||
tickFormatter: (val, axis) -> ""
|
tickFormatter: (val, axis) -> ""
|
||||||
}
|
}
|
||||||
|
@ -895,7 +932,7 @@ tgBacklogGraphDirective = ->
|
||||||
link = ($scope, $el, $attrs) ->
|
link = ($scope, $el, $attrs) ->
|
||||||
element = angular.element($el)
|
element = angular.element($el)
|
||||||
|
|
||||||
$scope.$watch 'stats', (value) ->
|
$scope.$watch "stats", (value) ->
|
||||||
if $scope.stats?
|
if $scope.stats?
|
||||||
redrawChart(element, $scope.stats)
|
redrawChart(element, $scope.stats)
|
||||||
|
|
||||||
|
@ -908,7 +945,4 @@ tgBacklogGraphDirective = ->
|
||||||
return {link: link}
|
return {link: link}
|
||||||
|
|
||||||
|
|
||||||
module.directive("tgBacklog", ["$tgRepo", "$rootScope", BacklogDirective])
|
|
||||||
module.directive("tgUsPoints", ["$tgRepo", UsPointsDirective])
|
|
||||||
module.directive("tgUsRolePointsSelector", ["$rootScope", UsRolePointsSelectorDirective])
|
|
||||||
module.directive("tgGmBacklogGraph", tgBacklogGraphDirective)
|
module.directive("tgGmBacklogGraph", tgBacklogGraphDirective)
|
||||||
|
|
|
@ -198,16 +198,17 @@ CreateEditUserstoryDirective = ($repo, $model, $rs, $rootScope, lightboxService,
|
||||||
isNew = true
|
isNew = true
|
||||||
|
|
||||||
$scope.$on "usform:new", (ctx, projectId, status, statusList) ->
|
$scope.$on "usform:new", (ctx, projectId, status, statusList) ->
|
||||||
|
isNew = true
|
||||||
$scope.usStatusList = statusList
|
$scope.usStatusList = statusList
|
||||||
|
|
||||||
$scope.us = {
|
$scope.us = {
|
||||||
project: projectId
|
project: projectId
|
||||||
|
points : {}
|
||||||
status: status
|
status: status
|
||||||
is_archived: false
|
is_archived: false
|
||||||
tags: []
|
tags: []
|
||||||
}
|
}
|
||||||
|
|
||||||
isNew = true
|
|
||||||
# Update texts for creation
|
# Update texts for creation
|
||||||
$el.find(".button-green span").html("Create") #TODO: i18n
|
$el.find(".button-green span").html("Create") #TODO: i18n
|
||||||
$el.find(".title").html("New user story ") #TODO: i18n
|
$el.find(".title").html("New user story ") #TODO: i18n
|
||||||
|
@ -222,6 +223,7 @@ CreateEditUserstoryDirective = ($repo, $model, $rs, $rootScope, lightboxService,
|
||||||
$scope.$on "usform:edit", (ctx, us) ->
|
$scope.$on "usform:edit", (ctx, us) ->
|
||||||
$scope.us = us
|
$scope.us = us
|
||||||
isNew = false
|
isNew = false
|
||||||
|
|
||||||
# Update texts for edition
|
# Update texts for edition
|
||||||
$el.find(".button-green span").html("Save") #TODO: i18n
|
$el.find(".button-green span").html("Save") #TODO: i18n
|
||||||
$el.find(".title").html("Edit user story ") #TODO: i18n
|
$el.find(".title").html("Edit user story ") #TODO: i18n
|
||||||
|
@ -254,6 +256,7 @@ CreateEditUserstoryDirective = ($repo, $model, $rs, $rootScope, lightboxService,
|
||||||
return
|
return
|
||||||
|
|
||||||
$loading.start(target)
|
$loading.start(target)
|
||||||
|
|
||||||
if isNew
|
if isNew
|
||||||
promise = $repo.create("userstories", $scope.us)
|
promise = $repo.create("userstories", $scope.us)
|
||||||
broadcastEvent = "usform:new:success"
|
broadcastEvent = "usform:new:success"
|
||||||
|
|
|
@ -101,6 +101,10 @@ UsStatusDirective = ($repo, popoverService) ->
|
||||||
|
|
||||||
module.directive("tgUsStatus", ["$tgRepo", UsStatusDirective])
|
module.directive("tgUsStatus", ["$tgRepo", UsStatusDirective])
|
||||||
|
|
||||||
|
#############################################################################
|
||||||
|
## Related Task Status Directive
|
||||||
|
#############################################################################
|
||||||
|
|
||||||
RelatedTaskStatusDirective = ($repo, popoverService) ->
|
RelatedTaskStatusDirective = ($repo, popoverService) ->
|
||||||
###
|
###
|
||||||
Print the status of a related task and a popover to change it.
|
Print the status of a related task and a popover to change it.
|
||||||
|
@ -178,6 +182,10 @@ RelatedTaskStatusDirective = ($repo, popoverService) ->
|
||||||
|
|
||||||
module.directive("tgRelatedTaskStatus", ["$tgRepo", RelatedTaskStatusDirective])
|
module.directive("tgRelatedTaskStatus", ["$tgRepo", RelatedTaskStatusDirective])
|
||||||
|
|
||||||
|
#############################################################################
|
||||||
|
## jQuery plugin for Popover
|
||||||
|
#############################################################################
|
||||||
|
|
||||||
$.fn.popover = () ->
|
$.fn.popover = () ->
|
||||||
$el = @
|
$el = @
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,19 @@ timeout = @.taiga.timeout
|
||||||
|
|
||||||
module = angular.module("taigaKanban")
|
module = angular.module("taigaKanban")
|
||||||
|
|
||||||
|
# Vars
|
||||||
|
|
||||||
|
defaultViewMode = "maximized"
|
||||||
|
defaultViewModes = {
|
||||||
|
maximized: {
|
||||||
|
cardClass: "kanban-task-maximized"
|
||||||
|
}
|
||||||
|
minimized: {
|
||||||
|
cardClass: "kanban-task-minimized"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#############################################################################
|
#############################################################################
|
||||||
## Kanban Controller
|
## Kanban Controller
|
||||||
#############################################################################
|
#############################################################################
|
||||||
|
@ -51,6 +64,7 @@ class KanbanController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
|
||||||
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location, @appTitle, tgLoader) ->
|
constructor: (@scope, @rootscope, @repo, @confirm, @rs, @params, @q, @location, @appTitle, tgLoader) ->
|
||||||
_.bindAll(@)
|
_.bindAll(@)
|
||||||
@scope.sectionName = "Kanban"
|
@scope.sectionName = "Kanban"
|
||||||
|
@scope.statusViewModes = {}
|
||||||
|
|
||||||
promise = @.loadInitialData()
|
promise = @.loadInitialData()
|
||||||
|
|
||||||
|
@ -147,6 +161,8 @@ class KanbanController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
|
||||||
@scope.usStatusById = groupBy(project.us_statuses, (x) -> x.id)
|
@scope.usStatusById = groupBy(project.us_statuses, (x) -> x.id)
|
||||||
@scope.usStatusList = _.sortBy(project.us_statuses, "order")
|
@scope.usStatusList = _.sortBy(project.us_statuses, "order")
|
||||||
|
|
||||||
|
@.generateStatusViewModes()
|
||||||
|
|
||||||
@scope.$emit("project:loaded", project)
|
@scope.$emit("project:loaded", project)
|
||||||
return project
|
return project
|
||||||
|
|
||||||
|
@ -161,6 +177,31 @@ class KanbanController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
|
||||||
.then(=> @.loadKanban())
|
.then(=> @.loadKanban())
|
||||||
.then(=> @scope.$broadcast("redraw:wip"))
|
.then(=> @scope.$broadcast("redraw:wip"))
|
||||||
|
|
||||||
|
## View Mode methods
|
||||||
|
|
||||||
|
generateStatusViewModes: ->
|
||||||
|
storedStatusViewModes = @rs.kanban.getStatusViewModes(@scope.projectId)
|
||||||
|
|
||||||
|
@scope.statusViewModes = {}
|
||||||
|
for status in @scope.usStatusList
|
||||||
|
mode = storedStatusViewModes[status.id]
|
||||||
|
@scope.statusViewModes[status.id] = if _.has(defaultViewModes, mode) then mode else defaultViewMode
|
||||||
|
|
||||||
|
@.storeStatusViewModes()
|
||||||
|
|
||||||
|
storeStatusViewModes: ->
|
||||||
|
@rs.kanban.storeStatusViewModes(@scope.projectId, @scope.statusViewModes)
|
||||||
|
|
||||||
|
updateStatusViewMode: (statusId, newViewMode) ->
|
||||||
|
@scope.statusViewModes[statusId] = newViewMode
|
||||||
|
@.storeStatusViewModes()
|
||||||
|
|
||||||
|
getCardClass: (statusId)->
|
||||||
|
mode = @scope.statusViewModes[statusId] or defaultViewMode
|
||||||
|
return defaultViewModes[mode].cardClass or defaultViewModes[defaultViewMode].cardClass
|
||||||
|
|
||||||
|
# Utils methods
|
||||||
|
|
||||||
prepareBulkUpdateData: (uses, field="kanban_order") ->
|
prepareBulkUpdateData: (uses, field="kanban_order") ->
|
||||||
return _.map(uses, (x) -> {"us_id": x.id, "order": x[field]})
|
return _.map(uses, (x) -> {"us_id": x.id, "order": x[field]})
|
||||||
|
|
||||||
|
|
|
@ -133,5 +133,6 @@ module.run([
|
||||||
"$tgAttachmentsResourcesProvider",
|
"$tgAttachmentsResourcesProvider",
|
||||||
"$tgMdRenderResourcesProvider",
|
"$tgMdRenderResourcesProvider",
|
||||||
"$tgHistoryResourcesProvider",
|
"$tgHistoryResourcesProvider",
|
||||||
|
"$tgKanbanResourcesProvider",
|
||||||
initResources
|
initResources
|
||||||
])
|
])
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
###
|
||||||
|
# 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/resources/kanban.coffee
|
||||||
|
###
|
||||||
|
|
||||||
|
|
||||||
|
taiga = @.taiga
|
||||||
|
|
||||||
|
generateHash = taiga.generateHash
|
||||||
|
|
||||||
|
resourceProvider = ($storage) ->
|
||||||
|
service = {}
|
||||||
|
hashSuffixStatusViewModes = "kanban-statusviewmodels"
|
||||||
|
|
||||||
|
service.storeStatusViewModes = (projectId, params) ->
|
||||||
|
ns = "#{projectId}:#{hashSuffixStatusViewModes}"
|
||||||
|
hash = generateHash([projectId, ns])
|
||||||
|
$storage.set(hash, params)
|
||||||
|
|
||||||
|
service.getStatusViewModes = (projectId) ->
|
||||||
|
ns = "#{projectId}:#{hashSuffixStatusViewModes}"
|
||||||
|
hash = generateHash([projectId, ns])
|
||||||
|
return $storage.get(hash) or {}
|
||||||
|
|
||||||
|
return (instance) ->
|
||||||
|
instance.kanban = service
|
||||||
|
|
||||||
|
|
||||||
|
module = angular.module("taigaResources")
|
||||||
|
module.factory("$tgKanbanResourcesProvider", ["$tgStorage", resourceProvider])
|
|
@ -149,8 +149,6 @@ class UserStoryDetailController extends mixOf(taiga.Controller, taiga.PageMixin)
|
||||||
|
|
||||||
module.controller("UserStoryDetailController", UserStoryDetailController)
|
module.controller("UserStoryDetailController", UserStoryDetailController)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#############################################################################
|
#############################################################################
|
||||||
## User story Main Directive
|
## User story Main Directive
|
||||||
#############################################################################
|
#############################################################################
|
||||||
|
@ -188,8 +186,8 @@ UsDirective = ($tgrepo, $log, $location, $confirm, $navUrls, $loading) ->
|
||||||
|
|
||||||
return {link:link}
|
return {link:link}
|
||||||
|
|
||||||
module.directive("tgUsDetail", ["$tgRepo", "$log", "$tgLocation", "$tgConfirm", "$tgNavUrls", "$tgLoading", UsDirective])
|
module.directive("tgUsDetail", ["$tgRepo", "$log", "$tgLocation", "$tgConfirm",
|
||||||
|
"$tgNavUrls", "$tgLoading", UsDirective])
|
||||||
|
|
||||||
#############################################################################
|
#############################################################################
|
||||||
## User story status directive
|
## User story status directive
|
||||||
|
@ -365,3 +363,129 @@ UsStatusDetailDirective = () ->
|
||||||
return {link:link, require:"ngModel"}
|
return {link:link, require:"ngModel"}
|
||||||
|
|
||||||
module.directive("tgUsStatusDetail", UsStatusDetailDirective)
|
module.directive("tgUsStatusDetail", UsStatusDetailDirective)
|
||||||
|
|
||||||
|
#############################################################################
|
||||||
|
## User story estimation directive
|
||||||
|
#############################################################################
|
||||||
|
|
||||||
|
UsEstimationDirective = ($log) ->
|
||||||
|
mainTemplate = _.template("""
|
||||||
|
<ul class="points-per-role">
|
||||||
|
<li class="total">
|
||||||
|
<span class="points"><%- totalPoints %></span>
|
||||||
|
<span class="role">total</span>
|
||||||
|
</li>
|
||||||
|
<% _.each(roles, function(role) { %>
|
||||||
|
<li class="total clickable" data-role-id="<%- role.id %>">
|
||||||
|
<span class="points"><%- role.points %></span>
|
||||||
|
<span class="role"><%- role.name %></span></li>
|
||||||
|
<% }); %>
|
||||||
|
</ul>
|
||||||
|
""")
|
||||||
|
|
||||||
|
pointsTemplate = _.template("""
|
||||||
|
<ul class="popover pop-points-open">
|
||||||
|
<% _.each(points, function(point) { %>
|
||||||
|
<li>
|
||||||
|
<% if (point.selected) { %>
|
||||||
|
<a href="" class="point" title="<%- point.name %>"
|
||||||
|
data-point-id="<%- point.id %>" data-role-id="<%- roleId %>"><%- point.name %></a>
|
||||||
|
<% } else { %>
|
||||||
|
<a href="" class="point active" title="<%- point.name %>"
|
||||||
|
data-point-id="<%- point.id %>" data-role-id="<%- roleId %>"><%- point.name %></a>
|
||||||
|
<% } %>
|
||||||
|
</li>
|
||||||
|
<% }); %>
|
||||||
|
</ul>
|
||||||
|
""")
|
||||||
|
|
||||||
|
link = ($scope, $el, $attrs) ->
|
||||||
|
render = (us) ->
|
||||||
|
totalPoints = us.total_points or 0
|
||||||
|
computableRoles = _.filter($scope.project.roles, "computable")
|
||||||
|
|
||||||
|
roles = _.map computableRoles, (role) ->
|
||||||
|
pointId = us.points[role.id]
|
||||||
|
pointObj = $scope.pointsById[pointId]
|
||||||
|
|
||||||
|
role = _.clone(role, true)
|
||||||
|
role.points = if pointObj? and pointObj.name? then pointObj.name else "?"
|
||||||
|
return role
|
||||||
|
|
||||||
|
html = mainTemplate({totalPoints: totalPoints, roles: roles})
|
||||||
|
$el.html(html)
|
||||||
|
|
||||||
|
renderPoints = (us, roleId) ->
|
||||||
|
points = _.map $scope.project.points, (point) ->
|
||||||
|
point = _.clone(point, true)
|
||||||
|
point.selected = if us.points[roleId] == point.id then false else true
|
||||||
|
return point
|
||||||
|
|
||||||
|
html = pointsTemplate({"points": points, roleId: roleId})
|
||||||
|
|
||||||
|
# Remove any prevous state
|
||||||
|
$el.find(".popover").popover().close()
|
||||||
|
$el.find(".pop-points-open").remove()
|
||||||
|
|
||||||
|
# If not showing role selection let's move to the left
|
||||||
|
if not $el.find(".pop-role:visible").css("left")?
|
||||||
|
$el.find(".pop-points-open").css("left", "110px")
|
||||||
|
|
||||||
|
$el.find(".pop-points-open").remove()
|
||||||
|
|
||||||
|
# Render into DOM and show the new created element
|
||||||
|
$el.find(".points-per-role").append(html)
|
||||||
|
|
||||||
|
$el.find(".pop-points-open").popover().open(-> $(this).removeClass("active"))
|
||||||
|
$el.find(".pop-points-open").show()
|
||||||
|
|
||||||
|
calculateTotalPoints = (us) ->
|
||||||
|
values = _.map(us.points, (v, k) -> $scope.pointsById[v]?.value or 0)
|
||||||
|
if values.length == 0
|
||||||
|
return "0"
|
||||||
|
return _.reduce(values, (acc, num) -> acc + num)
|
||||||
|
|
||||||
|
$scope.$watch $attrs.ngModel, (us) ->
|
||||||
|
render(us) if us
|
||||||
|
|
||||||
|
$scope.$on "$destroy", ->
|
||||||
|
$el.off()
|
||||||
|
|
||||||
|
$el.on "click", ".total.clickable", (event) ->
|
||||||
|
event.preventDefault()
|
||||||
|
event.stopPropagation()
|
||||||
|
target = angular.element(event.currentTarget)
|
||||||
|
roleId = target.data("role-id")
|
||||||
|
|
||||||
|
us = $scope.$eval($attrs.ngModel)
|
||||||
|
renderPoints(us, roleId)
|
||||||
|
|
||||||
|
target.siblings().removeClass('active')
|
||||||
|
target.addClass('active')
|
||||||
|
|
||||||
|
$el.on "click", ".point", (event) ->
|
||||||
|
event.preventDefault()
|
||||||
|
event.stopPropagation()
|
||||||
|
|
||||||
|
us = $scope.$eval($attrs.ngModel)
|
||||||
|
|
||||||
|
target = angular.element(event.currentTarget)
|
||||||
|
roleId = target.data("role-id")
|
||||||
|
pointId = target.data("point-id")
|
||||||
|
|
||||||
|
$el.find(".popover").popover().close()
|
||||||
|
|
||||||
|
points = _.clone(us.points, true)
|
||||||
|
points[roleId] = pointId
|
||||||
|
|
||||||
|
$scope.$apply ->
|
||||||
|
us.points = points
|
||||||
|
us.total_points = calculateTotalPoints(us)
|
||||||
|
render(us)
|
||||||
|
|
||||||
|
return {
|
||||||
|
link: link
|
||||||
|
restrict: "EA"
|
||||||
|
}
|
||||||
|
|
||||||
|
module.directive("tgUsEstimation", UsEstimationDirective)
|
||||||
|
|
Binary file not shown.
|
@ -42,4 +42,6 @@
|
||||||
<glyph unicode="H" d="M128 384l64 0 0-64-64 0z m0-96l64 0 0-64-64 0z m0-96l64 0 0-64-64 0z m128 0l128 0 0-64-128 0z m224 320l-448 0c-18 0-32-14-32-32l0-448c0-18 14-32 32-32l448 0c18 0 32 14 32 32l0 448c0 18-14 32-32 32z m-32-448l-384 0 0 384 384 0z m-192 224l128 0 0-64-128 0z m0 96l128 0 0-64-128 0z"/>
|
<glyph unicode="H" d="M128 384l64 0 0-64-64 0z m0-96l64 0 0-64-64 0z m0-96l64 0 0-64-64 0z m128 0l128 0 0-64-128 0z m224 320l-448 0c-18 0-32-14-32-32l0-448c0-18 14-32 32-32l448 0c18 0 32 14 32 32l0 448c0 18-14 32-32 32z m-32-448l-384 0 0 384 384 0z m-192 224l128 0 0-64-128 0z m0 96l128 0 0-64-128 0z"/>
|
||||||
<glyph unicode="I" d="M184 20c0 0 0 54 0 54 0 0 144 0 144 0 0 0 0-54 0-54-24-14-48-21-73-20-23-1-47 6-71 20m141 84c0 0-138 0-138 0 0 25-6 49-19 72-12 23-25 43-40 58-14 15-26 34-37 57-11 23-16 47-14 71 3 41 19 77 48 106 30 29 73 44 130 44 58 0 102-15 131-44 29-29 45-65 49-106 1-20-2-39-9-57-6-18-15-35-26-50-11-14-22-29-33-43-12-14-21-31-30-49-8-19-12-38-12-59m-193 254c-2-1-2-4 0-10 1-5 1-9 1-10-1-1 0-5 2-10 3-5 4-8 3-9 0-1 1-4 4-9 3-5 5-9 6-10 1-1 3-5 7-10 3-5 6-8 7-9 1-2 3-5 7-11 5-6 7-10 9-12 30-42 49-78 57-108 0 0 42 0 42 0 8 32 27 68 57 108 2 2 6 8 13 18 7 10 12 16 13 18 1 3 4 8 9 15 4 8 7 13 8 17 1 4 2 9 3 14 1 6 1 12 0 18-5 67-47 101-125 101-77 0-118-34-123-101"/>
|
<glyph unicode="I" d="M184 20c0 0 0 54 0 54 0 0 144 0 144 0 0 0 0-54 0-54-24-14-48-21-73-20-23-1-47 6-71 20m141 84c0 0-138 0-138 0 0 25-6 49-19 72-12 23-25 43-40 58-14 15-26 34-37 57-11 23-16 47-14 71 3 41 19 77 48 106 30 29 73 44 130 44 58 0 102-15 131-44 29-29 45-65 49-106 1-20-2-39-9-57-6-18-15-35-26-50-11-14-22-29-33-43-12-14-21-31-30-49-8-19-12-38-12-59m-193 254c-2-1-2-4 0-10 1-5 1-9 1-10-1-1 0-5 2-10 3-5 4-8 3-9 0-1 1-4 4-9 3-5 5-9 6-10 1-1 3-5 7-10 3-5 6-8 7-9 1-2 3-5 7-11 5-6 7-10 9-12 30-42 49-78 57-108 0 0 42 0 42 0 8 32 27 68 57 108 2 2 6 8 13 18 7 10 12 16 13 18 1 3 4 8 9 15 4 8 7 13 8 17 1 4 2 9 3 14 1 6 1 12 0 18-5 67-47 101-125 101-77 0-118-34-123-101"/>
|
||||||
<glyph unicode="E" d="M480 224l-64 0c-18 0-32 14-32 32 0 18 14 32 32 32l64 0c18 0 32-14 32-32 0-18-14-32-32-32z m-88 122c-13-12-33-12-45 0-13 13-13 33 0 46l45 45c12 12 33 12 45 0 13-13 13-33 0-45z m-136-346c-18 0-32 14-32 32l0 64c0 18 14 32 32 32 18 0 32-14 32-32l0-64c0-18-14-32-32-32z m0 384c-18 0-32 14-32 32l0 64c0 18 14 32 32 32 18 0 32-14 32-32l0-64c0-18-14-32-32-32z m-136-309c-12-13-32-13-45 0-12 12-12 33 0 45l45 45c13 13 33 13 46 0 12-12 12-32 0-45z m0 271l-45 46c-12 12-12 32 0 45 13 12 33 12 45 0l46-45c12-13 12-33 0-46-13-12-33-12-46 0z m8-90c0-18-14-32-32-32l-64 0c-18 0-32 14-32 32 0 18 14 32 32 32l64 0c18 0 32-14 32-32z m264-91l45-45c13-12 13-33 0-45-12-13-33-13-45 0l-45 45c-13 13-13 33 0 45 12 13 32 13 45 0z"/>
|
<glyph unicode="E" d="M480 224l-64 0c-18 0-32 14-32 32 0 18 14 32 32 32l64 0c18 0 32-14 32-32 0-18-14-32-32-32z m-88 122c-13-12-33-12-45 0-13 13-13 33 0 46l45 45c12 12 33 12 45 0 13-13 13-33 0-45z m-136-346c-18 0-32 14-32 32l0 64c0 18 14 32 32 32 18 0 32-14 32-32l0-64c0-18-14-32-32-32z m0 384c-18 0-32 14-32 32l0 64c0 18 14 32 32 32 18 0 32-14 32-32l0-64c0-18-14-32-32-32z m-136-309c-12-13-32-13-45 0-12 12-12 33 0 45l45 45c13 13 33 13 46 0 12-12 12-32 0-45z m0 271l-45 46c-12 12-12 32 0 45 13 12 33 12 45 0l46-45c12-13 12-33 0-46-13-12-33-12-46 0z m8-90c0-18-14-32-32-32l-64 0c-18 0-32 14-32 32 0 18 14 32 32 32l64 0c18 0 32-14 32-32z m264-91l45-45c13-12 13-33 0-45-12-13-33-13-45 0l-45 45c-13 13-13 33 0 45 12 13 32 13 45 0z"/>
|
||||||
|
<glyph unicode="J" d="M184 54l24 145c1 3 0 5-2 7 0 0 0 0 0 0-2 2-5 3-7 3l-145-25c-3 0-5-2-6-5-1-3 0-7 2-9l28-28-76-76c-3-3-3-8 0-11l53-53c3-3 8-3 11 0l76 76 28-28c3-2 6-3 9-2 3 1 5 3 5 6z m144 404l-24-145c-1-3 0-5 2-7 0 0 0 0 0 0 2-2 5-3 7-3l145 25c3 0 5 2 6 5 1 3 0 7-2 9l-28 28 76 76c3 3 3 8 0 11l-53 53c-3 3-8 3-11 0l-76-76-28 28c-3 2-6 3-9 2-3-1-5-3-5-6z m130-274l-145 24c-3 1-5 0-7-2 0 0 0 0 0 0-2-2-3-5-3-7l25-145c0-3 2-5 5-6 3-1 7 0 9 2l28 28 76-76c3-3 8-3 11 0l53 53c3 3 3 8 0 11l-76 76 28 28c2 3 3 6 2 9-1 3-3 5-6 5z m-404 144l145-24c3-1 5 0 7 2 0 0 0 0 0 0 2 2 3 5 3 7l-25 145c0 3-2 5-5 6-3 1-6 0-9-2l-28-28-76 76c-3 3-8 3-11 0l-53-53c-3-3-3-8 0-11l76-76-28-28c-2-3-3-6-2-9 1-3 3-5 6-5z"/>
|
||||||
|
<glyph unicode="K" d="M24 154l-24-144c0-3 1-6 2-8 0 0 0 0 0 0 2-1 5-2 8-2l144 25c3 0 6 2 6 5 1 3 1 6-2 8l-28 29 76 75c3 4 3 9 0 12l-52 52c-3 4-9 4-12 0l-76-75-28 28c-2 2-5 3-8 2-3-1-5-4-6-7z m464 204l24 144c0 3-1 6-2 8 0 0 0 0 0 0-2 1-5 2-8 2l-144-25c-3 0-6-2-6-5-1-3-1-6 2-8l28-29-76-75c-3-4-3-9 0-12l52-52c3-4 9-4 12 0l76 75 28-28c2-2 5-3 8-2 3 1 5 4 6 7z m-130-334l144-24c3 0 6 1 8 2 0 0 0 0 0 0 1 2 2 5 2 8l-25 144c0 3-2 6-5 6-3 1-6 1-8-2l-29-28-75 76c-4 3-9 3-12 0l-52-52c-4-3-4-9 0-12l75-76-28-28c-2-2-3-5-2-8 1-3 4-5 7-6z m-204 464l-144 24c-3 0-6-1-8-2 0 0 0 0 0 0-1-2-2-5-2-8l25-144c0-3 2-6 5-6 3-1 6-1 8 2l29 28 75-76c4-3 9-3 12 0l52 52c4 3 4 9 0 12l-75 76 28 28c2 2 3 5 2 8-1 3-4 5-7 6z"/>
|
||||||
</font></defs></svg>
|
</font></defs></svg>
|
||||||
|
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 16 KiB |
Binary file not shown.
Binary file not shown.
|
@ -20,7 +20,7 @@ block content
|
||||||
//-include views/modules/list-filters-kanban
|
//-include views/modules/list-filters-kanban
|
||||||
include views/modules/kanban-table
|
include views/modules/kanban-table
|
||||||
|
|
||||||
div.lightbox.lightbox-generic-form(tg-lb-create-edit-userstory)
|
div.lightbox.lightbox-generic-form.lb-create-edit-userstory(tg-lb-create-edit-userstory)
|
||||||
include views/modules/lightbox-us-create-edit
|
include views/modules/lightbox-us-create-edit
|
||||||
|
|
||||||
div.lightbox.lightbox-generic-bulk(tg-lb-create-bulk-userstories)
|
div.lightbox.lightbox-generic-bulk(tg-lb-create-bulk-userstories)
|
||||||
|
|
|
@ -18,7 +18,7 @@ div.row.us-item-row(ng-repeat="us in visibleUserstories|orderBy:order track by u
|
||||||
span.us-status-bind
|
span.us-status-bind
|
||||||
span.icon.icon-arrow-bottom(tg-check-permission="modify_us")
|
span.icon.icon-arrow-bottom(tg-check-permission="modify_us")
|
||||||
|
|
||||||
div.points(tg-us-points="us")
|
div.points(tg-backlog-us-points="us")
|
||||||
a.us-points(href="", title="Points")
|
a.us-points(href="", title="Points")
|
||||||
span.points-value 0
|
span.points-value 0
|
||||||
span.icon.icon-arrow-bottom(tg-check-permission="modify_us")
|
span.icon.icon-arrow-bottom(tg-check-permission="modify_us")
|
||||||
|
|
|
@ -4,9 +4,9 @@ div.kanban-task-inner
|
||||||
div.task-text
|
div.task-text
|
||||||
a.task-assigned(href="", title="Assign User Story")
|
a.task-assigned(href="", title="Assign User Story")
|
||||||
span.task-num(tg-bo-ref="us.ref")
|
span.task-num(tg-bo-ref="us.ref")
|
||||||
a.task-name(href="", title="", tg-bind-html="us.subject",
|
a.task-name(href="", tg-bo-title="us.subject", tg-bind-html="us.subject",
|
||||||
tg-nav="project-userstories-detail:project=project.slug,ref=us.ref")
|
tg-nav="project-userstories-detail:project=project.slug,ref=us.ref")
|
||||||
p.task-points
|
a.task-points(href="", title="Total Us points")
|
||||||
span(tg-bind-html="us.total_points") --
|
span(tg-bind-html="us.total_points") --
|
||||||
span points
|
span points
|
||||||
a.icon.icon-edit(tg-check-permission="modify_us", href="", title="Edit")
|
a.icon.icon-edit(tg-check-permission="modify_us", href="", title="Edit")
|
||||||
|
|
|
@ -4,8 +4,21 @@ div.kanban-table
|
||||||
h2.task-colum_name(ng-repeat="s in usStatusList track by s.id",
|
h2.task-colum_name(ng-repeat="s in usStatusList track by s.id",
|
||||||
ng-style="{'border-top-color':s.color}")
|
ng-style="{'border-top-color':s.color}")
|
||||||
span(tg-bo-bind="s.name")
|
span(tg-bo-bind="s.name")
|
||||||
a.icon.icon-plus(tg-check-permission="add_us", href="", title="Add New task", ng-click="ctrl.addNewUs('standard', s.id)")
|
|
||||||
a.icon.icon-bulk(tg-check-permission="add_us", href="", title="Add New bulk", ng-click="ctrl.addNewUs('bulk', s.id)")
|
a.icon.icon-minimize(href="", title="Minimize",
|
||||||
|
ng-if="statusViewModes[s.id] == 'maximized'",
|
||||||
|
ng-click="ctrl.updateStatusViewMode(s.id, 'minimized')")
|
||||||
|
a.icon.icon-maximize(href="", title="Maximize",
|
||||||
|
ng-if="statusViewModes[s.id] == 'minimized'",
|
||||||
|
ng-click="ctrl.updateStatusViewMode(s.id, 'maximized')")
|
||||||
|
|
||||||
|
a.icon.icon-plus(href="", title="Add New task",
|
||||||
|
ng-click="ctrl.addNewUs('standard', s.id)",
|
||||||
|
tg-check-permission="add_us")
|
||||||
|
|
||||||
|
a.icon.icon-bulk(href="", title="Add New bulk",
|
||||||
|
ng-click="ctrl.addNewUs('bulk', s.id)",
|
||||||
|
tg-check-permission="add_us")
|
||||||
|
|
||||||
div.kanban-table-body
|
div.kanban-table-body
|
||||||
div.kanban-table-inner(tg-kanban-row-width-fixer)
|
div.kanban-table-inner(tg-kanban-row-width-fixer)
|
||||||
|
@ -14,4 +27,5 @@ div.kanban-table
|
||||||
tg-kanban-wip-limit,
|
tg-kanban-wip-limit,
|
||||||
tg-kanban-column-height-fixer)
|
tg-kanban-column-height-fixer)
|
||||||
div.kanban-task(ng-repeat="us in usByStatus[status.id] track by us.id",
|
div.kanban-task(ng-repeat="us in usByStatus[status.id] track by us.id",
|
||||||
tg-kanban-userstory, ng-model="us")
|
tg-kanban-userstory, ng-model="us",
|
||||||
|
ng-class="ctrl.getCardClass(status.id)")
|
||||||
|
|
|
@ -5,9 +5,15 @@ form
|
||||||
fieldset
|
fieldset
|
||||||
input(type="text", name="subject", ng-model="us.subject", tg-i18n="placeholder:common.subject",
|
input(type="text", name="subject", ng-model="us.subject", tg-i18n="placeholder:common.subject",
|
||||||
data-required="true", data-maxlength="500")
|
data-required="true", data-maxlength="500")
|
||||||
|
|
||||||
|
fieldset.estimation
|
||||||
|
tg-us-estimation(ng-model="us")
|
||||||
|
//- Render by tg-lb-create-edit-userstory
|
||||||
|
|
||||||
fieldset
|
fieldset
|
||||||
select(name="status", ng-model="us.status", ng-options="s.id as s.name for s in usStatusList",
|
select(name="status", ng-model="us.status", ng-options="s.id as s.name for s in usStatusList",
|
||||||
tg-i18n="placeholder:common.status")
|
tg-i18n="placeholder:common.status")
|
||||||
|
|
||||||
fieldset
|
fieldset
|
||||||
div(tg-tag-line, editable="true", ng-model="us.tags")
|
div(tg-tag-line, editable="true", ng-model="us.tags")
|
||||||
|
|
||||||
|
|
|
@ -28,20 +28,18 @@
|
||||||
}
|
}
|
||||||
.kanban-tagline {
|
.kanban-tagline {
|
||||||
@include table-flex();
|
@include table-flex();
|
||||||
background: $gray-light; //Fallback
|
background: $postit-hover; //Fallback
|
||||||
height: .3rem;
|
//height: .3rem;
|
||||||
}
|
}
|
||||||
.kanban-tag {
|
.kanban-tag {
|
||||||
@include table-flex-child(1, 0, 0, 0);
|
@include table-flex-child(1, 0, 0, 0);
|
||||||
background: $postit-hover; //Fallback
|
background: $postit-hover; //Fallback
|
||||||
height: .3rem;
|
//height: .3rem;
|
||||||
}
|
}
|
||||||
.kanban-task-inner {
|
.kanban-task-inner {
|
||||||
@include table-flex();
|
@include table-flex();
|
||||||
padding: 1rem 1rem 2rem;
|
|
||||||
}
|
}
|
||||||
.avatar {
|
.avatar {
|
||||||
@include table-flex-child($flex-basis: 50px);
|
|
||||||
a {
|
a {
|
||||||
@extend %small;
|
@extend %small;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
@ -69,37 +67,24 @@
|
||||||
color: $postit-dark-hover;
|
color: $postit-dark-hover;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
.task-text {
|
||||||
|
@include table-flex-child($flex-grow: 10, $flex-basis: 50px);
|
||||||
|
@extend %small;
|
||||||
|
padding: 0 .5rem 0 .8rem;
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
.task-num {
|
.task-num {
|
||||||
color: $grayer;
|
color: $grayer;
|
||||||
margin-right: .5em;
|
margin-right: .3rem;
|
||||||
}
|
}
|
||||||
.task-name {
|
.task-name {
|
||||||
@extend %bold;
|
@extend %bold;
|
||||||
color: $grayer;
|
color: $grayer;
|
||||||
word-wrap: break-word;
|
|
||||||
}
|
|
||||||
.task-text {
|
|
||||||
@include table-flex-child($flex-grow: 10, $flex-basis: 50px);
|
|
||||||
@extend %small;
|
|
||||||
padding: 0 .5rem 0 1rem;
|
|
||||||
word-wrap: break-word;
|
|
||||||
}
|
|
||||||
.task-points {
|
|
||||||
@extend %small;
|
|
||||||
color: darken($postit-hover, 15%);
|
|
||||||
margin: 0;
|
|
||||||
span {
|
|
||||||
display: inline-block;
|
|
||||||
&:first-child {
|
|
||||||
padding-right: .2rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.icon-edit,
|
.icon-edit,
|
||||||
.icon-drag-h {
|
.icon-drag-h {
|
||||||
@include transition(opacity .2s linear);
|
@include transition(opacity .2s linear);
|
||||||
@extend %large;
|
@extend %large;
|
||||||
bottom: .2rem;
|
|
||||||
color: $postit-hover;
|
color: $postit-hover;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -108,12 +93,86 @@
|
||||||
color: darken($postit-hover, 15%);
|
color: darken($postit-hover, 15%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.kanban-task-maximized {
|
||||||
|
.kanban-task-inner {
|
||||||
|
padding: 1rem 1rem 2rem;
|
||||||
|
}
|
||||||
|
.avatar {
|
||||||
|
@include table-flex-child($flex-basis: 50px);
|
||||||
|
}
|
||||||
|
.task-name {
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
.icon-edit {
|
.icon-edit {
|
||||||
|
bottom: .2rem;
|
||||||
right: .5rem;
|
right: .5rem;
|
||||||
}
|
}
|
||||||
.icon-drag-h {
|
.icon-drag-h {
|
||||||
@extend %xlarge;
|
@extend %xlarge;
|
||||||
|
bottom: .2rem;
|
||||||
cursor: move;
|
cursor: move;
|
||||||
right: 45%;
|
right: 45%;
|
||||||
}
|
}
|
||||||
|
.task-points {
|
||||||
|
@extend %small;
|
||||||
|
color: darken($postit-hover, 15%);
|
||||||
|
display: block;
|
||||||
|
margin: 0;
|
||||||
|
span {
|
||||||
|
display: inline-block;
|
||||||
|
&:first-child {
|
||||||
|
padding-right: .2rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.kanban-tagline {
|
||||||
|
height: .3rem;
|
||||||
|
}
|
||||||
|
.kanban-tag {
|
||||||
|
height: .3rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.kanban-task-minimized {
|
||||||
|
.kanban-task-inner {
|
||||||
|
padding: .3rem;
|
||||||
|
}
|
||||||
|
.avatar {
|
||||||
|
@include table-flex-child($flex-basis: 40px);
|
||||||
|
}
|
||||||
|
.task-num {
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
.task-name {
|
||||||
|
display: inline-block;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
width: 70%;
|
||||||
|
}
|
||||||
|
.task-points {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.icon-edit,
|
||||||
|
.icon-drag-h {
|
||||||
|
top: 1.4rem;
|
||||||
|
}
|
||||||
|
.icon-edit {
|
||||||
|
bottom: .2rem;
|
||||||
|
right: 1rem;
|
||||||
|
}
|
||||||
|
.icon-drag-h {
|
||||||
|
@extend %medium;
|
||||||
|
@include transform(rotate(90deg));
|
||||||
|
cursor: move;
|
||||||
|
right: .1rem;
|
||||||
|
}
|
||||||
|
.kanban-tagline {
|
||||||
|
height: .2rem;
|
||||||
|
}
|
||||||
|
.kanban-tag {
|
||||||
|
height: .2rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,9 @@
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: '#{$font-face}';
|
font-family: '#{$font-face}';
|
||||||
src: url('../fonts/#{$font-face}.eot?#iefix') format('embedded-opentype'),
|
src: url('../fonts/#{$font-face}.eot?#iefix') format('embedded-opentype'),
|
||||||
url('../fonts/#{$font-face}.woff') format('woff'),
|
url('../fonts/#{$font-face}.woff') format('woff'),
|
||||||
url('../fonts/#{$font-face}.ttf') format('truetype'),
|
url('../fonts/#{$font-face}.ttf') format('truetype'),
|
||||||
url('../fonts/#{$font-face}.svg#{$font-face}') format('svg');
|
url('../fonts/#{$font-face}.svg#{$font-face}') format('svg');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,7 +88,7 @@ p {
|
||||||
img {
|
img {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
em { font-style: italic; }
|
em { font-style: italic; }
|
||||||
strong {
|
strong {
|
||||||
|
@ -247,3 +247,9 @@ a:visited {
|
||||||
.icon-spinner:before {
|
.icon-spinner:before {
|
||||||
content: 'E';
|
content: 'E';
|
||||||
}
|
}
|
||||||
|
.icon-minimize:before {
|
||||||
|
content: 'J';
|
||||||
|
}
|
||||||
|
.icon-maximize:before {
|
||||||
|
content: 'K';
|
||||||
|
}
|
||||||
|
|
|
@ -160,8 +160,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.points-per-role {
|
.points-per-role {
|
||||||
|
@include table-flex();
|
||||||
position: relative;
|
position: relative;
|
||||||
> li {
|
> li {
|
||||||
|
@include table-flex-child(1, 18%, 0);
|
||||||
@include transition(color .3s linear);
|
@include transition(color .3s linear);
|
||||||
border-right: 1px solid rgba($grayer, .3);
|
border-right: 1px solid rgba($grayer, .3);
|
||||||
color: rgba($grayer, .3);
|
color: rgba($grayer, .3);
|
||||||
|
@ -169,14 +171,13 @@
|
||||||
margin: .5rem .1rem;
|
margin: .5rem .1rem;
|
||||||
position: relative;
|
position: relative;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
width: 18%;
|
|
||||||
&.active {
|
&.active {
|
||||||
color: rgba($green-taiga, 1);
|
color: rgba($green-taiga, 1);
|
||||||
}
|
}
|
||||||
&:first-child {
|
&:first-child {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
&:nth-child(5n) {
|
&:last-child {
|
||||||
border: 0;
|
border: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -501,3 +501,12 @@
|
||||||
width: 600px;
|
width: 600px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.lb-create-edit-userstory {
|
||||||
|
.points-per-role {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
li {
|
||||||
|
margin: .5rem .1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -44,6 +44,11 @@ $column-margin: 0 10px 0 0;
|
||||||
&.icon-plus {
|
&.icon-plus {
|
||||||
right: 2rem;
|
right: 2rem;
|
||||||
}
|
}
|
||||||
|
&.icon-maximize,
|
||||||
|
&.icon-minimize {
|
||||||
|
left: .5rem;
|
||||||
|
right: inherit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,7 +56,6 @@ $column-margin: 0 10px 0 0;
|
||||||
.kanban-table-body {
|
.kanban-table-body {
|
||||||
@include table-flex();
|
@include table-flex();
|
||||||
@extend %medium;
|
@extend %medium;
|
||||||
//height: 700px;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
Loading…
Reference in New Issue