Update, Delete and Order Custom Attributes

stable
David Barragán Merino 2015-02-20 16:25:12 +01:00
parent 4d2fe26be9
commit 408c6f95cc
14 changed files with 365 additions and 45 deletions

View File

@ -39,17 +39,23 @@ taiga.sessionId = taiga.generateUniqueSessionIdentifier()
configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEventsProvider, tgLoaderProvider) ->
$routeProvider.when("/",
{templateUrl: "project/projects.html", resolve: {loader: tgLoaderProvider.add()}})
$routeProvider.when("/project/:pslug/",
{templateUrl: "project/project.html"})
$routeProvider.when("/project/:pslug/backlog",
{templateUrl: "backlog/backlog.html", resolve: {loader: tgLoaderProvider.add()}})
$routeProvider.when("/project/:pslug/taskboard/:sslug",
{templateUrl: "taskboard/taskboard.html", resolve: {loader: tgLoaderProvider.add()}})
$routeProvider.when("/project/:pslug/search",
{templateUrl: "search/search.html", reloadOnSearch: false})
$routeProvider.when("/project/:pslug/backlog",
{templateUrl: "backlog/backlog.html", resolve: {loader: tgLoaderProvider.add()}})
$routeProvider.when("/project/:pslug/kanban",
{templateUrl: "kanban/kanban.html", resolve: {loader: tgLoaderProvider.add()}})
# Milestone
$routeProvider.when("/project/:pslug/taskboard/:sslug",
{templateUrl: "taskboard/taskboard.html", resolve: {loader: tgLoaderProvider.add()}})
# User stories
$routeProvider.when("/project/:pslug/us/:usref",
{templateUrl: "us/us-detail.html", resolve: {loader: tgLoaderProvider.add()}})
@ -74,7 +80,7 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven
$routeProvider.when("/project/:pslug/issue/:issueref",
{templateUrl: "issue/issues-detail.html", resolve: {loader: tgLoaderProvider.add()}})
# Admin
# Admin - Project Profile
$routeProvider.when("/project/:pslug/admin/project-profile/details",
{templateUrl: "admin/admin-project-profile.html"})
$routeProvider.when("/project/:pslug/admin/project-profile/default-values",
@ -83,6 +89,7 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven
{templateUrl: "admin/admin-project-modules.html"})
$routeProvider.when("/project/:pslug/admin/project-profile/export",
{templateUrl: "admin/admin-project-export.html"})
# Admin Project Values
$routeProvider.when("/project/:pslug/admin/project-values/us-status",
{templateUrl: "admin/admin-project-values-us-status.html"})
$routeProvider.when("/project/:pslug/admin/project-values/us-points",
@ -91,6 +98,8 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven
{templateUrl: "admin/admin-project-values-us-extras.html"})
$routeProvider.when("/project/:pslug/admin/project-values/task-status",
{templateUrl: "admin/admin-project-values-task-status.html"})
$routeProvider.when("/project/:pslug/admin/project-values/task-extras",
{templateUrl: "admin/admin-project-values-task-extras.html"})
$routeProvider.when("/project/:pslug/admin/project-values/issue-status",
{templateUrl: "admin/admin-project-values-issue-status.html"})
$routeProvider.when("/project/:pslug/admin/project-values/issue-types",
@ -99,10 +108,15 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven
{templateUrl: "admin/admin-project-values-issue-priorities.html"})
$routeProvider.when("/project/:pslug/admin/project-values/issue-severities",
{templateUrl: "admin/admin-project-values-issue-severities.html"})
$routeProvider.when("/project/:pslug/admin/project-values/issue-extras",
{templateUrl: "admin/admin-project-values-issue-extras.html"})
# Admin - Memberships
$routeProvider.when("/project/:pslug/admin/memberships",
{templateUrl: "admin/admin-memberships.html"})
# Admin - Roles
$routeProvider.when("/project/:pslug/admin/roles",
{templateUrl: "admin/admin-roles.html"})
# Admin - Third Parties
$routeProvider.when("/project/:pslug/admin/third-parties/webhooks",
{templateUrl: "admin/admin-third-parties-webhooks.html"})
$routeProvider.when("/project/:pslug/admin/third-parties/github",
@ -111,6 +125,7 @@ configure = ($routeProvider, $locationProvider, $httpProvider, $provide, $tgEven
{templateUrl: "admin/admin-third-parties-gitlab.html"})
$routeProvider.when("/project/:pslug/admin/third-parties/bitbucket",
{templateUrl: "admin/admin-third-parties-bitbucket.html"})
# Admin - Contrib Plugins
$routeProvider.when("/project/:pslug/admin/contrib/:plugin",
{templateUrl: "contrib/main.html"})

View File

@ -320,3 +320,197 @@ ColorSelectionDirective = () ->
}
module.directive("tgColorSelection", ColorSelectionDirective)
#############################################################################
## Custom Attributes Controller
#############################################################################
class ProjectCustomAttributesController extends mixOf(taiga.Controller, taiga.PageMixin)
@.$inject = [
"$scope",
"$rootScope",
"$tgRepo",
"$tgResources",
"$routeParams",
"$q",
"$tgLocation",
"$tgNavUrls",
"$appTitle",
]
constructor: (@scope, @rootscope, @repo, @rs, @params, @q, @location, @navUrls, @appTitle) ->
@scope.project = {}
promise = @.loadInitialData()
promise.then () =>
@appTitle.set("Project Custom Attributes - " + @scope.sectionName + " - " + @scope.project.name)
promise.then null, @.onInitialDataError.bind(@)
loadInitialData: =>
promise = @repo.resolve({pslug: @params.pslug}).then (data) =>
@scope.projectId = data.project
return data
return promise.then( => @q.all([
@.loadProject(),
@.loadCustomAttributes(),
]))
#########################
# Project
#########################
loadProject: =>
return @rs.projects.get(@scope.projectId).then (project) =>
@scope.project = project
@scope.$emit('project:loaded', project)
return project
#########################
# Custom Attribute
#########################
loadCustomAttributes: =>
return @rs.customAttributes[@scope.type].list(@scope.projectId).then (customAttributes) =>
@scope.customAttributes = customAttributes
@scope.maxOrder = _.max(customAttributes, "order").order
return customAttributes
moveCustomAttributes: (attrModel, newIndex) =>
customAttributes = @scope.customAttributes
r = customAttributes.indexOf(attrModel)
customAttributes.splice(r, 1)
customAttributes.splice(newIndex, 0, attrModel)
_.each customAttributes, (val, idx) ->
val.order = idx
@repo.saveAll(customAttributes)
deleteCustomAttribute: (attrModel) =>
return @repo.remove(attrModel)
saveCustomAttribute: (attrModel) =>
return @repo.save(attrModel)
module.controller("ProjectCustomAttributesController", ProjectCustomAttributesController)
#############################################################################
## Custom Attributes Directive
#############################################################################
ProjectCustomAttributesDirective = ($log, $confirm, animationFrame) ->
link = ($scope, $el, $attrs) ->
$ctrl = $el.controller()
$scope.$on "$destroy", ->
$el.off()
##################################
# Drag & Drop
##################################
sortableEl = $el.find(".js-sortable")
sortableEl.sortable({
handle: ".js-view-custom-field",
dropOnEmpty: true
revert: 400
axis: "y"
})
sortableEl.on "sortstop", (event, ui) ->
itemEl = ui.item
itemAttr = itemEl.scope().attr
itemIndex = itemEl.index()
$ctrl.moveCustomAttributes(itemAttr, itemIndex)
##################################
# New custom attribute
##################################
##################################
# Edit custom attribute
##################################
showEditForm = (formEl) ->
formEl.find(".js-view-custom-field").addClass("hidden")
formEl.find(".js-edit-custom-field").removeClass("hidden")
hideEditForm = (formEl) ->
formEl.find(".js-edit-custom-field").addClass("hidden")
formEl.find(".js-view-custom-field").removeClass("hidden")
revertChangesInCustomAttribute = (formEl) ->
$scope.$apply ->
formEl.scope().attr.revert()
update = (formEl) ->
onSucces = ->
$ctrl.loadCustomAttributes()
hideEditForm(formEl)
$confirm.notify("success")
onError = ->
$confirm.notify("error")
attr = formEl.scope().attr
$ctrl.saveCustomAttribute(attr).then(onSucces, onError)
$el.on "click", ".js-edit-custom-field-button", (event) ->
event.preventDefault()
target = angular.element(event.currentTarget)
formEl = target.closest("form")
showEditForm(formEl)
$el.on "click", ".js-cancel-edit-custom-field-button", (event) ->
event.preventDefault()
target = angular.element(event.currentTarget)
formEl = target.closest("form")
hideEditForm(formEl)
revertChangesInCustomAttribute(formEl)
$el.on "click", ".js-update-custom-field-button", (event) ->
event.preventDefault()
target = angular.element(event.currentTarget)
formEl = target.closest("form")
update(formEl)
##################################
# Delete custom attribute
##################################
deleteCustomAttribute = (formEl) ->
attr = formEl.scope().attr
title = "Delete custom attribute" # i18n
message = attr.name
$confirm.askOnDelete(title, message).then (finish) ->
onSucces = ->
$ctrl.loadCustomAttributes().finally ->
finish()
onError = ->
finish(false)
$confirm.notify("error", null, "We have not been able to delete '#{message}'.")
$ctrl.deleteCustomAttribute(attr).then(onSucces, onError)
$el.on "click", ".js-delete-custom-field-button", (event) ->
event.preventDefault()
target = angular.element(event.currentTarget)
formEl = target.closest("form")
deleteCustomAttribute(formEl)
return {link: link}
module.directive("tgProjectCustomAttributes", ["$log", "$tgConfirm", "animationFrame", ProjectCustomAttributesDirective])

