From 71f5087140a5630e812a961cd7707778835cf228 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavier=20Juli=C3=A1n?= Date: Mon, 15 Sep 2014 17:28:21 +0200 Subject: [PATCH 1/9] US #903: Task #1012: Compact and expanded US --- app/fonts/taiga.eot | Bin 6812 -> 7372 bytes app/fonts/taiga.svg | 2 + app/fonts/taiga.ttf | Bin 6656 -> 7216 bytes app/fonts/taiga.woff | Bin 4880 -> 5300 bytes .../views/components/kanban-task.jade | 33 ++++- app/partials/views/modules/kanban-table.jade | 7 +- app/styles/components/kanban-task.scss | 118 ++++++++++++++---- app/styles/dependencies/typography.scss | 14 ++- app/styles/modules/kanban/kanban-table.scss | 6 +- 9 files changed, 143 insertions(+), 37 deletions(-) diff --git a/app/fonts/taiga.eot b/app/fonts/taiga.eot index 9a108ee0436cb9d01db9ae8c104720856095763f..b6af29305c6c1f9e99bac64a391346739cbbe180 100644 GIT binary patch delta 973 zcmah{Pe@cj9R7YY^X4sjFmV6s6JyQyj99nTeQy&a3(O9?Vf4p@qT0-qa&^-nJ8cC? zN0$dB5_Apf5TxL(OXx)`@lXVvxF9JAM$j=rrjpqOzQ@WG+!szzS)JvdA=}Z=N4D&>2i{kagt;@MOg@8lwlCW=X-fqas!qz`k*93HLzMdZ>1s<|G%_*Kf0}_U?YJZw-`Gl qQg))pU5MSK4!5r93{Rlb9cpT|ERdgFrAbwzC+V delta 406 zcmYL^JxD@f6o#MAdr_gG0wL5}G?-2j357QrBKjey4EmuVjEnt2UhfSXYHEp^;~QFB z8X|~-q9q~-LUIZE8AG?01|g`HhHhLPI`Hxy4xIBo=Rq%YdkY~TwD0XvzkJoR{eClZ z@@_%!4bVmghlZKL3&A4Lo|@`Rq$=~bAm)JCjA_`%m5UO1R1k|RM&5QD6bRk`{*~<3 z^0ofF1IiTyBCAWrqFBpVvPraV?Vd;K8k@XmjtsJHH-kbH8iyRK!J8zKT*D zm$)Xab_vobwN682!2P7Y1B}WSPnYy5DcPm8NK>g^rqx(L9)&)933TElWg#3<6M8%; fi{W( + + diff --git a/app/fonts/taiga.ttf b/app/fonts/taiga.ttf index f5e6e2462806f17e39e1829e4ae0bb0535c324ad..8a253daeef27057eb2122faa8f49e1606349131e 100644 GIT binary patch delta 959 zcmah{O-NKx7(L&;_uV%r;li1DZ<-gzsP7q}(~L836if+hTFi)&Rzg9IW)A5%le)0g zNMN+o9+bFg8?+JnLE5y5q)j9RML(-{Q3O((h%w$9hye$>aJiTBo$sFS9PV;rv!@IK zZ~)8T7)htchKlcYb^yZzs!^5M|`0(co&j1<+aCWkgE8Umx4S@Lo z%1`GON_%_YK=>N~-}LPDD-Z5`=>qt3AUK$x%3Y@Q%npE00PK8zs+<$Y7zfA|Y|9rG zml|&IZGbibjL#M)bC)XTuL8UT;B+CkRKh&&0@ULUG?yz(?fx#_0}i?a%$14@i%L}d z0K!WE<~?%+Jihtn(Z!~|Uua}-*XzEOnd-?_W^U7Q>TvpLJ60`nz}tq`4NQQ0HvE7%)J#*{Vil z3#qb&f&-)lQ+Bf|gWajP9i@~VC9{bvQhxnN(!fP=vCom+PQ6)`Sc-xI`r>W!B)tDtQ-Egi;II`+2m!AFF(=FYPIp z8S&bQ00=jD<|Y`xQJlazAf@^>*L||qzHCn=EH|h?ZBjPZ;wjgEYpwqvw<4+=Rd;Px zrmmNn5UY|2BdaJ0*R1f-D}6t>yeb3>DGEQ7QI&+xmoYedQ3uOHu8 zX4*)J{iFrdfskpY%Z4_gs32T`&&(A|y~k<|#0{{XwUWk0ef0%Y6IjU^B^#SF5aAY3 z-pHlDo7M%m>mc&BRVZprtqPuHAm5e~aNThH><&!-(x=dx7r!&wxr#G-DrSUV&Wc)q z7J7TV?I!G?wv!i)xt0>$bY3jUkTNOvm4rM|-11dv+*Q?4m+Tq*as-JGAbhI(v|uC< amSg&X@XK>OE0ANwO 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 diff --git a/app/partials/views/components/kanban-task.jade b/app/partials/views/components/kanban-task.jade index 95e500dc..9b02859d 100644 --- a/app/partials/views/components/kanban-task.jade +++ b/app/partials/views/components/kanban-task.jade @@ -4,10 +4,35 @@ 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 - span(tg-bind-html="us.total_points") -- - span points + ul.task-points-per-role + li + a(href="", title="Role Points") + span User Experience + span(tg-bind-html="us.total_points") -- + span points + span.icon.icon-arrow-bottom + li + a(href="", title="Role Points") + span Design + span(tg-bind-html="us.total_points") -- + span points + span.icon.icon-arrow-bottom + li + a(href="", title="Role Points") + span Front + span(tg-bind-html="us.total_points") -- + span points + span.icon.icon-arrow-bottom + li + a(href="", title="Role Points") + span Back + span(tg-bind-html="us.total_points") -- + span points + span.icon.icon-arrow-bottom + //-p.task-points + //- span(tg-bind-html="us.total_points") -- + //- span points a.icon.icon-edit(tg-check-permission="modify_us", href="", title="Edit") a.icon.icon-drag-h(tg-check-permission="modify_us", href="", title="Drag&Drop") diff --git a/app/partials/views/modules/kanban-table.jade b/app/partials/views/modules/kanban-table.jade index 0fc2499c..0e907dd6 100644 --- a/app/partials/views/modules/kanban-table.jade +++ b/app/partials/views/modules/kanban-table.jade @@ -4,14 +4,15 @@ 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-minimize(href="", title="Add New task") + //-a.icon.icon-maximize(href="", title="Add New task") 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)") - div.kanban-table-body div.kanban-table-inner(tg-kanban-row-width-fixer) div.kanban-uses-box.task-column(ng-repeat="status in usStatusList track by status.id", tg-kanban-sortable, 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") + div.kanban-task.kanban-task-maximized(ng-repeat="us in usByStatus[status.id] track by us.id", + tg-kanban-userstory, ng-model="us") diff --git a/app/styles/components/kanban-task.scss b/app/styles/components/kanban-task.scss index 8ac1565f..63e4c9af 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,95 @@ 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-per-role { + @extend %small; + margin: 0; + a { + @include transition(color .2s linear); + color: darken($postit-hover, 15%); + &:hover { + @include transition(color .2s linear); + color: darken($postit-hover, 30%); + } + } + span { + display: inline-block; + padding-left: .2rem; + &:first-child { + color: darken($postit-hover, 30%); + padding-left: 0; + 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-per-role { + 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/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%; From 0d25604f9ee35fd3353cb37c16a843bd8c3965c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xavier=20Juli=C3=A1n?= Date: Mon, 15 Sep 2014 18:07:34 +0200 Subject: [PATCH 2/9] US #903: Task #1032: Kanban estimation from lightbox --- app/partials/kanban.jade | 2 +- .../views/components/kanban-task.jade | 31 ++----------------- .../modules/lightbox-us-create-edit.jade | 22 +++++++++++-- app/styles/components/kanban-task.scss | 17 +++------- app/styles/layout/us-detail.scss | 5 +-- app/styles/modules/common/lightbox.scss | 9 ++++++ 6 files changed, 39 insertions(+), 47 deletions(-) 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/kanban-task.jade b/app/partials/views/components/kanban-task.jade index 9b02859d..55012439 100644 --- a/app/partials/views/components/kanban-task.jade +++ b/app/partials/views/components/kanban-task.jade @@ -6,33 +6,8 @@ div.kanban-task-inner span.task-num(tg-bo-ref="us.ref") 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") - ul.task-points-per-role - li - a(href="", title="Role Points") - span User Experience - span(tg-bind-html="us.total_points") -- - span points - span.icon.icon-arrow-bottom - li - a(href="", title="Role Points") - span Design - span(tg-bind-html="us.total_points") -- - span points - span.icon.icon-arrow-bottom - li - a(href="", title="Role Points") - span Front - span(tg-bind-html="us.total_points") -- - span points - span.icon.icon-arrow-bottom - li - a(href="", title="Role Points") - span Back - span(tg-bind-html="us.total_points") -- - span points - span.icon.icon-arrow-bottom - //-p.task-points - //- span(tg-bind-html="us.total_points") -- - //- span 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") a.icon.icon-drag-h(tg-check-permission="modify_us", href="", title="Drag&Drop") diff --git a/app/partials/views/modules/lightbox-us-create-edit.jade b/app/partials/views/modules/lightbox-us-create-edit.jade index ab8024fa..377a068d 100644 --- a/app/partials/views/modules/lightbox-us-create-edit.jade +++ b/app/partials/views/modules/lightbox-us-create-edit.jade @@ -5,9 +5,25 @@ form fieldset input(type="text", name="subject", ng-model="us.subject", tg-i18n="placeholder:common.subject", data-required="true", data-maxlength="500") - fieldset - select(name="status", ng-model="us.status", ng-options="s.id as s.name for s in usStatusList", - tg-i18n="placeholder:common.status") + // Add estimation points + fieldset.estimation + ul.points-per-role + li.total + span.points 40 + span.role UX + li.total + span.points 30 + span.role Front + li.total + span.points 20 + span.role Design + li.total + span.points 10 + span.role Back + // Remove status input + //-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 63e4c9af..ebe3c3b0 100644 --- a/app/styles/components/kanban-task.scss +++ b/app/styles/components/kanban-task.scss @@ -115,23 +115,14 @@ cursor: move; right: 45%; } - .task-points-per-role { + .task-points { @extend %small; + color: darken($postit-hover, 15%); + display: block; margin: 0; - a { - @include transition(color .2s linear); - color: darken($postit-hover, 15%); - &:hover { - @include transition(color .2s linear); - color: darken($postit-hover, 30%); - } - } span { display: inline-block; - padding-left: .2rem; &:first-child { - color: darken($postit-hover, 30%); - padding-left: 0; padding-right: .2rem; } } @@ -161,7 +152,7 @@ white-space: nowrap; width: 70%; } - .task-points-per-role { + .task-points { display: none; } .icon-edit, 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; + } + } +} From ed5cf1ba698f8fcf3177f8e14deee59fb4ff3b33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Tue, 16 Sep 2014 20:08:35 +0200 Subject: [PATCH 3/9] US #903: Task #1013: US can be estimated from the lightbox --- app/coffee/modules/common/lightboxes.coffee | 98 ++++++++++++++++++- .../modules/lightbox-us-create-edit.jade | 25 ++--- 2 files changed, 104 insertions(+), 19 deletions(-) diff --git a/app/coffee/modules/common/lightboxes.coffee b/app/coffee/modules/common/lightboxes.coffee index b75e280d..ff5b6298 100644 --- a/app/coffee/modules/common/lightboxes.coffee +++ b/app/coffee/modules/common/lightboxes.coffee @@ -194,20 +194,83 @@ module.directive("tgBlockingMessageInput", ["$log", BlockingMessageInputDirectiv ############################################################################# CreateEditUserstoryDirective = ($repo, $model, $rs, $rootScope, lightboxService, $loading) -> + usPointsTemplate = _.template(""" +
    +
  • + <%- totalPoints %> + total +
  • + <% _.each(rolePoints, function(rolePoint) { %> +
  • + <%- rolePoint.points %> + <%- rolePoint.name %>
  • + <% }); %> +
