Header detail WIP

stable
Xavier Julián 2016-08-11 16:51:50 +02:00 committed by David Barragán Merino
parent 8e901b0067
commit 58a19f1425
8 changed files with 567 additions and 316 deletions

View File

@ -493,90 +493,90 @@ DeleteButtonDirective = ($log, $repo, $confirm, $location, $template) ->
module.directive("tgDeleteButton", ["$log", "$tgRepo", "$tgConfirm", "$tgLocation", "$tgTemplate", DeleteButtonDirective]) module.directive("tgDeleteButton", ["$log", "$tgRepo", "$tgConfirm", "$tgLocation", "$tgTemplate", DeleteButtonDirective])
############################################################################# # #############################################################################
## Editable subject directive # ## Editable subject directive
############################################################################# # #############################################################################
#
EditableSubjectDirective = ($rootscope, $repo, $confirm, $loading, $modelTransform, $template) -> # EditableSubjectDirective = ($rootscope, $repo, $confirm, $loading, $modelTransform, $template) ->
template = $template.get("common/components/editable-subject.html") # template = $template.get("common/components/editable-subject.html")
#
link = ($scope, $el, $attrs, $model) -> # link = ($scope, $el, $attrs, $model) ->
#
$scope.$on "object:updated", () -> # $scope.$on "object:updated", () ->
$el.find('.edit-subject').hide() # $el.find('.edit-subject').hide()
$el.find('.view-subject').show() # $el.find('.view-subject').show()
#
isEditable = -> # isEditable = ->
return $scope.project.my_permissions.indexOf($attrs.requiredPerm) != -1 # return $scope.project.my_permissions.indexOf($attrs.requiredPerm) != -1
#
save = (subject) -> # save = (subject) ->
currentLoading = $loading() # currentLoading = $loading()
.target($el.find('.save-container')) # .target($el.find('.save-container'))
.start() # .start()
#
transform = $modelTransform.save (item) -> # transform = $modelTransform.save (item) ->
item.subject = subject # item.subject = subject
#
return item # return item
#
transform.then => # transform.then =>
$confirm.notify("success") # $confirm.notify("success")
$rootscope.$broadcast("object:updated") # $rootscope.$broadcast("object:updated")
$el.find('.edit-subject').hide() # $el.find('.edit-subject').hide()
$el.find('.view-subject').show() # $el.find('.view-subject').show()
#
transform.then null, -> # transform.then null, ->
$confirm.notify("error") # $confirm.notify("error")
#
transform.finally -> # transform.finally ->
currentLoading.finish() # currentLoading.finish()
#
return transform # return transform
#
$el.click -> # $el.click ->
return if not isEditable() # return if not isEditable()
$el.find('.edit-subject').show() # $el.find('.edit-subject').show()
$el.find('.view-subject').hide() # $el.find('.view-subject').hide()
$el.find('input').focus() # $el.find('input').focus()
#
$el.on "click", ".save", (e) -> # $el.on "click", ".save", (e) ->
e.preventDefault() # e.preventDefault()
#
subject = $scope.item.subject # subject = $scope.item.subject
save(subject) # save(subject)
#
$el.on "keyup", "input", (event) -> # $el.on "keyup", "input", (event) ->
if event.keyCode == 13 # if event.keyCode == 13
subject = $scope.item.subject # subject = $scope.item.subject
save(subject) # save(subject)
else if event.keyCode == 27 # else if event.keyCode == 27
$scope.$apply () => $model.$modelValue.revert() # $scope.$apply () => $model.$modelValue.revert()
#
$el.find('.edit-subject').hide() # $el.find('.edit-subject').hide()
$el.find('.view-subject').show() # $el.find('.view-subject').show()
#
$el.find('.edit-subject').hide() # $el.find('.edit-subject').hide()
#
$scope.$watch $attrs.ngModel, (value) -> # $scope.$watch $attrs.ngModel, (value) ->
return if not value # return if not value
$scope.item = value # $scope.item = value
#
if not isEditable() # if not isEditable()
$el.find('.view-subject .edit').remove() # $el.find('.view-subject .edit').remove()
#
$scope.$on "$destroy", -> # $scope.$on "$destroy", ->
$el.off() # $el.off()
#
#
return { # return {
link: link # link: link
restrict: "EA" # restrict: "EA"
require: "ngModel" # require: "ngModel"
template: template # template: template
} # }
#
module.directive("tgEditableSubject", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgLoading", "$tgQueueModelTransformation", # module.directive("tgEditableSubject", ["$rootScope", "$tgRepo", "$tgConfirm", "$tgLoading", "$tgQueueModelTransformation",
"$tgTemplate", EditableSubjectDirective]) # "$tgTemplate", EditableSubjectDirective])
############################################################################# #############################################################################

