diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c840d86..64e9cc8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - Display the current user (me) at first in assignment lightbox (thanks to [@mikaoelitiana](https://github.com/mikaoelitiana)) - Divide the user dashboard in two columns in large screens, - Upvote and downvote issues from the issues list. +- Show points per role in statsection of the taskboard panel. - Comments: - Add a new permissions to allow add comments instead of use the existent modify permission for this purpose. - Ability to edit comments, view edition history and redesign comments module UI. diff --git a/app/coffee/modules/taskboard/main.coffee b/app/coffee/modules/taskboard/main.coffee index 68620f62..d52d8dff 100644 --- a/app/coffee/modules/taskboard/main.coffee +++ b/app/coffee/modules/taskboard/main.coffee @@ -220,6 +220,7 @@ class TaskboardController extends mixOf(taiga.Controller, taiga.PageMixin) return promise.then(=> @.loadProject()) .then(=> @.loadTaskboard()) + .then(=> @.setRolePoints()) refreshTasksOrder: (tasks) -> items = @.resortTasks(tasks) @@ -273,6 +274,33 @@ class TaskboardController extends mixOf(taiga.Controller, taiga.PageMixin) editTaskAssignedTo: (task) -> @rootscope.$broadcast("assigned-to:add", task) + setRolePoints: () -> + computableRoles = _.filter(@scope.project.roles, "computable") + + getRole = (roleId) => + roleId = parseInt(roleId, 10) + return _.find computableRoles, (role) -> role.id == roleId + + getPoint = (pointId) => + poitnId = parseInt(pointId, 10) + return _.find @scope.project.points, (point) -> point.id == pointId + + pointsByRole = _.reduce @scope.userstories, (result, us, key) => + _.forOwn us.points, (pointId, roleId) -> + role = getRole(roleId) + point = getPoint(pointId) + + if !result[role.id] + result[role.id] = role + result[role.id].points = 0 + + result[role.id].points += point.value + + return result + , {} + + @scope.pointsByRole = Object.keys(pointsByRole).map (key) -> return pointsByRole[key] + module.controller("TaskboardController", TaskboardController) diff --git a/app/locales/taiga/locale-en.json b/app/locales/taiga/locale-en.json index 83b897de..16777909 100644 --- a/app/locales/taiga/locale-en.json +++ b/app/locales/taiga/locale-en.json @@ -1151,7 +1151,8 @@ "CLOSED_TASKS": "closed
tasks", "IOCAINE_DOSES": "iocaine
doses", "SHOW_STATISTICS_TITLE": "Show statistics", - "TOGGLE_BAKLOG_GRAPH": "Show/Hide burndown graph" + "TOGGLE_BAKLOG_GRAPH": "Show/Hide burndown graph", + "POINTS_PER_ROLE": "Points per role" }, "SUMMARY": { "PROJECT_POINTS": "project
points", diff --git a/app/partials/includes/components/sprint-summary.jade b/app/partials/includes/components/sprint-summary.jade index 364a336a..b4f5f6ac 100644 --- a/app/partials/includes/components/sprint-summary.jade +++ b/app/partials/includes/components/sprint-summary.jade @@ -1,29 +1,44 @@ div.summary.large-summary div.large-summary-wrapper - div.summary-progress-wrapper + .summary-progress-wrapper div.summary-progress-bar(tg-progress-bar="stats.completedPercentage") div.data - span.number(ng-bind="stats.completedPercentage + '%'") + span.number(ng-bind="stats.completedPercentage + '%'") - div.summary-stats - span.number(ng-bind="stats.totalPointsSum|default:'--'") - span.description(translate="BACKLOG.SPRINT_SUMMARY.TOTAL_POINTS") - div.summary-stats - span.number(ng-bind="stats.completedPointsSum|default:'--'") - span.description(translate="BACKLOG.SPRINT_SUMMARY.COMPLETED_POINTS") + .stats-wrapper(ng-class="{'show-role-points': showRolePoints}") + .main-summary-stats + span.summary-stats.toggle-points-per-role(ng-click="showRolePoints = true") + tg-svg(svg-icon="icon-arrow-down") + span.number(ng-bind="stats.totalPointsSum|default:'--'") + span.description(translate="BACKLOG.SPRINT_SUMMARY.TOTAL_POINTS") + div.summary-stats.summary-completed-points + span.number(ng-bind="stats.completedPointsSum|default:'--'") + span.description(translate="BACKLOG.SPRINT_SUMMARY.COMPLETED_POINTS") - div.summary-stats - tg-svg(svg-icon="icon-bulk") - span.number(ng-bind="stats.openTasks|default:'--'") - span.description(translate="BACKLOG.SPRINT_SUMMARY.OPEN_TASKS") - div.summary-stats - span.number(ng-bind="stats.completed_tasks|default:'--'") - span.description(translate="BACKLOG.SPRINT_SUMMARY.CLOSED_TASKS") + div.summary-stats.summary-open-tasks + tg-svg(svg-icon="icon-bulk") + span.number(ng-bind="stats.openTasks|default:'--'") + span.description(translate="BACKLOG.SPRINT_SUMMARY.OPEN_TASKS") + div.summary-stats.summary-closed-tasks + span.number(ng-bind="stats.completed_tasks|default:'--'") + span.description(translate="BACKLOG.SPRINT_SUMMARY.CLOSED_TASKS") - div.summary-stats(title="{{'COMMON.IOCAINE_TEXT' | translate}}") - tg-svg(svg-icon="icon-iocaine") - span.number(ng-bind="stats.iocaine_doses|default:'--'") - span.description(translate="BACKLOG.SPRINT_SUMMARY.IOCAINE_DOSES") + div.summary-stats.summary-iocaine(title="{{'COMMON.IOCAINE_TEXT' | translate}}") + tg-svg(svg-icon="icon-iocaine") + span.number(ng-bind="stats.iocaine_doses|default:'--'") + span.description(translate="BACKLOG.SPRINT_SUMMARY.IOCAINE_DOSES") + + .points-per-role-stats.toggle-points-per-role( + ng-click="showRolePoints = false" + ) + span.points-per-role-stats-title + tg-svg(svg-icon="icon-arrow-up") + span(translate="BACKLOG.SPRINT_SUMMARY.POINTS_PER_ROLE") + + .points-per-role-stats-content + .summary-stats(ng-repeat="rolePoint in pointsByRole") + span.number {{rolePoint.points}} + span.role {{rolePoint.name}} div.stats.toggle-analytics-visibility(title="{{'BACKLOG.SPRINT_SUMMARY.SHOW_STATISTICS_TITLE' | translate}}") tg-svg(svg-icon="icon-graph") diff --git a/app/partials/includes/modules/sprint.jade b/app/partials/includes/modules/sprint.jade index f56ca58b..d66c5ca7 100644 --- a/app/partials/includes/modules/sprint.jade +++ b/app/partials/includes/modules/sprint.jade @@ -1,6 +1,7 @@ header(tg-backlog-sprint-header, ng-model="sprint") -div.sprint-progress-bar(tg-progress-bar="100 * sprint.closed_points / sprint.total_points") +.summary-progress-wrapper + div.sprint-progress-bar(tg-progress-bar="100 * sprint.closed_points / sprint.total_points") div.sprint-table(tg-bind-scope, ng-class="{'sprint-empty-wrapper': !sprint.user_stories.length}") div.sprint-empty(ng-if="!sprint.user_stories.length") diff --git a/app/styles/components/summary.scss b/app/styles/components/summary.scss index 2b9a8cda..52e419fd 100644 --- a/app/styles/components/summary.scss +++ b/app/styles/components/summary.scss @@ -2,14 +2,18 @@ $summary-background: $grayer; .summary { align-content: center; + align-items: center; background: $summary-background; color: $white; display: flex; flex-wrap: wrap; + height: 65px; justify-content: flex-start; margin-bottom: 2rem; - padding: 1em; + overflow: hidden; + padding: 1rem; .summary-stats { + align-items: center; display: flex; margin: 0 .5rem; } @@ -20,7 +24,7 @@ $summary-background: $grayer; } .number { @include font-size(xlarge); - @include font-type(bold); + @include font-type(light); line-height: .9; margin-right: .3rem; } @@ -69,6 +73,21 @@ $summary-background: $grayer; transition: fill .2s; } } + .main-summary-stats { + display: flex; + transform: translateY(0); + transition: all .2s ease-in-out; + } + + + .show-role-points { + .points-per-role-stats { + transform: translateY(-35px); + } + .main-summary-stats { + transform: translateY(-65px); + } + } } .summary-progress-bar { @@ -102,7 +121,12 @@ $summary-background: $grayer; } .large-summary { + align-items: stretch; justify-content: space-between; + padding: .75rem 1rem; + .stats-wrapper { + padding-top: .35rem; + } .large-summary-wrapper { align-content: center; display: flex; @@ -110,6 +134,7 @@ $summary-background: $grayer; justify-content: flex-start; } .summary-progress-wrapper { + align-items: center; display: flex; } .summary-progress-bar { @@ -122,13 +147,23 @@ $summary-background: $grayer; border: 0; margin: 0; } + &.summary-completed-points, + &.summary-closed-tasks { + border-right: 1px solid $blackish; + margin-right: 0; + padding-right: 1rem; + +.summary-stats { + border-left: 1px solid $gray; + margin-left: 0; + padding-left: 1rem; + } + } } .icon { + @include svg-size(1.3rem); fill: currentColor; - height: 1.5rem; margin-right: .4rem; vertical-align: middle; - width: 1.5rem; &.icon-stats { color: $primary; float: right; @@ -146,6 +181,33 @@ $summary-background: $grayer; } } } + .points-per-role-stats-content { + display: flex; + padding-left: 1rem; + .summary-stats { + padding: 0; + } + } + .toggle-points-per-role { + color: $white; + cursor: pointer; + svg { + @include svg-size(); + } + } + .points-per-role-stats { + margin-left: .5rem; + transform: translateY(35px); + transition: all .2s ease-in-out; + .number { + @include font-size(large); + @include font-type(normal); + } + .role { + @include font-size(xsmall); + @include font-type(light); + } + } } .empty-burndown {