+ """) + + selectionPointsTemplate = _.template(""" + + """) + + link = ($scope, $el, attrs) -> isNew = true + renderUSPoints = () -> + totalPoints = $scope.us.total_points or 0 + rolePoints = _.clone(_.filter($scope.project.roles, "computable"), true) + _.map rolePoints, (v, k) -> + val = $scope.pointsById[$scope.us.points[v.id]]?.name + val = $scope.pointsById[$scope.project.default_points]?.name if not val + val = "#" if not val + v.points = val + + html = usPointsTemplate({ + totalPoints: totalPoints + rolePoints: rolePoints + }) + + $el.find("fieldset.estimation").html(html) + + renderSelectPoints = (roleId, onCloseCallback) -> + html = selectionPointsTemplate({ + roleId: roleId + points: $scope.project.points + }) + $el.find(".pop-points-open").remove() + $el.find(".points-per-role").append(html) + $el.find(".pop-points-open a[data-point-id='#{$scope.us.points[roleId]}']").addClass("active") + # If not showing role selection let's move to the left + $el.find(".pop-points-open").popover().open(onCloseCallback) + + calculateTotalPoints = -> + values = _.map($scope.us.points, (v, k) -> $scope.pointsById[v]?.value or 0) + if values.length == 0 + return "0" + + return _.reduce(values, (acc, num) -> acc + num) + $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 + # Show role points + renderUSPoints() + # Update texts for creation $el.find(".button-green span").html("Create") #TODO: i18n $el.find(".title").html("New user story ") #TODO: i18n @@ -222,6 +285,10 @@ CreateEditUserstoryDirective = ($repo, $model, $rs, $rootScope, lightboxService, $scope.$on "usform:edit", (ctx, us) -> $scope.us = us isNew = false + + # Show role points + renderUSPoints() + # Update texts for edition $el.find(".button-green span").html("Save") #TODO: i18n $el.find(".title").html("Edit user story ") #TODO: i18n @@ -245,6 +312,34 @@ CreateEditUserstoryDirective = ($repo, $model, $rs, $rootScope, lightboxService, lightboxService.open($el) + $el.on "click", ".total.clickable", (event) -> + event.preventDefault() + event.stopPropagation() + target = angular.element(event.currentTarget) + + roleId = target.data("role-id") + + target.siblings().removeClass('active') + target.addClass('active') + renderSelectPoints(roleId, () -> target.removeClass('active')) + + $el.on "click", ".point", (event) -> + event.preventDefault() + event.stopPropagation() + + target = angular.element(event.currentTarget) + roleId = target.data("role-id") + pointId = target.data("point-id") + + $.fn.popover().closeAll() + + $scope.$apply () -> + usPoints = _.clone($scope.us.points, true) + usPoints[roleId] = pointId + $scope.us.points = usPoints + $scope.us.total_points = calculateTotalPoints() + renderUSPoints() + $el.on "click", ".button-green", (event) -> event.preventDefault() form = $el.find("form").checksley() @@ -254,6 +349,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/partials/views/modules/lightbox-us-create-edit.jade b/app/partials/views/modules/lightbox-us-create-edit.jade index 377a068d..9e189700 100644 --- a/app/partials/views/modules/lightbox-us-create-edit.jade +++ b/app/partials/views/modules/lightbox-us-create-edit.jade @@ -5,25 +5,14 @@ form fieldset input(type="text", name="subject", ng-model="us.subject", tg-i18n="placeholder:common.subject", data-required="true", data-maxlength="500") - // Add estimation points + fieldset.estimation - ul.points-per-role - li.total - span.points 40 - span.role UX - li.total - span.points 30 - span.role Front - li.total - span.points 20 - span.role Design - li.total - span.points 10 - span.role Back - // Remove status input - //-fieldset - //- select(name="status", ng-model="us.status", ng-options="s.id as s.name for s in usStatusList", - //- tg-i18n="placeholder:common.status") + //- 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") From dfcc1b567d17d58c9e7caf2d39d561f9fed06817 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Wed, 17 Sep 2014 14:12:02 +0200 Subject: [PATCH 4/9] US #903: Task #1014: Toggle view mode --- app/coffee/modules/kanban/main.coffee | 30 ++++++++++++++++++++ app/partials/views/modules/kanban-table.jade | 25 ++++++++++++---- 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/app/coffee/modules/kanban/main.coffee b/app/coffee/modules/kanban/main.coffee index 4bc3b172..5887216d 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 ############################################################################# @@ -147,6 +160,7 @@ 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") + @.loadStatusViewMode() @scope.$emit("project:loaded", project) return project @@ -161,6 +175,22 @@ class KanbanController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi .then(=> @.loadKanban()) .then(=> @scope.$broadcast("redraw:wip")) + ## View Mode methods + + loadStatusViewMode: -> + @scope.statusViewModes = {} + for status in @scope.usStatusList + @scope.statusViewModes[status.id] = defaultViewMode + + updateStatusViewMode: (statusId, newViewMode) -> + @scope.statusViewModes[statusId] = newViewMode + + 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/partials/views/modules/kanban-table.jade b/app/partials/views/modules/kanban-table.jade index 0e907dd6..6e40e0ec 100644 --- a/app/partials/views/modules/kanban-table.jade +++ b/app/partials/views/modules/kanban-table.jade @@ -4,15 +4,28 @@ 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-minimize(href="", title="Add New task") - //-a.icon.icon-maximize(href="", title="Add New task") - 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) div.kanban-uses-box.task-column(ng-repeat="status in usStatusList track by status.id", tg-kanban-sortable, tg-kanban-wip-limit, tg-kanban-column-height-fixer) - div.kanban-task.kanban-task-maximized(ng-repeat="us in usByStatus[status.id] track by us.id", - tg-kanban-userstory, ng-model="us") + div.kanban-task(ng-repeat="us in usByStatus[status.id] track by us.id", + tg-kanban-userstory, ng-model="us", + ng-class="ctrl.getCardClass(status.id)") From 4a68f728f6b91b1d2d7ae38af9509b8cc0e976b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Wed, 17 Sep 2014 17:31:44 +0200 Subject: [PATCH 5/9] US #903: Task #1035: Saved statusViewModes in local storage --- app/coffee/modules/kanban/main.coffee | 17 ++++++-- app/coffee/modules/resources.coffee | 1 + app/coffee/modules/resources/kanban.coffee | 46 ++++++++++++++++++++++ 3 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 app/coffee/modules/resources/kanban.coffee diff --git a/app/coffee/modules/kanban/main.coffee b/app/coffee/modules/kanban/main.coffee index 5887216d..52f3911b 100644 --- a/app/coffee/modules/kanban/main.coffee +++ b/app/coffee/modules/kanban/main.coffee @@ -64,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() @@ -160,7 +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") - @.loadStatusViewMode() + @.generateStatusViewModes() + @scope.$emit("project:loaded", project) return project @@ -177,13 +179,22 @@ class KanbanController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.Fi ## View Mode methods - loadStatusViewMode: -> + generateStatusViewModes: -> + storedStatusViewModes = @rs.kanban.getStatusViewModes(@scope.projectId) + @scope.statusViewModes = {} for status in @scope.usStatusList - @scope.statusViewModes[status.id] = defaultViewMode + 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 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]) From 1e19ddd4851e30e6fe26dfbcf1a86227d0c99e37 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Thu, 18 Sep 2014 00:00:56 +0200 Subject: [PATCH 6/9] US #903: Task #1034: Refactorized tgUsPoints directive --- app/coffee/modules/backlog/main.coffee | 168 +++++++++++------- .../views/components/backlog-row.jade | 2 +- 2 files changed, 102 insertions(+), 68 deletions(-) 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/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") From e94e2a41b3c60790439de64dc5d9bdad05e2491f Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Thu, 18 Sep 2014 11:29:52 +0200 Subject: [PATCH 7/9] Remove code from us-create-edit lightbox that are now handled by specific directive. --- app/coffee/modules/common/lightboxes.coffee | 93 --------------------- 1 file changed, 93 deletions(-) diff --git a/app/coffee/modules/common/lightboxes.coffee b/app/coffee/modules/common/lightboxes.coffee index ff5b6298..7fa1c96f 100644 --- a/app/coffee/modules/common/lightboxes.coffee +++ b/app/coffee/modules/common/lightboxes.coffee @@ -194,68 +194,9 @@ module.directive("tgBlockingMessageInput", ["$log", BlockingMessageInputDirectiv ############################################################################# CreateEditUserstoryDirective = ($repo, $model, $rs, $rootScope, lightboxService, $loading) -> - usPointsTemplate = _.template(""" -
      -
    • - <%- totalPoints %> - total -
    • - <% _.each(rolePoints, function(rolePoint) { %> -
    • - <%- rolePoint.points %> - <%- rolePoint.name %>
    • - <% }); %> -
    - """) - - selectionPointsTemplate = _.template(""" - - """) - - link = ($scope, $el, attrs) -> isNew = true - renderUSPoints = () -> - totalPoints = $scope.us.total_points or 0 - rolePoints = _.clone(_.filter($scope.project.roles, "computable"), true) - _.map rolePoints, (v, k) -> - val = $scope.pointsById[$scope.us.points[v.id]]?.name - val = $scope.pointsById[$scope.project.default_points]?.name if not val - val = "#" if not val - v.points = val - - html = usPointsTemplate({ - totalPoints: totalPoints - rolePoints: rolePoints - }) - - $el.find("fieldset.estimation").html(html) - - renderSelectPoints = (roleId, onCloseCallback) -> - html = selectionPointsTemplate({ - roleId: roleId - points: $scope.project.points - }) - $el.find(".pop-points-open").remove() - $el.find(".points-per-role").append(html) - $el.find(".pop-points-open a[data-point-id='#{$scope.us.points[roleId]}']").addClass("active") - # If not showing role selection let's move to the left - $el.find(".pop-points-open").popover().open(onCloseCallback) - - calculateTotalPoints = -> - values = _.map($scope.us.points, (v, k) -> $scope.pointsById[v]?.value or 0) - if values.length == 0 - return "0" - - return _.reduce(values, (acc, num) -> acc + num) - $scope.$on "usform:new", (ctx, projectId, status, statusList) -> isNew = true $scope.usStatusList = statusList @@ -268,9 +209,6 @@ CreateEditUserstoryDirective = ($repo, $model, $rs, $rootScope, lightboxService, tags: [] } - # Show role points - renderUSPoints() - # Update texts for creation $el.find(".button-green span").html("Create") #TODO: i18n $el.find(".title").html("New user story ") #TODO: i18n @@ -286,9 +224,6 @@ CreateEditUserstoryDirective = ($repo, $model, $rs, $rootScope, lightboxService, $scope.us = us isNew = false - # Show role points - renderUSPoints() - # Update texts for edition $el.find(".button-green span").html("Save") #TODO: i18n $el.find(".title").html("Edit user story ") #TODO: i18n @@ -312,34 +247,6 @@ CreateEditUserstoryDirective = ($repo, $model, $rs, $rootScope, lightboxService, lightboxService.open($el) - $el.on "click", ".total.clickable", (event) -> - event.preventDefault() - event.stopPropagation() - target = angular.element(event.currentTarget) - - roleId = target.data("role-id") - - target.siblings().removeClass('active') - target.addClass('active') - renderSelectPoints(roleId, () -> target.removeClass('active')) - - $el.on "click", ".point", (event) -> - event.preventDefault() - event.stopPropagation() - - target = angular.element(event.currentTarget) - roleId = target.data("role-id") - pointId = target.data("point-id") - - $.fn.popover().closeAll() - - $scope.$apply () -> - usPoints = _.clone($scope.us.points, true) - usPoints[roleId] = pointId - $scope.us.points = usPoints - $scope.us.total_points = calculateTotalPoints() - renderUSPoints() - $el.on "click", ".button-green", (event) -> event.preventDefault() form = $el.find("form").checksley() From b5499f2eefd690784e27b2397f33b26580dc40c7 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Thu, 18 Sep 2014 11:30:34 +0200 Subject: [PATCH 8/9] Code style improvements. --- app/coffee/modules/common/popovers.coffee | 8 ++++++++ app/coffee/modules/userstories/detail.coffee | 6 ++---- 2 files changed, 10 insertions(+), 4 deletions(-) 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/userstories/detail.coffee b/app/coffee/modules/userstories/detail.coffee index 1dd2d84c..1e94fe92 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 From e9c859948a84c707bbad0c4f71d4268af01214e6 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Thu, 18 Sep 2014 11:31:09 +0200 Subject: [PATCH 9/9] Add tgUsEstimation directive. --- app/coffee/modules/userstories/detail.coffee | 126 ++++++++++++++++++ .../modules/lightbox-us-create-edit.jade | 3 +- 2 files changed, 128 insertions(+), 1 deletion(-) diff --git a/app/coffee/modules/userstories/detail.coffee b/app/coffee/modules/userstories/detail.coffee index 1e94fe92..6da90974 100644 --- a/app/coffee/modules/userstories/detail.coffee +++ b/app/coffee/modules/userstories/detail.coffee @@ -363,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/partials/views/modules/lightbox-us-create-edit.jade b/app/partials/views/modules/lightbox-us-create-edit.jade index 9e189700..17af9326 100644 --- a/app/partials/views/modules/lightbox-us-create-edit.jade +++ b/app/partials/views/modules/lightbox-us-create-edit.jade @@ -7,7 +7,8 @@ form data-required="true", data-maxlength="500") fieldset.estimation - //- Render by tg-lb-create-edit-userstory + 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",