View File

@ -13,6 +13,7 @@
border-radius: 50%; border-radius: 50%;
display: inline-block; display: inline-block;
height: .7rem; height: .7rem;
margin: 0 .1rem;
position: relative; position: relative;
width: .7rem; width: .7rem;
} }

View File

@ -0,0 +1,67 @@
###
# Copyright (C) 2014-2015 Taiga Agile LLC <taiga@taiga.io>
#
# 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: epics.dashboard.controller.coffee
###
module = angular.module("taigaUserStories")
class StoryHeaderController
@.$inject = [
"$rootScope",
"$tgConfirm",
"$tgQueueModelTransformation"
]
constructor: (@rootScope, @confirm, @modelTransform) ->
@.editMode = false
@.loadingSubject = false
@.originalSubject = @.item.subject
_checkPermissions: () ->
@.permissions = {
canEdit: _.includes(@.project.my_permissions, @.requiredPerm)
}
editSubject: (value) ->
if value
@.editMode = true
if !value
@.editMode = false
onCancelEdition: (event) ->
if event.which == 27
@.item.subject = @.originalSubject
@.editSubject(false)
saveSubject: () ->
onEditSubjectSuccess = () =>
@.loadingSubject = false
@rootScope.$broadcast("object:updated")
@confirm.notify('success')
onEditSubjectError = () =>
@.loadingSubject = false
@confirm.notify('error')
@.editMode = false
@.loadingSubject = true
item = @.item
transform = @modelTransform.save (item) ->
return item
return transform.then(onEditSubjectSuccess, onEditSubjectError)
module.controller("StoryHeaderCtrl", StoryHeaderController)

View File

@ -0,0 +1,42 @@
###
# Copyright (C) 2014-2016 Taiga Agile LLC <taiga@taiga.io>
#
# 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: story-header.directive.coffee
###
module = angular.module('taigaUserStories')
DetailHeaderDirective = () ->
@.$inject = []
link = (scope, el, attrs, ctrl) ->
ctrl._checkPermissions()
return {
link: link,
controller: "StoryHeaderCtrl",
bindToController: true,
scope: {
item: "=",
project: "=",
requiredPerm: "@"
},
controllerAs: "vm",
templateUrl:"stories/header/story-header.html"
}
module.directive("tgDetailHeader", DetailHeaderDirective)

View File

@ -0,0 +1,76 @@
.detail-title-wrapper
h2.detail-title-text.ng-animate-disabled(
ng-show="!vm.editMode"
ng-hide="vm.editMode"
)
span.detail-number {{'#' + vm.item.ref}}
span.detail-subject(
ng-click="vm.editSubject(true)"
ng-if="vm.permissions.canEdit"
) {{vm.item.subject}}
span.detail-subject(
ng-if="!vm.permissions.canEdit"
) {{vm.item.subject}}
tg-svg.detail-edit(
ng-if="vm.permissions.canEdit"
svg-icon="icon-edit"
ng-click="vm.editSubject(true)"
)
.edit-title-wrapper(ng-if="vm.editMode")
input.edit-title-input(
type="text"
ng-model="vm.item.subject"
maxlength="500"
autofocus
required
ng-keydown="vm.onCancelEdition($event)"
)
button.edit-title-button(
ng-click="vm.saveSubject()"
tg-loading="vm.loadingSubject"
)
tg-svg(
svg-icon="icon-save"
)
.belong-to-epics-wrapper(ng-if="vm.item.epics")
span This User Story belongs to
tg-belong-to-epics(
ng-if="::vm.item.epics"
epics="::vm.item.epics"
format="text"
project="project"
)
.item-origin-issue(
ng-if="vm.item.origin_issue"
)
span(translate="US.PROMOTED")
a(
href=""
tg-check-permission="view_us"
tg-nav="project-issues-detail:project=vm.project.slug,ref=vm.item.origin_issue.ref"
title="{{'US.TITLE_LINK_GO_TO_ISSUE' | translate}}"
)
span {{'#' + vm.item.origin_issue.ref}}
span {{vm.item.origin_issue.subject}}
.block-desc-container(ng-show="vm.item.is_blocked")
span.block-description-title(translate="COMMON.BLOCKED")
span.block-description(
ng-if="vm.item.blocked_note"
) {{vm.item.blocked_note}}
.issue-nav
a(
ng-show="previousUrl"
tg-bo-href="previousUrl"
title="{{'US.PREVIOUS' | translate}}"
)
tg-svg(svg-icon="icon-arrow-left")
a(
ng-show="nextUrl"
tg-bo-href="nextUrl"
title="{{'US.NEXT' | translate}}"
)
tg-svg(svg-icon="icon-arrow-right")