View File

@ -88,10 +88,12 @@ urls = {
"project-admin-project-values-us-points": "/project/:project/admin/project-values/us-points"
"project-admin-project-values-us-extras": "/project/:project/admin/project-values/us-extras"
"project-admin-project-values-task-status": "/project/:project/admin/project-values/task-status"
"project-admin-project-values-task-extras": "/project/:project/admin/project-values/task-extras"
"project-admin-project-values-issue-status": "/project/:project/admin/project-values/issue-status"
"project-admin-project-values-issue-types": "/project/:project/admin/project-values/issue-types"
"project-admin-project-values-issue-priorities": "/project/:project/admin/project-values/issue-priorities"
"project-admin-project-values-issue-severities": "/project/:project/admin/project-values/issue-severities"
"project-admin-project-values-issue-extras": "/project/:project/admin/project-values/issue-extras"
"project-admin-memberships": "/project/:project/admin/memberships"
"project-admin-roles": "/project/:project/admin/roles"
"project-admin-third-parties-webhooks": "/project/:project/admin/third-parties/webhooks"

View File

@ -102,8 +102,13 @@ urls = {
"attachments/task": "/tasks/attachments"
"attachments/wiki_page": "/wiki/attachments"
# Custom Attributess
"custom-attributes/us": "/userstory-custom-attributes"
"custom-attributes/issue": "/issue-custom-attributes"
"custom-attributes/task": "/task-custom-attributes"
# Custom field values
"custom-attributes-values/userstory": "/userstories/custom-attributes-values"
"custom-attributes-values/us": "/userstories/custom-attributes-values"
"custom-attributes-values/issue": "/issues/custom-attributes-values"
"custom-attributes-values/task": "/tasks/custom-attributes-values"
@ -138,6 +143,8 @@ module.run([
"$log",
"$tgResources",
"$tgProjectsResourcesProvider",
"$tgCustomAttributesResourcesProvider",
"$tgCustomAttributesValuesResourcesProvider",
"$tgMembershipsResourcesProvider",
"$tgNotifyPoliciesResourcesProvider",
"$tgInvitationsResourcesProvider",
@ -150,7 +157,6 @@ module.run([
"$tgWikiResourcesProvider",
"$tgSearchResourcesProvider",
"$tgAttachmentsResourcesProvider",
"$tgCustomAttributesValuesResourcesProvider",
"$tgMdRenderResourcesProvider",
"$tgHistoryResourcesProvider",
"$tgKanbanResourcesProvider",

View File

@ -0,0 +1,48 @@
###
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
#
# 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: modules/resources/projects.coffee
###
taiga = @.taiga
sizeFormat = @.taiga.sizeFormat
resourceProvider = ($repo) ->
_list = (projectId, resource) ->
return $repo.queryMany(resource, {project: projectId})
service = {
us:{
list: (projectId) -> _list(projectId, "custom-attributes/us")
}
task:{
list: (projectId) -> _list(projectId, "custom-attributes/task")
}
issue: {
list: (projectId) -> _list(projectId, "custom-attributes/issue")
}
}
return (instance) ->
instance.customAttributes = service
module = angular.module("taigaResources")
module.factory("$tgCustomAttributesResourcesProvider", ["$tgRepo", resourceProvider])

View File

@ -0,0 +1,16 @@
div.wrapper(tg-project-custom-attributes, ng-controller="ProjectCustomAttributesController as ctrl",
ng-init="section='admin'; type='issue'; sectionName='Issue extra'")
sidebar.menu-secondary.sidebar(tg-admin-navigation="project-values")
include ../includes/modules/admin-menu
sidebar.menu-tertiary.sidebar(tg-admin-navigation="values-issue-extras")
include ../includes/modules/admin-submenu-project-values
section.main.admin-common
include ../includes/components/mainTitle
p.admin-subtitle Specify here issue custom fields. The new field will appear on your issue detail.
div.custom-field-options
a.button.button-green.js-add-custom-field(href="",title="Add a custom field in issues") Add custom field
include ../includes/modules/admin/admin-custom-attributes

View File

@ -0,0 +1,16 @@
div.wrapper(tg-project-custom-attributes, ng-controller="ProjectCustomAttributesController as ctrl",
ng-init="section='admin'; type='task'; sectionName='Task extra'")
sidebar.menu-secondary.sidebar(tg-admin-navigation="project-values")
include ../includes/modules/admin-menu
sidebar.menu-tertiary.sidebar(tg-admin-navigation="values-task-extras")
include ../includes/modules/admin-submenu-project-values
section.main.admin-common
include ../includes/components/mainTitle
p.admin-subtitle Specify here task custom fields. The new field will appear on your task detail.
div.custom-field-options
a.button.button-green.js-add-custom-field(href="",title="Add a custom field in tasks") Add custom field
include ../includes/modules/admin/admin-custom-attributes

View File

@ -1,6 +1,5 @@
div.wrapper(tg-project-values, ng-controller="ProjectValuesController as ctrl",
ng-init="section='admin'; resource='userstories'; type='points'; sectionName='Us points'",
type="points")
div.wrapper(tg-project-custom-attributes, ng-controller="ProjectCustomAttributesController as ctrl",
ng-init="section='admin'; type='us'; sectionName='US extra'")
sidebar.menu-secondary.sidebar(tg-admin-navigation="project-values")
include ../includes/modules/admin-menu
@ -12,9 +11,6 @@ div.wrapper(tg-project-values, ng-controller="ProjectValuesController as ctrl",
p.admin-subtitle Specify here user story custom fields. The new field will appear on your user story detail.
div.custom-field-options
a.button.button-green.add-custom-field(href="",title="Add a custom field in user stories") Add custom field
a.button.button-green.js-add-custom-field(href="",title="Add a custom field in user stories") Add custom field
include ../includes/modules/admin/admin-us-extras
div.lightbox.lightbox-generic-notion.notion-admin-project-values-us-points(id="notion-admin-project-values-us-points", tg-lb-notion)
include ../includes/modules/help-notions/lightbox-notion-admin-project-values-us-points
include ../includes/modules/admin/admin-custom-attributes

View File

@ -24,6 +24,11 @@ section.admin-submenu
span.title Task statuses
span.icon.icon-arrow-right
li#adminmenu-values-task-extras
a(href="", tg-nav="project-admin-project-values-task-extras:project=project.slug")
span.title Task extras
span.icon.icon-arrow-right
li#adminmenu-values-issue-status
a(href="", tg-nav="project-admin-project-values-issue-status:project=project.slug")
span.title Issue statuses
@ -43,3 +48,8 @@ section.admin-submenu
a(href="", tg-nav="project-admin-project-values-issue-severities:project=project.slug")
span.title Issue Severities
span.icon.icon-arrow-right
li#adminmenu-values-issue-extras
a(href="", tg-nav="project-admin-project-values-issue-extras:project=project.slug")
span.title Issue extras
span.icon.icon-arrow-right

View File

@ -0,0 +1,46 @@
section.custom-fields-table.basic-table
div.table-header
div.row
div.custom-name
span Name
div.custom-description
span Description
div.custom-options
div.table-body
div.js-sortable
form.js-form(ng-repeat="attr in customAttributes track by attr.id")
div.row.single-custom-field.js-view-custom-field
span.icon.icon-drag-v
div.custom-name
span {{ attr.name }}
div.custom-description
span {{ attr.description }}
div.custom-options
div.custom-options-wrapper
a.js-edit-custom-field-button.icon.icon-edit(href="", title="Edit Custom Field")
a.js-delete-custom-field-button.icon.icon-delete(href="", title="Delete Custom Field")
div.row.single-custom-field.js-edit-custom-field.hidden
fieldset.custom-name
input(type="text", placeholder="Set your custom field name", ng-model="attr.name",
data-required="true" data-maxlength="64")
fieldset.custom-description
input(type="text", placeholder="Set your custom field description", ng-model="attr.description")
fieldset.custom-options
div.custom-options-wrapper
a.js-update-custom-field-button.icon.icon-floppy(href="", title="Update Custom Field")
a.js-cancel-edit-custom-field-button.icon.icon-delete(href="", title="Cancel edition")
form.row.single-custom-field.js-new-custom-field.hidden(tg-new-custom-attribute)
fieldset.custom-name
input(type="text", placeholder="Set your custom field name", ng-model="newAttr.name",
data-required="true", data-maxlength="64")
fieldset.custom-description
input(type="text", placeholder="Set your custom field description", ng-model="newAttr.description")
fieldset.custom-options
div.custom-options-wrapper
a.js-save-custom-field-button.icon.icon-floppy(href="", title="Save Custom Field")
a.js-cancel-new-custom-field-button.icon.icon-delete(href="", title="Cancel creation")

View File

@ -1,29 +0,0 @@
section.custom-fields-table.basic-table
div.table-header
div.row
div.custom-name
span Name
div.custom-description
span Description
div.custom-options
div.table-body
div.row.single-custom-field
span.icon.icon-drag-v
div.custom-name
span Custom field name
div.custom-description
span Custom field looong Description
div.custom-options
div.custom-options-wrapper
a.edit-webhook.icon.icon-edit(href="", title="Edit Custom Field")
a.delete-webhook.icon.icon-delete(href="", title="Delete Custom Field")
form.row.single-custom-field
fieldset.custom-name
input(type="text", placeholder="Set your custom field name")
fieldset.custom-description
input(type="text", placeholder="Set your custom field description")
fieldset.custom-options
div.custom-options-wrapper
a.edit-webhook.icon.icon-floppy(href="", title="Save Custom Field")
a.delete-webhook.icon.icon-delete(href="", title="Delete Custom Field")

View File

@ -126,7 +126,7 @@ exports.files = function () {
'modules/admin/admin-membership-table',
'modules/admin/admin-project-profile',
'modules/admin/default-values',
'modules/admin/admin-custom-fields',
'modules/admin/admin-custom-attributes',
'modules/admin/project-values',
'modules/admin/third-parties',
'modules/admin/admin-third-parties-webhooks',