diff --git a/app/coffee/modules/backlog/main.coffee b/app/coffee/modules/backlog/main.coffee
index eb0faefa..3285f012 100644
--- a/app/coffee/modules/backlog/main.coffee
+++ b/app/coffee/modules/backlog/main.coffee
@@ -593,6 +593,9 @@ BacklogDirective = ($repo, $rootscope) ->
return {link: link}
+
+module.directive("tgBacklog", ["$tgRepo", "$rootScope", BacklogDirective])
+
#############################################################################
## User story points directive
#############################################################################
@@ -600,13 +603,15 @@ BacklogDirective = ($repo, $rootscope) ->
UsRolePointsSelectorDirective = ($rootscope) ->
#TODO: i18n
selectionTemplate = _.template("""
-
+
""")
link = ($scope, $el, $attrs) ->
@@ -616,7 +621,7 @@ UsRolePointsSelectorDirective = ($rootscope) ->
numberOfRoles = _.size(roles)
if numberOfRoles > 1
- $el.append(selectionTemplate({ 'roles': roles }))
+ $el.append(selectionTemplate({"roles":roles}))
else
$el.find(".icon-arrow-bottom").remove()
@@ -655,15 +660,18 @@ UsRolePointsSelectorDirective = ($rootscope) ->
return {link: link}
+module.directive("tgUsRolePointsSelector", ["$rootScope", UsRolePointsSelectorDirective])
+
UsPointsDirective = ($repo) ->
- selectionTemplate = _.template("""
+ rolesTemplate = _.template("""
- <% _.each(rolePoints, function(rolePointsElement) { %>
- -
- <%- rolePointsElement.name %>
- (<%- rolePointsElement.points %>)
+ <% _.each(roles, function(role) { %>
+
-
+
+ <%- role.name %>
+ (<%- role.points %>)
<% }); %>
@@ -673,8 +681,14 @@ UsPointsDirective = ($repo) ->
pointsTemplate = _.template("""
@@ -682,95 +696,116 @@ UsPointsDirective = ($repo) ->
link = ($scope, $el, $attrs) ->
$ctrl = $el.controller()
- us = $scope.$eval($attrs.tgUsPoints)
+
+ us = $scope.$eval($attrs.tgBacklogUsPoints)
+
updatingSelectedRoleId = null
selectedRoleId = null
numberOfRoles = _.size(us.points)
- # Preselect the rol if we have only one
+ # Preselect the role if we have only one
if numberOfRoles == 1
selectedRoleId = _.keys(us.points)[0]
- showPopPoints = () ->
- $(".popover").popover().close()
+ renderPointsSelector = (us, roleId) ->
+ # 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.append(pointsTemplate({ "points": $scope.project.points }))
- dataPointId = us.points[updatingSelectedRoleId]
- $el.find(".pop-points-open a[data-point-id='#{dataPointId}']").addClass("active")
+
+ # Render into DOM and show the new created element
+ $el.append(html)
# 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')
+ if not $el.find(".pop-role:visible").css("left")?
+ $el.find(".pop-points-open").css("left", "110px")
$el.find(".pop-points-open").show()
- showPopRoles = () ->
- $el.find(".pop-role").remove()
- rolePoints = _.clone(_.filter($scope.project.roles, "computable"), true)
+ renderRolesSelector = (us) ->
+ # Prepare data for rendering
+ computableRoles = _.filter($scope.project.roles, "computable")
- undefinedToQuestion = (val) ->
- return "?" if not val?
- return val
+ roles = _.map computableRoles, (role) ->
+ pointId = us.points[role.id]
+ pointObj = $scope.pointsById[pointId]
- _.map rolePoints, (v, k) ->
- v.points = undefinedToQuestion($scope.pointsById[us.points[v.id]]?.value)
- $el.append(selectionTemplate({ "rolePoints": rolePoints }))
+ role = _.clone(role, true)
+ role.points = if pointObj.value? then pointObj.value else "?"
+ 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())
- updatePoints = (roleId) ->
- # Update the dom with the points
- pointsDom = $el.find("a > span.points-value")
- usTotalPoints = calculateTotalPoints(us)
- us.total_points = usTotalPoints
- if not roleId? or numberOfRoles == 1
- pointsDom.text(us.total_points)
+ renderPoints = (us, roleId) ->
+ dom = $el.find("a > span.points-value")
+
+ totalPoints = calculateTotalPoints(us)
+ if roleId == null or numberOfRoles == 1
+ dom.text(us.total_points)
else
pointId = us.points[roleId]
- points = $scope.pointsById[pointId]
- pointsDom.html("#{points.name} / #{us.total_points}")
+ pointObj = $scope.pointsById[pointId]
+ dom.html("#{pointObj.name} / #{us.total_points}")
calculateTotalPoints = ->
values = _.map(us.points, (v, k) -> $scope.pointsById[v].value)
values = _.filter(values, (num) -> num?)
+
if values.length == 0
return "?"
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) ->
- updatePoints(roleId)
+ us = $scope.$eval($attrs.tgBacklogUsPoints)
+ renderPoints(us, roleId)
selectedRoleId = roleId
$scope.$on "uspoints:clear-selection", (ctx) ->
- updatePoints(null)
+ us = $scope.$eval($attrs.tgBacklogUsPoints)
+ renderPoints(us, null)
selectedRoleId = null
$el.on "click", "a.us-points span", (event) ->
event.preventDefault()
- target = angular.element(event.target)
-
event.stopPropagation()
+ us = $scope.$eval($attrs.tgBacklogUsPoints)
+ updatingSelectedRoleId = selectedRoleId
+
if selectedRoleId?
- updatingSelectedRoleId = selectedRoleId
- showPopPoints()
+ renderPointsSelector(us, selectedRoleId)
else
- showPopRoles()
+ renderRolesSelector(us)
$el.on "click", ".role", (event) ->
event.preventDefault()
event.stopPropagation()
-
target = angular.element(event.currentTarget)
+
+ us = $scope.$eval($attrs.tgBacklogUsPoints)
+
updatingSelectedRoleId = target.data("role-id")
popRolesDom = $el.find(".pop-role")
popRolesDom.find("a").removeClass("active")
popRolesDom.find("a[data-role-id='#{updatingSelectedRoleId}']").addClass("active")
- showPopPoints()
+
+ renderPointsSelector(us, updatingSelectedRoleId)
$el.on "click", ".point", (event) ->
event.preventDefault()
@@ -780,15 +815,16 @@ UsPointsDirective = ($repo) ->
$el.find(".pop-points-open").hide()
$el.find(".pop-role").hide()
- $scope.$apply () ->
- usPoints = _.clone(us.points, true)
- usPoints[updatingSelectedRoleId] = target.data("point-id")
- us.points = usPoints
+ us = $scope.$eval($attrs.tgBacklogUsPoints)
- usTotalPoints = calculateTotalPoints(us)
- us.total_points = usTotalPoints
+ points = _.clone(us.points, true)
+ 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 ->
# Little Hack for refresh.
@@ -806,6 +842,7 @@ UsPointsDirective = ($repo) ->
return {link: link}
+module.directive("tgBacklogUsPoints", ["$tgRepo", UsPointsDirective])
#############################################################################
## Burndown graph directive
@@ -837,14 +874,14 @@ tgBacklogGraphDirective = ->
lines:
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: _.zip(milestonesRange, team_increment_line)
lines:
fillColor : "rgba(153,51,51,0.3)"
})
client_increment_line = _.map dataToDraw.milestones, (ml) ->
- -ml['team-increment'] - ml['client-increment']
+ -ml["team-increment"] - ml["client-increment"]
data.push({
data: _.zip(milestonesRange, client_increment_line)
lines:
@@ -862,14 +899,14 @@ tgBacklogGraphDirective = ->
options = {
grid: {
borderWidth: { top: 0, right: 1, left:0, bottom: 0 }
- borderColor: '#ccc'
+ borderColor: "#ccc"
}
xaxis: {
ticks: dataToDraw.milestones.length
axisLabel: "Sprints"
axisLabelUseCanvas: true
axisLabelFontSizePixels: 14
- axisLabelFontFamily: 'Verdana, Arial, Helvetica, Tahoma, sans-serif'
+ axisLabelFontFamily: "Verdana, Arial, Helvetica, Tahoma, sans-serif"
axisLabelPadding: 15
tickFormatter: (val, axis) -> ""
}
@@ -895,7 +932,7 @@ tgBacklogGraphDirective = ->
link = ($scope, $el, $attrs) ->
element = angular.element($el)
- $scope.$watch 'stats', (value) ->
+ $scope.$watch "stats", (value) ->
if $scope.stats?
redrawChart(element, $scope.stats)
@@ -908,7 +945,4 @@ tgBacklogGraphDirective = ->
return {link: link}
-module.directive("tgBacklog", ["$tgRepo", "$rootScope", BacklogDirective])
-module.directive("tgUsPoints", ["$tgRepo", UsPointsDirective])
-module.directive("tgUsRolePointsSelector", ["$rootScope", UsRolePointsSelectorDirective])
module.directive("tgGmBacklogGraph", tgBacklogGraphDirective)
diff --git a/app/coffee/modules/common/lightboxes.coffee b/app/coffee/modules/common/lightboxes.coffee
index b75e280d..7fa1c96f 100644
--- a/app/coffee/modules/common/lightboxes.coffee
+++ b/app/coffee/modules/common/lightboxes.coffee
@@ -198,16 +198,17 @@ CreateEditUserstoryDirective = ($repo, $model, $rs, $rootScope, lightboxService,
isNew = true
$scope.$on "usform:new", (ctx, projectId, status, statusList) ->
+ isNew = true
$scope.usStatusList = statusList
$scope.us = {
project: projectId
+ points : {}
status: status
is_archived: false
tags: []
}
- isNew = true
# Update texts for creation
$el.find(".button-green span").html("Create") #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.us = us
isNew = false
+
# Update texts for edition
$el.find(".button-green span").html("Save") #TODO: i18n
$el.find(".title").html("Edit user story ") #TODO: i18n
@@ -254,6 +256,7 @@ CreateEditUserstoryDirective = ($repo, $model, $rs, $rootScope, lightboxService,
return
$loading.start(target)
+
if isNew
promise = $repo.create("userstories", $scope.us)
broadcastEvent = "usform:new:success"
diff --git a/app/coffee/modules/common/popovers.coffee b/app/coffee/modules/common/popovers.coffee
index 4abf6c95..44f748b2 100644
--- a/app/coffee/modules/common/popovers.coffee
+++ b/app/coffee/modules/common/popovers.coffee
@@ -101,6 +101,10 @@ UsStatusDirective = ($repo, popoverService) ->
module.directive("tgUsStatus", ["$tgRepo", UsStatusDirective])
+#############################################################################
+## Related Task Status Directive
+#############################################################################
+
RelatedTaskStatusDirective = ($repo, popoverService) ->
###
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])
+#############################################################################
+## jQuery plugin for Popover
+#############################################################################
+
$.fn.popover = () ->
$el = @
diff --git a/app/coffee/modules/kanban/main.coffee b/app/coffee/modules/kanban/main.coffee
index 4bc3b172..52f3911b 100644
--- a/app/coffee/modules/kanban/main.coffee
+++ b/app/coffee/modules/kanban/main.coffee
@@ -30,6 +30,19 @@ timeout = @.taiga.timeout
module = angular.module("taigaKanban")
+# Vars
+
+defaultViewMode = "maximized"
+defaultViewModes = {
+ maximized: {
+ cardClass: "kanban-task-maximized"
+ }
+ minimized: {
+ cardClass: "kanban-task-minimized"
+ }
+}
+
+
#############################################################################
## 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) ->
_.bindAll(@)
@scope.sectionName = "Kanban"
+ @scope.statusViewModes = {}
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.usStatusList = _.sortBy(project.us_statuses, "order")
+ @.generateStatusViewModes()
+
@scope.$emit("project:loaded", project)
return project
@@ -161,6 +177,31 @@ class KanbanController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi
.then(=> @.loadKanban())
.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") ->
return _.map(uses, (x) -> {"us_id": x.id, "order": x[field]})
diff --git a/app/coffee/modules/resources.coffee b/app/coffee/modules/resources.coffee
index 6ee34072..aa486d60 100644
--- a/app/coffee/modules/resources.coffee
+++ b/app/coffee/modules/resources.coffee
@@ -133,5 +133,6 @@ module.run([
"$tgAttachmentsResourcesProvider",
"$tgMdRenderResourcesProvider",
"$tgHistoryResourcesProvider",
+ "$tgKanbanResourcesProvider",
initResources
])
diff --git a/app/coffee/modules/resources/kanban.coffee b/app/coffee/modules/resources/kanban.coffee
new file mode 100644
index 00000000..a5c1fd53
--- /dev/null
+++ b/app/coffee/modules/resources/kanban.coffee
@@ -0,0 +1,46 @@
+###
+# Copyright (C) 2014 Andrey Antukh
+# Copyright (C) 2014 Jesús Espino Garcia
+# Copyright (C) 2014 David Barragán Merino
+#
+# 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: 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])
diff --git a/app/coffee/modules/userstories/detail.coffee b/app/coffee/modules/userstories/detail.coffee
index 1dd2d84c..6da90974 100644
--- a/app/coffee/modules/userstories/detail.coffee
+++ b/app/coffee/modules/userstories/detail.coffee
@@ -149,8 +149,6 @@ class UserStoryDetailController extends mixOf(taiga.Controller, taiga.PageMixin)
module.controller("UserStoryDetailController", UserStoryDetailController)
-
-
#############################################################################
## User story Main Directive
#############################################################################
@@ -188,8 +186,8 @@ UsDirective = ($tgrepo, $log, $location, $confirm, $navUrls, $loading) ->
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
@@ -365,3 +363,129 @@ UsStatusDetailDirective = () ->
return {link:link, require:"ngModel"}
module.directive("tgUsStatusDetail", UsStatusDetailDirective)
+
+#############################################################################
+## User story estimation directive
+#############################################################################
+
+UsEstimationDirective = ($log) ->
+ mainTemplate = _.template("""
+
+ -
+ <%- totalPoints %>
+ total
+
+ <% _.each(roles, function(role) { %>
+ -
+ <%- role.points %>
+ <%- role.name %>
+ <% }); %>
+
+ """)
+
+ pointsTemplate = _.template("""
+
+ """)
+
+ 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)
diff --git a/app/fonts/taiga.eot b/app/fonts/taiga.eot
index 9a108ee0..b6af2930 100644
Binary files a/app/fonts/taiga.eot and b/app/fonts/taiga.eot differ
diff --git a/app/fonts/taiga.svg b/app/fonts/taiga.svg
index b1779c43..048c962b 100644
--- a/app/fonts/taiga.svg
+++ b/app/fonts/taiga.svg
@@ -42,4 +42,6 @@
+
+
diff --git a/app/fonts/taiga.ttf b/app/fonts/taiga.ttf
index f5e6e246..8a253dae 100644
Binary files a/app/fonts/taiga.ttf and b/app/fonts/taiga.ttf differ
diff --git a/app/fonts/taiga.woff b/app/fonts/taiga.woff
index 54508d1e..588dcb30 100644
Binary files a/app/fonts/taiga.woff and b/app/fonts/taiga.woff differ
diff --git a/app/partials/kanban.jade b/app/partials/kanban.jade
index 246c2dbb..85e36fda 100644
--- a/app/partials/kanban.jade
+++ b/app/partials/kanban.jade
@@ -20,7 +20,7 @@ block content
//-include views/modules/list-filters-kanban
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
div.lightbox.lightbox-generic-bulk(tg-lb-create-bulk-userstories)
diff --git a/app/partials/views/components/backlog-row.jade b/app/partials/views/components/backlog-row.jade
index 8c07c189..4e3a84e6 100644
--- a/app/partials/views/components/backlog-row.jade
+++ b/app/partials/views/components/backlog-row.jade
@@ -18,7 +18,7 @@ div.row.us-item-row(ng-repeat="us in visibleUserstories|orderBy:order track by u
span.us-status-bind
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")
span.points-value 0
span.icon.icon-arrow-bottom(tg-check-permission="modify_us")
diff --git a/app/partials/views/components/kanban-task.jade b/app/partials/views/components/kanban-task.jade
index 95e500dc..55012439 100644
--- a/app/partials/views/components/kanban-task.jade
+++ b/app/partials/views/components/kanban-task.jade
@@ -4,9 +4,9 @@ div.kanban-task-inner
div.task-text
a.task-assigned(href="", title="Assign User Story")
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")
- p.task-points
+ a.task-points(href="", title="Total Us points")
span(tg-bind-html="us.total_points") --
span points
a.icon.icon-edit(tg-check-permission="modify_us", href="", title="Edit")
diff --git a/app/partials/views/modules/kanban-table.jade b/app/partials/views/modules/kanban-table.jade
index 0fc2499c..6e40e0ec 100644
--- a/app/partials/views/modules/kanban-table.jade
+++ b/app/partials/views/modules/kanban-table.jade
@@ -4,8 +4,21 @@ div.kanban-table
h2.task-colum_name(ng-repeat="s in usStatusList track by s.id",
ng-style="{'border-top-color':s.color}")
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-inner(tg-kanban-row-width-fixer)
@@ -14,4 +27,5 @@ div.kanban-table
tg-kanban-wip-limit,
tg-kanban-column-height-fixer)
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)")
diff --git a/app/partials/views/modules/lightbox-us-create-edit.jade b/app/partials/views/modules/lightbox-us-create-edit.jade
index ab8024fa..17af9326 100644
--- a/app/partials/views/modules/lightbox-us-create-edit.jade
+++ b/app/partials/views/modules/lightbox-us-create-edit.jade
@@ -5,9 +5,15 @@ form
fieldset
input(type="text", name="subject", ng-model="us.subject", tg-i18n="placeholder:common.subject",
data-required="true", data-maxlength="500")
+
+ fieldset.estimation
+ tg-us-estimation(ng-model="us")
+ //- Render by tg-lb-create-edit-userstory
+
fieldset
select(name="status", ng-model="us.status", ng-options="s.id as s.name for s in usStatusList",
tg-i18n="placeholder:common.status")
+
fieldset
div(tg-tag-line, editable="true", ng-model="us.tags")
diff --git a/app/styles/components/kanban-task.scss b/app/styles/components/kanban-task.scss
index 8ac1565f..ebe3c3b0 100644
--- a/app/styles/components/kanban-task.scss
+++ b/app/styles/components/kanban-task.scss
@@ -28,20 +28,18 @@
}
.kanban-tagline {
@include table-flex();
- background: $gray-light; //Fallback
- height: .3rem;
+ background: $postit-hover; //Fallback
+ //height: .3rem;
}
.kanban-tag {
@include table-flex-child(1, 0, 0, 0);
background: $postit-hover; //Fallback
- height: .3rem;
+ //height: .3rem;
}
.kanban-task-inner {
@include table-flex();
- padding: 1rem 1rem 2rem;
}
.avatar {
- @include table-flex-child($flex-basis: 50px);
a {
@extend %small;
text-align: center;
@@ -69,37 +67,24 @@
color: $postit-dark-hover;
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 {
color: $grayer;
- margin-right: .5em;
+ margin-right: .3rem;
}
.task-name {
@extend %bold;
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-drag-h {
@include transition(opacity .2s linear);
@extend %large;
- bottom: .2rem;
color: $postit-hover;
opacity: 0;
position: absolute;
@@ -108,12 +93,86 @@
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 {
+ bottom: .2rem;
right: .5rem;
}
.icon-drag-h {
@extend %xlarge;
+ bottom: .2rem;
cursor: move;
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;
+ }
}
diff --git a/app/styles/dependencies/typography.scss b/app/styles/dependencies/typography.scss
index 68d878d5..114f7af8 100755
--- a/app/styles/dependencies/typography.scss
+++ b/app/styles/dependencies/typography.scss
@@ -6,9 +6,9 @@
@font-face {
font-family: '#{$font-face}';
src: url('../fonts/#{$font-face}.eot?#iefix') format('embedded-opentype'),
- url('../fonts/#{$font-face}.woff') format('woff'),
- url('../fonts/#{$font-face}.ttf') format('truetype'),
- url('../fonts/#{$font-face}.svg#{$font-face}') format('svg');
+ url('../fonts/#{$font-face}.woff') format('woff'),
+ url('../fonts/#{$font-face}.ttf') format('truetype'),
+ url('../fonts/#{$font-face}.svg#{$font-face}') format('svg');
}
}
@@ -88,7 +88,7 @@ p {
img {
margin: 0;
}
- }
+}
em { font-style: italic; }
strong {
@@ -247,3 +247,9 @@ a:visited {
.icon-spinner:before {
content: 'E';
}
+.icon-minimize:before {
+ content: 'J';
+}
+.icon-maximize:before {
+ content: 'K';
+}
diff --git a/app/styles/layout/us-detail.scss b/app/styles/layout/us-detail.scss
index 82f5c64b..2ec0ea6e 100644
--- a/app/styles/layout/us-detail.scss
+++ b/app/styles/layout/us-detail.scss
@@ -160,8 +160,10 @@
}
.points-per-role {
+ @include table-flex();
position: relative;
> li {
+ @include table-flex-child(1, 18%, 0);
@include transition(color .3s linear);
border-right: 1px solid rgba($grayer, .3);
color: rgba($grayer, .3);
@@ -169,14 +171,13 @@
margin: .5rem .1rem;
position: relative;
text-align: center;
- width: 18%;
&.active {
color: rgba($green-taiga, 1);
}
&:first-child {
opacity: 1;
}
- &:nth-child(5n) {
+ &:last-child {
border: 0;
}
}
diff --git a/app/styles/modules/common/lightbox.scss b/app/styles/modules/common/lightbox.scss
index d4622e61..a4c2f665 100644
--- a/app/styles/modules/common/lightbox.scss
+++ b/app/styles/modules/common/lightbox.scss
@@ -501,3 +501,12 @@
width: 600px;
}
}
+
+.lb-create-edit-userstory {
+ .points-per-role {
+ margin-bottom: 1rem;
+ li {
+ margin: .5rem .1rem;
+ }
+ }
+}
diff --git a/app/styles/modules/kanban/kanban-table.scss b/app/styles/modules/kanban/kanban-table.scss
index 966500b5..e82a7d3f 100644
--- a/app/styles/modules/kanban/kanban-table.scss
+++ b/app/styles/modules/kanban/kanban-table.scss
@@ -44,6 +44,11 @@ $column-margin: 0 10px 0 0;
&.icon-plus {
right: 2rem;
}
+ &.icon-maximize,
+ &.icon-minimize {
+ left: .5rem;
+ right: inherit;
+ }
}
}
}
@@ -51,7 +56,6 @@ $column-margin: 0 10px 0 0;
.kanban-table-body {
@include table-flex();
@extend %medium;
- //height: 700px;
overflow: hidden;
overflow-x: auto;
width: 100%;