View File

@ -0,0 +1,251 @@
.detail-header-container {
background: $mass-white;
flex: 1;
padding: 1rem;
&:hover {
.detail-edit {
opacity: 1;
}
}
&.blocked {
background: $red;
color: $white;
transition: all .2s linear;
a,
.detail-number,
.detail-subject {
color: $white;
}
svg {
fill: $white;
}
}
.item-origin-issue,
.belong-to-epics-wrapper,
.block-desc-container {
@include font-size(small);
margin-top: .5rem;
}
.item-origin-issue {
a {
padding: 0 .2rem;
}
}
}
.detail-title-wrapper {
@include font-size(larger);
@include font-type(text);
align-content: center;
display: flex;
position: relative;
transition: all .2s linear;
&.blocked {
background: $red;
transition: all .2s linear;
}
.detail-title-text {
margin: 0;
}
.detail-number {
color: $gray-light;
flex-shrink: 0;
margin-right: .5rem;
}
.detail-subject {
color: $gray;
flex-grow: 1;
}
.detail-edit {
cursor: pointer;
margin-left: .75rem;
opacity: 0;
transition: opacity .2s;
svg {
@include svg-size(1.25rem);
}
}
}
.edit-title-wrapper {
@include font-size(larger);
@include font-type(text);
display: flex;
flex: 1;
.edit-title-input {
background: $white;
flex: 1;
}
.edit-title-button {
background: none;
display: inline;
margin-left: 1rem;
transition: fill .2s;
&:hover {
fill: $primary;
}
}
}
.block-desc-container {
.block-description-title {
@include font-type(bold);
margin-right: .5rem;
}
}
.issue-nav {
position: absolute;
right: 1rem;
top: 1rem;
a {
display: inline-block;
}
svg {
@include svg-size(1.2rem);
fill: currentColor;
}
}
//
// .us-title {
//
// &.blocked {
// background: $red;
// transition: all .2s linear;
// vertical-align: middle;
// .us-title-text,
// input {
// margin-bottom: .5rem;
// }
// .us-number,
// .us-name,
// .us-related-task {
// color: $white;
// }
// a {
// color: $white;
// transition: color .3s linear;
// }
// a:hover {
// color: $red-light;
// }
// .unblock {
// @include font-type(bold);
// color: $white;
// float: right;
// }
// .unblock:hover {
// color: $red-light;
// transition: color .3s linear;
// }
// }
// p {
// margin-bottom: 0;
// }
// .us-edit-name-inner {
// display: flex;
// }
// .edit-subject {
// align-content: center;
// align-items: center;
// display: flex;
// width: 100%;
// }
// input {
// background: $white;
// flex-grow: 1;
// }
// .save-container {
// flex-grow: 1;
// .save {
// display: block;
// }
// }
// .us-title-text {
// @include font-size(larger);
// @include font-type(text);
// align-content: center;
// align-items: center;
// display: flex;
// flex: 1;
// margin-bottom: 0;
// max-width: 92%;
// width: 100%;
// }
// .us-title-text:hover {
// .edit {
// opacity: 1;
// transition: opacity .3s linear;
// }
// }
// .us-number {
// @include font-type(text);
// color: $gray-light;
// flex-shrink: 0;
// line-height: 2.2rem;
// margin-right: .5rem;
// }
// .us-name {
// color: $gray;
// display: inline-block;
// flex-grow: 1;
// line-height: 2.2rem;
// padding-right: 1rem;
// width: 100%;
// }
// .save,
// .edit {
// cursor: pointer;
// margin-left: .5rem;
// svg {
// fill: $gray-light;
// }
// }
// .edit {
// opacity: 0;
// }
// .us-related-task {
// @include font-size(small);
// color: $gray-light;
// margin-top: .5rem;
// a {
// border-left: 1px solid $gray-light;
// padding: 0 .2rem;
// }
// a:hover {
// color: $primary;
// }
// a:first-child {
// border: 0;
// }
// }
// .block-desc-container {
// @include font-size(small);
// }
// .block-description-title {
// @include font-type(bold);
// color: $white;
// margin-right: .5rem;
// }
// .block-description {
// color: $white;
// display: inline-block;
// margin-right: 5rem;
// }
// }
// .belong-to-epics-wrapper {
// @include font-size(small);
// color: $gray-light;
// margin-top: .5rem;
// a:hover {
// color: $primary;
// }
// }
// .loading-spinner {
// @include loading-spinner;
// max-height: 1.5rem;
// max-width: 1.5rem;
// }

