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

View File

@ -13,6 +13,7 @@
border-radius: 50%;
display: inline-block;
height: .7rem;
margin: 0 .1rem;
position: relative;
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-downvote="ctrl.onDownvote"
)
div.us-title(ng-class="{blocked: us.is_blocked}")
h2.us-title-text
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"
tg-detail-header.detail-header-container(
item="us"
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 }}
a(
href=""
tg-check-permission="view_us"
tg-nav="project-issues-detail:project=project.slug,ref=us.origin_issue.ref"
tg-bo-title="'#' + us.origin_issue.ref + ' ' + us.origin_issue.subject"
title="{{'US.TITLE_LINK_GO_TO_ISSUE' | translate}}"
)
span(tg-bo-ref="us.origin_issue.ref")
p.external-reference(ng-if="us.external_reference")
| {{ 'US.EXTERNAL_REFERENCE'|translate }}
a(
tg-bo-href="us.external_reference[1]",
title="{{'US.GO_TO_EXTERNAL_REFERENCE' | translate}}"
target="_blank"
)
span {{ us.external_reference[1] }}
p.block-desc-container(ng-show="us.is_blocked")
span.block-description-title(translate="COMMON.BLOCKED")
span.block-description(ng-bind="us.blocked_note || ('US.BLOCKED' | translate)")
div.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")
//- div.us-title(ng-class="{blocked: us.is_blocked}")
//- h2.us-title-text
//-
//-
//- p.us-related-task(ng-if="us.origin_issue") {{ 'US.PROMOTED'|translate }}
//- a(
//- href=""
//- tg-check-permission="view_us"
//- tg-nav="project-issues-detail:project=project.slug,ref=us.origin_issue.ref"
//- tg-bo-title="'#' + us.origin_issue.ref + ' ' + us.origin_issue.subject"
//- title="{{'US.TITLE_LINK_GO_TO_ISSUE' | translate}}"
//- )
//- span(tg-bo-ref="us.origin_issue.ref")
//-
//- p.external-reference(ng-if="us.external_reference")
//- | {{ 'US.EXTERNAL_REFERENCE'|translate }}
//- a(
//- tg-bo-href="us.external_reference[1]",
//- title="{{'US.GO_TO_EXTERNAL_REFERENCE' | translate}}"
//- target="_blank"
//- )
//- span {{ us.external_reference[1] }}
//-
//- p.block-desc-container(ng-show="us.is_blocked")
//- span.block-description-title(translate="COMMON.BLOCKED")
//- span.block-description(ng-bind="us.blocked_note || ('US.BLOCKED' | translate)")
//- div.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")
.subheader
tg-tag-line.tags-block(
ng-if="us && project"

View File

@ -7,186 +7,6 @@
justify-content: center;
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 {