Admin area for tags
parent
527713985b
commit
6386dd57b2
|
@ -8,6 +8,7 @@
|
|||
- Add the tribe button to link stories from tree.taiga.io with gigs in tribe.taiga.io.
|
||||
- Errors (not found, server error, permissions and blocked project) don't change the current url.
|
||||
- Attachments image slider
|
||||
- New admin area to edit the tag colors used in your project
|
||||
|
||||
### Misc
|
||||
- Lots of small and not so small bugfixes.
|
||||
|
|
|
@ -289,7 +289,12 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven
|
|||
section: "admin"
|
||||
}
|
||||
)
|
||||
|
||||
$routeProvider.when("/project/:pslug/admin/project-values/tags",
|
||||
{
|
||||
templateUrl: "admin/admin-project-values-tags.html",
|
||||
section: "admin"
|
||||
}
|
||||
)
|
||||
$routeProvider.when("/project/:pslug/admin/memberships",
|
||||
{
|
||||
templateUrl: "admin/admin-memberships.html",
|
||||
|
|
|
@ -266,14 +266,6 @@ ProjectValuesDirective = ($log, $repo, $confirm, $location, animationFrame, $tra
|
|||
editionRow.removeClass('hidden')
|
||||
editionRow.find('input:visible').first().focus().select()
|
||||
|
||||
$el.on "keyup", ".edition input", (event) ->
|
||||
if event.keyCode == 13
|
||||
target = angular.element(event.currentTarget)
|
||||
saveValue(target)
|
||||
else if event.keyCode == 27
|
||||
target = angular.element(event.currentTarget)
|
||||
cancel(target)
|
||||
|
||||
$el.on "keyup", ".new-value input", (event) ->
|
||||
if event.keyCode == 13
|
||||
target = $el.find(".new-value")
|
||||
|
@ -349,7 +341,7 @@ ColorSelectionDirective = () ->
|
|||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
target = angular.element(event.currentTarget)
|
||||
$el.find(".select-color").hide()
|
||||
$(".select-color").hide()
|
||||
target.siblings(".select-color").show()
|
||||
# Hide when click outside
|
||||
body = angular.element("body")
|
||||
|
@ -372,6 +364,15 @@ ColorSelectionDirective = () ->
|
|||
$model.$modelValue.color = $scope.color
|
||||
$el.find(".select-color").hide()
|
||||
|
||||
$el.on "keyup", "input", (event) ->
|
||||
if event.keyCode == 13
|
||||
$scope.$apply ->
|
||||
$model.$modelValue.color = $scope.color
|
||||
$el.find(".select-color").hide()
|
||||
|
||||
else if event.keyCode == 27
|
||||
$el.find(".select-color").hide()
|
||||
|
||||
$scope.$on "$destroy", ->
|
||||
$el.off()
|
||||
|
||||
|
@ -689,3 +690,65 @@ ProjectCustomAttributesDirective = ($log, $confirm, animationFrame, $translate)
|
|||
|
||||
module.directive("tgProjectCustomAttributes", ["$log", "$tgConfirm", "animationFrame", "$translate",
|
||||
ProjectCustomAttributesDirective])
|
||||
|
||||
|
||||
#############################################################################
|
||||
## Tags Controller
|
||||
#############################################################################
|
||||
|
||||
class ProjectTagsController extends mixOf(taiga.Controller, taiga.PageMixin)
|
||||
@.$inject = [
|
||||
"$scope",
|
||||
"$rootScope",
|
||||
"$tgRepo",
|
||||
"tgAppMetaService",
|
||||
"$translate"
|
||||
]
|
||||
|
||||
constructor: (@scope, @rootscope, @repo, @appMetaService, @translate) ->
|
||||
@.loading = true
|
||||
@rootscope.$on "project:loaded", =>
|
||||
sectionName = @translate.instant(@scope.sectionName)
|
||||
title = @translate.instant("ADMIN.CUSTOM_ATTRIBUTES.PAGE_TITLE", {
|
||||
"sectionName": sectionName,
|
||||
"projectName": @scope.project.name
|
||||
})
|
||||
description = @scope.project.description
|
||||
@appMetaService.setAll(title, description)
|
||||
|
||||
@.loading = false
|
||||
@.tagNames = Object.keys(@scope.project.tags_colors).sort()
|
||||
@scope.projectTags = _.map(@.tagNames, (tagName) => {name: tagName, color: @scope.project.tags_colors[tagName]})
|
||||
|
||||
updateTag: (tag) ->
|
||||
tags_colors = angular.copy(@scope.project.tags_colors)
|
||||
tags_colors[tag.name] = tag.color
|
||||
@scope.project.tags_colors = tags_colors
|
||||
return @repo.save(@scope.project)
|
||||
|
||||
module.controller("ProjectTagsController", ProjectTagsController)
|
||||
|
||||
|
||||
#############################################################################
|
||||
## Tags Directive
|
||||
#############################################################################
|
||||
|
||||
ProjectTagDirective = () ->
|
||||
link = ($scope, $el, $attrs) ->
|
||||
$el.color = $scope.tag.color
|
||||
$ctrl = $el.controller()
|
||||
|
||||
$scope.$on "$destroy", ->
|
||||
$el.off()
|
||||
|
||||
$scope.$watch "tag.color", (newColor) =>
|
||||
if $el.color != newColor
|
||||
promise = $ctrl.updateTag($scope.tag)
|
||||
promise.then null, (data) ->
|
||||
form.setErrors(data)
|
||||
|
||||
$el.color = newColor
|
||||
|
||||
return {link: link}
|
||||
|
||||
module.directive("tgProjectTag", [ProjectTagDirective])
|
||||
|
|
|
@ -99,6 +99,7 @@ urls = {
|
|||
"project-admin-project-values-severities": "/project/:project/admin/project-values/severities"
|
||||
"project-admin-project-values-types": "/project/:project/admin/project-values/types"
|
||||
"project-admin-project-values-custom-fields": "/project/:project/admin/project-values/custom-fields"
|
||||
"project-admin-project-values-tags": "/project/:project/admin/project-values/tags"
|
||||
|
||||
"project-admin-memberships": "/project/:project/admin/memberships"
|
||||
"project-admin-roles": "/project/:project/admin/roles"
|
||||
|
|
|
@ -557,6 +557,12 @@
|
|||
"ISSUE_TITLE": "Issues types",
|
||||
"ACTION_ADD": "Add new {{objName}}"
|
||||
},
|
||||
"PROJECT_VALUES_TAGS": {
|
||||
"TITLE": "Tags",
|
||||
"SUBTITLE": "View and edit the color of your user stories",
|
||||
"EMPTY": "Currently there are no tags",
|
||||
"EMPTY_SEARCH": "It looks like nothing was found with your search criteria"
|
||||
},
|
||||
"ROLES": {
|
||||
"PAGE_TITLE": "Roles - {{projectName}}",
|
||||
"WARNING_NO_ROLE": "Be careful, no role in your project will be able to estimate the point value for user stories",
|
||||
|
@ -682,7 +688,8 @@
|
|||
"PRIORITIES": "Priorities",
|
||||
"SEVERITIES": "Severities",
|
||||
"TYPES": "Types",
|
||||
"CUSTOM_FIELDS": "Custom fields"
|
||||
"CUSTOM_FIELDS": "Custom fields",
|
||||
"TAGS": "Tags"
|
||||
},
|
||||
"SUBMENU_PROJECT_PROFILE": {
|
||||
"TITLE": "Project Profile"
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
doctype html
|
||||
|
||||
div.wrapper(
|
||||
ng-controller="ProjectValuesSectionController",
|
||||
ng-init="sectionName='ADMIN.PROJECT_VALUES_TAGS.TITLE'"
|
||||
)
|
||||
|
||||
tg-project-menu
|
||||
|
||||
sidebar.menu-secondary.sidebar.settings-nav(tg-admin-navigation="project-values")
|
||||
include ../includes/modules/admin-menu
|
||||
|
||||
sidebar.menu-tertiary.sidebar(tg-admin-navigation="values-tags")
|
||||
include ../includes/modules/admin-submenu-project-values
|
||||
|
||||
section.main.admin-common.admin-attributes.colors-table
|
||||
include ../includes/components/mainTitle
|
||||
p.admin-subtitle(translate="ADMIN.PROJECT_VALUES_TAGS.SUBTITLE")
|
||||
|
||||
.admin-attributes-section(
|
||||
ng-controller="ProjectTagsController as ctrl"
|
||||
)
|
||||
.admin-attributes-section-wrapper-empty(
|
||||
ng-if="!projectTags.length"
|
||||
tg-loading="ctrl.loading"
|
||||
)
|
||||
p(translate="ADMIN.PROJECT_VALUES_TAGS.EMPTY")
|
||||
.admin-attributes-section-wrapper(
|
||||
ng-if="projectTags.length"
|
||||
)
|
||||
.table-header.table-tags-editor
|
||||
.row
|
||||
.color-column(translate="COMMON.FIELDS.COLOR")
|
||||
.color-name(translate="COMMON.FIELDS.NAME")
|
||||
.color-filter
|
||||
input.e2e-tags-filter(
|
||||
type="text"
|
||||
name="name"
|
||||
ng-model="tagsFilter.name"
|
||||
ng-model-options="{debounce: 200}"
|
||||
)
|
||||
tg-svg(
|
||||
svg-icon="icon-search"
|
||||
)
|
||||
|
||||
p.admin-attributes-section-wrapper-empty(
|
||||
tg-loading="ctrl.loading"
|
||||
translate="ADMIN.PROJECT_VALUES_TAGS.EMPTY_SEARCH"
|
||||
ng-if="!(projectTags | filter:tagsFilter).length"
|
||||
)
|
||||
.table-main(
|
||||
ng-repeat="tag in projectTags | filter:tagsFilter"
|
||||
tg-bind-scope
|
||||
)
|
||||
form(
|
||||
tg-project-tag
|
||||
ng-model="tag"
|
||||
)
|
||||
.row.edition.no-draggable
|
||||
.color-column(
|
||||
tg-color-selection
|
||||
ng-model="tag"
|
||||
)
|
||||
.current-color(ng-style="{background: tag.color}")
|
||||
include ../includes/components/select-color
|
||||
|
||||
.color-name {{ tag.name }}
|
|
@ -24,3 +24,7 @@ section.admin-submenu
|
|||
li#adminmenu-values-custom-fields
|
||||
a(href="", tg-nav="project-admin-project-values-custom-fields:project=project.slug")
|
||||
span.title(translate="ADMIN.SUBMENU_PROJECT_VALUES.CUSTOM_FIELDS")
|
||||
|
||||
li#adminmenu-values-tags
|
||||
a(href="", tg-nav="project-admin-project-values-tags:project=project.slug")
|
||||
span.title(translate="ADMIN.SUBMENU_PROJECT_VALUES.TAGS")
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
.table-tags-editor {
|
||||
input[type="text"] {
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
border-bottom: 1px solid transparent;
|
||||
box-shadow: none;
|
||||
transition: border-bottom .2s linear;
|
||||
&:focus {
|
||||
border-bottom: 1px solid $gray;
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
.color-filter {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
padding: 0 10px;
|
||||
position: relative;
|
||||
}
|
||||
}
|
|
@ -11,6 +11,15 @@
|
|||
width: 100%;
|
||||
}
|
||||
}
|
||||
.admin-attributes-section-wrapper-empty {
|
||||
color: $gray-light;
|
||||
padding: 10vh 0 0;
|
||||
text-align: center;
|
||||
}
|
||||
.loading-spinner {
|
||||
max-height: 3rem;
|
||||
max-width: 3rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,32 +8,8 @@
|
|||
}
|
||||
.row {
|
||||
padding-left: 50px;
|
||||
}
|
||||
}
|
||||
.table-main {
|
||||
.row:hover {
|
||||
background: lighten($primary, 60%);
|
||||
cursor: move;
|
||||
transition: background .2s ease-in;
|
||||
.icon {
|
||||
opacity: 1;
|
||||
transition: opacity .2s ease-in;
|
||||
}
|
||||
.options-column {
|
||||
opacity: 1;
|
||||
transition: opacity .3s linear;
|
||||
}
|
||||
}
|
||||
.options-column {
|
||||
a {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
form {
|
||||
&:last-child {
|
||||
.row {
|
||||
border: 0;
|
||||
&:hover {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -58,6 +34,25 @@
|
|||
&.hidden {
|
||||
display: none;
|
||||
}
|
||||
&:hover {
|
||||
background: lighten($primary, 60%);
|
||||
cursor: move;
|
||||
transition: background .2s ease-in;
|
||||
.icon {
|
||||
opacity: 1;
|
||||
transition: opacity .2s ease-in;
|
||||
}
|
||||
.options-column {
|
||||
opacity: 1;
|
||||
transition: opacity .3s linear;
|
||||
}
|
||||
}
|
||||
&.no-draggable {
|
||||
padding-left: 50px;
|
||||
&:hover {
|
||||
cursor: auto;
|
||||
}
|
||||
}
|
||||
.color-column {
|
||||
flex-basis: 60px;
|
||||
flex-grow: 1;
|
||||
|
@ -70,10 +65,13 @@
|
|||
.status-wip-limit {
|
||||
flex-basis: 100px;
|
||||
flex-grow: 1;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.status-name {
|
||||
.status-name,
|
||||
.color-name {
|
||||
flex-basis: 150px;
|
||||
flex-grow: 6;
|
||||
flex-grow: 1;
|
||||
flex-shrink: 0;
|
||||
padding: 0 10px;
|
||||
position: relative;
|
||||
span {
|
||||
|
@ -81,6 +79,9 @@
|
|||
display: block;
|
||||
}
|
||||
}
|
||||
.color-name {
|
||||
flex-basis: 100px;
|
||||
}
|
||||
.status-slug {
|
||||
flex-basis: 150px;
|
||||
flex-grow: 6;
|
||||
|
@ -105,7 +106,21 @@
|
|||
padding: 0 0 0 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
}
|
||||
.options-column {
|
||||
a {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
form {
|
||||
&:last-child {
|
||||
.row {
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.row-edit {
|
||||
.options-column {
|
||||
opacity: 1;
|
||||
|
@ -132,6 +147,11 @@
|
|||
fill: $primary;
|
||||
opacity: 1;
|
||||
}
|
||||
&.icon-search {
|
||||
cursor: none;
|
||||
fill: $primary;
|
||||
opacity: 1;
|
||||
}
|
||||
&.icon-drag {
|
||||
cursor: move;
|
||||
}
|
||||
|
|
|
@ -34,6 +34,22 @@ helper.getSection = function(item) {
|
|||
};
|
||||
};
|
||||
|
||||
helper.getTagsSection = function(item) {
|
||||
let section = $$('.admin-attributes-section').get(item);
|
||||
|
||||
return {
|
||||
el: section,
|
||||
rows: function() {
|
||||
return section.$$('.table-main > div');
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
helper.getTagsFilter = function() {
|
||||
return $('.table-header .e2e-tags-filter');
|
||||
}
|
||||
|
||||
helper.getStatusNames = function(section) {
|
||||
return section.$$('.status-name span').getText();
|
||||
};
|
||||
|
@ -94,6 +110,14 @@ helper.getGenericForm = function(form) {
|
|||
return form.$('.status-name input');
|
||||
};
|
||||
|
||||
obj.colorBox = function() {
|
||||
return form.$('.edition .current-color');
|
||||
}
|
||||
|
||||
obj.colorText = function() {
|
||||
return form.$('.select-color input');
|
||||
}
|
||||
|
||||
return obj;
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
var utils = require('../../../utils');
|
||||
|
||||
var adminAttributesHelper = require('../../../helpers').adminAttributes;
|
||||
|
||||
var chai = require('chai');
|
||||
var chaiAsPromised = require('chai-as-promised');
|
||||
|
||||
chai.use(chaiAsPromised);
|
||||
var expect = chai.expect;
|
||||
|
||||
describe.only('attributes - tags', function() {
|
||||
before(async function(){
|
||||
browser.get(browser.params.glob.host + 'project/project-0/admin/project-values/tags');
|
||||
|
||||
await adminAttributesHelper.waitLoad();
|
||||
|
||||
utils.common.takeScreenshot('attributes', 'tags');
|
||||
});
|
||||
|
||||
it('edit', async function() {
|
||||
let section = adminAttributesHelper.getTagsSection(0);
|
||||
let rows = section.rows();
|
||||
let row = rows.get(0);
|
||||
|
||||
let form = adminAttributesHelper.getGenericForm(row.$('form'));
|
||||
|
||||
var colorBox = form.colorBox();
|
||||
await colorBox.click();
|
||||
await form.colorText().clear();
|
||||
await form.colorText().sendKeys('#000000');
|
||||
await browser.actions().sendKeys(protractor.Key.ENTER).perform();
|
||||
|
||||
await browser.waitForAngular();
|
||||
|
||||
section = adminAttributesHelper.getTagsSection(0);
|
||||
rows = section.rows();
|
||||
row = rows.get(0);
|
||||
let backgroundColor = await row.$$('.edition .current-color').get(0).getCssValue('background-color');
|
||||
expect(backgroundColor).to.be.equal('rgba(0, 0, 0, 1)');
|
||||
utils.common.takeScreenshot('attributes', 'tag edited is black');
|
||||
});
|
||||
|
||||
it('filter', async function() {
|
||||
let tagsFilter = adminAttributesHelper.getTagsFilter();
|
||||
await tagsFilter.clear();
|
||||
await tagsFilter.sendKeys('ad');
|
||||
await browser.waitForAngular();
|
||||
|
||||
let section = adminAttributesHelper.getTagsSection(0);
|
||||
let rows = section.rows();
|
||||
let count = await rows.count();
|
||||
expect(count).to.be.equal(2);
|
||||
});
|
||||
|
||||
|
||||
});
|
Loading…
Reference in New Issue