View File

@ -26,59 +26,53 @@ div.wrapper(
on-upvote="ctrl.onUpvote" on-upvote="ctrl.onUpvote"
on-downvote="ctrl.onDownvote" on-downvote="ctrl.onDownvote"
) )
div.us-title(ng-class="{blocked: us.is_blocked}") tg-detail-header.detail-header-container(
h2.us-title-text item="us"
span.us-number(tg-bo-ref="us.ref")
span.us-name(
tg-editable-subject
ng-model="us"
required-perm="modify_us"
)
p.belong-to-epics-wrapper(ng-if="us.epics")
span This User Story belongs to
tg-belong-to-epics(
ng-if="us.epics"
epics="us.epics"
format="text"
project="project" project="project"
required-perm="modify_us"
ng-class="{blocked: us.is_blocked}"
ng-if="project && us"
) )
p.us-related-task(ng-if="us.origin_issue") {{ 'US.PROMOTED'|translate }} //- div.us-title(ng-class="{blocked: us.is_blocked}")
a( //- h2.us-title-text
href="" //-
tg-check-permission="view_us" //-
tg-nav="project-issues-detail:project=project.slug,ref=us.origin_issue.ref" //- p.us-related-task(ng-if="us.origin_issue") {{ 'US.PROMOTED'|translate }}
tg-bo-title="'#' + us.origin_issue.ref + ' ' + us.origin_issue.subject" //- a(
title="{{'US.TITLE_LINK_GO_TO_ISSUE' | translate}}" //- href=""
) //- tg-check-permission="view_us"
span(tg-bo-ref="us.origin_issue.ref") //- tg-nav="project-issues-detail:project=project.slug,ref=us.origin_issue.ref"
//- tg-bo-title="'#' + us.origin_issue.ref + ' ' + us.origin_issue.subject"
p.external-reference(ng-if="us.external_reference") //- title="{{'US.TITLE_LINK_GO_TO_ISSUE' | translate}}"
| {{ 'US.EXTERNAL_REFERENCE'|translate }} //- )
a( //- span(tg-bo-ref="us.origin_issue.ref")
tg-bo-href="us.external_reference[1]", //-
title="{{'US.GO_TO_EXTERNAL_REFERENCE' | translate}}" //- p.external-reference(ng-if="us.external_reference")
target="_blank" //- | {{ 'US.EXTERNAL_REFERENCE'|translate }}
) //- a(
span {{ us.external_reference[1] }} //- tg-bo-href="us.external_reference[1]",
//- title="{{'US.GO_TO_EXTERNAL_REFERENCE' | translate}}"
p.block-desc-container(ng-show="us.is_blocked") //- target="_blank"
span.block-description-title(translate="COMMON.BLOCKED") //- )
span.block-description(ng-bind="us.blocked_note || ('US.BLOCKED' | translate)") //- span {{ us.external_reference[1] }}
div.issue-nav //-
a( //- p.block-desc-container(ng-show="us.is_blocked")
ng-show="previousUrl" //- span.block-description-title(translate="COMMON.BLOCKED")
tg-bo-href="previousUrl" //- span.block-description(ng-bind="us.blocked_note || ('US.BLOCKED' | translate)")
title="{{'US.PREVIOUS' | translate}}" //- div.issue-nav
) //- a(
tg-svg(svg-icon="icon-arrow-left") //- ng-show="previousUrl"
a( //- tg-bo-href="previousUrl"
ng-show="nextUrl" //- title="{{'US.PREVIOUS' | translate}}"
tg-bo-href="nextUrl" //- )
title="{{'US.NEXT' | translate}}" //- tg-svg(svg-icon="icon-arrow-left")
) //- a(
tg-svg(svg-icon="icon-arrow-right") //- ng-show="nextUrl"
//- tg-bo-href="nextUrl"
//- title="{{'US.NEXT' | translate}}"
//- )
//- tg-svg(svg-icon="icon-arrow-right")
.subheader .subheader
tg-tag-line.tags-block( tg-tag-line.tags-block(
ng-if="us && project" ng-if="us && project"

View File

@ -7,186 +7,6 @@
justify-content: center; justify-content: center;
margin-bottom: .5rem; margin-bottom: .5rem;
} }
.us-title {
@include font-size(large);
@include font-type(text);
align-items: flex-start;
background: $mass-white;
display: flex;
flex: 1;
flex-direction: column;
padding: .5rem;
position: relative;
transition: all .2s linear;
&.blocked {
background: $red;
transition: all .2s linear;
vertical-align: middle;
.us-title-text,
input {
margin-bottom: .5rem;
}
.us-number,
.us-name,
.us-related-task {
color: $white;
}
a {
color: $white;
transition: color .3s linear;
}
a:hover {
color: $red-light;
}
.unblock {
@include font-type(bold);
color: $white;
float: right;
}
.unblock:hover {
color: $red-light;
transition: color .3s linear;
}
}
p {
margin-bottom: 0;
}
.us-edit-name-inner {
display: flex;
}
.edit-subject {
align-content: center;
align-items: center;
display: flex;
width: 100%;
}
input {
background: $white;
flex-grow: 1;
}
.save-container {
flex-grow: 1;
.save {
display: block;
}
}
.us-title-text {
@include font-size(larger);
@include font-type(text);
align-content: center;
align-items: center;
display: flex;
flex: 1;
margin-bottom: 0;
max-width: 92%;
width: 100%;
}
.us-title-text:hover {
.edit {
opacity: 1;
transition: opacity .3s linear;
}
}
.us-number {
@include font-type(text);
color: $gray-light;
flex-shrink: 0;
line-height: 2.2rem;
margin-right: .5rem;
}
.us-name {
color: $gray;
display: inline-block;
flex-grow: 1;
line-height: 2.2rem;
padding-right: 1rem;
width: 100%;
}
.save,
.edit {
cursor: pointer;
margin-left: .5rem;
svg {
fill: $gray-light;
}
}
.edit {
opacity: 0;
}
.us-related-task {
@include font-size(small);
color: $gray-light;
margin-top: .5rem;
a {
border-left: 1px solid $gray-light;
padding: 0 .2rem;
}
a:hover {
color: $primary;
}
a:first-child {
border: 0;
}
}
.block-desc-container {
@include font-size(small);
}
.block-description-title {
@include font-type(bold);
color: $white;
margin-right: .5rem;
}
.block-description {
color: $white;
display: inline-block;
margin-right: 5rem;
}
}
.belong-to-epics-wrapper {
@include font-size(small);
color: $gray-light;
margin-top: .5rem;
a:hover {
color: $primary;
}
}
.loading-spinner {
@include loading-spinner;
max-height: 1.5rem;
max-width: 1.5rem;
}
}
.blocked-warning {
margin-bottom: 1rem;
.blocked {
@include font-type(text);
@include font-size(xlarge);
color: $red;
line-height: 2.5rem;
margin-bottom: .5rem;
}
.icon {
@include font-size(xlarge);
vertical-align: middle;
}
.block-description {
color: $grayer;
margin: 0;
}
}
.issue-nav {
position: absolute;
right: 1rem;
top: 1rem;
a {
display: inline-block;
}
svg {
@include svg-size(1.2rem);
fill: currentColor;
}
} }
.subheader { .subheader {